mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-27 13:45:36 -04:00
Merge branch 'develop' into heat-photon
This commit is contained in:
commit
c3e27cf24a
19 changed files with 887 additions and 690 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -16,7 +16,6 @@ particles = 1000
|
|||
time_step = 1*24*60*60 # s
|
||||
final_time = 5*24*60*60 # s
|
||||
time_steps = np.full(final_time // time_step, time_step)
|
||||
|
||||
chain_file = './chain_simple.xml'
|
||||
power = 174 # W/cm, for 2D simulations only (use W for 3D)
|
||||
|
||||
|
|
@ -100,7 +99,7 @@ geometry = openmc.Geometry(root)
|
|||
|
||||
# Compute cell areas
|
||||
area = {}
|
||||
area[fuel] = np.pi * fuel_or.coefficients['R'] ** 2
|
||||
area[fuel] = np.pi * fuel_or.coefficients['r'] ** 2
|
||||
|
||||
# Set materials volume for depletion. Set to an area for 2D simulations
|
||||
uo2.volume = area[fuel]
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ constexpr int32_t OP_UNION {std::numeric_limits<int32_t>::max() - 4};
|
|||
|
||||
class Cell;
|
||||
class Universe;
|
||||
class UniversePartitioner;
|
||||
|
||||
namespace model {
|
||||
extern std::vector<std::unique_ptr<Cell>> cells;
|
||||
|
|
@ -65,6 +66,8 @@ public:
|
|||
//! \brief Write universe information to an HDF5 group.
|
||||
//! \param group_id An HDF5 group id.
|
||||
void to_hdf5(hid_t group_id) const;
|
||||
|
||||
std::unique_ptr<UniversePartitioner> partitioner_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -193,6 +196,36 @@ public:
|
|||
};
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
//! Speeds up geometry searches by grouping cells in a search tree.
|
||||
//
|
||||
//! Currently this object only works with universes that are divided up by a
|
||||
//! bunch of z-planes. It could be generalized to other planes, cylinders,
|
||||
//! and spheres.
|
||||
//==============================================================================
|
||||
|
||||
class UniversePartitioner
|
||||
{
|
||||
public:
|
||||
explicit UniversePartitioner(const Universe& univ);
|
||||
|
||||
//! Return the list of cells that could contain the given coordinates.
|
||||
const std::vector<int32_t>& get_cells(Position r, Direction u) const;
|
||||
|
||||
private:
|
||||
//! A sorted vector of indices to surfaces that partition the universe
|
||||
std::vector<int32_t> surfs_;
|
||||
|
||||
//! Vectors listing the indices of the cells that lie within each partition
|
||||
//
|
||||
//! There are n+1 partitions with n surfaces. `partitions_.front()` gives the
|
||||
//! cells that lie on the negative side of `surfs_.front()`.
|
||||
//! `partitions_.back()` gives the cells that lie on the positive side of
|
||||
//! `surfs_.back()`. Otherwise, `partitions_[i]` gives cells sandwiched
|
||||
//! between `surfs_[i-1]` and `surfs_[i]`.
|
||||
std::vector<std::vector<int32_t>> partitions_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// Non-member functions
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -35,6 +35,14 @@ struct BoundaryInfo {
|
|||
std::array<int, 3> lattice_translation {}; //!< which way lattice indices will change
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Check two distances by coincidence tolerance
|
||||
//==============================================================================
|
||||
|
||||
inline bool coincident(double d1, double d2) {
|
||||
return std::abs(d1 - d2) < FP_COINCIDENT;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//! Check for overlapping cells at a particle's position.
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -180,7 +180,6 @@ public:
|
|||
|
||||
class SurfaceXPlane : public PeriodicSurface
|
||||
{
|
||||
double x0_;
|
||||
public:
|
||||
explicit SurfaceXPlane(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
|
|
@ -190,6 +189,8 @@ public:
|
|||
bool periodic_translate(const PeriodicSurface* other, Position& r,
|
||||
Direction& u) const;
|
||||
BoundingBox bounding_box() const;
|
||||
|
||||
double x0_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -200,7 +201,6 @@ public:
|
|||
|
||||
class SurfaceYPlane : public PeriodicSurface
|
||||
{
|
||||
double y0_;
|
||||
public:
|
||||
explicit SurfaceYPlane(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
|
|
@ -210,6 +210,8 @@ public:
|
|||
bool periodic_translate(const PeriodicSurface* other, Position& r,
|
||||
Direction& u) const;
|
||||
BoundingBox bounding_box() const;
|
||||
|
||||
double y0_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -220,7 +222,6 @@ public:
|
|||
|
||||
class SurfaceZPlane : public PeriodicSurface
|
||||
{
|
||||
double z0_;
|
||||
public:
|
||||
explicit SurfaceZPlane(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
|
|
@ -230,6 +231,8 @@ public:
|
|||
bool periodic_translate(const PeriodicSurface* other, Position& r,
|
||||
Direction& u) const;
|
||||
BoundingBox bounding_box() const;
|
||||
|
||||
double z0_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -240,7 +243,6 @@ public:
|
|||
|
||||
class SurfacePlane : public PeriodicSurface
|
||||
{
|
||||
double A_, B_, C_, D_;
|
||||
public:
|
||||
explicit SurfacePlane(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
|
|
@ -250,6 +252,8 @@ public:
|
|||
bool periodic_translate(const PeriodicSurface* other, Position& r,
|
||||
Direction& u) const;
|
||||
BoundingBox bounding_box() const;
|
||||
|
||||
double A_, B_, C_, D_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -261,13 +265,14 @@ public:
|
|||
|
||||
class SurfaceXCylinder : public CSGSurface
|
||||
{
|
||||
double y0_, z0_, radius_;
|
||||
public:
|
||||
explicit SurfaceXCylinder(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
double distance(Position r, Direction u, bool coincident) const;
|
||||
Direction normal(Position r) const;
|
||||
void to_hdf5_inner(hid_t group_id) const;
|
||||
|
||||
double y0_, z0_, radius_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -279,13 +284,14 @@ public:
|
|||
|
||||
class SurfaceYCylinder : public CSGSurface
|
||||
{
|
||||
double x0_, z0_, radius_;
|
||||
public:
|
||||
explicit SurfaceYCylinder(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
double distance(Position r, Direction u, bool coincident) const;
|
||||
Direction normal(Position r) const;
|
||||
void to_hdf5_inner(hid_t group_id) const;
|
||||
|
||||
double x0_, z0_, radius_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -297,13 +303,14 @@ public:
|
|||
|
||||
class SurfaceZCylinder : public CSGSurface
|
||||
{
|
||||
double x0_, y0_, radius_;
|
||||
public:
|
||||
explicit SurfaceZCylinder(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
double distance(Position r, Direction u, bool coincident) const;
|
||||
Direction normal(Position r) const;
|
||||
void to_hdf5_inner(hid_t group_id) const;
|
||||
|
||||
double x0_, y0_, radius_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -315,13 +322,14 @@ public:
|
|||
|
||||
class SurfaceSphere : public CSGSurface
|
||||
{
|
||||
double x0_, y0_, z0_, radius_;
|
||||
public:
|
||||
explicit SurfaceSphere(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
double distance(Position r, Direction u, bool coincident) const;
|
||||
Direction normal(Position r) const;
|
||||
void to_hdf5_inner(hid_t group_id) const;
|
||||
|
||||
double x0_, y0_, z0_, radius_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -333,13 +341,14 @@ public:
|
|||
|
||||
class SurfaceXCone : public CSGSurface
|
||||
{
|
||||
double x0_, y0_, z0_, radius_sq_;
|
||||
public:
|
||||
explicit SurfaceXCone(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
double distance(Position r, Direction u, bool coincident) const;
|
||||
Direction normal(Position r) const;
|
||||
void to_hdf5_inner(hid_t group_id) const;
|
||||
|
||||
double x0_, y0_, z0_, radius_sq_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -351,13 +360,14 @@ public:
|
|||
|
||||
class SurfaceYCone : public CSGSurface
|
||||
{
|
||||
double x0_, y0_, z0_, radius_sq_;
|
||||
public:
|
||||
explicit SurfaceYCone(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
double distance(Position r, Direction u, bool coincident) const;
|
||||
Direction normal(Position r) const;
|
||||
void to_hdf5_inner(hid_t group_id) const;
|
||||
|
||||
double x0_, y0_, z0_, radius_sq_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -369,13 +379,14 @@ public:
|
|||
|
||||
class SurfaceZCone : public CSGSurface
|
||||
{
|
||||
double x0_, y0_, z0_, radius_sq_;
|
||||
public:
|
||||
explicit SurfaceZCone(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
double distance(Position r, Direction u, bool coincident) const;
|
||||
Direction normal(Position r) const;
|
||||
void to_hdf5_inner(hid_t group_id) const;
|
||||
|
||||
double x0_, y0_, z0_, radius_sq_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -386,14 +397,15 @@ public:
|
|||
|
||||
class SurfaceQuadric : public CSGSurface
|
||||
{
|
||||
// Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0
|
||||
double A_, B_, C_, D_, E_, F_, G_, H_, J_, K_;
|
||||
public:
|
||||
explicit SurfaceQuadric(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
double distance(Position r, Direction u, bool coincident) const;
|
||||
Direction normal(Position r) const;
|
||||
void to_hdf5_inner(hid_t group_id) const;
|
||||
|
||||
// Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0
|
||||
double A_, B_, C_, D_, E_, F_, G_, H_, J_, K_;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -688,89 +688,6 @@ class IncidentPhoton(EqualityMixin):
|
|||
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group_or_filename):
|
||||
"""Generate photon reaction from an HDF5 group
|
||||
|
||||
Parameters
|
||||
----------
|
||||
group_or_filename : h5py.Group or str
|
||||
HDF5 group containing interaction data. If given as a string, it is
|
||||
assumed to be the filename for the HDF5 file, and the first group is
|
||||
used to read from.
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.IncidentPhoton
|
||||
Photon interaction data
|
||||
|
||||
"""
|
||||
if isinstance(group_or_filename, h5py.Group):
|
||||
group = group_or_filename
|
||||
else:
|
||||
h5file = h5py.File(str(group_or_filename), 'r')
|
||||
|
||||
# Make sure version matches
|
||||
if 'version' in h5file.attrs:
|
||||
major, minor = h5file.attrs['version']
|
||||
# For now all versions of HDF5 data can be read
|
||||
else:
|
||||
raise IOError(
|
||||
'HDF5 data does not indicate a version. Your installation '
|
||||
'of the OpenMC Python API expects version {}.x data.'
|
||||
.format(HDF5_VERSION_MAJOR))
|
||||
|
||||
group = list(h5file.values())[0]
|
||||
|
||||
Z = group.attrs['Z']
|
||||
data = cls(Z)
|
||||
|
||||
# Read energy grid
|
||||
energy= group['energy'].value
|
||||
|
||||
# Read cross section data
|
||||
for mt, (name, key) in _REACTION_NAME.items():
|
||||
if key in group:
|
||||
rgroup = group[key]
|
||||
elif key in group['subshells']:
|
||||
rgroup = group['subshells'][key]
|
||||
else:
|
||||
continue
|
||||
|
||||
data.reactions[mt] = PhotonReaction.from_hdf5(rgroup, mt, energy)
|
||||
|
||||
# Check for necessary reactions
|
||||
for mt in [502, 504, 522]:
|
||||
assert mt in data, "reaction {} not found".format(mt)
|
||||
|
||||
# Read atomic relaxation
|
||||
data.atomic_relaxation = AtomicRelaxation.from_hdf5(group['subshells'])
|
||||
|
||||
# Read Compton profiles
|
||||
if 'compton_profiles' in group:
|
||||
rgroup = group['compton_profiles']
|
||||
profile = data.compton_profiles
|
||||
profile['num_electrons'] = rgroup['num_electrons'].value
|
||||
profile['binding_energy'] = rgroup['binding_energy'].value
|
||||
|
||||
# Get electron momentum values
|
||||
pz = rgroup['pz'].value
|
||||
J = rgroup['J'].value
|
||||
if pz.size != J.shape[1]:
|
||||
raise ValueError("'J' array shape is not consistent with the "
|
||||
"'pz' array shape")
|
||||
profile['J'] = [Tabulated1D(pz, Jk) for Jk in J]
|
||||
|
||||
# Read bremsstrahlung
|
||||
if 'bremsstrahlung' in group:
|
||||
rgroup = group['bremsstrahlung']
|
||||
data.bremsstrahlung['I'] = rgroup.attrs['I']
|
||||
for key in ('dcs', 'electron_energy', 'ionization_energy',
|
||||
'num_electrons', 'photon_energy'):
|
||||
data.bremsstrahlung[key] = rgroup[key].value
|
||||
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group_or_filename):
|
||||
"""Generate photon reaction from an HDF5 group
|
||||
|
|
@ -889,7 +806,7 @@ class IncidentPhoton(EqualityMixin):
|
|||
designators = []
|
||||
for mt, rx in self.reactions.items():
|
||||
name, key = _REACTION_NAME[mt]
|
||||
if mt in [502, 504, 515, 517, 522, 525]:
|
||||
if mt in (502, 504, 515, 517, 522, 525):
|
||||
sub_group = group.create_group(key)
|
||||
elif mt >= 534 and mt <= 572:
|
||||
# Subshell
|
||||
|
|
|
|||
|
|
@ -3926,7 +3926,7 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
# to match scattering matrix shape for tally arithmetic
|
||||
energy_filter = copy.deepcopy(energy_filter)
|
||||
scatter_p1 = \
|
||||
scatter_p1.diagonalize_filter(energy_filter)
|
||||
scatter_p1.diagonalize_filter(energy_filter, 1)
|
||||
|
||||
self._rxn_rate_tally = scatter_p0 - scatter_p1
|
||||
|
||||
|
|
@ -4022,7 +4022,7 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
# to match scattering matrix shape for tally arithmetic
|
||||
energy_filter = flux.find_filter(openmc.EnergyFilter)
|
||||
energy_filter = copy.deepcopy(energy_filter)
|
||||
scatter_p1 = scatter_p1.diagonalize_filter(energy_filter)
|
||||
scatter_p1 = scatter_p1.diagonalize_filter(energy_filter, 1)
|
||||
|
||||
# Compute the trasnport correction term
|
||||
correction = scatter_p1 / flux
|
||||
|
|
|
|||
|
|
@ -2964,7 +2964,7 @@ class Tally(IDManagerMixin):
|
|||
tally_avg.sparse = self.sparse
|
||||
return tally_avg
|
||||
|
||||
def diagonalize_filter(self, new_filter):
|
||||
def diagonalize_filter(self, new_filter, filter_position=-1):
|
||||
"""Diagonalize the tally data array along a new axis of filter bins.
|
||||
|
||||
This is a helper method for the tally arithmetic methods. This method
|
||||
|
|
@ -2979,6 +2979,9 @@ class Tally(IDManagerMixin):
|
|||
----------
|
||||
new_filter : Filter
|
||||
The filter along which to diagonalize the data in the new
|
||||
filter_position : int
|
||||
Where to place the new filter in the Tally.filters list. Defaults
|
||||
to last position.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -2988,6 +2991,7 @@ class Tally(IDManagerMixin):
|
|||
"""
|
||||
|
||||
cv.check_type('new_filter', new_filter, _FILTER_CLASSES)
|
||||
cv.check_type('filter_position', filter_position, Integral)
|
||||
|
||||
if new_filter in self.filters:
|
||||
msg = 'Unable to diagonalize Tally ID="{0}" which already ' \
|
||||
|
|
@ -2996,7 +3000,7 @@ class Tally(IDManagerMixin):
|
|||
|
||||
# Add the new filter to a copy of this Tally
|
||||
new_tally = copy.deepcopy(self)
|
||||
new_tally.filters.append(new_filter)
|
||||
new_tally.filters.insert(filter_position, new_filter)
|
||||
|
||||
# Determine "base" indices along the new "diagonal", and the factor
|
||||
# by which the "base" indices should be repeated to account for all
|
||||
|
|
|
|||
|
|
@ -216,24 +216,40 @@ class MeshPlotter(tk.Frame):
|
|||
index = self.filterBoxes[f.short_name].current()
|
||||
spec_list.append((type(f), (index,)))
|
||||
|
||||
dims = (self.nx, self.ny, self.nz)
|
||||
|
||||
text = self.basisBox.get()
|
||||
if text == 'xy':
|
||||
dims = (self.nx, self.ny)
|
||||
h_ind = 0
|
||||
v_ind = 1
|
||||
elif text == 'yz':
|
||||
dims = (self.ny, self.nz)
|
||||
h_ind = 1
|
||||
v_ind = 2
|
||||
else:
|
||||
dims = (self.nx, self.nz)
|
||||
h_ind = 0
|
||||
v_ind = 2
|
||||
|
||||
axial_ind = 3 - (h_ind + v_ind)
|
||||
dims = (dims[h_ind], dims[v_ind])
|
||||
|
||||
mesh_dim = len(self.mesh.dimension)
|
||||
if mesh_dim == 3:
|
||||
mesh_indices = [0,0,0]
|
||||
else:
|
||||
mesh_indices = [0,0]
|
||||
|
||||
matrix = np.zeros(dims)
|
||||
for i in range(dims[0]):
|
||||
for j in range(dims[1]):
|
||||
if text == 'xy':
|
||||
meshtuple = (i + 1, j + 1, axial_level)
|
||||
elif text == 'yz':
|
||||
meshtuple = (axial_level, i + 1, j + 1)
|
||||
if mesh_dim == 3:
|
||||
mesh_indices[h_ind] = i + 1
|
||||
mesh_indices[v_ind] = j + 1
|
||||
mesh_indices[axial_ind] = axial_level
|
||||
else:
|
||||
meshtuple = (i + 1, axial_level, j + 1)
|
||||
mesh_indices[0] = i + 1
|
||||
mesh_indices[1] = j + 1
|
||||
filters, filter_bins = zip(*spec_list + [
|
||||
(type(mesh_filter), (meshtuple,))])
|
||||
(type(mesh_filter), (tuple(mesh_indices),))])
|
||||
mean = selectedTally.get_values(
|
||||
[self.scoreBox.get()], filters, filter_bins)
|
||||
stdev = selectedTally.get_values(
|
||||
|
|
|
|||
137
src/cell.cpp
137
src/cell.cpp
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "openmc/capi.h"
|
||||
|
|
@ -647,6 +648,142 @@ void DAGCell::to_hdf5(hid_t group_id) const { return; }
|
|||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
// UniversePartitioner implementation
|
||||
//==============================================================================
|
||||
|
||||
UniversePartitioner::UniversePartitioner(const Universe& univ)
|
||||
{
|
||||
// Define an ordered set of surface indices that point to z-planes. Use a
|
||||
// functor to to order the set by the z0_ values of the corresponding planes.
|
||||
struct compare_surfs {
|
||||
bool operator()(const int32_t& i_surf, const int32_t& j_surf) const
|
||||
{
|
||||
const auto* surf = model::surfaces[i_surf].get();
|
||||
const auto* zplane = dynamic_cast<const SurfaceZPlane*>(surf);
|
||||
double zi = zplane->z0_;
|
||||
surf = model::surfaces[j_surf].get();
|
||||
zplane = dynamic_cast<const SurfaceZPlane*>(surf);
|
||||
double zj = zplane->z0_;
|
||||
return zi < zj;
|
||||
}
|
||||
};
|
||||
std::set<int32_t, compare_surfs> surf_set;
|
||||
|
||||
// Find all of the z-planes in this universe. A set is used here for the
|
||||
// O(log(n)) insertions that will ensure entries are not repeated.
|
||||
for (auto i_cell : univ.cells_) {
|
||||
for (auto token : model::cells[i_cell]->rpn_) {
|
||||
if (token < OP_UNION) {
|
||||
auto i_surf = std::abs(token) - 1;
|
||||
const auto* surf = model::surfaces[i_surf].get();
|
||||
if (const auto* zplane = dynamic_cast<const SurfaceZPlane*>(surf))
|
||||
surf_set.insert(i_surf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate the surfs_ vector from the ordered set.
|
||||
surfs_.insert(surfs_.begin(), surf_set.begin(), surf_set.end());
|
||||
|
||||
// Populate the partition lists.
|
||||
partitions_.resize(surfs_.size() + 1);
|
||||
for (auto i_cell : univ.cells_) {
|
||||
// Find the tokens for bounding z-planes.
|
||||
int32_t lower_token = 0, upper_token = 0;
|
||||
double min_z, max_z;
|
||||
for (auto token : model::cells[i_cell]->rpn_) {
|
||||
if (token < OP_UNION) {
|
||||
const auto* surf = model::surfaces[std::abs(token) - 1].get();
|
||||
if (const auto* zplane = dynamic_cast<const SurfaceZPlane*>(surf)) {
|
||||
if (lower_token == 0 || zplane->z0_ < min_z) {
|
||||
lower_token = token;
|
||||
min_z = zplane->z0_;
|
||||
}
|
||||
if (upper_token == 0 || zplane->z0_ > max_z) {
|
||||
upper_token = token;
|
||||
max_z = zplane->z0_;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no bounding z-planes, add this cell to all partitions.
|
||||
if (lower_token == 0) {
|
||||
for (auto& p : partitions_) p.push_back(i_cell);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the first partition this cell lies in. If the lower_token indicates
|
||||
// a negative halfspace, then the cell is unbounded in the lower direction
|
||||
// and it lies in the first partition onward. Otherwise, it is bounded by
|
||||
// the positive halfspace given by the lower_token.
|
||||
int first_partition = 0;
|
||||
if (lower_token > 0) {
|
||||
for (int i = 0; i < surfs_.size(); ++i) {
|
||||
if (lower_token == surfs_[i] + 1) {
|
||||
first_partition = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the last partition this cell lies in. The logic is analogous to the
|
||||
// logic for first_partition.
|
||||
int last_partition = surfs_.size();
|
||||
if (upper_token < 0) {
|
||||
for (int i = first_partition; i < surfs_.size(); ++i) {
|
||||
if (upper_token == -(surfs_[i] + 1)) {
|
||||
last_partition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the cell to all relevant partitions.
|
||||
for (int i = first_partition; i <= last_partition; ++i) {
|
||||
partitions_[i].push_back(i_cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<int32_t>&
|
||||
UniversePartitioner::get_cells(Position r, Direction u) const
|
||||
{
|
||||
// Perform a binary search for the partition containing the given coordinates.
|
||||
int left = 0;
|
||||
int middle = (surfs_.size() - 1) / 2;
|
||||
int right = surfs_.size() - 1;
|
||||
while (true) {
|
||||
// Check the sense of the coordinates for the current surface.
|
||||
const auto& surf = *model::surfaces[surfs_[middle]];
|
||||
if (surf.sense(r, u)) {
|
||||
// The coordinates lie in the positive halfspace. Recurse if there are
|
||||
// more surfaces to check. Otherwise, return the cells on the positive
|
||||
// side of this surface.
|
||||
int right_leaf = right - (right - middle) / 2;
|
||||
if (right_leaf != middle) {
|
||||
left = middle + 1;
|
||||
middle = right_leaf;
|
||||
} else {
|
||||
return partitions_[middle+1];
|
||||
}
|
||||
|
||||
} else {
|
||||
// The coordinates lie in the negative halfspace. Recurse if there are
|
||||
// more surfaces to check. Otherwise, return the cells on the negative
|
||||
// side of this surface.
|
||||
int left_leaf = left + (middle - left) / 2;
|
||||
if (left_leaf != middle) {
|
||||
right = middle-1;
|
||||
middle = left_leaf;
|
||||
} else {
|
||||
return partitions_[middle];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Non-method functions
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -89,7 +89,12 @@ find_cell_inner(Particle* p, const NeighborList* neighbor_list)
|
|||
|
||||
} else {
|
||||
int i_universe = p->coord_[p->n_coord_-1].universe;
|
||||
const auto& cells {model::universes[i_universe]->cells_};
|
||||
const auto& univ {*model::universes[i_universe]};
|
||||
const auto& cells {
|
||||
!univ.partitioner_
|
||||
? model::universes[i_universe]->cells_
|
||||
: univ.partitioner_->get_cells(p->r_local(), p->u_local())
|
||||
};
|
||||
for (auto it = cells.cbegin(); it != cells.cend(); it++) {
|
||||
i_cell = *it;
|
||||
|
||||
|
|
@ -396,7 +401,7 @@ BoundaryInfo distance_to_boundary(Particle* p)
|
|||
// a higher level then we need to make sure that the higher level boundary
|
||||
// is selected. This logic must consider floating point precision.
|
||||
double& d = info.distance;
|
||||
if (d_surf < d_lat) {
|
||||
if (d_surf < d_lat - FP_COINCIDENT) {
|
||||
if (d == INFINITY || (d - d_surf)/d >= FP_REL_PRECISION) {
|
||||
d = d_surf;
|
||||
|
||||
|
|
|
|||
|
|
@ -120,6 +120,40 @@ adjust_indices()
|
|||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//! Partition some universes with many z-planes for faster find_cell searches.
|
||||
|
||||
void
|
||||
partition_universes()
|
||||
{
|
||||
// Iterate over universes with more than 10 cells. (Fewer than 10 is likely
|
||||
// not worth partitioning.)
|
||||
for (const auto& univ : model::universes) {
|
||||
if (univ->cells_.size() > 10) {
|
||||
// Collect the set of surfaces in this universe.
|
||||
std::unordered_set<int32_t> surf_inds;
|
||||
for (auto i_cell : univ->cells_) {
|
||||
for (auto token : model::cells[i_cell]->rpn_) {
|
||||
if (token < OP_UNION) surf_inds.insert(std::abs(token) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Partition the universe if there are more than 5 z-planes. (Fewer than
|
||||
// 5 is likely not worth it.)
|
||||
int n_zplanes = 0;
|
||||
for (auto i_surf : surf_inds) {
|
||||
if (dynamic_cast<const SurfaceZPlane*>(model::surfaces[i_surf].get())) {
|
||||
++n_zplanes;
|
||||
if (n_zplanes > 5) {
|
||||
univ->partitioner_ = std::make_unique<UniversePartitioner>(*univ);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
|
|
@ -210,6 +244,7 @@ void finalize_geometry(std::vector<std::vector<double>>& nuc_temps,
|
|||
// Perform some final operations to set up the geometry
|
||||
adjust_indices();
|
||||
count_cell_instances(model::root_universe);
|
||||
partition_universes();
|
||||
|
||||
// Assign temperatures to cells that don't have temperatures already assigned
|
||||
assign_temperatures();
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "openmc/cell.h"
|
||||
#include "openmc/error.h"
|
||||
#include "openmc/geometry.h"
|
||||
#include "openmc/geometry_aux.h"
|
||||
#include "openmc/hdf5_interface.h"
|
||||
#include "openmc/string_utils.h"
|
||||
|
|
@ -291,7 +292,7 @@ RectLattice::get_indices(Position r, Direction u) const
|
|||
double ix_ {(r.x - lower_left_.x) / pitch_.x};
|
||||
long ix_close {std::lround(ix_)};
|
||||
int ix;
|
||||
if (std::abs(ix_ - ix_close) < FP_COINCIDENT) {
|
||||
if (coincident(ix_, ix_close)) {
|
||||
ix = (u.x > 0) ? ix_close : ix_close - 1;
|
||||
} else {
|
||||
ix = std::floor(ix_);
|
||||
|
|
@ -301,7 +302,7 @@ RectLattice::get_indices(Position r, Direction u) const
|
|||
double iy_ {(r.y - lower_left_.y) / pitch_.y};
|
||||
long iy_close {std::lround(iy_)};
|
||||
int iy;
|
||||
if (std::abs(iy_ - iy_close) < FP_COINCIDENT) {
|
||||
if (coincident(iy_, iy_close)) {
|
||||
iy = (u.y > 0) ? iy_close : iy_close - 1;
|
||||
} else {
|
||||
iy = std::floor(iy_);
|
||||
|
|
@ -312,7 +313,7 @@ RectLattice::get_indices(Position r, Direction u) const
|
|||
if (is_3d_) {
|
||||
double iz_ {(r.z - lower_left_.z) / pitch_.z};
|
||||
long iz_close {std::lround(iz_)};
|
||||
if (std::abs(iz_ - iz_close) < FP_COINCIDENT) {
|
||||
if (coincident(iz_, iz_close)) {
|
||||
iz = (u.z > 0) ? iz_close : iz_close - 1;
|
||||
} else {
|
||||
iz = std::floor(iz_);
|
||||
|
|
@ -710,69 +711,84 @@ const
|
|||
std::array<int, 3>
|
||||
HexLattice::get_indices(Position r, Direction u) const
|
||||
{
|
||||
// The implementation for HexLattice currently doesn't use direction
|
||||
// information. As a result, we move the position slightly forward to
|
||||
// determine what lattice index the particle is most likely to be in.
|
||||
r += TINY_BIT * u;
|
||||
|
||||
// Offset the xyz by the lattice center.
|
||||
Position r_o {r.x - center_.x, r.y - center_.y, r.z};
|
||||
if (is_3d_) {r_o.z -= center_.z;}
|
||||
|
||||
// Index the z direction.
|
||||
std::array<int, 3> out;
|
||||
// Index the z direction, accounting for coincidence
|
||||
int iz = 0;
|
||||
if (is_3d_) {
|
||||
out[2] = std::floor(r_o.z / pitch_[1] + 0.5 * n_axial_);
|
||||
} else {
|
||||
out[2] = 0;
|
||||
double iz_ {r_o.z / pitch_[1] + 0.5 * n_axial_};
|
||||
long iz_close {std::lround(iz_)};
|
||||
if (coincident(iz_, iz_close)) {
|
||||
iz = (u.z > 0) ? iz_close : iz_close - 1;
|
||||
} else {
|
||||
iz = std::floor(iz_);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert coordinates into skewed bases. The (x, alpha) basis is used to
|
||||
// find the index of the global coordinates to within 4 cells.
|
||||
double alpha = r_o.y - r_o.x / std::sqrt(3.0);
|
||||
out[0] = std::floor(r_o.x / (0.5*std::sqrt(3.0) * pitch_[0]));
|
||||
out[1] = std::floor(alpha / pitch_[0]);
|
||||
int ix = std::floor(r_o.x / (0.5*std::sqrt(3.0) * pitch_[0]));
|
||||
int ia = std::floor(alpha / pitch_[0]);
|
||||
|
||||
// Add offset to indices (the center cell is (i_x, i_alpha) = (0, 0) but
|
||||
// the array is offset so that the indices never go below 0).
|
||||
out[0] += n_rings_-1;
|
||||
out[1] += n_rings_-1;
|
||||
ix += n_rings_-1;
|
||||
ia += n_rings_-1;
|
||||
|
||||
// Calculate the (squared) distance between the particle and the centers of
|
||||
// the four possible cells. Regular hexagonal tiles form a Voronoi
|
||||
// tessellation so the xyz should be in the hexagonal cell that it is closest
|
||||
// to the center of. This method is used over a method that uses the
|
||||
// remainders of the floor divisions above because it provides better finite
|
||||
// precision performance. Squared distances are used becasue they are more
|
||||
// precision performance. Squared distances are used because they are more
|
||||
// computationally efficient than normal distances.
|
||||
int k {1};
|
||||
int k_min {1};
|
||||
|
||||
// COINCIDENCE CHECK
|
||||
// if a distance to center, d, is within the coincidence tolerance of the
|
||||
// current minimum distance, d_min, the particle is on an edge or vertex.
|
||||
// In this case, the dot product of the position vector and direction vector
|
||||
// for the current indices, dp, and the dot product for the currently selected
|
||||
// indices, dp_min, are compared. The cell which the particle is moving into
|
||||
// is kept (i.e. the cell with the lowest dot product as the vectors will be
|
||||
// completely opposed if the particle is moving directly toward the center of
|
||||
// the cell).
|
||||
int ix_chg {};
|
||||
int ia_chg {};
|
||||
double d_min {INFTY};
|
||||
double dp_min {INFTY};
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
const std::array<int, 3> i_xyz {out[0] + j, out[1] + i, 0};
|
||||
// get local coordinates
|
||||
const std::array<int, 3> i_xyz {ix + j, ia + i, 0};
|
||||
Position r_t = get_local_position(r, i_xyz);
|
||||
// calculate distance
|
||||
double d = r_t.x*r_t.x + r_t.y*r_t.y;
|
||||
if (d < d_min) {
|
||||
// check for coincidence
|
||||
bool on_boundary = coincident(d, d_min);
|
||||
if (d < d_min || on_boundary) {
|
||||
// normalize r_t and find dot product
|
||||
r_t /= std::sqrt(d);
|
||||
double dp = u.x * r_t.x + u.y * r_t.y;
|
||||
// do not update values if particle is on a
|
||||
// boundary and not moving into this cell
|
||||
if (on_boundary && dp > dp_min) continue;
|
||||
// update values
|
||||
d_min = d;
|
||||
k_min = k;
|
||||
ix_chg = j;
|
||||
ia_chg = i;
|
||||
dp_min = dp;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
// Select the minimum squared distance which corresponds to the cell the
|
||||
// coordinates are in.
|
||||
if (k_min == 2) {
|
||||
++out[0];
|
||||
} else if (k_min == 3) {
|
||||
++out[1];
|
||||
} else if (k_min == 4) {
|
||||
++out[0];
|
||||
++out[1];
|
||||
}
|
||||
// update outgoing indices
|
||||
ix += ix_chg;
|
||||
ia += ia_chg;
|
||||
|
||||
return out;
|
||||
return {ix, ia, iz};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -261,12 +261,13 @@ score_fission_eout(const Particle* p, int i_tally, int i_score, int score_bin)
|
|||
|
||||
} else if (score_bin == SCORE_DELAYED_NU_FISSION && g != 0) {
|
||||
|
||||
// Get the index of the delayed group filter
|
||||
auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_];
|
||||
|
||||
// If the delayed group filter is present, tally to corresponding delayed
|
||||
// group bin if it exists
|
||||
if (i_dg_filt >= 0) {
|
||||
if (tally.delayedgroup_filter_ >= 0) {
|
||||
|
||||
// Get the index of the delayed group filter
|
||||
auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_];
|
||||
|
||||
const DelayedGroupFilter& dg_filt {*dynamic_cast<DelayedGroupFilter*>(
|
||||
model::tally_filters[i_dg_filt].get())};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<geometry>
|
||||
<cell id="13" material="13" region="-9 10 -11" universe="9" />
|
||||
<cell id="14" material="14" region="9" universe="9" />
|
||||
<cell id="15" material="16" name="coolant" region="-12 10 -11" universe="10" />
|
||||
<cell id="16" material="17" name="zirconium_shell" region="12 -13 10 -11" universe="10" />
|
||||
<cell id="17" material="15" name="lead_shell" region="13 -14 10 -11" universe="10" />
|
||||
<cell id="18" material="14" name="matrix coolant surround" region="14 10 -11" universe="10" />
|
||||
<cell id="19" material="14" universe="11" />
|
||||
<cell fill="12" id="20" name="container cell" region="-15 16 -17 18 19 -20 10 -11" universe="13" />
|
||||
<hex_lattice id="12" n_rings="2" name="regular fuel assembly">
|
||||
<pitch>1.4</pitch>
|
||||
<outer>11</outer>
|
||||
<center>0.0 0.0</center>
|
||||
<universes>
|
||||
10
|
||||
10 10
|
||||
9
|
||||
10 10
|
||||
10</universes>
|
||||
</hex_lattice>
|
||||
<surface coeffs="0.0 0.0 0.7" id="9" type="z-cylinder" />
|
||||
<surface boundary="reflective" coeffs="0.0" id="10" type="z-plane" />
|
||||
<surface boundary="reflective" coeffs="10.0" id="11" type="z-plane" />
|
||||
<surface coeffs="0.0 0.0 0.293" id="12" type="z-cylinder" />
|
||||
<surface coeffs="0.0 0.0 0.35" id="13" type="z-cylinder" />
|
||||
<surface coeffs="0.0 0.0 0.352" id="14" type="z-cylinder" />
|
||||
<surface boundary="reflective" coeffs="1.4" id="15" type="y-plane" />
|
||||
<surface boundary="reflective" coeffs="-1.4" id="16" type="y-plane" />
|
||||
<surface boundary="reflective" coeffs="1.7320508075688772 1.0 0.0 2.8" id="17" type="plane" />
|
||||
<surface boundary="reflective" coeffs="-1.7320508075688772 1.0 0.0 -2.8" id="18" type="plane" />
|
||||
<surface boundary="reflective" coeffs="1.7320508075688772 1.0 0.0 -2.8" id="19" type="plane" />
|
||||
<surface boundary="reflective" coeffs="-1.7320508075688772 1.0 0.0 2.8" id="20" type="plane" />
|
||||
</geometry>
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<materials>
|
||||
<material depletable="true" id="13">
|
||||
<density units="sum" />
|
||||
<nuclide ao="0.0049817" name="U235" />
|
||||
</material>
|
||||
<material id="14">
|
||||
<density units="atom/b-cm" value="0.087742" />
|
||||
<nuclide ao="1.0" name="C0" />
|
||||
<sab name="c_Graphite" />
|
||||
</material>
|
||||
<material id="15" name="Lead">
|
||||
<density units="g/cm3" value="10.32" />
|
||||
<nuclide ao="0.014" name="Pb204" />
|
||||
<nuclide ao="0.241" name="Pb206" />
|
||||
<nuclide ao="0.221" name="Pb207" />
|
||||
<nuclide ao="0.524" name="Pb208" />
|
||||
</material>
|
||||
<material id="16">
|
||||
<density units="atom/b-cm" value="0.00054464" />
|
||||
<nuclide ao="1.0" name="He4" />
|
||||
</material>
|
||||
<material id="17" name="Zirc4">
|
||||
<density units="sum" />
|
||||
<nuclide ao="0.02217" name="Zr90" />
|
||||
<nuclide ao="0.004781" name="Zr91" />
|
||||
<nuclide ao="0.007228" name="Zr92" />
|
||||
<nuclide ao="0.007169" name="Zr94" />
|
||||
<nuclide ao="0.001131" name="Zr96" />
|
||||
</material>
|
||||
</materials>
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<settings>
|
||||
<run_mode>eigenvalue</run_mode>
|
||||
<particles>1000</particles>
|
||||
<batches>10</batches>
|
||||
<inactive>5</inactive>
|
||||
<source strength="1.0">
|
||||
<space type="box">
|
||||
<parameters>-0.9899494936611666 -0.9899494936611666 0.0 0.9899494936611666 0.9899494936611666 10.0</parameters>
|
||||
</space>
|
||||
</source>
|
||||
<output>
|
||||
<summary>false</summary>
|
||||
</output>
|
||||
<seed>22</seed>
|
||||
</settings>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
k-combined:
|
||||
1.741370E+00 1.384609E-03
|
||||
150
tests/regression_tests/lattice_hex_coincident/test.py
Normal file
150
tests/regression_tests/lattice_hex_coincident/test.py
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import numpy as np
|
||||
import openmc
|
||||
|
||||
from tests.testing_harness import PyAPITestHarness
|
||||
|
||||
|
||||
class HexLatticeCoincidentTestHarness(PyAPITestHarness):
|
||||
def _build_inputs(self):
|
||||
materials = openmc.Materials()
|
||||
|
||||
fuel_mat = openmc.Material()
|
||||
fuel_mat.add_nuclide('U235', 4.9817E-03, 'ao')
|
||||
materials.append(fuel_mat)
|
||||
|
||||
matrix = openmc.Material()
|
||||
matrix.set_density('atom/b-cm', 8.7742E-02)
|
||||
matrix.add_element('C', 1.0, 'ao')
|
||||
matrix.add_s_alpha_beta('c_Graphite')
|
||||
materials.append(matrix)
|
||||
|
||||
lead = openmc.Material(name="Lead")
|
||||
lead.set_density('g/cm3', 10.32)
|
||||
lead.add_nuclide('Pb204', 0.014, 'ao')
|
||||
lead.add_nuclide('Pb206', 0.241, 'ao')
|
||||
lead.add_nuclide('Pb207', 0.221, 'ao')
|
||||
lead.add_nuclide('Pb208', 0.524, 'ao')
|
||||
materials.append(lead)
|
||||
|
||||
coolant = openmc.Material()
|
||||
coolant.set_density('atom/b-cm', 5.4464E-04)
|
||||
coolant.add_nuclide('He4', 1.0, 'ao')
|
||||
materials.append(coolant)
|
||||
|
||||
zirc = openmc.Material(name="Zirc4")
|
||||
zirc.add_nuclide('Zr90', 2.217E-02, 'ao')
|
||||
zirc.add_nuclide('Zr91', 4.781E-03, 'ao')
|
||||
zirc.add_nuclide('Zr92', 7.228E-03, 'ao')
|
||||
zirc.add_nuclide('Zr94', 7.169E-03, 'ao')
|
||||
zirc.add_nuclide('Zr96', 1.131E-03, 'ao')
|
||||
materials.append(zirc)
|
||||
|
||||
materials.export_to_xml()
|
||||
|
||||
### Geometry ###
|
||||
pin_rad = 0.7 # cm
|
||||
assembly_pitch = 1.4 # cm
|
||||
|
||||
cool_rad = 0.293 # cm
|
||||
zirc_clad_thickness = 0.057 # cm
|
||||
zirc_ir = cool_rad # cm
|
||||
zirc_or = cool_rad + zirc_clad_thickness # cm
|
||||
lead_thickness = 0.002 # cm
|
||||
lead_ir = zirc_or # cm
|
||||
lead_or = zirc_or + lead_thickness # cm
|
||||
|
||||
cyl = openmc.ZCylinder(x0=0., y0=0., r=pin_rad)
|
||||
fuel_btm = openmc.ZPlane(z0=0.0, boundary_type = 'reflective')
|
||||
fuel_top = openmc.ZPlane(z0=10.0, boundary_type = 'reflective')
|
||||
region = -cyl & +fuel_btm & -fuel_top
|
||||
|
||||
container = openmc.Cell(region=region)
|
||||
container.fill = fuel_mat
|
||||
|
||||
fuel_outside = openmc.Cell()
|
||||
fuel_outside.region = +cyl
|
||||
fuel_outside.fill = matrix
|
||||
|
||||
fuel_ch_univ = openmc.Universe(cells=[container, fuel_outside])
|
||||
|
||||
# Coolant Channel
|
||||
cool_outer = openmc.ZCylinder(x0=0.0, y0=0.0, r=cool_rad)
|
||||
zirc_outer = openmc.ZCylinder(x0=0.0, y0=0.0, r=zirc_or)
|
||||
lead_outer = openmc.ZCylinder(x0=0.0, y0=0.0, r=lead_or)
|
||||
|
||||
coolant_ch = openmc.Cell(name="coolant")
|
||||
coolant_ch.region = -cool_outer & +fuel_btm & -fuel_top
|
||||
coolant_ch.fill = coolant
|
||||
|
||||
zirc_shell = openmc.Cell(name="zirconium_shell")
|
||||
zirc_shell.region = +cool_outer & -zirc_outer & +fuel_btm & -fuel_top
|
||||
zirc_shell.fill = zirc
|
||||
|
||||
lead_shell = openmc.Cell(name="lead_shell")
|
||||
lead_shell.region = +zirc_outer & -lead_outer & +fuel_btm & -fuel_top
|
||||
lead_shell.fill = lead
|
||||
|
||||
coolant_matrix = openmc.Cell(name="matrix coolant surround")
|
||||
coolant_matrix.region = +lead_outer & +fuel_btm & -fuel_top
|
||||
coolant_matrix.fill = matrix
|
||||
|
||||
coolant_channel = [coolant_ch, zirc_shell, lead_shell, coolant_matrix]
|
||||
|
||||
coolant_univ = openmc.Universe(name="coolant universe")
|
||||
coolant_univ.add_cells(coolant_channel)
|
||||
|
||||
half_width = assembly_pitch # cm
|
||||
edge_length = (2./np.sqrt(3.0)) * half_width
|
||||
|
||||
inf_mat = openmc.Cell()
|
||||
inf_mat.fill = matrix
|
||||
|
||||
inf_mat_univ = openmc.Universe(cells=[inf_mat,])
|
||||
|
||||
# a hex surface for the core to go inside of
|
||||
hexprism = openmc.model.get_hexagonal_prism(edge_length=edge_length,
|
||||
origin=(0.0, 0.0),
|
||||
boundary_type = 'reflective',
|
||||
orientation='x')
|
||||
|
||||
pincell_only_lattice = openmc.HexLattice(name="regular fuel assembly")
|
||||
pincell_only_lattice.center = (0., 0.)
|
||||
pincell_only_lattice.pitch = (assembly_pitch,)
|
||||
pincell_only_lattice.outer = inf_mat_univ
|
||||
|
||||
# setup hex rings
|
||||
ring0 = [fuel_ch_univ]
|
||||
ring1 = [coolant_univ] * 6
|
||||
pincell_only_lattice.universes = [ring1, ring0]
|
||||
|
||||
pincell_only_cell = openmc.Cell(name="container cell")
|
||||
pincell_only_cell.region = hexprism & +fuel_btm & -fuel_top
|
||||
pincell_only_cell.fill = pincell_only_lattice
|
||||
|
||||
root_univ = openmc.Universe(name="root universe", cells=[pincell_only_cell,])
|
||||
|
||||
geom = openmc.Geometry(root_univ)
|
||||
geom.export_to_xml()
|
||||
|
||||
### Settings ###
|
||||
|
||||
settings = openmc.Settings()
|
||||
settings.run_mode = 'eigenvalue'
|
||||
|
||||
source = openmc.Source()
|
||||
corner_dist = np.sqrt(2) * pin_rad
|
||||
ll = [-corner_dist, -corner_dist, 0.0]
|
||||
ur = [corner_dist, corner_dist, 10.0]
|
||||
source.space = openmc.stats.Box(ll, ur)
|
||||
source.strength = 1.0
|
||||
settings.source = source
|
||||
settings.output = {'summary' : False}
|
||||
settings.batches = 10
|
||||
settings.inactive = 5
|
||||
settings.particles = 1000
|
||||
settings.seed = 22
|
||||
settings.export_to_xml()
|
||||
|
||||
def test_lattice_hex_coincident_surf():
|
||||
harness = HexLatticeCoincidentTestHarness('statepoint.10.h5')
|
||||
harness.main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue