openmc-designs/BEAVRS/beavrs/corebuilder.py

335 lines
11 KiB
Python

""" corebuilder.py
This module provides several classes that facilitate easier modeling of LWRs
with OpenMC
"""
import openmc
class TemplatedLattice(openmc.RectLattice):
"""Extends OpenMC lattices for setting universes in a templated fashion"""
def __init__(self, *args, **kwargs):
super(TemplatedLattice, self).__init__(*args, **kwargs)
self.positions = {}
self.template = []
def setTemplate(self, template):
""" Set the lattice template
:param template:
"""
self.template = template
def setPosition(self, key, univ):
"""Set an individual position in the lattice"""
self.positions[key] = univ
def updatePositions(self, univs):
"""Update multiple positions in the lattice with a dictionary"""
self.positions.update(univs)
def finalize(self):
if self.template == []:
raise Exception("No template set for:\n{0}".format(self))
universes = []
for row in self.template:
r = []
for item in row:
try:
r.append(self.positions[item.strip()])
except KeyError:
raise Exception("Must set template position '{0}' for template " +\
" in:\n{1}".format(item.strip(), self))
universes.append(r)
self.universes = universes
class InfinitePinCell(openmc.Universe):
""" Class for creating a simple pincell universe infinite in the z direction
InfinitePinCells consist of a set of radii and materials that define rings.
This class provides an easy way to wrap a pincell inside additional rings, or
a square grid around the outside.
"""
def __init__(self, *args, **kwargs):
""" Create a new InfinitePinCell
"""
super(InfinitePinCell, self).__init__(*args, **kwargs)
self.radii = []
self.box = []
self.fills = []
self.rot = []
self.finalized = False
def add_ring(self, fill, surf, box=False, rot=None):
""" Adds a ring to the pincell
Pincells must be built from the inside out. Materials for new rings are from
the surface of the previous ring to the provided new surface.
If the ring we want to add is a box, surf should be a rectangular prism.
It's up to the user to check for overlapping cell definitions.
:param fill: material or filling universe for new ring
:param surf: outer surface of new ring (or a rectangular region)
:param box: whether or not we're adding a boxy ring (e.g. for grids)
:param rot: openmc rotation string for filled cells
"""
self.radii.append(surf)
self.box.append(box)
self.fills.append(fill)
self.rot.append(rot)
def add_last_ring(self, fill, rot=None):
""" Adds the outermost cell in the pincell that goes to infinity
:param fill: material or filling universe for outermost region
"""
self.fills.append(fill)
self.rot.append(rot)
def finalize(self):
""" Creates Cell objects according to the pincell specification"""
if self.finalized:
return
## Loop over each ring and add cells for inner rings
params = zip(self.radii, self.box, self.fills, self.rot)
for i, (radius, box, fill, rot) in enumerate(params):
label = "{0} radial {1}: {2}".format(self._name, i, fill._name)
if i == 0:
# this is the first ring
cell = openmc.Cell(name=label, fill=fill)
cell.region = -radius
if not rot is None: cell.rotation = rot
self.add_cell(cell)
else:
# this is not the first ring
if self.box[i-1]:
# the last ring was a box
if box:
# this is a box ring, and the last one was also a box ring
cell = openmc.Cell(name=label, fill=fill)
cell.region = ~radius
if not rot is None: cell.rotation = rot
self.add_cell(cell)
else:
# this is a regular cylinder, and the last one was a box ring
cell = openmc.Cell(name=label, fill=fill)
cell.region = -r & ~self.radii[i-1]
self.add_cell(cell)
else:
# the last ring was a regular cylinder
if box:
# this is a box ring, and the last one was a regular cylinder
cell = openmc.Cell(name=label, fill=fill)
cell.region = +self.radii[i-1] & radius
if not rot is None: cell.rotation = rot
self.add_cell(cell)
else:
# this is a regular ring, and the last one was a regular cylinder
cell = openmc.Cell(name=label, fill=fill)
cell.region = +self.radii[i-1] & -radius
if not rot is None: cell.rotation = rot
self.add_cell(cell)
## Now write the outermost cell(s) that go to infinity
label = "{0} radial outer: {1}".format(self._name, self.fills[-1]._name)
if self.box[-1]:
# the last one is a box, we need 4 outer cells to infinity
cell = openmc.Cell(name=label, fill=self.fills[-1])
cell.region = +radius
if not self.rot[-1] is None: cell.rotation = self.rot[-1]
self.add_cell(cell)
else:
# the last one is a regular cylindrical ring - just one cell to infinity
cell = openmc.Cell(name=label, fill=self.fills[-1])
cell.region = +self.radii[-1]
if not self.rot[-1] is None: cell.rotation = self.rot[-1]
self.add_cell(cell)
self.finalized = True
class AxialPinCell(openmc.Universe):
""" Class for containing a complete axial description of a pincell
AxialPinCells consist of a set of InfinitePincells and the axial planes that
define the axial boundaries of each. They also allow for a fully-constructed
pincell to be "wrapped" by another pincell, e.g., pincells containing grids or
guide tubes.
"""
_created_cells = {}
def __init__(self, *args, **kwargs):
""" Create a new AxialPinCell"""
super(AxialPinCell, self).__init__(*args, **kwargs)
self.axials = []
self.pincells = []
self.finalized = False
self.outermost = None
def add_axial_section(self, axial_plane, pincell):
""" Adds an axial section to the stack
Stacks must be built from the bottom-up. Each new section goes from the
previous axial_plane to the given axial_plane (or from infinity to the
given plane if it's the first section added).
It's up to the user to ensure that all planes are z-planes, and that
sections are added in the correct order.
A call to add_last_axial_section must be made to add the top-most section.
:param axial_plane: Axial surface above which to add the new pincell
:param pincell: InfinitePincell or material to add to the top of the stack
"""
self.axials.append(axial_plane)
self.pincells.append(pincell)
if isinstance(pincell, InfinitePinCell):
self.compare_outermost(pincell)
def add_last_axial_section(self, pincell):
""" Adds the last axial section to the top of the stack
:param pincell: InfinitePincell or material that goes to infinity at the top
"""
self.pincells.append(pincell)
if isinstance(pincell, InfinitePinCell):
self.compare_outermost(pincell)
def compare_outermost(self, pincell):
""" Finds if the pincell has the largest outer radius amongst all sections"""
if not self.outermost:
self.outermost = pincell
else:
if isinstance(self.outermost.radii[-1], openmc.ZCylinder):
# current is a cylinder
current = self.outermost.radii[-1].coefficients['r']
else:
# current is a box
current = self.outermost.radii[-1].max_x2.y0
if isinstance(pincell.radii[-1], openmc.ZCylinder):
# new one is a cylinder
new = pincell.radii[-1].coefficients['r']
else:
# new one is a box
new = self.outermost.radii[-1].max_x2.y0
if new > current:
self.outermost = pincell
def add_wrapper(self, wrapper, surf=None):
""" Adds a pincell to wrap the height, returning a new InfinitePinCell
This should only be called AFTER all axial sections are added.
If the splitting surface is not given, this function will use the outer-most
radius amongst all pincells in the wrappee to form the ring of the new
InfinitePincell. It's up to the user to make sure all radii of the outer
pin are larger than this radius, otherwise cells in the wrapper may be clipped.
:param wrapper: Another InfinitePinCell or AxialPinCell to wrap this pincell
:param surf: Splitting surface of new ring
"""
if self == wrapper:
return self
new_name = "({0}) wrapped by ({1})".format(self._name, wrapper._name)
if new_name in self._created_cells:
return self._created_cells[new_name]
# Finalize the current pincell
self.finalize()
# Make a new pincell
new_pin = InfinitePinCell(name=new_name)
# If splitting surface is not given, use the outermost radius
box = False
rot = None
if surf is None:
pin = self.outermost
surf = pin.radii[-1]
box = pin.box[-1]
rot = pin.rot[-1]
# Fill the inner ring with the wrapped universe
new_pin.add_ring(self, surf, box=box, rot=rot)
# Fill the outermost ring with the wrapping universe
new_pin.add_last_ring(wrapper)
self._created_cells[new_name] = new_pin
return new_pin
def finalize(self):
""" Creates the Cells for this universe"""
if self.finalized:
return
# Instantiate radial cells
for pin in self.pincells:
if isinstance(pin, InfinitePinCell) and not pin.finalized:
pin.finalize()
# Instantiate the axial cells
for i, (pin, plane) in enumerate(zip(self.pincells, self.axials)):
label = "{0} axial {1}: {2}".format(self.name, i, pin.name)
cell = openmc.Cell(name=label, fill=pin)
if i == 0:
# Bottom section
cell.region = -plane
else:
# Middle section
cell.region = -plane & +self.axials[i-1]
self.add_cell(cell)
# Top section
label = "{0} axial top: {1}".format(self.name, self.pincells[-1].name)
cell = openmc.Cell(name=label, fill=self.pincells[-1])
cell.region = +self.axials[-1]
self.add_cell(cell)
self.finalized = True