Pin Type Fuel Assemblies

Collects all macros used to construct pin type fuel assemblies, as used in LWR (PWR, and BWR) reactor designs.

Unlike the typical MTR macros, which creates all fueled components during construction, pin type assemblies are constructed in two phases:

  1. First define all pins (fuel pins, guide tubes, etc), usually in separate build scripts.

  2. Then provide the pin layout in another build script defining the actual assembly.

This strategy was adapted to avoid having an extremely large number of very similar cells in the assembly.

Pins

Attention

Although pins are constructed separately, their activation during irradiation calculations are still controlled by the parent assembly. This is why burnable materials are not assigned directly to the pins, but rather bundle_tag instances.

Pin type assemblies are constructed by axially stacking two dimensional (radial) cell definitions. The latter is constructed by the PinCell macro, while the former is accomplished using a PinCellStack. Both are available form the core.fuel_assembly module.

Defining radial structure

Radial pin layers are constructed using the PinCell structure. A detailed description of its general use can be found here. The core.fuel_assembly also provides the following convenience function to create a typical fuel pin radial layout:

fuel_assembly.fuel_pin(diameter, gap, clad, bundle=assembly.fuel_bundle(), gas=Helium(), cladding=Zircaloy4(), fill=BoratedWater())

Create the radial structure of a typical fuel pin, consisting of a meat region, a gap and cladding.

Parameters:
  • diameter (length) – The diameter of the active region.

  • gap (length) – The gap size.

  • clad (length) – The cladding thickness.

  • bundle (bundle_tag) – Contents of the active region.

  • gas (material) – Material filling the gap between fuel and cladding.

  • cladding (material) – Cladding material.

  • fill (material) – Material surrounding the pin.

Returns:

A PinCell.

Note

This macro is not general enough to treat more sophisticated cases, e.g integrated burnable absorber layers, or inclusion of crate like structures. In these cases the PinCell construction should be used instead.

Defining axial structure

Different radial layers are stacked to form a complete pin using a PinCellStack. It is contained in the core.fuel_assembly module and is used in your build module as follows:

from core import *

def build(*args, **kwargs):

    asm = fuel_assembly.PinCellStack(name='MY_REACTOR_some_awesome_pin')

Apart from all the standard assembly parameters, it also defines the following:

pin_cell_stack.top_fill_material

: material

= `DryAir`

The material that will be used to fill the space above the last layer added.

pin_cell_stack.bottom_fill_material

: material

= `DryAir`

The material that will be used to fill the space below the last layer added.

pin_cell_stack.pitch

: length or region

This parameter defines the lattice (cookie cutter) cell which this pin will fill. It is either

  • A single length value, for a square pitch.

  • Two length values, the \(x\) and \(y\) dimensions of a rectangular pitch.

  • Any CSG region. For example a hexagon for hexagonal pin layouts.

Note

This parameter is optional, and will only be used to clip unbounded radial regions for visualization purposes.

The actual pin is constructed by adding consecutive layers using the following method:

pin_cell_stack.add_layer(lower_bound, upper_bound, data)

Adds a new radial layer.

Parameters:
  • lower_bound (length) – The minimum axial (\(z\)) value of the layer.

  • upper_bound – The maximum axial (\(z\)) value of the layer. Must be strictly larger than lower_bound.

  • data – The radial structure of the layer. Usually a PinCell instance, but can be any region.

Attention

Currently, the layers must be added in an increasing fashion from bottom to top.

Note

Even though any region can be added, make sure to use PinCell for at least the active region, otherwise the burnable material zones will not be set up correctly.

Finally, the pin structure is created using the pin_cell_stack.build method:

pin_cell_stack.build()

Creates all the radial cells, and stacks them axially. Returns the resulting Assembly.

Example:

>>> from core import *
>>> from core.fuel_assembly import PinCell, PinCellStack, Helium, ZircAlloy4
>>> fuel = PinCell() | (0.39218 * units.cm, bundle_tags.fuel) | (0.40005 * units.cm, Helium()) | (0.45720 * units.cm, Zircalloy4())
>>> solid = PinCell() | (0.45720 * units.cm, Zircalloy4())
>>> stack = PinCellStack(name='REACTOR-fuel-pin')
>>> stack.add_layer(20.0000 * units.cm, 35.1600 * units.cm, solid)
>>> stack.add_layer(35.1600 * units.cm, 300 * units.cm, fuel)
>>> stack.add_layer(300 * units.cm, 315.16 * units.cm, solid)
>>> stack.pitch = 2.0 * 0.62992 * units.cm
>>> pin = stack.build()
>>> pin.show()

Pins with different burnable materials

Many pin type assemblies contain pins with different fuel material compositions. To accommodate this, additional bundle_tag should be used. New tags of the same type are created by passing an index. For example,

>>> from core import *
>>> from core.fuel_assembly import PinCell, PinCellStack, Helium, ZircAlloy4
>>> std = PinCell() | (0.39218 * units.cm, bundle_tags.fuel) | (0.40005 * units.cm, Helium()) | (0.45720 * units.cm, Zircalloy4())
>>> rpr = PinCell() | (0.39218 * units.cm, bundle_tags.fuel(1)) | (0.40005 * units.cm, Helium()) | (0.45720 * units.cm, Zircalloy4())

will assign the fuel tag with index 0 (the default) to the first pin, and the fuel tag with index 1 to the second pin. These now represent two different burnable structures, which are managed independently. In particular, initial material distributions must be assigned for each one. See and Depletion meshes for pin type assemblies for more information.

Axially heterogeneous materials

Although axially heterogeneous fuel materials (e.g. breeder zones) can also be treated using the above bundle_tag mechanism (tags are assigned per radial region after all), this can become cumbersome. It is recommended to used and explicit initial (axial) material mesh instead. See Depletion meshes for pin type assemblies for an example.

Guide tubes

Guide tubes, used for control structures, irradiation targets or instruments, are added just like normal fuel pins (that is, by stacking different radial zones). Instead of assigning a bundle_tag to the interior region, a facility name is assigned. For example,

>>> from core import *
>>> from core.fuel_assembly import PinCell, ZircAlloy4
>>> gt = PinCell() | (0.56134 * units.cm, 'guide-tube') | (0.60198 * units.cm, ZircAlloy4())

This facility name is then used later to load targets, like control control fingers or absorbers.

Removable absorbers

Non integrated absorbers, that is, absorbers which can be added and removed from the assembly, typically in guide tubes, are modelled using a separate component. It can be constructed as a pin, or any other assembly.

Attention

Only the actual absorber structure should be modelled, and not the entire guide tube with the absorber. This is because the absorber will be loaded to the appropriately marked facility in the guide tube.

Absorbers are loaded using the pin_assembly.load_burnable_absorbers() method, or during core configuration.

Note

If removable absorber activation should be tracked, the absorber rods should be made burnable and added to the inventory before loading into the assembly.

Control fingers

The control rods entering intra assembly guides are modelled as separate control assemblies. The one difference is that a single instance can be used, as it would be automatically duplicated to produce all the fingers.

Pin Bundles

These are the assembly types that collects the pins in a particular layout, and adds some additional structure. They are contained in the fuel_bundle module.

Although the assembly types differ in pin layout and assembly shape, they all allow for the inclusion of cavities in the pin lattice. A cavity is any structure which breaks the regular lattice, and is typically used for additional control mechanisms like rods or burnable absorbers. They are added using the following call:

pin_assembly.add_cavity(region)

Register a the boundary of a hole in the pin lattice.

Parameters:

region (region) – The boundary of the hole.

The pins specified in the pin_assembly.pin_layout fill the bounding region outside these cavities.

Attention

Only the boundary of the cavity should be specified here. All cells filling the cavity are specified using the normal cell addition methods.

As with other assembly macros, after all parameters are specified, cells are constructed by calling construct_bundle().

Square and rectangular pin bundles

Assemblies with pins arranged in a rectangular mesh. There are two types in core.fuel_assembly:

  1. SquarePinBundle (or RectangularPinBundle), which defines only the cells containing the pin lattice.

  2. SquarePinAssembly (or RectangularPinAssembly), which also defines additional sleeve or spacer cells outside the pin lattice.

Since both are Assemblies, all the standard assembly parameters are available. In addition, the following parameters are exposed:

pin_assembly.size

: tuple

!

The number of rows and columns of the pin lattice (grid). For example, asm.size = 17, 18 means there are 17 rows (stacked along the \(y\)-axis) and 18 columns in the pin lattice.

pin_assembly.pin_cell_pitch

: length or tuple (length)

!

The pitch, or distance between neighboring pins in the lattice. Either a single length value if the lattice is square, or a two length values if the lattice is rectangular. In the latter case the \(x\) distance (neighboring columns) is given first, and the \(y\) distance (between neighboring rows) is the second value.

Note

Only fixed pitch, regular lattices are allowed.

pin_assembly.filler

: material or component

The material or component that should be used in the empty positions (denoted by the placeholder _) in the layout grid. This parameter is only required if there are empty positions in the grid, which typically arise if cavities (as specified using pin_assembly.add_cavity()) are present.

pin_assembly.pin_layout

: grid

!

The layout of pins in the rectangular lattice. This is specified as a two dimensional grid of size equal to pin_assembly.size, containing pin or empty placeholder tags.

from core import *
from . import assemblies

fp = assemblies.MY_REACTOR_fuel_pin()
gt = assemblies.MY_REACTOR_guide_pin()

fa.pin_layout = \
  [[fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
   [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
   [fp, fp, fp, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, fp, fp, fp],
   [fp, fp, fp, gt, fp, fp, fp, fp, fp, fp, fp, fp, fp, gt, fp, fp, fp],
   [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
   [fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp],
   [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
   [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
   [fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp],
   [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
   [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
   [fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp],
   [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
   [fp, fp, fp, gt, fp, fp, fp, fp, fp, fp, fp, fp, fp, gt, fp, fp, fp],
   [fp, fp, fp, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, fp, fp, fp],
   [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
   [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp]
   ]

Only the symmetric portion of the pin layout may be specified. For example, the above can be simplified by only specifying the upper left corner (1/4 of assembly), and then unfolding:

fa.pin_layout = \
    [[fp, fp, fp, fp, fp, fp, fp, fp, fa],
     [fp, fp, fp, fp, fp, fp, fp, fp, fp],
     [fp, fp, fp, fp, fp, gt, fp, fp, gt],
     [fp, fp, fp, gt, fp, fp, fp, fp, fp],
     [fp, fp, fp, fp, fp, fp, fp, fp, fp],
     [fp, fp, gt, fp, fp, gt, fp, fp, gt],
     [fp, fp, fp, fp, fp, fp, fp, fp, fp],
     [fp, fp, fp, fp, fp, fp, fp, fp, fp],
     [fp, fp, gt, fp, fp, gt, fp, fp, ip]]

fa.pin_layout = fa.pin_layout.unfold('r', 'b')

Attention

All rows and column entries must be specified. If there are cavities (added using pin_assembly.add_cavity()) in the lattice, use the empty placeholder _ in the positions that will be clipped.

pin_assembly.axial_region

: region

Specify the axial bound of the region containing the pin cell lattice. The radial bound is deduced automatically from pin_assembly.size and pin_assembly.pin_cell_pitch.

If not specified, the assembly will not be bounded axially (which is usually not a problem).

pin_assembly.background_material

: material

The material that should be used to fill the region outside the pin lattice.

Note

Do not specify this parameter if you plan to add custom cells (e.g an assembly box) outside the pin lattice!

Attention

If this parameter is not specified, no cells will be added around the lattice. Thus, in order complete the assembly construction, these cells must be added automatically, or one of the mesh completion mechanisms must be used.

The SquarePinAssembly (or RectangularPinAssembly) extends the basic SquarePinBundle by adding spacer regions outside the pin lattice. In order to accommodate this, the following additional parameters are required:

pin_assembly.spacer_pitch

: length or tuple [length]

This is the total pitch of assembly including the sleeve region. It must be larger that the size of the pin lattice, but less than or equal to the actual core pitch (distance between neighboring assemblies in the reactor core).

Either a single length value if the pitch is square, or a tuple of two length values for a rectangular pitch.

Note

This parameter is optional, and if it is not specified, the assembly will be equivalent to a SquarePinBundle.

pin_assembly.sleeve_material

: material

Material that fills the axial gap between spacer sleeve layers. Will be initiated to pin_assembly.background_material if this parameter was set.

The spacer sleeves (which usually coincides with crate structures within the pin lattice) are from bottom to top using the following:

pin_assembly.add_spacer(lower, upper, material)
Parameters:
  • lower (length) – The lower axial bound of the sleeve region.

  • upper – The upper axial bound of the sleeve region.

  • material – The material which should fill the sleeve region between lower and upper.

The spacers must be added in strictly increasing order. The regions between the spacers are filled with pin_assembly.sleeve_material.

../_images/pin-sleeve-il.png

Schematic showing were the sleeve spacers are present in the final assembly.

Hexagonal pin bundles

Under construction!

Loading burnable absorbers

All pin bundle type assemblies also allow burnable absorbers to be loaded using the following method:

pin_assembly.load_burnable_absorbers(layout, facility_name=None)

Loads absorbers into specified positions.

Parameters:
  • layout (grid [component]) – A grid giving the absorber instances to load. The grid should have the same dimensions as the assembly pin_assembly.size. Use empty placeholder tokens _ to denote positions that can not be loaded or should be skipped. E

  • facility_name (str) – The facility to target. If not specified, the first facility added to a pin will be assumed.

Removable absorber patterns can also be specified and loaded during core configuration, using the model.removable_absorber_load_map parameter.

Note

By its very definition, removable absorber loading is usually not defined in the assembly build script.

Depletion meshes for pin type assemblies

The principal fuel (or burnable) primitive in these assemblies are Pins, and bundles represent groups of pins. Pins are bundled by assigning them an index in the pin grid. For example, if the assembly has a \(5\times 5\) pin grid, then:

asm.burn_bundles[bundle_tags.fuel].depletion_mesh.bundle =\
 [[0, 0, 0, 0, 0],
  [0, 1, 1, 1, 0],
  [0, 1, 1, 1, 0],
  [0, 1, 1, 1, 0],
  [0, 0, 0, 0, 0]]

will group (and therefore activate) all outside pins together, while the interior pins belong to another group. Use the empty placeholder token _ to indicate positions that are not burnable pins (e.g instrument or control positions), or are managed by another bundle.

Grouping the pins in other fuel bundles or burnable absorber bundles, works exactly the same way.

Attention

If you have multiple bundles remember to initiate their materials. For example,

asm.fuel_bundles[bundle_tags.fuel] = materials.base_fuel()
asm.fuel_bundles[bundle_tags.fuel(1)] = materials.second_fuel_type()

asm.burn_bundles[bundle_tags.ba] = materials.ba_material()
#etc

The radial segments parameter of the fuel bundle’s depletion mesh divides the pins into radial rings. For example, in the above example case:

asm.burn_bundles[bundle_tags.fuel].depletion_mesh.segments = \
     [[0.5, 0.35, 0.15], [0.5, 0.5]]

will subdivide the outer pins (bundle 0) into three rings, with the inner most region containing 50% of the total area, the second 35% and the final ring 15%. The inner pins (bundle 1), is subdivided into two equal sized rings.

Finally, as with other primitives, the axial mesh divides the pins along their active height. If the pin contains different initial materials along its axial height (e.g. breeder zones), it is specified by manually setting the axial mesh, and the initial material distribution. For example,

asm.burn_bundles[bundle_tags.fuel].depletion_mesh.axial_layers =\
 [[0.25, 0.25, 0.25, 0.25]] * 2

asm.burn_bundles[bundle_tags.fuel] =\
 [materials.breeding(), materials.fuel(), materials.fuel(), materials.breeding()]

will divide the pins into 4 axial zones, with different initial material compositions.

Note

The materials will be assigned to all the pin bundles for that bundle tag. Pins with different axial structure should be handled by assigning them a different bundle tag.

Attention

Currently, if the axial structure and initial material distribution is defined here, it cannot be refined later. Thus make sure you use a fine enough mesh for depletion purposes. This restriction will be lifted in future versions.

Examples

Basic fuel pin

"""
Fuel Pin
=========

Standard fuel pin definition.



"""

from csg.composites import PinCell
from core.fuel_assembly import PinCellStack
from core import *
from . import materials


def build(bundle=bundle_tags.fuel, moderator=materials.borated_water()):

    # ==================================================================================================================
    # Active region pin cell
    # Source: Figure 2
    active = PinCell()

    active.add_cylindrical_shell(radius=0.39218 * units.cm, bundle=bundle)
    active.add_cylindrical_shell(radius=0.40005 * units.cm, material=materials.helium())
    active.add_cylindrical_shell(radius=0.45720 * units.cm, material=materials.zircaloy())
    active.fill_material = moderator

    # ==================================================================================================================
    # Pin cell for Inconel top and bottom spacer inner egg crates
    # Source: Figure 20
    inconel_spacer = PinCell()

    inconel_spacer.add_cylindrical_shell(radius=0.39218 * units.cm, bundle=bundle)
    inconel_spacer.add_cylindrical_shell(radius=0.40005 * units.cm, material=materials.helium())
    inconel_spacer.add_cylindrical_shell(radius=0.45720 * units.cm, material=materials.zircaloy())
    inconel_spacer.add_square_shell(radius=0.62208 * units.cm, material=moderator)
    inconel_spacer.fill_material = materials.inconel()

    # ==================================================================================================================
    # Pin cell for zircalloy intermediate spacer inner egg crates
    # Source: Figure 21
    zirc_spacer = PinCell()

    zirc_spacer.add_cylindrical_shell(radius=0.39218 * units.cm, bundle=bundle)
    zirc_spacer.add_cylindrical_shell(radius=0.40005 * units.cm, material=materials.helium())
    zirc_spacer.add_cylindrical_shell(radius=0.45720 * units.cm, material=materials.zircaloy())
    zirc_spacer.add_square_shell(radius=0.60978 * units.cm, material=moderator)
    zirc_spacer.fill_material = materials.zircaloy()

    # ==================================================================================================================
    # Pin cell for upper plenum
    # Source: Figure 3
    upper_plenum = PinCell()

    upper_plenum.add_cylindrical_shell(radius=0.06459 * units.cm, material=materials.inconel())
    upper_plenum.add_cylindrical_shell(radius=0.40005 * units.cm, material=materials.helium())
    upper_plenum.add_cylindrical_shell(radius=0.45720 * units.cm, material=materials.zircaloy())
    upper_plenum.fill_material = moderator

    # ==================================================================================================================
    # Pin cell for upper plenum in inconel egg crate
    # Source: Figure 3
    upper_plenum_grid = PinCell()

    upper_plenum_grid.add_cylindrical_shell(radius=0.06459 * units.cm, material=materials.inconel())
    upper_plenum_grid.add_cylindrical_shell(radius=0.40005 * units.cm, material=materials.helium())
    upper_plenum_grid.add_cylindrical_shell(radius=0.45720 * units.cm, material=materials.zircaloy())
    upper_plenum_grid.add_square_shell(radius=0.62208 * units.cm, material=moderator)
    upper_plenum_grid.fill_material = materials.inconel()

    # ==================================================================================================================
    # Solid stainless steel pin
    steel_pin = PinCell()

    steel_pin.add_cylindrical_shell(radius=0.45720 * units.cm, material=materials.zircaloy())
    steel_pin.fill_material = moderator

    # ==================================================================================================================
    # Solid zircaloy pin
    zirc_pin = PinCell()
    zirc_pin.add_cylindrical_shell(radius=0.45720 * units.cm, material=materials.zircaloy())
    zirc_pin.fill_material = moderator

    # ==================================================================================================================
    # Empty pin cell filled with water
    empty = PinCell()
    empty.fill_material = moderator

    # ==================================================================================================================
    # Pin cell stack
    # Source: Figure 29

    stack = PinCellStack(name='BEAVRS-fuel-pin')
    stack.add_layer(0.00000 * units.cm, 20.0000 * units.cm, empty)
    stack.add_layer(20.0000 * units.cm, 35.1600 * units.cm, steel_pin)
    stack.add_layer(35.1600 * units.cm, 36.0070 * units.cm, zirc_pin)
    stack.add_layer(36.0070 * units.cm, 37.8790 * units.cm, active)         # active region starts
    stack.add_layer(37.8790 * units.cm, 42.0700 * units.cm, inconel_spacer) # grid 1
    stack.add_layer(42.0700 * units.cm, 99.1640 * units.cm, active)
    stack.add_layer(99.1640 * units.cm, 104.879 * units.cm, zirc_spacer)    # grid 2
    stack.add_layer(104.879 * units.cm, 151.361 * units.cm, active)
    stack.add_layer(151.361 * units.cm, 157.076 * units.cm, zirc_spacer)    # grid 3
    stack.add_layer(157.076 * units.cm, 203.558 * units.cm, active)
    stack.add_layer(203.558 * units.cm, 209.273 * units.cm, zirc_spacer)    # grid 4
    stack.add_layer(209.273 * units.cm, 255.755 * units.cm, active)
    stack.add_layer(255.755 * units.cm, 261.470 * units.cm, zirc_spacer)    # grid 5
    stack.add_layer(261.470 * units.cm, 307.952 * units.cm, active)
    stack.add_layer(307.952 * units.cm, 313.667 * units.cm, zirc_spacer)    # grid 6
    stack.add_layer(313.667 * units.cm, 360.149 * units.cm, active)
    stack.add_layer(360.149 * units.cm, 365.864 * units.cm, zirc_spacer)    # grid 7
    stack.add_layer(365.864 * units.cm, 401.767 * units.cm, active)         # active region ends
    stack.add_layer(401.767 * units.cm, 412.529 * units.cm, upper_plenum)
    stack.add_layer(412.529 * units.cm, 416.720 * units.cm, upper_plenum_grid) # grid 8
    stack.add_layer(416.720 * units.cm, 421.223 * units.cm, upper_plenum)
    stack.add_layer(421.223 * units.cm, 423.272 * units.cm, zirc_pin)
    stack.add_layer(423.272 * units.cm, 426.617 * units.cm, empty)
    stack.add_layer(426.617 * units.cm, 435.444 * units.cm, steel_pin)
    stack.add_layer(435.444 * units.cm, 455.444 * units.cm, empty)

    stack.pitch = 2.0 * 0.62992 * units.cm

    stack.top_fill_material = moderator
    stack.bottom_fill_material = moderator

    return stack.build()


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

Guide pin

"""
Guide Pin
=========

Guide pin definition.

"""

from csg.composites import PinCell
from core.fuel_assembly import PinCellStack
from core import *
from . import materials


def build(moderator=materials.borated_water(), facility='guide-tube'):

    # ==================================================================================================================
    # Empty guide tube above dashpot
    # Source: Figure 4

    guide = PinCell()
    guide.add_cylindrical_shell(radius=0.56134 * units.cm, facility=facility, material=moderator)
    guide.add_cylindrical_shell(radius=0.60198 * units.cm, material=materials.zircaloy())
    guide.fill_material = moderator

    # ==================================================================================================================
    # Empty guide tube at dashpot
    # Source: Figure 5

    dashpot = PinCell()
    dashpot.add_cylindrical_shell(radius=0.50419 * units.cm, facility=facility, material=moderator)
    dashpot.add_cylindrical_shell(radius=0.54610 * units.cm, material=materials.zircaloy())
    dashpot.fill_material = moderator

    # ==================================================================================================================
    # Pin cell for Inconel top spacer inner egg crates
    # Source: Figure 20
    inconel_spacer = PinCell()

    inconel_spacer.add_cylindrical_shell(radius=0.56134 * units.cm, facility=facility, material=moderator)
    inconel_spacer.add_cylindrical_shell(radius=0.60198 * units.cm, material=materials.zircaloy())
    inconel_spacer.add_square_shell(radius=0.62208 * units.cm, material=moderator)
    inconel_spacer.fill_material = materials.inconel()

    # ==================================================================================================================
    # Pin cell for Inconel bottom spacer inner egg crates
    # Source: Figure 20
    inconel_spacer_dp = PinCell()

    inconel_spacer_dp.add_cylindrical_shell(radius=0.50519 * units.cm, facility=facility, material=moderator)
    inconel_spacer_dp.add_cylindrical_shell(radius=0.54610 * units.cm, material=materials.zircaloy())
    inconel_spacer_dp.add_square_shell(radius=0.62208 * units.cm, material=moderator)
    inconel_spacer_dp.fill_material = materials.inconel()

    # ==================================================================================================================
    # Pin cell for zircalloy intermediate spacer inner egg crates
    # Source: Figure 21
    zirc_spacer = PinCell()

    zirc_spacer.add_cylindrical_shell(radius=0.56134 * units.cm, facility=facility, material=moderator)
    zirc_spacer.add_cylindrical_shell(radius=0.60198 * units.cm, material=materials.zircaloy())
    zirc_spacer.add_square_shell(radius=0.60978 * units.cm, material=moderator)
    zirc_spacer.fill_material = materials.zircaloy()

    # ==================================================================================================================
    # Empty pin cell filled with water
    empty = PinCell()
    empty.fill_material = moderator

    # ==================================================================================================================
    # Pin cell stack
    # Source: Figure 30

    stack = PinCellStack(name='BEAVRS-guide-pin')

    stack.add_layer(0.00000 * units.cm, 35.1600 * units.cm, empty)
    stack.add_layer(35.1600 * units.cm, 37.8790 * units.cm, dashpot)  # active region starts
    stack.add_layer(37.8790 * units.cm, 42.0700 * units.cm, inconel_spacer_dp)  # grid 1
    stack.add_layer(42.0700 * units.cm, 45.0790 * units.cm, dashpot)
    stack.add_layer(45.0790 * units.cm, 99.1640 * units.cm, guide)
    stack.add_layer(99.1640 * units.cm, 104.879 * units.cm, zirc_spacer)  # grid 2
    stack.add_layer(104.879 * units.cm, 151.361 * units.cm, guide)
    stack.add_layer(151.361 * units.cm, 157.076 * units.cm, zirc_spacer)  # grid 3
    stack.add_layer(157.076 * units.cm, 203.558 * units.cm, guide)
    stack.add_layer(203.558 * units.cm, 209.273 * units.cm, zirc_spacer)  # grid 4
    stack.add_layer(209.273 * units.cm, 255.755 * units.cm, guide)
    stack.add_layer(255.755 * units.cm, 261.470 * units.cm, zirc_spacer)  # grid 5
    stack.add_layer(261.470 * units.cm, 307.952 * units.cm, guide)
    stack.add_layer(307.952 * units.cm, 313.667 * units.cm, zirc_spacer)  # grid 6
    stack.add_layer(313.667 * units.cm, 360.149 * units.cm, guide)
    stack.add_layer(360.149 * units.cm, 365.864 * units.cm, zirc_spacer)  # grid 7
    stack.add_layer(365.864 * units.cm, 401.767 * units.cm, guide)
    stack.add_layer(401.767 * units.cm, 416.720 * units.cm, inconel_spacer)  # grid 8
    stack.add_layer(416.720 * units.cm, 426.617 * units.cm, guide)
    stack.add_layer(426.617 * units.cm, 455.444 * units.cm, empty)

    stack.top_fill_material = moderator
    stack.bottom_fill_material = moderator

    stack.pitch = 2.0 * 0.62992 * units.cm

    # add the guide tube facility and fill it with water
    stack.add_facility(facility, state=moderator, shared=False)

    return stack.build()


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

Pin assembly

"""
Standard fuel assembly (3.4% enriched)
======================================

"""
from core.fuel_assembly import SquarePinAssembly
from csg import *
from . import materials


from . import assemblies


def add_spacers(target):
    """
    Add the grid spacers sleeves (outside the active pin cell mesh)

    """
    target.add_spacer(37.8790 * units.cm, 42.0700 * units.cm, material=materials.zircaloy())
    target.add_spacer(99.1640 * units.cm, 104.879 * units.cm, material=materials.zircaloy())
    target.add_spacer(151.361 * units.cm, 157.076 * units.cm, material=materials.zircaloy())
    target.add_spacer(203.558 * units.cm, 209.273 * units.cm, material=materials.zircaloy())
    target.add_spacer(255.755 * units.cm, 261.470 * units.cm, material=materials.zircaloy())
    target.add_spacer(307.952 * units.cm, 313.667 * units.cm, material=materials.zircaloy())
    target.add_spacer(360.149 * units.cm, 365.864 * units.cm, material=materials.zircaloy())
    target.add_spacer(401.767 * units.cm, 416.720 * units.cm, material=materials.zircaloy())


def build(moderator=materials.borated_water(), fuel=materials.fuel34(), *args, **kwargs):
    """

    moderator: Moderator material
    fuel: Fuel material

    return:
        Basic BEAVRS fuel assembly with empty control and instrument pins
    """

    fa = SquarePinAssembly(name='BEAVRS-fuel-assembly', size=(17, 17))

    fa.pin_cell_pitch = 2.0 * 0.62992 * units.cm
    fa.spacer_pitch = 2.0 * 10.73635 * units.cm
    # just to ensure the model has a finite bounding box
    fa.axial_region = region_macros.axial_strip(-0.1 * units.cm, 456 * units.cm)

    # pins
    fp = assemblies.BEAVRS_fuel_pin()
    gt = assemblies.BEAVRS_guide_pin()
    ip = assemblies.BEAVRS_instrument_guide()

    # Calls the helper routine to add all the external spacer grid positions
    #
    add_spacers(fa)

    # Material that should be used in the sleeve or spacer region outside the fuel pin lattice and spacer grids
    #
    fa.sleeve_material = moderator

    # qa =
    # [[fp, fp, fp, fp, fp, fp, fp, fp, fa],
    #  [fp, fp, fp, fp, fp, fp, fp, fp, fp],
    #  [fp, fp, fp, fp, fp, gt, fp, fp, gt],
    #  [fp, fp, fp, gt, fp, fp, fp, fp, fp],
    #  [fp, fp, fp, fp, fp, fp, fp, fp, fp],
    #  [fp, fp, gt, fp, fp, gt, fp, fp, gt],
    #  [fp, fp, fp, fp, fp, fp, fp, fp, fp],
    #  [fp, fp, fp, fp, fp, fp, fp, fp, fp],
    #  [fp, fp, gt, fp, fp, gt, fp, fp, ip]]
    #
    # fa.pin_layout = Grid(qa).unfold('r', 'b')

    fa.pin_layout = \
        [[fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
         [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
         [fp, fp, fp, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, fp, fp, fp],
         [fp, fp, fp, gt, fp, fp, fp, fp, fp, fp, fp, fp, fp, gt, fp, fp, fp],
         [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
         [fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp],
         [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
         [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
         [fp, fp, gt, fp, fp, gt, fp, fp, ip, fp, fp, gt, fp, fp, gt, fp, fp],
         [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
         [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
         [fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp],
         [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
         [fp, fp, fp, gt, fp, fp, fp, fp, fp, fp, fp, fp, fp, gt, fp, fp, fp],
         [fp, fp, fp, fp, fp, gt, fp, fp, gt, fp, fp, gt, fp, fp, fp, fp, fp],
         [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp],
         [fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp, fp]
         ]

    # Distribute a fixed material to all the pins (this can be changed later for assemblies with different
    # enrichment)
    fa.fuel_bundle().initial_material_distribution = fuel

    # construct
    fa.build()

    # add the cell outside the sleeve (to complete the universe

    fa.add_cell(~fa.sleeve_box(),
                material=moderator,
                description='Water around assembly (to complete universe)')

    fa.set_background(material=materials.borated_water())

    return fa


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