High Temperature Reactor (HTR) Designs

This chapter collects all macros and functions used to model typical HTR fuel and core designs. Common to all components is the notion of a dispersed particle distribution, which plays the role of a fueled primitive (just like plates are for MTR, and pins for LWR).

Note

It is important to note that the entire dispersed distribution is usually considered a single primitive, and not the individual coated particles, or pebbles. This design principle is followed because the number of individual fueled components (on their own), are usually orders more than what is encountered in other designs.

Generating Dispersed Particle Distributions

A dispersed particle distribution is defined by a number of particle (usually spheres) centers, sizes and identifiers defining the particle type. The pre processor has a number of options for creating or importing distributed particle distributions. This covers both the generation of coated particles inside fuel compacts, and pebbles in the core. These are described in the following sections.

All tools are contained in the core.disperse module.

Note

All particle distribution routines make use of the discrete element package YADE. See Installing YADE for instructions on how to obtain the tool.

Importing from external sources

There are a number of external tools that can generate both random or regular particle distributions. One example is using the disperser routine in Serpent. Output from these tools can be placed in a simple text file which is imported using the following function:

read_ascii_spheres(filename, u=units.cm, predicate=None, id_map=None)

Read dispersed spherical particle distribution from a text file. The file is assumed to have the following format:

  • Each particle entry is separated by a new line break.

  • Each line contains at least the 5 entries:

    • The \(x\), \(y\), \(z\) coordinates of the sphere center (floating point values)

    • The radius of the sphere (floating point value)

    • Particle identifier (integer or string).

Parameters:
  • filename (filepath) – Full path to target file name.

  • u – The units particle center coordinates are given in.

  • predicate – Used to filter particles. Must be a function object accepting five arguments (the particle center \(x\), \(y\), \(z\), size and id), returning True if the particle should be included, or False otherwise.

  • id_map (dict) – Used to change string ids present in the file to integer ids.

Attention

If the ids in filename are not integers, the id_map parameter is required.

Regular lattice distributions

Currently, the core.disperse can produce hexagonal close-packed distribution using

hcp(radius, shape, spacing=0.0 * units.cm, tag=0)

Create a hexagonal close-packed (HCP) particle distribution of equal shaped spheres.

Parameters:
  • radius (length) – Radius of the spheres that will be packed.

  • shape (region) – The boundary of the region to which the lattice should be clipped.

  • spacing (length) – The distance between neighboring spheres. If 0, spheres will touch.

  • tag – How ids should be assigned to generated spheres. If a fixed integer value, all particles will have the same id, otherwise this parameter must be a function accepting the particle position (\(x\), \(y\), \(z\)) coordinates as input, and returning an integer.

Attention

The region must be bounded!

For example, the following will create a close packing of spheres with radius 1 mm in a cylindrical region:

>>> from csg import *
>>> from core import disperse
>>> spheres = disperse.hzp(1 * units.mm, Cylinder(radius=1 * units.cm, lower_bound=-10 * units.cm, upper_bound=10 * units.cm))

Note

This will create a explicit distribution of spheres, contained within the bounding region. Instead of creating such an explicit list, which is not supported by all attached target codes, the HexagonalClosePacked, option can be used. Since it models the distribution as standard hexagonal lattice, it is supported by more attached solvers.

Closed packed spheres are also modelled using the HexagonalClosePacked or FaceCenteredCubic objects.

Random dense packing

A random dense packing (with spheres potentially touching), is produced by trying to fit as many randomly positioned spheres as possible into a specified region.

rdp(radius, shape, tag=0)

Create a Random Dense Packing of equal sized spheres, clipped to a specified region.

Parameters:
  • radius (length) – Radius of the spheres that will be packed.

  • shape (region) – The boundary of the region to which the lattice should be clipped.

  • tag – How ids should be assigned to generated spheres. If a fixed integer value, all particles will have the same id, otherwise this parameter must be a function accepting the particle position (\(x\), \(y\), \(z\)) coordinates as input, and returning an integer.

Note

Currently, there is no way to specify the target packing fraction or number of particles. If this is required, rather use a different tool Serpent disperser routine.

Gravity deposition

These distributions are generated using an explicit simulation of dropping particles into a container, and modelling their interaction under the relevant physical forces. The discrete element solver YADE can be used to perform such simulations.

Examples of simulation scripts can be found in the relevant HTR benchmark sets.

Coated Particles

The concept of a generic coated particle is represented by a CoatedParticle instance, which creates an arbitrary number of spherical shells, with the inner most sphere the kernel (or core) region. It is located in the fuel_assembly module, and apart from the standard component parameters:

cp.kernel_radius

: length

!

The radius of the innermost region.

cp.kernel_contents

: material or bundle_tag

The contents of the innermost region.

Note

If the coated particle will form part of a dispersed distribution, the kernel contents can also be specified when initiating the material of that distribution. Thus, it is not necessary to specify it when defining the coated particle.

Once the inner kernel size is specified, additional shells are added using:

cp.add_shell(radius, contents)

Add a new layer to the coated particle.

Parameters:
  • radius (length) – Radius of the new layer. Must be larger than the previously added layer.

  • contents (material) – Contents of the new layer.

Attention

The kernel_radius must be set prior to making this call.

For example, the following will create a simple particle with a single coating:

>>> from core import *
>>> from material_library.structural import SiliconCarbide
>>> cp = fuel_assembly.CoatedParticle(name='CP')
>>> cp.kernel_radius = 0.1 * units.mm
>>> cp.add_shell(0.15 * units.mm, SiliconCarbide())

The fuel_assembly also contains a macro for creating typical TRISO fuel particles:

fuel_assembly.TRISO(fuel_radius=0.2125 * units.mm, buffer_radius=0.3125 * units.mm, inner_pyc=0.3475 * units.mm, sic_radius=0.3825 * units.mm, outer_pyc=0.4225 * units.mm, buffer=PorousGraphite(), pyc=PyroliticGraphite(), sic=SiliconCarbide(), **kwargs)

Standard TRI-structural ISO-tropic fuel particle design.

Parameters:
  • fuel_radius (length) – Radius of the inner fuel kernel.

  • buffer_radius (length) – Radius of the buffer zone.

  • inner_pyc (length) – Radius of the inner pyrolitic carbon layer.

  • sic_radius (length) – Radius of the silicon carbide layer.

  • outer_pyc (length) – Radius of the outer pyrolitic carbon layer. This is also the total size of the particle.

  • buffer (material) – Material used in the buffer zone.

  • pyc (material) – Material used in the pyrolitic carbon layers.

  • sic (material) – Material used in the silicon carbide layer.

Pebble Bed Designs

Pebbles

The Pebble class in the fuel_assembly can be used to model typical pebble designs. Apart from the standard component parameters, it is defined by setting the following:

pebble.radius

: length

= 3 cm

Total (outer) radius of the pebble.

pebble.reflector_thickness

: length

= 5 mm

Thickness of the outer reflector (fuel free) layer.

pebble.reflector_material

: material

= Graphite

Material used in the reflector zone.

pebble.matrix

: material

= Graphite

Material used in the coated particle compact (fueled region), that is, the material filling the region outside the coated particles.

pebble.coolant

: material

= Helium

Material used to fill space outside the pebble.

Note

The default value used for reflector_material and matrix, is pure graphite (at 1.74 g/cc) without any impurities. Any reactivity estimation will be quite sensitive to the material used here, so be sure to replace the defaults with the correct composition.

Once all of the parameters are set, the actual fueled region is defined using:

pebble.pack(distribution, fuel_zone=None, coated_particle=TRISO(), fuel=None, packing_fraction=None, bundle=fuel)

Add the dispersed fuel particles to the fueled region of the pebble.

Parameters:
  • distribution

    Source describing the particle distribution. Can be produced by any of the mechanisms described in Generating Dispersed Particle Distributions:

    Distribution

    Description

    filepath

    Text file in a format supported by read_ascii_spheres()

    HexagonalClosePacked()

    HCP sphere distribution, modelled as a lattice.

    FaceCenteredCubic()

    FCC sphere distribution, modelled as a lattice.

  • fuel_zone (region) – Sub region bounding the active fuel region. If not specified, the full inner spherical region (with radius radius - reflector_thickness) will be used.

  • coated_particle (component or dict) – The particle(s) that should be used in the dispersion. If a single value, this particle will be used in all locations. Alternatively, a dict object with keys the ids appearing in the distribution and values the particle types.

  • fuel – The material that should fill the kernel region of the coated_particle. This parameter is optional here, as it can also be specified when initiating the burnable material(s).

  • packing_fraction (float) – The packing fraction of a close packed sphere distribution. This is only used if distribution is HexagonalClosePacked or FaceCenteredCubic.

Note

The pre generated distribution must be clipped to the fuel_zone. In particular, if using the default zone, the dispersed distribution should be generated in a sphere with radius radius - reflector_thickness.

Attention

This call will create all the cells defining the pebble structure, thus all parameters must be specified prior to this call.

Note

The reflector region is now defined as the region inside the outer sphere, but inside the fuel_region.

For example, the following will create a standard pebble using the shipped kernel distribution (with packing fraction):

Note

Dummy pebbles, or pebbles that do not contain any fuel compacts, should not be generated using the Pebble macro. Instead they are defined as generic assemblies. For example, the following will create a solid graphite pebble:

>>> from core import *
>>> from csg import *
>>> from material_library.reflectors import Graphite
>>> from material_library.gasses import Helium
>>> dp = assembly.Assembly(name='Dummy-pebble')
>>> dp.add_cell(primitives.Sphere(radius=3 * units.cm), material=Graphite())
>>> dp.add_cell(~primitives.Sphere(radius=3 * units.cm), material=Helium())

Pebble bed core

The core component is modelled using the PebbleBed class available in the reactor_core module. It is used in the build script as follows:

from core import *
from csg import *
from core.reactor_core import PebbleBed
from material_library.gasses import Helium

def build():

    bed = PebbleBed(name='MY_HTR-pebble-bed')
    bed.coolant = Helium()

The PebbleBed object is a standard assembly, with all the general parameters, and also uses the following parameters and methods:

pebble.coolant

: material

= Helium

The coolant used in the core. This is the material that surrounds the pebbles.

Similar to the coated particle distribution in pebbles, the distribution of pebbles in the core is set using the pack method:

pebble_bed.pack(distribution, pebbles, cavity=None)

Set the distribution of pebbles in the core.

Parameters:
  • distribution – Source describing the particle distribution. Can be produced by any of the mechanisms described in Generating Dispersed Particle Distributions. If this is a filepath, the file must be in the format readable by read_ascii_spheres().

  • pebbles – The pebbles(s) that should be used in the dispersion. If a single value, this pebble will be used in all locations. Alternatively, if more than one pebble type is present, a dict object with keys the ids appearing in the distribution and values the particle types.

  • cavity – Region in which the distribution should be placed. This parameter is optional here, as it can be specified later using the pebble_bed.add_fueled_cell() method.

Although any distribution can be used, ones generated using gravity sphere drop simulation are the most appropriate in this case.

Additional cells containing the pebble distribution are specified using pebble_bed.add_fueled_cell().

pebble_bed.add_fueled_cell(region)

Add additional cells which are filled with the pebble distribution (specified using pebble_ped.pack().

Parameters:

region (region) – Boundary of the cell.

Prismatic Designs

A typical prismatic element is modelled using the following steps:

  1. Create one or more fuel compacts.

  2. Stack the compacts or pellets to form one or more fuel (or absorber) pins.

  3. Distribute the pins, and specify the coolant holes, for one or more elements.

  4. Stack the elements to form a complete assembly.

Cylindrical compacts

The CylindricalCompact class in the fuel_assembly can be used to model typical cylindrical compact (pellet) designs. Apart from the standard component parameters, it is defined by setting the following:

pellet.radius

: length

= 3 cm

Total (outer) radius of the cylindrical compact.

pellet.matrix

: material

= Graphite

Material used in the coated particle compact (fueled region), that is, the material filling the region outside the coated particles.

pellet.coolant

: material

= Helium

Material used to fill space outside the pebble.

Note

The default value used for matrix, is pure graphite (at 1.74 g/cc) without any impurities. Any reactivity estimation will be quite sensitive to the material used here, so be sure to replace the defaults with the correct composition.

Once all of the parameters are set, the actual fueled region is defined using the same approach as pebble.pack().

Pellet stacks

A stack of cylindrical compacts are modelled using CompactPin. Compacts (pellets), as well as plugs or spacers, are added using the following interface:

compact_pin.add_layer(lower=None, upper=None, size=None, target=None)

Adds a layer to the compact stack.

Parameters:
  • lower (length) – The axial lower bound of the layer. Only needed for the first layer, as subsequent layers will assume the previous layer’s upper bound.

  • upper (length) – Upper bound of the axial layer.

  • size (length) – Size of the layer. Can be specified instead of upper.

  • target (component) – Contents of the layer. Usually a cylindrical compact, plug or spacer element.

Note

  1. Layers must be added from top to bottom.

  2. If lower is specified, a check is performed to see that it is the same as upper of the previous layer (if present).

The following example will create a simple pin with 5 pellets, and a graphite plug on the top and bottom:

from core.fuel_assembly import CompactPin, CylindricalCompact
from core.disperse import HexagonalClosePacked
from core.assembly import Assembly
from csg.primitives import Cylinder
from material_library.reflectors import Graphite
from material_library.gasses import Helium

# create the plug
plug = Assembly(name='Fuel_pin_plug')
plug.add_cell(Cylinder(radius=hole, lower_bound=0 * units.cm, upper_bound=2 * units.cm),
              material=Graphite())
plug.add_cell(~Cylinder(radius=hole, lower_bound=0 * units.cm, upper_bound=2 * units.cm),
              material=Helium())

# create the compact
p = (total_height - 4 * units.cm)/5.0
compact = CylindricalCompact(name='Test')
compact.radius = 0.98 * hole
compact.height = p
compact.pack(HexagonalClosePacked(), packing_fraction=0.1)

pin = CompactPin(name='Fuel-Pin')

pin.add_layer(-0.5 * total_height, size=2.0 * units.cm, target=plug)

for i in range(5):
    pin.add_layer(size=p, target=compact)

pin.add_layer(size=2.0 * units.cm, target=plug)

Prismatic element

A single prismatic block is modelled using PrismaticElement available in the :module:`fuel_assembly` module. Apart from the standard component parameters, it is defined by setting the following:

prismatic_element.size

: length

= 36 cm

The inner diameter of the hexagonal boundary of the element.

prismatic_element.height

: length

= 79.3 cm

The height (axial extent) of the element.

prismatic_element.orientation = X_AXIS

The orientation of the bounding hexagonal shape.

prismatic_element.grid_cell

: region

Set a custom bounding region of the underlying hexagonal pin layout. This should be used if the element has cavities, such as a central hole, or a control rod position.

If not specified, it defaults to the radial bound (a hexagon with diameter prismatic_element.size and orientation prismatic_element.orientation), extruded from 0 to prismatic_element.height.

prismatic_element.cell_pitch

: length

= 1.8796 cm

The pitch of the underlying hexagonal pin lattice.

Note

Equal to the inner diameter of the hexagonal elements in the tesselation.

prismatic_element.lattice_orientation = Y_AXIS

The orientation of the underlying hexagonal grid.

prismatic_element.fuel_hole_diameter

: length

= 1.27 cm

Diameter of the hole containing fuel. Can be used in prismatic_element.pin_cell().

prismatic_element.large_coolant_hole_diameter

: length

= 1.588 cm

The diameter of a large coolant hole. Used by prismatic_element.large_coolant_cell().

prismatic_element.small_coolant_hole_diameter

: length

= 1.27 cm

The diameter of a small coolant hole. Used by prismatic_element.small_coolant_cell().

prismatic_element.coolant

: material

= Helium

The material that fills coolant holes, as well as the space outside the prismatic_element.grid_cell.

prismatic_element.moderator

: material

= Graphite

The material that fills the interior of prismatic_element.grid_cell, that is, around all pins, and the filler region around the hexagonal lattice.

prismatic_element.layout

: hexagonal grid

Set the layout of pins within the hexagonal lattice. The pins are usually those produced by the functions, prismatic_element.pin_cell(), prismatic_element.solid_cell(), prismatic_element.large_coolant_cell(), or prismatic_element.small_coolant_cell(). Layout is specified using a hexagonal grid, in either axial coordinates or using a grid. An empty grid is created using prismatic_element.new_grid().

For example:

elem = PrismaticElement(name='test-prismatic-block')
elem.height = 70 * units.cm
elem.size = 5 * elem.cell_pitch

# set the layout
grid = elem.new_grid()
grid.orientation = 'y'

fc = elem.pin_cell(elem.fuel_hole_diameter, pin)
hl = elem.large_coolant_cell()

# fill the grid
grid[0] = -2, [fc, hl, fc, hl, fc]
grid[1] = -2, [fc, hl, hl, fc]
grid[2] = -2, [fc, fc, fc]

grid.fold()        # symmetric distribution

elem.layout = grid
elem.show()

Attention

Any new pin type created (using prismatic_element.pin_cell()), and added to the layout, will be activated separately during burnup calculations. See depletion_mesh_prismatic.

prismatic_element.pin_cell(diameter, pin, name=None)

Create a hexagonal pin cell.

Parameters:
  • diameter (length) – The hole within the graphite body that will contain the pin.

  • pin – Pin that will be placed in the hole. Either a compact stack, or cylindrical stack.

  • name (string) – Name of the pin cell. If not specified, one will be created based on the name of pin.

Returns an element that can be used when specifying prismatic_element.layout.

prismatic_element.solid_cell(name=None)

Creates and returns a simple solid pin cell, that can be used when specifying prismatic_element.layout.

prismatic_element.large_coolant_cell()

Returns a hexagonal pin, with a prismatic_element.large_coolant_hole_diameter size hole filled with prismatic_element.coolant.

prismatic_element.small_coolant_cell()

Returns a hexagonal pin, with a prismatic_element.small_coolant_hole_diameter size hole filled with prismatic_element.coolant.

Prismatic assembly

Models a complete prismatic

Customizing the depletion mesh for prismatic elements and assemblies

Custom Designs

Installing YADE

If you are working on Linux, follow the YADE installation instructions.

Note

It is our intention to also distribute YADE as a conda package once the compilation issues have been sorted out.

Examples

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

Pebble bed core build script

"""
Core Barrel and Central Reflector
=================================
"""

from core import *
from csg import *

from material_library.reflectors import Graphite
from core.reactor_core import PebbleBed


def build(*args, **kwargs):

    bed = PebbleBed(name='PBMR-400-core')

    # ------------------------------------------------------------------------------------------------------------------
    # assign pebbles
    axial = region_macros.axial_strip(0.0 * units.m, 11 * units.m)

    inner_column = primitives.Cylinder(diameter=1.0 * units.m)
    core_bound = primitives.Cylinder(diameter=3.7 * units.m)

    # ------------------------------------------------------------------------------------------------------------------
    # central reflector column (currently without SAS)
    A = Graphite(mass_density=1.80)
    A.name = 'Graphite (Grade A)'

    axial = region_macros.axial_strip(-250 * units.cm, 11 * units.m + 150 * units.cm)

    bed.add_cell(axial & inner_column & ~ primitives.Cylinder(diameter=0.2 * units.m),
                 material=A,
                 part='CentralReflector')

    bed.add_cell(axial & primitives.Cylinder(diameter=0.2 * units.m),
                 material=bed.coolant)

    return bed

Coated particles build script

Pebble build script