armi.reactor.grids module

This contains structured meshes in multiple geometries and spatial locators (i.e. locations).

Grids are objects that map indices (i, j, k) to spatial locations (x,y,z) or (t,r,z). They are useful for arranging things in reactors, such as:

  • Fuel assemblies in a reactor

  • Plates in a heat exchanger

  • Pins in a fuel assembly

  • Blocks in a fuel assembly (1-D)

Fast reactors often use a hexagonal grid, while other reactors may be better suited for Cartesian or RZT grids. This module contains representations of all these.

``Grid``s can be defined by any arbitrary combination of absolute grid boundaries and unit step directions.

Associated with grids are IndexLocations. Each of these maps to a single cell in a grid, or to an arbitrary point in the continuous space represented by a grid. When a Grid` is built, it builds a collection of ``IndexLocation``s, one for each cell.

In the ARMI armi.reactor module, each object is assigned a locator either from a grid or in arbitrary, continuous space (using a CoordinateLocation) on the spatialLocator attribute.

Below is a basic example of how to use a 2-D grid:

>>> grid = cartesianGridFromRectangle(1.0, 1.0)  # 1 cm square-pitch Cartesian grid
>>> location = grid[1,2,0]
>>> location.getGlobalCoordinates()

array([ 1., 2., 0.])

Grids can be chained together in a parent-child relationship. This is often used in ARMI where a 1-D axial grid (e.g. in an assembly) is being positioned in a core or spent-fuel pool. See example in armi.reactor.tests.test_grids.TestSpatialLocator.test_recursion().

The “radial” (ring, position) indexing used in DIF3D can be converted to and from the more quasi-Cartesian indexing in a hex mesh easily with the utility methods HexGrid.getRingPos() and indicesToRingPos().

Note

IndexLocation is intended to replace armi.reactor.locations.Location.

This module is designed to satisfy the spatial arrangement requirements of the Reactor package.

Throughout the module, the term global refers to the top-level coordinate system while the word local refers to within the current coordinate system defined by the current grid.

class armi.reactor.grids.LocationBase(i, j, k, grid)[source]

Bases: object

A namedtuple-like object for storing location information.

It’s immutable (you can’t set things after construction) and has names.

Notes

This was originally a namedtuple but there was a somewhat unbelievable bug in Python 2.7.8 where unpickling a reactor full of these ended up inexplicably replacing one of them with an AssemblyParameterCollection. The bug did not show up in Python 3.

Converting to this class solved that problem.

__getstate__()[source]

Used in pickling and deepcopy, this detaches the grid.

__setstate__(state)[source]

Unpickle a locator, the grid will attach itself if it was also pickled, otherwis this will be detached.

property i
property j
property k
property grid
__hash__()[source]

Define a hash so we can use these as dict keys w/o having exact object.

Notes

Including the grid attribute may be more robust; however, using only (i, j, k) allows dictionaries to use IndexLocations and (i,j,k) tuples interchangeably.

__lt__(that)[source]

A Locationbase is less than another if the pseudo-radius is less, or if equal, in order any index is less.

Examples

>>> grid = grids.hexGridFromPitch(1.0)
>>> grid[0, 0, 0] < grid[2, 3, 4]   # the "radius" is less
True
>>> grid[2, 3, 4] < grid[2, 3, 4]   # they are equal
False
>>> grid[2, 3, 4] < grid[-2, 3, 4]  # 2 is greater than -2
False
>>> grid[-2, 3, 4] < grid[2, 3, 4]  # -2 is less than 2
True
>>> grid[1, 3, 4] < grid[-2, 3, 4]  # the "radius" is less
True
__len__()[source]

Returns 3, the number of directions.

class armi.reactor.grids.IndexLocation(i, j, k, grid)[source]

Bases: armi.reactor.grids.LocationBase

An immutable location representing one cell in a grid.

The locator is intimately tied to a grid and together, they represent a grid cell somewhere in the coordinate system of the grid.

grid is not in the constructor (must be added after construction ) because the extra argument (grid) gives an inconsistency between __init__ and __new__. Unfortunately this decision makes whipping up IndexLocations on the fly awkward. But perhaps that’s ok because they should only be created by their grids.

__add__(that)[source]

Enable adding with other objects like this and/or 3-tuples.

Tuples are needed so we can terminate the recursive additions with a (0,0,0) basis.

detachedCopy()[source]

Make a copy of this locator that is not associated with a grid.

See also

armi.reactor.reactors.detach()

uses this

property parentLocation

Get the spatialLocator of the ArmiObject that this locator’s grid is anchored to.

For example, if this is one of many spatialLocators in a 2-D grid representing a reactor, then the parentLocation is the spatialLocator of the reactor, which will often be a CoordinateLocation.

property indices

Get the non-grid indices (i,j,k) of this locator.

This strips off the annoying grid tagalong which is there to ensure proper equality (i.e. (0,0,0) in a storage rack is not equal to (0,0,0) in a core).

It is a numpy array for two reasons: 1. It can be added and subtracted for the recursive computations

through different coordinate systems

  1. It can be written/read from the database.

getCompleteIndices() → Tuple[int, int, int][source]

Transform the indices of this object up to the top mesh.

The top mesh is either the one where there’s no more parent (true top) or when an axis gets added twice. Unlike with coordinates, you can only add each index axis one time. Thus a complete set of indices is one where an index for each axis has been defined by a set of 1, 2, or 3 nested grids.

This is useful for getting the reactor-level (i,j,k) indices of an object in a multi-layered 2-D(assemblies in core)/1-D(blocks in assembly) mesh like the one mapping blocks up to reactor in Hex reactors.

The benefit of that particular mesh over a 3-D one is that different assemblies can have different axial meshes, a common situation.

It will just return local indices for pin-meshes inside of blocks.

A tuple is returned so that it is easy to compare pairs of indices.

getLocalCoordinates(nativeCoords=False)[source]

Return the coordinates of the center of the mesh cell here in cm.

getGlobalCoordinates(nativeCoords=False)[source]

Get coordinates in global 3D space of the centroid of this object.

getGlobalCellBase()[source]

Return the cell base (i.e. “bottom left”), in global coordinate system.

getGlobalCellTop()[source]

Return the cell top (i.e. “top right”), in global coordinate system.

getRingPos()[source]

Return ring and position of this locator.

class armi.reactor.grids.CoordinateLocation(i, j, k, grid)[source]

Bases: armi.reactor.grids.IndexLocation

A triple representing a point in space.

This is still associated with a grid. The grid defines the continuous coordinate space and axes that the location is within. This also links to the composite tree.

getLocalCoordinates(nativeCoords=False)[source]

Return x,y,z coordinates in cm within the grid’s coordinate system.

getCompleteIndices()[source]

Top of chain. Stop recursion and return basis.

getGlobalCellBase()[source]

Return the cell base (i.e. “bottom left”), in global coordinate system.

getGlobalCellTop()[source]

Return the cell top (i.e. “top right”), in global coordinate system.

class armi.reactor.grids.Grid(unitSteps=(0, 0, 0), bounds=(None, None, None), unitStepLimits=((0, 1), (0, 1), (0, 1)), offset=None, armiObject=None)[source]

Bases: object

A connected set of cells characterized by indices mapping to space and vice versa.

The cells may be characterized by any mixture of regular repeating steps and user-defined steps in any direction.

For example, a 2-D hex lattice has constant, regular steps whereas a 3-D hex mesh may have user-defined axial meshes. Similar for Cartesian, RZT, etc.

Parameters
  • unitSteps (tuple of tuples, optional) –

    Describes the grid spatially as a function on indices. Each tuple describes how each (x,y,or z) dimension is influenced by (i,j,k). In other words, it is:

    (dxi, dxj, jxk), (dyi, dyj, dyk), (dzi, dzj, dzk)
    
    where dmn is the distance (in cm) that dimension m will change as a

    function of index n.

    Unit steps are used as a generic method for defining repetitive grids in a variety of geometries, including hexagonal and Cartesian. The tuples are not vectors in the direction of the translation, but rather grouped by direction. If the bounds argument is described for a direction, the bounds will be used rather than the unit step information. The default of (0, 0, 0) makes all dimensions insensitive to indices since the coordinates are calculated by the dot product of this and the indices. With this default, any dimension that is desired to change with indices should be defined with bounds. RZtheta grids are created exclusively with bounds.

  • bounds (3-tuple) – Absolute increasing bounds in cm including endpoints of a non-uniform grid. Each item represents the boundaries in the associated direction. Use Nones when unitSteps should be applied instead. Most useful for thetaRZ grids or other non-uniform grids.

  • unitStepLimits (3-tuple) – The limit of the steps in all three directions. This constrains step-defined grids to be finite so we can populate them with SpatialLocator objects.

  • offset (3-tuple, optional) – Offset in cm for each axis. By default the center of the (0,0,0)-th object is in the center of the grid. Offsets can move it so that the (0,0,0)-th object can be fully within a quadrant (i.e. in a Cartesian grid).

  • armiObject (ArmiObject, optional) – The ArmiObject that this grid describes. For example if it’s a 1-D assembly grid, the armiObject is the assembly. Note that self.armiObject.spatialGrid is self.

Examples

A 2D a rectangular grid with width (x) 2 and height (y) 3 would be:

>>> grid = Grid(unitSteps=((2, 0, 0), (0, 3, 0),(0, 0, 0)))

A regular hex grid with pitch 1 is:

>>> grid = Grid(unitSteps= ((sqrt(3)/2, 0.0, 0.0), (0.5, 1.0, 0.0), (0, 0, 0))

Note

For this unit hex the magnitude of the vector constructed using the 0th index of each tuple is 1.0.

Notes

Each dimension must either be defined through unitSteps or bounds. The combination of unitSteps with bounds was settled upon after some struggle to have one unified definition of a grid (i.e. just bounds). A hexagonal grid is somewhat challenging to represent with bounds because the axes are not orthogonal, so a unit-direction vector plus bounds would be required. And then the bounds would be wasted space because they can be derived simply by unit steps. Memory efficiency is important in this object so the compact representation of unitSteps-when-possible, bounds-otherwise was settled upon.

Design considerations include:

  • unitSteps are more intuitive as operations starting from the center of a cell, particularly with hexagons and rectangles. Otherwise the 0,0 position of a hexagon in the center of 1/3-symmetric hexagon is at the phantom bottom left of the hexagon.

  • Users generally prefer to input mesh bounds rather than centers (e.g. starting at 0.5 instead of 0.0 in a unit mesh is weird).

  • If we store bounds, computing bounds is simple and computing centers takes ~2x the effort. If we store centers, it’s the opposite.

  • Regardless of how we store things, we’ll need a Grid that has the lower-left assembly fully inside the problem (i.e. for full core Cartesian) as well as another one that has the lower-left assembly half-way or quarter-way sliced off (for 1/2, 1/4, and 1/8 symmetries). The offset parameter handles this.

  • Looking up mesh boundaries (to define a mesh in another code) is generally more common than looking up centers (for plotting or measuring distance).

  • A grid can be anchored to the object that it is in with a backreference. This gives it the ability to traverse the composite tree and map local to global locations without having to duplicate the composite pattern on grids. This remains optional so grids can be used for non-reactor-package reasons. It may seem slightly cleaner to set the armiObject to the parent’s spatialLocator itself but the major disadvantage of this is that when an object moves, the armiObject would have to be updated. By anchoring directly to Composite objects, the parent is always up to date no matter where or how things get moved.

  • Unit step calculations use dot products and must not be polluted by the bound indices. Thus we reduce the size of the unitSteps tuple accordingly.

reduce()[source]

Return the set of arguments used to create this Grid.

This is very much like the argument tuple from __reduce__, but we do not implement __reduce__ for real, because we are generally happy with __getstate__ and __setstate__ for pickling purposes. However, getting these arguments to __init__ is useful for storing Grids to the database, as they are more stable (less likely to change) than the actual internal state of the objects.

The return value should be hashable, such that a set of these can be created.

__getstate__()[source]

Pickling removes reference to armiObject

Removing the armiObject allows us to pickle an assembly without pickling the entire reactor. An Assembly.spatialLocator.grid.armiObject is the reactor, by removing the link here, we still have spatial orientation, but are not required to pickle the entire reactor to pickle an assembly.

This relies on the armiObject.__setstate__ to assign itself.

__setstate__(state)[source]

Pickling removes reference to armiObject

This relies on the ArmiObject.__setstate__ to assign itself.

__getitem__(ijk)[source]

Get a location by (i, j, k) indices. If it does not exist, create a new one and return it.

Notes

The method is defaultdict-like, in that it will create a new location on the fly. However, the class itself is not really a dictionary, it is just index-able. For example, there is no desire to have a __setitem__ method, because the only way to create a location is by retrieving it or through buildLocations.

items()[source]

Return list of ((i, j, k), IndexLocation) tuples.

backUp()[source]

Gather internal info that should be restored within a retainState.

restoreBackup()[source]
getCoordinates(indices, nativeCoords=False)[source]

Return the coordinates of the center of the mesh cell at the given given indices in cm.

getCellBase(indices)[source]

Get the mesh base (lower left) of this mesh cell in cm

getCellTop(indices)[source]

Get the mesh top (upper right) of this mesh cell in cm

getNeighboringCellIndices(i, j=0, k=0)[source]

Return the indices of the immediate neighbors of a mesh point in the plane.

getAboveAndBelowCellIndices(indices)[source]
cellIndicesContainingPoint(x, y=0.0, z=0.0)[source]

Return the indices of a mesh cell that contains a point.

overlapsWhichSymmetryLine(indices)[source]
getLabel(indices)[source]

Get a string label from a 0-based spatial locator.

getLocationFromRingAndPos(i, j, k=0)[source]

Return the location based on ring and position.

Parameters
  • i (int) – Ring index

  • j (int) – Position index

  • k (int, optional) – Axial index

See also

HexGrid.getLocationFromRingAndPos()

This implements a special method to transform the i, j location based on ring and position.

getIndexBounds()[source]

Get min index and number of indices in this grid.

Step-defined grids would be infinite but for the step limits defined in the constructor.

Notes

This produces output that is intended to be passed to a range statement.

getIndicesFromRingAndPos(ring, pos)[source]
getAllIndices()[source]

Get all possible indices in this grid.

buildLocations()[source]

Populate all grid cells with a characteristic SpatialLocator.

getRingPos(indices) → Tuple[int, int][source]

Get ring and position number in this grid.

For non-hex grids this is just i and j.

A tuple is returned so that it is easy to compare pairs of indices.

changePitch(xw, yw)[source]

Change the pitch of a Cartesian grid.

This also scales the offset.

class armi.reactor.grids.HexGrid(unitSteps=(0, 0, 0), bounds=(None, None, None), unitStepLimits=((0, 1), (0, 1), (0, 1)), offset=None, armiObject=None)[source]

Bases: armi.reactor.grids.Grid

Has 6 neighbors in plane.

property pitch

Get the hex-pitch of a regular hexagonal array.

getNeighboringCellIndices(i, j=0, k=0)[source]

Return the indices of the immediate neighbors of a mesh point in the plane.

Note that these neighbors are ordered counter-clockwise beginning from 2 o’clock. This is very important!

getLabel(indices)[source]

Hex labels start at 1.

getIndicesFromRingAndPos(ring, pos)[source]
getLocationFromRingAndPos(i, j, k=0)[source]

Return the location based on ring and position.

Parameters
  • i (int) – Ring index

  • j (int) – Position index

  • k (int, optional) – Axial index

getRingPos(indices)[source]

Get 1-based ring and position from normal indices.

See also

getIndicesFromRingAndPos()

does the reverse

overlapsWhichSymmetryLine(indices)[source]

Return a list of which lines of symmetry this is on.

If none, returns [] If on a line of symmetry in 1/6 geometry, returns a list containing a 6. If on a line of symmetry in 1/3 geometry, returns a list containing a 3. Only the 1/3 core view geometry is actually coded in here right now.

Being “on” a symmety line means the line goes through the middle of you.

getSymmetricIdenticalsThird(indices)[source]

This works by rotating the indices by 120 degrees twice, counterclockwise.

triangleCoords(indices)[source]

Return 6 coordinate pairs representing the centers of the 6 triangles in a hexagon centered here.

Ignores z-coordinate and only operates in 2D for now.

changePitch(newPitchCm)[source]

Change the hex pitch.

isInFirstThird(locator, includeTopEdge=False)[source]

True if locator is in first third of hex grid.

generateSortedHexLocationList(nLocs)[source]

Generate a list IndexLocations, sorted based on their distance from the center.

IndexLocation are taken from a full core.

Ties between locations with the same distance (e.g. A3001 and A3003) are broken by ring number then position number.

allPositionsInThird(ring, includeEdgeAssems=False)[source]

Returns a list of all the positions in a ring (in the first third)

Parameters
  • ring (int) – The ring to check

  • includeEdgeAssems (bool, optional) – If True, include repeated positions in odd ring numbers. Default: False

Notes

Rings start at 1, positions start at 1

Returns

positions

Return type

int

class armi.reactor.grids.ThetaRZGrid(unitSteps=(0, 0, 0), bounds=(None, None, None), unitStepLimits=((0, 1), (0, 1), (0, 1)), offset=None, armiObject=None)[source]

Bases: armi.reactor.grids.Grid

A grid characterized by azimuthal, radial, and zeta indices.

The angular meshes are limited to 0 to 2pi radians. R and Zeta are as in other meshes.

See Figure 2.2 in Derstine 1984, ANL. [DIF3D].

getCoordinates(indices, nativeCoords=False)[source]

Return the coordinates of the center of the mesh cell at the given given indices in cm.

indicesOfBounds(rad0, rad1, theta0, theta1, sigma=0.0001)[source]

Return indices corresponding to upper and lower radial and theta bounds.

Parameters
  • rad0 (float) – inner radius of control volume

  • rad1 (float) – outer radius of control volume

  • theta0 (float) – inner azimuthal location of control volume in radians

  • theta1 (float) – inner azimuthal of control volume in radians

  • sigma (float) – acceptable relative error (i.e. if one of the positions in the mesh are within this error it’ll act the same if it matches a position in the mesh)

Returns

tuple

Return type

i, j, k of given bounds

armi.reactor.grids.hexGridFromPitch(pitch, numRings=25, armiObject=None, pointedEndUp=False)[source]

Build a finite step-based 2-D hex grid from a hex pitch in cm.

Parameters
  • pitch (float) – Hex pitch (flat-to-flat) in cm

  • numRings (int, optional) – The number of rings in the grid to pre-populate with locatator objects. Even if positions are not pre-populated, locators will be generated there on the fly.

  • armiObject (ArmiObject, optional) – The object that this grid is anchored to (i.e. the reactor for a grid of assemblies)

  • pointedEndUp (bool, optional) – Rotate the hexagons 30 degrees so that the pointed end faces up instead of the flat.

Returns

A functional hexagonal grid object.

Return type

HexGrid

armi.reactor.grids.cartesianGridFromRectangle(width, height, numRings=25, isOffset=False, armiObject=None)[source]

Build a finite step-based 2-D Cartesian grid based on a width and height in cm.

isOffsetbool

If true will put first mesh cell fully within the grid instead of centering it on the crosshairs.

armi.reactor.grids.axialUnitGrid(numCells, armiObject=None)[source]

Build a 1-D unit grid in the k-direction based on a number of times. Each mesh is 1cm wide.

numCells + 1 mesh boundaries are added, since one block would require a bottom and a top.

armi.reactor.grids.thetaRZGridFromGeom(geom, armiObject=None)[source]

Build 2-D R-theta grid based on a Geometry object.

Parameters

geomInfo (list) – list of ((indices), assemName) tuples for all positions in core with input in radians

See also

armi.reactor.geometry.SystemLayoutInput.readGeomXML()

produces the geomInfo structure

Examples

>>> grid = grids.thetaRZGridFromGeom(geomInfo)
armi.reactor.grids.ringPosFromRingLabel(ringLabel)[source]

Convert a ring-based label like A2003B into 1-based ring, location indices.

armi.reactor.grids.indicesToRingPos(i, j)[source]

Convert spatialLocator indices to ring/position.

One benefit it has is that it never has negative numbers.

Notes

Ring, pos index system goes in counterclockwise hex rings.

armi.reactor.grids.getIndicesFromRingAndPos(ring, pos)[source]
armi.reactor.grids.addingIsValid(myGrid, parentGrid)[source]

True if adding a indices from one grid to another is considered valid.

In ARMI we allow the addition of a 1-D axial grid with a 2-D grid. We do not allow any other kind of adding. This enables the 2D/1D grid layout in Assemblies/Blocks but does not allow 2D indexing in pins to become inconsistent.