Combine cuts to create a 3D reactor model
The final step in a cOMPoSe input module is to stack cuts so that they form a complete three dimensional model. This involves two parts. The first is to specify the nodal structure of all loadable assemblies. These are frequently configuration independent, and can be set up in the loadable assembly’s individual modules. However, since each core configuration yields a single homogenized library, the main module must import all the loadable assembly mixtures. This is covered in Defining loadable assemblies. The second part defines the rest of nodalized model by specifying all remaining static positions. This is covered in `Building core configuration`_.
Data for the final nodal configuration is set using a new parameter pack, which is created using the
create_nodal_configuration
method of the parent parameter set. The method accepts the
following arguments:
name
: Descriptive name of the configuration. Used to name the generated LINX library file.
tag
: Very short (two characters or less) tag used to identify the nodal model. By default, thetag
parameter of the heterogeneous configuration will be used. In this case, the nodal model will be automatically associated with that configuration. If this parameter was not set during model creation, or if you choose not to directly couple the nodal model with the heterogeneous model, you can customize the tag in this call.
Example:
# To use the model tag set in the heterogeneous model
nodal_model = parameters.create_nodal_configuration(name='MY_REACTOR_configuration_1')
# Or use a custom tag
nodal_model = parameters.create_nodal_configuration(name='MY_REACTOR_configuration_1',
tag='C1')
Note
Since the tag will be used to automatically name position dependent base types, the restricting to two characters
for the tag and homogenization_grid
labels stem from the 6 character naming restriction in MGRAC.
The object returned by this call has the following parameters:
- axial_mesh
Axial mesh of the three dimensional model used by the nodal solver for flux calculations. The list passed to this parameter contains the size of each axial mesh, so that the total number of axial meshes is equal to the length of the list, and the total height of the nodal model is equal to the sum of the elements in the list.
The finer the meshing, the more axial detail will be available, but the calculational time will also increase.
Note
This parameter is independent of the material meshes defined later. However, it is recommended to the set this mesh so that each exposure material mesh for loadable assemblies is only in one axial mesh segment.
-
assembly_library
:
!core.assembly.AssemblyLibrary
The heterogeneous assembly library. This is needed so that the system can store the defined nodal configurations in the library for easy access and distribution. It is usually just the
assembly
container imported from themodel
package:from ..model import assemblies nodal_model.assembly_library = assemblies
-
save_assemblies
: bool
= ``True`` Flag indicating if loadable assembly nodal definitions should be saved to the archives. This flag can be set to
False
if the loadable definitions have already been saved in another cOMPoSe module, and only the mixtures are required.
-
active_height
: length
= Derived Total height of fissionable materials. This parameter is usually derived correctly from the heterogenuous model, and and should only be set manually if the nodal model active height deviates (for some reason) from the underlying model.
-
active_bottom
: length
= Derived Start of the active region relative to the model axial bottom, which is at 0. If not specified, this parameter is deduced by taking the minimum fueled mixture position over all loadable assemblies.
Attention
Unlike the heterogeneous model, whose axial coordinate system is frequently defined by the core center line, the nodal model always has the bottom of the entire model as the axial zero position. This inconsistency was introduced to more closely align cOMPoSe generated models with legacy MGRAC models.
-
bank_map
: labeledgrid [placeholder | string]
Define the nodal model’s control element banks. Must be a map with the same shape and labels as
homogenization_grid
. This parameter only needs to be set if the nodal model has a different bank structure than the underlying heterogeneous model, or if the overlay mesh intersected bank positions.
Defining loadable assemblies
A normal loadable assembly, which fills an entire core pitch channel, is registered using the add_assembly
method,
which has the following arguments:
base_name
: Short (6 characters or less) name used to identify the base nodal configuration for the assembly. This parameter is currently required, as there is no consistent way to deduce a short name from the more descriptive one specified during assembly creation. Moreover, multiple nodal models might be associated with a given heterogeneous assembly, and each must have a unique base name.
lib_assembly
: Heterogeneous assembly this loadable is associated with. This parameter is required.
seating
: Axial seating of this model relative to the bottom of the nodal model, which is the axial zero position. It defaults to 0, that is, the bottom of the assembly mixture mesh aligns with the bottom of the nodal model.
type
: Assembly type tag. Defaults to thelib_assembly
type.
The following shows a typical example:
fa = assemblies.MY_REACTOR_assembly_type_1()
hf = nodal_model.add_assembly(base_name='TYPE_1',
lib_assembly=fa)
Meshed loadable assemblies, that is, assemblies for which a number of segments in homogenization_grid
must
be combined in order to fill a full core pitch, are added using the add_meshed_assembly
method. It accepts all the
above arguments, as well as the following:
pitches
: List of sub-segment sizes.
Attention
If your model is an extension of an OSCAR-3 or OSCAR-4 model, use the same base names for loadable assemblies that are already in use. This will ensure compatibility with existing history files.
Both calls return a data object, with the following additional parameters:
-
material_mesh
: list
Axial stack of homogenized mixtures which defines the assembly’s material mesh. This parameter is a list of tuple’s, with 2 or 3 values each:
The first value is the length of the segment.
The second value is a reference to the mixture that should fill this segment. Mixture references are obtained using the
[]
method of a cut reference, or the value returned by theimport_library
method.An optional third value, which defines the mixture type. Currently, you only need to specify this value if the mixture has a non-zero fission cross section. In this case, the value should be an instance of the material type tag
core.material.fuel
.
The order of the material mesh is from bottom to top, that is, the first mixture is at the axial top of the homogenized assembly, and the last mixture is at the axial bottom. For example, the following will create a material mesh of total length 72 cm, with three different mixtures:
h = parameters.generator.mode.active_height() upper = parameters.add_axial_reflector_cut('TOP', rng=(0.5 * h, 0.5 * h + 6 * units.cm), description='Baffle region above active fuel') act, data = parameters.add_lattice_cut('ACT', rng=(-0.5 * h, 0.5 * h), description='Active region') # Set additional data # ... lower = parameters.add_axial_reflector_cut('BOT', rng=(-0.5 * h - 6 * units.cm, -0.5 * h), description='Baffle region below active fuel') hf.material_mesh = \ [(6 * units.cm, upper['A1']), (60 * units.cm, active['A1'], core.material.fuel()), (6 * units.cm, lower['A1'])]
Note that, apart from defining cross sections, the material mesh is also the assembly exposure mesh, defining how the assembly will be depleted. Thus, the above example will deplete the entire active region on a single mesh. A better approach would be to subdivide this region into a number of sub-segments:
hf.material_mesh = \ [(6 * units.cm, upper['A1'])] + \ [(6 * units.cm, active['A1'], core.material.fuel())] * 10 + \ [(6 * units.cm, lower['A1'])]
which burn the active region in 10 segments of equal length. If the
axial_exposure_mesh
parameter of the target heterogeneous assembly is set, theexposure
tag can also be used:fa = assemblies.MY_REACTOR_assembly_type_1() hf = nodal_model.add_assembly(base_name='TYPE_1', lib_assembly=fa) fa.axial_exposure_mesh = 10 hf.material_mesh = \ [(6 * units.cm, upper['A1']), exposure(active['A1']), (6 * units.cm, lower['A1'])]
which will also expand to 10 equal segments.
Note
Since loadable assemblies are frequently common to different core configurations, a lot of repetition can be avoided
by defining the assembly’s nodal configuration directly in the input script were homogenization for the assembly was
performed. This is achieved by defining a special method, called add_to
in the module. For example in the
inf_1 module:
h = parameters.generator.mode.active_height()
upper = parameters.add_axial_reflector_cut('TOP',
rng=(0.5 * h, 0.5 * h + 6 * units.cm),
description='Baffle region above active fuel')
act, data = parameters.add_lattice_cut('ACT',
rng=(-0.5 * h, 0.5 * h),
description='Active region')
lower = parameters.add_axial_reflector_cut('BOT',
rng=(-0.5 * h - 6 * units.cm, -0.5 * h),
description='Baffle region below active fuel')
def add_to(nodal_model):
fa = assemblies.MY_REACTOR_assembly_type_1()
hf = nodal_model.add_assembly(base_name='TYPE_1',
lib_assembly=fa)
hf.material_mesh = \
[(6 * units.cm, upper['A1'])] + \
[(6 * units.cm, active['A1'], core.material.fuel())] * 10 + \
[(6 * units.cm, lower['A1'])]
Then, in the core input module (e.g. operational), the following will add all the mixtures without having to re-specify material meshes:
import inf_1
nodal_model = parameters.create_nodal_configuration(name='MY_REACTOR_configuration_1')
inf_1.add_to(nodal_model)
The add_to
must take the nodal configuration as a parameter, but it can off course also include any additional
parameters you want to pass to the inf_1 module.
Building the core configuration
The final step in the cOMPoSe nodal configuration construction is to define the static (non-loadable) channels in
the homogenization_grid
. This is accomplished using the build
method of the nodal_model
parameter pack,
which requires the following two arguments:
pred
: A predicate which determine the channels in thehomogenization_grid
that should be included. The system will loop through all entriesx
in yourhomogenization_grid
, and only consider channels for whichpred(x)
evaluates toTrue
. For example, to only build mixtures for channels marked with a0
and4
, uselambda x : x in [0, 4]
.
layers
: The axial material mesh that will be applied to all channels. It has exactly the same form as thematerial_mesh
parameter used for loadable assemblies, except that, instead of passing a single mixture as the second argument, you pass a map of mixtures. This is usually accomplished by passing the entire cut placeholder.
For example:
h = parameters.generator.mode.active_height()
upper = parameters.add_axial_reflector_cut('TOP',
rng=(0.5 * h, 0.5 * h + 6 * units.cm),
description='Baffle region above active fuel')
act_u = parameters.add_cut('ACT-U',
rng=(0.0, 0.5 * h),
description='Upper active region')
act_l = parameters.add_cut('ACT-L',
rng=(-0.5 * h, 0.0),
description='Upper active region')
lower = parameters.add_axial_reflector_cut('BOT',
rng=(-0.5 * h - 6 * units.cm, -0.5 * h),
description='Baffle region below active fuel')
# ...
nodal_model = parameters.create_nodal_configuration(name='MY_REACTOR_configuration_1')
# ...
material_mesh = \
[(6 * units.cm, upper),
(0.5 * h, act_u),
(0.5 * h, act_l),
(6 * units.cm, lower)]
nodal_model.build(lambda x : x in [0,4],
material_mesh)