Source code for armi.utils.hexagon

# Copyright 2019 TerraPower, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

r"""
Generic hexagon math.

Hexagons are fundamental to advanced reactors.

.. image:: /.static/hexagon.png
    :width: 100%
"""

import math

import numpy as np

SQRT3 = math.sqrt(3.0)


[docs] def area(pitch): """ Area of a hex given the flat-to-flat pitch. Notes ----- The pitch is the distance between the center of the hexagons in the lattice. """ return SQRT3 / 2.0 * pitch**2
[docs] def side(pitch): r""" Side length of a hex given the flat-to-flat pitch. Pythagorean theorem says: .. math:: \frac{s}{2}^2 + \frac{p}{2}^2 = s^2 which you can solve to find p = sqrt(3)*s Notes ----- The pitch is the distance between the center of the hexagons in the lattice. """ return pitch / SQRT3
[docs] def corners(rotation=0): """ Return the coordinates of a unit hexagon, rotated as requested. Zero rotation implies flat-to-flat aligned with y-axis. Origin in the center. """ points = np.array( [ (1.0 / (2.0 * math.sqrt(3.0)), 0.5), (1.0 / math.sqrt(3.0), 0.0), (1.0 / (2.0 * math.sqrt(3.0)), -0.5), (-1.0 / (2.0 * math.sqrt(3.0)), -0.5), (-1.0 / math.sqrt(3.0), 0.0), (-1.0 / (2.0 * math.sqrt(3.0)), 0.5), ] ) rotation = rotation / 180.0 * math.pi rotation = np.array( [ [math.cos(rotation), -math.sin(rotation)], [math.sin(rotation), math.cos(rotation)], ] ) return np.array([tuple(rotation.dot(point)) for point in points])
[docs] def pitch(side): """ Calculate the pitch from the length of a hexagon side. Notes ----- The pitch is the distance between the center of the hexagons in the lattice. """ return side * SQRT3
[docs] def numRingsToHoldNumCells(numCells): """ Determine the number of rings in a hexagonal grid with this many hex cells. If the number of pins don't fit exactly into any ring, returns the ring just large enough to fit them. Parameters ---------- numCells : int The number of hex cells in a hex lattice Returns ------- numRings : int Number of rings required to contain numCells items. Notes ----- The first hex ring (center) holds 1 position. Each subsequent hex ring contains 6 more positions than the last. This method works by incrementing ring numbers until the number of items is reached or exceeded. It could easily be replaced by a lookup table if so desired. """ if numCells == 0: return 0 nPinRings = int(math.ceil(0.5 * (1 + math.sqrt(1 + 4 * (numCells - 1) // 3)))) return nPinRings
[docs] def numPositionsInRing(ring): """Number of positions in ring (starting at 1) of a hex lattice.""" return (ring - 1) * 6 if ring != 1 else 1
[docs] def totalPositionsUpToRing(ring: int) -> int: """Return the number of positions in a hexagon with a given number of rings.""" return 1 + 3 * ring * (ring - 1)
[docs] def getIndexOfRotatedCell(initialCellIndex: int, orientationNumber: int) -> int: """Obtain a new cell number after placing a hexagon in a new orientation. Parameters ---------- initialCellIndex : int Positive number for this cell's position in a hexagonal lattice. orientationNumber : Orientation in number of 60 degree, counter clockwise rotations. An orientation of zero means the first cell in each ring of a flags up hexagon is in the upper right corner. Returns ------- int New cell number across the rotation Raises ------ ValueError If ``initialCellIndex`` is not positive. If ``orientationNumber`` is less than zero or greater than five. """ if orientationNumber < 0 or orientationNumber > 5: raise ValueError(f"Orientation number must be in [0:5], got {orientationNumber}") if initialCellIndex > 1: if orientationNumber == 0: return initialCellIndex ring = numRingsToHoldNumCells(initialCellIndex) tot_pins = totalPositionsUpToRing(ring) newPinLocation = initialCellIndex + (ring - 1) * orientationNumber if newPinLocation > tot_pins: newPinLocation -= (ring - 1) * 6 return newPinLocation elif initialCellIndex == 1: return initialCellIndex raise ValueError(f"Cell number must be positive, got {initialCellIndex}")