Plate Type Fuel Assemblies

Collect all assemblies with plate type fuel. This includes typical irradiation targets using this fuel design.

Target plate holder

Models an assembly used to hold a variable number of target plates. It only constructs the grid plate structure, but also provides methods to load target plates into empty plate slots. The assembly type is called LoadablePlateAssembly, in the core.fuel_assembly module:

from core import *

def build(*args, **kwargs):

    asm = fuel_assembly.LoadablePlateAssembly(name='MY_REACTOR_assembly_base_name')

    asm.number_of_plates = 5
    asm.pitches = 15 * units.mm
    # etc

The macro has the following parameters:

target_holder.number_of_plates

: integer

!

The total number of plate slots.

target_holder.pitches

: length or list (length)

!

The distance between consecutive plate centers. Either a single value, in which case the pitch is assumed to be constant, or a list of values, giving the distance between plates from the bottom or left side. In the latter case, list must have exactly target_holder.number_of_plates - 1 entries.

target_holder.grid_plate_pitch

: length

!

The inner distance between grid (side) plates, along the length of the target plates.

Note

Grid plates are the structure supporting the plates. They are also frequently referred to as side plates.

target_holder.orientation

: orientation

= horizontal

The plate orientation. If horizontal, plates will be placed parallel to the \(x\)-axis, while if vertical, plates will be parallel to the \(y\)-axis.

target_holder.center

: point

= 0,0,0

The center of the plate structure. This parameter is used to position the plates and plate slots. For example, the center of the first plate is,

>>> center[j] - 0.5 * sum(pitches)

where j is 1 if target_holder.orientation is horizontal and 0 if target_holder.orientation is vertical.

The \(z\)-coordinate of this parameter also determines the axial center of the plates.

target_holder.slot_insert_depth

: length or list (length)

!

Depth of the plate slot inserts in the grid plate. Either a single value, if all slots have the same depth, or a list of length target_holder.number_of_plates.

target_holder.slot_insert_width

: length or list (length)

!

Width of the plate slot inserts in the grid plate. Either a single value, if all slots have the same width, or a list of length target_holder.number_of_plates.

target_holder.box

: list (length) or region

Radial bounding region. When the grid is clipped with a simple rectangular region, this parameter can be specified as a list of four values, giving the \(x\) and \(y\) limits respectively. For example:

>>> asm.box = [-4.05 * units.cm, 4.05 * units.cm, -4.05 * units.cm, 4.05 * units.cm]

will clip all grid cells with an \(8.1 \times 8.1\) square centered at the origin. Alternatively, any region can be specified.

Attention

The target_holder.box is independent of target_holder.center. That is, the \(xy\) center of this region is not necessarily the center of the assembly.

target_holder.axial_region

: list (length) or region

Axial bounding region, used to clip grid cells. When the grid is bounded by a simple axial strip, this parameter can be specified as a list of two values, giving the \(z\) bounds. For example,

>>> asm.axial_region = [-40 * units.cm, 35 * units.cm]

will clip all cells to the region \(-40 <= z <= 35\). Alternatively, any region can be specified.

Attention

The target_holder.axial_region is independent of target_holder.center. That is, the \(z\) center is not necessarily the center of this region.

target_holder.coolant_channels

: list (str)

Specify which coolant channels should be added by the macro. This is a list of string entries (or None), equal to the number of coolant channels (target_holder.number_of_plates - 1). The string value will be used to name the facility which fills the channel, while None is used to indicate a channel that should not be added. For example, if there are 4 plates,

asm.coolant_channels = ['channel-0', None, 'channel-1']

will add the channel between plate 0 and 1, and plate 2 and 3, but skip the central channel (between the plates 1 and 2).

By default, all channels are added with names "coolant-channel-i".

Note

Skipping channels are required when the space between plates will contain additional structure (e.g. control rods). Otherwise, intersecting cells will be present.

target_holder.grid_material

: material

!

Material used to fill grid plate (side plate) cells.

target_holder.moderator_material

: material

!

Material that fills the gap between plate slots and regions between grid slots and fueled plates. In the case of an empty plate holder, it is also used to fill the cells that can later be filled with target plates.

The following table summarizes the cells added by this assembly macro:

Cells Created

Material

Grid plates

target_holder.grid_plate_material

Dummy plate cells

target_holder.moderator_material

Coolant channels

target_holder.moderator_material

Remaining cells in target_holder.box and target_holder.axial_region

target_holder.moderator_material

Attention

The macro fills the entire region defined by target_holder.box and target_holder.axial_region. Thus, any additional cells added to the assembly should not intersect this region.

The following loadable facilities are added:

Facilities Added

Description

Default State

water-channel-i [1]

Coolant channel between plates \(i\) and \(i+1\) Can be used to load detectors between fuel plates.

target_holder.moderator_material

plate-slot-i

Slot for target plate \(i\).

target_holder.moderator_material

Targets can also be loaded using the [ ] method. For example, to load a target into slot 0:

asm[0] = assemblies.MY_REACTOR_target_plate(name='TG1')

Target plate

Single plate assembly, which can be loaded into a Target plate holder. The object TargetPlate is defined in the core.fuel_assembly module:

from core import *

def build(*args, **kwargs):

    asm = fuel_assembly.TargetPlate(name='MY_REACTOR_target_plate_type')


    asm.meat_width = 0.45 * units.mm
    # etc

    asm.construct_bundle()

Apart from the basic parameters described in Generic assembly, the following is used the define the plate structure:

target_plate.meat_width

: length

!

Width of the active fueled region.

Note

Since the orientation is fixed, with the plate extending along the \(x\)-axis, this is the \(y\) dimension of the meat region.

target_plate.meat_length

: length

!

Length of the active fueled region.

Note

Since the orientation is fixed, with the plate extending along the \(x\)-axis, this is the \(x\) dimension of the meat region.

target_plate.meat_height

: length

!

Axial (\(z\)-dimension) of the active region.

target_plate.plate_width

: length

!

Width of the total plate. This is equal to target_plate.meat_width plus two times the cladding thickness.

Note

Since the orientation is fixed, with the plate extending along the \(x\)-axis, this is the \(y\) dimension of the plate.

target_plate.plate_length

: length

!

Length of the total plate.

Note

Since the orientation is fixed, with the plate extending along the \(x\)-axis, this is the \(x\) dimension of the plate.

target_plate.clad_material

: material

!

Material used in the plate cladding.

target_plate.fuel_material

: material

!

Active material used in the meat region.

target_plate.moderator_material

: material

Material used to surround the plate.

Note

If not specified, there will be no cells added outside the plate, and it is the user’s responsibility to add the missing cells.

Plate fuel assembly

Models standard plate type (MTR) fuel assemblies. The PlateAssembly class extends the Target plate holder macro by adding all the fuel plates. The macro is also defined in the core.fuel_assembly module:

from core import *

def build(*args, **kwargs):

    asm = fuel_assembly.PlateAssembly(name='MY_REACTOR_assembly_base_name')

    asm.number_of_plates = 5
    asm.pitches = 15 * units.mm
    # etc

    asm.construct_bundle()

It has all the parameters listed in Target plate holder, as well as the following:

plate_asm.meat_width

: length or list (length)

!

Width of the active fueled region. Either a single value if all plates have the same active width, or a list of values giving the width per plate. In the latter case, the list must have length equal to target_holder.number_of_plates.

Note

The width is the dimension perpendicular to the target_holder.orientation. Thus, if the orientation is horizontal, the width is the \(y\) dimension of the plate, and the \(x\) dimension if the orientation is vertical.

plate_asm.meat_length

: length or list (length)

!

Length of the active fueled region. Either a single value if all plates have the same active length, or a list of values giving the width per plate. In the latter case, the list must have length equal to target_holder.number_of_plates.

Note

The length is the dimension parallel to the target_holder.orientation. Thus, if the orientation is horizontal, the length is the \(x\) dimension of the plate, and the \(y\) dimension if the orientation is vertical.

plate_asm.meat_height

: length or list (length)

!

Height (\(z\) dimension) of the active fueled region. Either a single value if all plates have the same active height, or a list of values giving the height per plate. In the latter case, the list must have length equal to target_holder.number_of_plates.

Note

The maximum plate_asm.meat_height determines the assembly’s active height.

plate_asm.plate_width

: length or list (length)

!

Total width of the fuel plate. This includes the meat width, and cladding on both sides. Either a single value if all plates have the same width, or a list of values giving the width per plate.

Attention

The plate width must be less than or equal to the target_holder.slot_insert_width.

plate_asm.plate_length

: length or list (length)

!

Total length of the fuel plate. This includes the meat length, and cladding on both sides. Either a single value if all plates have the same length, or a list of values giving the length per plate.

Attention

The plate length must be less than or equal to target_holder.grid_plate_pitch + 2 \(\times\) target_holder.slot_insert_depth.

plate_asm.plate_height

: length or list (length)

!

Total height of the fuel plate. This includes the meat height, and cladding on both sides. Either a single value if all plates have the same height, or a list of values giving the height per plate.

plate_asm.clad_material

: material

!

Material used for plate fuel cladding.

plate_asm.axially_fill_channels

: bool

= True

Flag indicating if the cells above the plates should be added. These cell will fill everything defined by target_holder.axial_region.

Attention

This should be set to False if there are structural components (e.g. adapters), that will intersect with this region.

These parameters are illustrated below in radial and axial schematics.

../_images/plate_fuel_assembly.svg

Radial schematic of PlateAssembly structure.

../_images/plate_fuel_assembly_axial.svg

Axial schematic of PlateAssembly structure. In the above, the outside plate’s plate_asm.plate_height is longer than interior plates. This is typical for boxed assemblies.

The following table summarizes the cells added by this assembly macro:

Cells Created

Material

Grid (side) plates

target_holder.grid_plate_material

Fuel plate cladding

plate_asm.clad_material

Active fuel meat

Contains fuel_bundle depletion mesh

Coolant channels [1]

target_holder.moderator_material

Remaining cells in target_holder.box and target_holder.axial_region

target_holder.moderator_material

Attention

If plate_asm.axially_fill_channels is True, the macro fills the entire region defined by target_holder.box and target_holder.axial_region. Thus, any additional cells added to the assembly should not intersect this region.

Otherwise, if plate_asm.axially_fill_channels is False, this will only create the grid (side plate), plate, and water channels [1] cells.

The following loadable facilities are added:

Facilities Added

Description

Default State

water-channel-i [1]

Coolant channel between plates \(i\) and \(i+1\) Can be used to load detectors between fuel plates.

target_holder.moderator_material

Attention

Remember to set the initial material for the depletion mesh in the build module:

asm.fuel_bundle().depletion_mesh.initial_material_distribution = materials.my_fuel()

See Depletion meshes for plate type assemblies for more details.

Follower type plate assembly

Models standard plate type (MTR) fuel follower assembly. The FollowerPlateAssembly macro is also defined in the core.fuel_assembly module:

from core import *

def build(*args, **kwargs):

    asm = fuel_assembly.FollowerPlateAssembly(name='MY_REACTOR_assembly_base_name')

    # plate bundle parameters
    asm.number_of_plates = 5
    asm.pitches = 15 * units.mm
    # etc

    # control parameters
    asm.base_position = control.fully_extracted()
    asm.travel_direction = control.up()
    asm.total_travel_distance = 74.79 * units.cm

This macro simply combines the Plate fuel assembly and Control assembly types. The macro behaves exactly the same way as Plate fuel assembly (adds the same cells, etc.).

Attention

This macro does not add any structure related to the absorber part of the assembly. These cells should be added manually.

Plate assembly with burnable absorbers

Models standard plate type (MTR) fuel follower assembly, with burnable absorbers embedded in the side plates. The PlateAssemblyWithBurnableAbsorbers macro is also defined in the core.fuel_bundle module:

from core import *

def build(*args, **kwargs):

    asm = fuel_assembly.PlateAssemblyWithBurnableAbsorbers(name='MY_REACTOR_assembly_base_name')

    # plate bundle parameters
    asm.number_of_plates = 5
    asm.pitches = 15 * units.mm
    # etc

    # new slot parameters
    asm.ba_groove_width = 2 * units.mm
    asm.ba_groove_depth = 2 * units.mm
    # etc

It adds the following parameters to the Plate fuel assembly macro:

plate_asm_with_ba.ba_groove_width

: length or list (length)

!

Width of the slot in the grid plate that will house the absorber. Either a single value, if the grooves all have the same width, or a list of values.

plate_asm_with_ba.ba_groove_depth

: length or list (length)

!

Depth of the slot in the grid plate that will house the absorber. Either a single value, if the grooves all have the same depth, or a list of values.

plate_asm_with_ba.slot_fill

: string or material

= "moderator_material"

Material that should be used to fill the groove slots outside the absorber material. It can be any material instance, or a string referring to one of the materials already used in the macro, that is:

  • grid_material,

  • clad_material, or

  • moderator_material.

plate_asm_with_ba.ba_loading

: list

!

List describing which plate positions have additional absorber slots, and what the structure of the burnable absorbers are. This takes the form of a list, with target_holder.number_of_plates entries. Each entry is again a list (or tuple) of two entries, giving the absorber structure that should be loaded into the left and right plate respectively. For example, assuming there are 21 plates, the following will load wires into every second plate slot:

ba = fuel_assembly.WireBurnableAbsorber()
ba.length = 308 * units.mm
ba.diameter = 0.493 * units.mm
ba.axial_seating = (-0.5 * 615 + 123 + 0.5 * 308) * units.mm


fa.ba_loading = \
[[_ , _ ],
 [ba, ba],
 [_ , _ ],
 [ba, ba],
 [_,   _],
 [ba, ba],
 [_,   _],
 [ba, ba],
 [_,   _],
 [ba, ba],
 [_,   _],
 [ba, ba],
 [_,   _],
 [ba, ba],
 [_,   _],
 [ba, ba],
 [_,   _],
 [ba, ba],
 [_,   _],
 [ba, ba],
 [_,   _]]

In the above, the WireBurnableAbsorber is a predefined absorber structure. It has the following parameters:

wire_ba.diameter

: length

!

The wire diameter.

wire_ba.length

: length

!

The axial length (\(z\) dimension) of the wire.

wire_ba.axial_seating

: length

= 0.0

Axial center of wire (relative to the fuel assembly).

The fuel_assembly module also contains a CladdedBurnableWireAbsorber definition, which models a cladded wire. In addition to the WireBurnableAbsorber parameters, it is defined by:

cladded_wire_ba.clad_thickness

: length

!

Radial thickness of the clad surrounding the wire. The total diameter of the wire plus clad is therefore wire_ba.diameter plus two times cladded_wire_ba.clad_thickness.

cladded_wire_ba.clad_axial_thickness

: length or list (length)

= 0.0

Axial length of wire. Either a single value, if the bottom and top cladding is the same, or two values, giving the bottom and top value respectively.

cladded_wire_ba.clad_material

: material

!

Material used to clad the wire.

The structure defined by this macro is illustrated below:

../_images/plate-assembly-with-wires.svg

Radial schematic of PlateAssemblyWithBurnableAbsorbers structure. Only the new parameters are illustrated. All other parameters are as shown in Fig plate_assembly_radial. Here, only every second plate has a slot for a burnable absorber.

Curved plate assembly

Under construction!

Depletion meshes for plate type assemblies

All assembly types adds a fuel_bundle, which manages the distribution of burnable materials among the fuel plates. Each plate is counted as a separate fueled region (or primitive), indexed from 0 to target_holder.number_of_plates - 1. Grouping the plates into bundles is accomplished by assigning a group index to each plate. For example, if there are 19 plates,

asm.burn_bundle[bundle_tags.fuel].depletion_mesh.bundles = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

will produce 19 bundles with each plate depleted separately. Setting,

asm.burn_bundle[bundle_tags.fuel].depletion_mesh.bundles = [0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0]

will create 3 different bundles, with

  • the first bundle containing the first three, and last three plates,

  • the second bundle contains plates 4, 5, 6 and 14, 15 and 16,

  • the third bundle contains all the remaining central plates.

The radial segments parameter of the fuel bundle’s depletion mesh divides the plates along its length. Currently, no subdivision in the width of the plate is performed.

The axial mesh simply divides the plates along their plate_asm.meat_height.

The default depletion mesh bundles all plates together, with no radial and axial sub-division. This can be customized later, depending on the application, by Customizing the depletion mesh.

When burnable absorber wires are present (e.g PlateAssemblyWithBurnableAbsorber), the depletion mesh for these structures can also be customized. In this case, the wires are the basic primitives, and they are bundled by specifying a two dimensional grid, with an index for each wire. Each row in the grid will have exactly two entries, one for each side plate.

For example, suppose there are 5 plates with a wire embedded on each side. Then,

asm.burn_bundle[bundle_tags.ba].depletion_mesh.bundles = \
  [[0, 1],
   [2, 3],
   [4, 5],
   [6, 7],
   [8, 9]]

will burn all wires seperately (total of 10 bundles), while

asm.burn_bundle[bundle_tags.ba].depletion_mesh.bundles = \
  [[0, 1],
   [0, 1],
   [0, 1],
   [0, 1],
   [0, 1]]

will group all wires on each side together (only two bundles).

In this case, the radial segments parameter of the fuel bundle’s depletion mesh divides the wires into radial rings. For example, if there are two bundles,

asm.burn_bundle[bundle_tags.ba].depletion_mesh.segments = \
 [[0.5, 0.5], [0.4, 0.3, 0.3]]

will divide the first bundle wires into two rings with the same area, and the second bundle wires into three rings, with the inner most (central) zone having 40 percent of the total area, and the next two rings 30 percent each.

Finally, as with the plate region, the axial mesh divides the wires along their length.

Examples

This section gives complete build script examples for some of the assembly types described in this chapter.

Target plate holder assembly build script


from core.fuel_assembly import LoadablePlateAssembly
from csg import primitives
from csg.primitives import *
from core.units import units

from . import materials
from material_library.moderators import LightWater


def build(*args, **kwargs):
    mp = LoadablePlateAssembly(name='IPEN-empty-irradiation-device', **kwargs)
    mp.description = 'Irradiation Device (Holder) without any plates'

    # ==================================================================================================================
    # set some QA parameters
    mp.__source__ = 'BRA_IPEN_B_2b_Irradiation Device'
    mp.__version__ = '0.1.0'
    mp.__author__ = 'lesego'

    # ==================================================================================================================
    # set plate bundle parameters
    mp.number_of_plates = 5
    mp.pitches = 2 * 2.5 * units.mm  # centre plate 1 to centre plate 2

    mp.grid_plate_pitch = 46.5 * units.mm

    mp.slot_insert_depth = 2.0 * units.mm
    mp.slot_insert_width = 2.5 * units.mm

    box_length = (33.85 - 2 * 3.175) * units.mm
    mp.box = [-27.75 * units.mm, 27.75 * units.mm, -0.5 * box_length, +0.5 * box_length]

    holder_bottom = (172.0 - 170) * units.mm
    mp.axial_region = [-87.0 * units.mm, 0.5 * 170.0 * units.mm]
    #    mp.axial_region = [-86 * units.mm, 86 * units.mm]

    mp.grid_material = materials.miniplate_clad()
    mp.moderator_material = LightWater(mass_density=0.99820 * units.g / units.cc)

    # Temporarily fill the bundle space (speeds up the fill universe algorithm)
    mp.create_bundle_place_holder()

    # Add the side plate of the irradiation plate holder

    side_plate_left_outer = Py(-16.925 * units.mm)
    side_plate_left_inner = Py(-13.750 * units.mm)

    side_plate_right_outer = Py(16.925 * units.mm)
    side_plate_right_inner = Py(13.750 * units.mm)

    side_plate_upper_bound = Px(31.750 * units.mm)
    side_plate_lower_bound = Px(-31.750 * units.mm)

    side_plate_top = Pz(85.0 * units.mm)
    side_plate_bottom = Pz(-87.0 * units.mm)

    holder_handler = (480.0 - 172.0) * units.mm
    handler_extension_top = Pz(holder_handler)

    side_plate_right = (~side_plate_right_inner & side_plate_right_outer & ~side_plate_bottom & handler_extension_top &
                        side_plate_upper_bound & ~side_plate_lower_bound)

    mp.add_cell(side_plate_right,
                material=materials.miniplate_clad(),
                description='side plate right')

    side_plate_left = (side_plate_left_inner & ~side_plate_left_outer & ~side_plate_bottom & side_plate_top &
                       side_plate_upper_bound & ~side_plate_lower_bound)

    cutout = primitives.Cylinder(radius=15 * units.mm,
                                 alignment=ALIGNMENT.Y_AXIS,
                                 center=(1.5875 * units.mm, 85.0 * units.mm))

    mp.add_cell(side_plate_left & ~cutout,
                material=materials.miniplate_clad(),
                description='side plate left')

    mp.complete_universe(material=LightWater(mass_density=0.99820 * units.g / units.cc))

    mp.construct_bundle()

    # mp.simplify_cells()

    return mp


if __name__ == '__main__':
    from applications.assembly_archiver import main
    main(source=build)

Basic plate type assembly build script

"""
SAFARI-1 fuel with simplified geometry
======================================

Plate type fuel assembly with 19.75% enriched U3Si2-Al fuel.

Sources
-------
SAFARI-1 benchmark description, Section 4.2

Assumptions and Simplifications
-------------------------------
 - Top and bottom adapters are approximated as cylinders.
 - Geometry of slots in side plates are guessed.

Version Log
-----------
 - 1.0.0 : Initial model definition
 - 1.1.0 : Update to better use new features

"""

import core.fuel_assembly
from csg import *
from csg.primitives import *
import material_library.moderators
from . import materials


def build(*args, **kwargs):
    fa = core.fuel_assembly.PlateAssembly(name='SAFARI-1-LEU-fuel')
    fa.description = 'SAFARI-1 fuel with simplified geometry'

    # ==================================================================================================================
    # Set some QA parameters
    fa.__source__ = 'SAFARI-1 benchmark specifications'
    fa.__version__ = '1.0.0'
    fa.__author__ = 'S.A. Groenewald'

    # ==================================================================================================================
    # Set plate bundle parameters
    fa.number_of_plates = 19
    fa.pitches = 2.95 * units.mm + 1.275 * units.mm  # coolant gap plus plate width
    fa.plate_width = 1.275 * units.mm
    fa.plate_length = 66.8 * units.mm + 2 * 2.5 * units.mm # distance between side plates + 2*(insert depth)
    fa.plate_height = [682.9 * units.mm] + [625.48 * units.mm]*17 + [682.9 * units.mm]  # outer plates extend further

    fa.meat_width = 0.510 * units.mm
    fa.meat_length = 63.00 * units.mm
    fa.meat_height = 593.7 * units.mm

    fa.grid_plate_pitch = 66.8 * units.mm

    fa.slot_insert_depth = 2.5 * units.mm  # slot insert depth guessed
    fa.slot_insert_width = 1.275 * units.mm

    fa.box = [-0.5 * 75.9 * units.mm, 0.5 * 75.9 * units.mm, -0.5 * 80.15 * units.mm, 0.5 * 80.15 * units.mm]
    fa.axial_region = [-0.5 * 682.9 * units.mm, 0.5 * 682.9 * units.mm]

    fa.clad_material = materials.al_clad()
    fa.grid_material = materials.al_clad()
    fa.moderator_material = material_library.moderators.H2O(mass_density=0.992 * units.g / units.cc)  # from specs TABLE 5

    # ==================================================================================================================
    # Building end adapters
    # Note: Cylinders are used as a simplified representation of both top and bottom adapters

    top_adap = region_macros.axial_strip(lower=0.5 * 682.9 * units.mm,
                                         upper=0.5 * 923.9 * units.mm)

    bot_adap = region_macros.axial_strip(lower=-0.5 * 923.9 * units.mm,
                                         upper=-0.5 * 682.9 * units.mm)

    rad_out = Cylinder(radius=(0.5 * 73.0) * units.mm)  # half of diameter, from specs FIG 10
    rad_in = Cylinder(radius=(0.5 * 63.5) * units.mm)

    # Top adapter
    fa.add_cell(rad_out & ~rad_in & top_adap,
                material=materials.al_ag3ne(),
                description='Top adapter',
                part='TopAdapter')

    # Bottom adapter
    fa.add_cell(rad_out & ~rad_in & bot_adap,
                material=materials.al_ag3ne(),
                description='Bottom adapter',
                part='BottomAdapter')

    # ==================================================================================================================
    # Fill model with water (recommended to avoid empty cells when exported to the external code)
    fa.complete_universe(material=fa.moderator_material)

    # ==================================================================================================================
    # Construct the fuel bundle (i.e. add all the fuel- and side plates, and moderator in between)
    # More efficient to construct bundle after filling universe with water
    fa.construct_bundle()

    # ==================================================================================================================
    # Initiate the fuel material
    fa.fuel_bundle().depletion_mesh.initial_material_distribution = \
        materials.fuel_leu(plate_volume=0.51 * units.mm * 63.00 * units.mm * 593.7 * units.mm)

    return fa


if __name__ == '__main__':
    from applications.assembly_archiver import main
    main(source=build, world_box=(7.71 * units.cm, 8.1 * units.cm))

Plate assembly with burnable absorbers build script

"""
Standard fuel assembly
======================

Standard fuel assembly with 484 grams U-235, and cadmium absorbers in the grid plates.

Assumptions and simplifications
-------------------------------

 1. To preserve a fixed water gap, the pitch (distance to fuel meat centers) for the outer plates were adjusted.
    Need to verify that this is the correct assumption.
 2. No additional handling structures were modeled on top of the assembly.

"""
from core.fuel_assembly import PlateAssemblyWithBurnableAbsorbers, WireBurnableAbsorber
from csg import *
from core.units import units
from core.place_holders import _
from material_library.moderators import LightWater
from . import materials


def build(*args, **kwargs):

    fa = PlateAssemblyWithBurnableAbsorbers(name='OPAL_standard_fuel_assembly', **kwargs)
    fa.description = 'Standard fuel assembly with burnable absorbers'

    # ==================================================================================================================
    # set some QA parameters
    fa.__source__ = 'OPAL_reactor_specification_V2.pdf, Table 3, Figures 3 & 4'
    fa.__version__ = '0.3.0'
    fa.__author__ = 'francois'

    # ==================================================================================================================
    # set plate bundle parameters
    fa.number_of_plates = 21
    # Note that, to preserve the water gap, the outer pitch from fuel plate to fuel plate should be larger
    fa.pitches = [2.45 * units.mm + 0.5 * 1.5 * units.mm + 0.5 * 1.35 * units.mm] + \
                 [2.45 * units.mm + 1.35 * units.mm] * 18 +\
                 [2.45 * units.mm + 0.5 * 1.5 * units.mm + 0.5 * 1.35 * units.mm]
    #                   exterior plate             inner plates                   exterior plate
    fa.plate_width = [1.5 * units.mm] + [1.35 * units.mm] * 19 + [1.5 * units.mm]
    fa.plate_length = 75.0 * units.mm
    fa.plate_height = [825 * units.mm] + [655 * units.mm] * 19 + [825 * units.mm]

    fa.meat_width = 0.61 * units.mm
    fa.meat_length = 65.0 * units.mm

    fa.meat_height = 615.0 * units.mm

    fa.grid_plate_pitch = 80.5 * units.mm - 2.0 * 5.0 * units.mm

    fa.slot_insert_depth = 2.5 * units.mm
    fa.slot_insert_width = [1.6 * units.mm] + [1.45 * units.mm] * 19 + [1.6 * units.mm]

    fa.box = [-40.25 * units.mm, 40.25 * units.mm, -40.25 * units.mm, 40.25 * units.mm]

    top = (1045 - 615 - 158 - 145) * units.mm
    fa.axial_region = [-0.5 * fa.meat_height[0] - 158 * units.mm, 0.5 * fa.meat_height[0] + top]

    fa.clad_material = materials.cladding()
    fa.grid_material = materials.al6061()
    fa.moderator_material = LightWater(mass_density=0.99160 * units.g / units.cc, temperature=(273.15 + 40.0) * units.K)

    # ==================================================================================================================
    # Set the BA parameters
    fa.ba_groove_width = 0.5 * units.mm
    fa.ba_groove_depth = 0.6 * units.mm

    ba = WireBurnableAbsorber()
    ba.length = 308 * units.mm
    ba.diameter = 0.493 * units.mm
    ba.axial_seating = (-0.5 * 615 + 123 + 0.5 * 308) * units.mm

    # Only odd numbered plates has a wire (on each side)
    fa.ba_loading = \
    [[_ , _ ],
     [ba, ba],
     [_ , _ ],
     [ba, ba],
     [_,   _],
     [ba, ba],
     [_,   _],
     [ba, ba],
     [_,   _],
     [ba, ba],
     [_,   _],
     [ba, ba],
     [_,   _],
     [ba, ba],
     [_,   _],
     [ba, ba],
     [_,   _],
     [ba, ba],
     [_,   _],
     [ba, ba],
     [_,   _]]

    # Add the fuel bundle cells
    fa.construct_bundle()

    # ==================================================================================================================
    # End box section

    cell.begin_part('Adapter')

    axial = region_macros.axial_strip(lower=(-0.5 * 615 - 158 - 145) * units.mm,
                                      upper=(-0.5 * 615 - 158) * units.mm)

    adapter = region_macros.clipped_box(diameter=69 * units.mm, cut=8 * units.mm)

    fa.add_cell(adapter & ~primitives.Cylinder(radius=30 * units.mm) & axial,
                material=materials.al6061(),
                description='Assembly end box')

    # ==================================================================================================================
    # Initiate the fuel material
    fa.fuel_bundle().depletion_mesh.initial_material_distribution = materials.standard_fuel()

    # ==================================================================================================================
    # Initiate the BA material
    fa.ba_bundle().depletion_mesh.initial_material_distribution = materials.cadmium()

    # ==================================================================================================================
    # Mesh completion settings
    fa.set_background(material=fa.moderator_material)      # fill remaining space with water
    fa.mesh_completion_required = True                     # ensure that missing water cells are added during archiving

    return fa


if __name__ == '__main__':
    from applications.assembly_archiver import main
    main(source=build)

Plate assembly with intra assembly control

"""
Control Fuel Assembly
=====================

Model of the special control (17 plate) fuel assembly with 93% enriched uranium.

Sources
-------
The IRR-1 facility specification document. In particular Table 5 and Figure 3.

Assumptions and Simplifications
-------------------------------

The upper section of the control assembly, containing the drive slot mechanisms, were not modelled.

Outstanding Issues
------------------

 1. The structure above the active fuel is not clear, and is currently simply modelled as water.

Version Log
-----------
 - 1.0.0 : Initial model definition

"""
from core import *
from csg import *
from material_library.moderators import LightWater

from . import materials
from .fuel_assembly import build_nozzle

upper_extent = 0.5 * 625.5 * units.mm + (1255 * units.mm - 625.5 * units.mm)


def guide_structure(ext=9.975 * units.mm, include_grid=False):

    cells = cell.Part()

    pitches = 2.1 * units.mm + 0.51 * units.mm + 2.0 * 0.38 * units.mm

    guide_box_center = 0.5 * 17 * pitches + 6.93 * units.mm

    lower_plate = (guide_box_center - 0.5 * 6.35 * units.mm - 1.27 * units.mm,
                   guide_box_center - 0.5 * 6.35 * units.mm)

    grid_plate_pitch = 76.1 * units.mm - 2.0 * 4.75 * units.mm

    axial = region_macros.axial_strip(-0.5 * 625.5 * units.mm,
                                      0.5 * 625.5 * units.mm + (1255 * units.mm - 625.5 * units.mm))

    cells.add_cell(axial &
                   region_macros.rectangle(-0.5 * grid_plate_pitch, 0.5 * grid_plate_pitch,
                                           lower_plate[0], lower_plate[1]),
                   material=materials.al6061(),
                   part='GuidePlates')

    upper_plate = (guide_box_center + 0.5 * 6.35 * units.mm,
                   guide_box_center + 0.5 * 6.35 * units.mm + 1.27 * units.mm)

    axial = region_macros.axial_strip(-0.5 * 604 * units.mm - 44 * units.mm,
                                      0.5 * 625.5 * units.mm + (1255 * units.mm - 625.5 * units.mm))

    cells.add_cell(axial &
                   region_macros.rectangle(-0.5 * grid_plate_pitch, 0.5 * grid_plate_pitch,
                                           upper_plate[0], upper_plate[1]),
                   material=materials.al6061(),
                   part='GuidePlates')

    # extended grid plates
    if include_grid:
        lower_bound = 0.0 * units.mm
    else:
        lower_bound = 0.5 * 17 * pitches + 0.5 * pitches

    cells.add_cell(axial & region_macros.rectangle(-0.5 * grid_plate_pitch - 4.75 * units.mm, -0.5 * grid_plate_pitch,
                                                   lower_bound, 0.5 * 80 * units.mm),
                   material=materials.al6061(),
                   part='Box')

    cells.add_cell(axial & region_macros.rectangle(0.5 * grid_plate_pitch, 0.5 * grid_plate_pitch + 4.75 * units.mm,
                                                   lower_bound, 0.5 * 80 * units.mm),
                   material=materials.al6061(),
                   part='Box')

    # add the guide channel
    axial = region_macros.axial_strip(-0.5 * 625.5 * units.mm - ext,
                                      0.5 * 625.5 * units.mm + (1255 * units.mm - 625.5 * units.mm))

    cells.add_cell(axial & region_macros.rectangle(-0.5 * grid_plate_pitch, 0.5 * grid_plate_pitch,
                                                   lower_plate[1], upper_plate[0]),
                   facility='control-guide')

    # reflect to get bottom channel

    reflected = cell.reflect(ALIGNMENT.Y_AXIS, *cells)

    cells.add_cells(*reflected)

    # add the adapter here

    clip = region_macros.rectangle(-0.5 * grid_plate_pitch, 0.5 * grid_plate_pitch,
                                   -upper_plate[0], upper_plate[0])

    nozzle = build_nozzle(2.0 * upper_plate[0],
                          ext, clip)

    cells.add_cells(*nozzle)

    return cells


def build(*args, **kwargs):

    asm = fuel_assembly.PlateAssembly(name='IRR-1-control-fuel-assembly')
    asm.description = 'Control Fuel Assembly'

    # ==================================================================================================================
    # Set some QA parameters
    asm.__source__ = 'IRR-1 REACTOR SPECIFICATION'
    asm.__version__ = '1.0.0'
    asm.__author__ = 'francois'

    # ==================================================================================================================
    # Set fuel bundle parameters
    asm.number_of_plates = 17

    asm.meat_height = 604 * units.mm
    asm.meat_width = 0.51 * units.mm
    asm.meat_length = 63 * units.mm

    asm.plate_length = 71 * units.mm
    asm.plate_width = 0.51 * units.mm + 2.0 * 0.38 * units.mm
    # Rest of side plate extensions modelled outside the bundle region

    bottom_external_plate = - 0.5 * 604 * units.mm - 44 * units.mm
    lower_external_plate_extension = - 0.5 * 625.5 * units.mm - bottom_external_plate
    upper_external_plate_extension = 709 * units.mm - 625.5 * units.mm - lower_external_plate_extension

    extension_est = 0.3 * lower_external_plate_extension  # Estimated distance between bottom of plates and nozzle insert
    asm.plate_height = 625.5 * units.mm

    asm.slot_insert_depth = 2.2 * units.mm
    asm.slot_insert_width = 1.3 * units.mm

    # Coolant gap plus two times plate width
    asm.pitches = 2.1 * units.mm + asm.plate_width[0]

    asm.grid_plate_pitch = 76.1 * units.mm - 2.0 * 4.75 * units.mm

    # outer assembly dimensions
    asm.box = [-0.5 * 76.1 * units.mm, 0.5 * 76.1 * units.mm,
               -0.5 * 17 * asm.pitches[0] - 0.5 * asm.pitches[0],
                0.5 * 17 * asm.pitches[0] + 0.5 * asm.pitches[0]]
    asm.axial_region = [- 0.5 * 625.5 * units.mm - extension_est, +0.5 * 625.5 * units.mm + extension_est]

    asm.clad_material = materials.al6061(material.tags.clad)
    asm.grid_material = materials.al6061(material.tags.structural)
    asm.moderator_material = LightWater(pressure=1.8 * units.bar, temperature=35 * units.degC)

    # use custom object to add side plate enforcing

    asm.create_bundle_place_holder()

    # ==================================================================================================================
    # Grid plate extensions and handling structure above assembly box

    axial = region_macros.axial_strip(+0.5 * 625.5 * units.mm + extension_est, upper_extent)

    asm.add_cell(axial & asm.box &
                 (primitives.Px(-0.5 * asm.grid_plate_pitch) |
                  ~primitives.Px(+0.5 * asm.grid_plate_pitch)),
                 material=asm.grid_material,
                 part='Box',
                 description='Grid upper extension')

    # ==================================================================================================================
    # Bottom grid extensions

    axial = region_macros.axial_strip(-0.5 * 604 * units.mm - 44 * units.mm,
                                      -0.5 * asm.plate_height[0] - extension_est)

    asm.add_cell(axial & asm.box &
                 (primitives.Px(-0.5 * asm.grid_plate_pitch) |
                  ~primitives.Px(+0.5 * asm.grid_plate_pitch)),
                 material=asm.grid_material,
                 part='Box',
                 description='Grid lower extension')

    # ==================================================================================================================
    # Guide structure

    asm.add_facility('control-guide', state=asm.moderator_material)

    asm.add_cells(*guide_structure(extension_est))

    asm.complete_universe(material=asm.moderator_material)

    asm.construct_bundle()

    asm.fuel_bundle().depletion_mesh.initial_material_distribution = materials.uranium_aluminum()

    return asm


if __name__ == '__main__':
    from applications.assembly_archiver import main
    main(source=build)