mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-21 22:45:33 -04:00
400 lines
18 KiB
ReStructuredText
400 lines
18 KiB
ReStructuredText
.. _usersguide_geometry:
|
|
|
|
=================
|
|
Defining Geometry
|
|
=================
|
|
|
|
.. currentmodule:: openmc
|
|
|
|
--------------------
|
|
Surfaces and Regions
|
|
--------------------
|
|
|
|
The geometry of a model in OpenMC is defined using `constructive solid
|
|
geometry`_ (CSG), also sometimes referred to as combinatorial geometry. CSG
|
|
allows a user to create complex regions using Boolean operators (intersection,
|
|
union, and complement) on simpler regions. In order to define a region that we
|
|
can assign to a cell, we must first define surfaces which bound the region. A
|
|
surface is a locus of zeros of a function of Cartesian coordinates
|
|
:math:`x,y,z`, e.g.
|
|
|
|
- A plane perpendicular to the :math:`x` axis: :math:`x - x_0 = 0`
|
|
- A cylinder parallel to the :math:`z` axis: :math:`(x - x_0)^2 + (y -
|
|
y_0)^2 - R^2 = 0`
|
|
- A sphere: :math:`(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 - R^2 = 0`
|
|
|
|
Defining a surface alone is not sufficient to specify a volume -- in order to
|
|
define an actual volume, one must reference the *half-space* of a surface. A
|
|
surface half-space is the region whose points satisfy a positive of negative
|
|
inequality of the surface equation. For example, for a sphere of radius one
|
|
centered at the origin, the surface equation is :math:`f(x,y,z) = x^2 + y^2 +
|
|
z^2 - 1 = 0`. Thus, we say that the negative half-space of the sphere, is
|
|
defined as the collection of points satisfying :math:`f(x,y,z) < 0`, which one
|
|
can reason is the inside of the sphere. Conversely, the positive half-space of
|
|
the sphere would correspond to all points outside of the sphere, satisfying
|
|
:math:`f(x,y,z) > 0`.
|
|
|
|
In the Python API, surfaces are created via subclasses of
|
|
:class:`openmc.Surface`. The available surface types and their corresponding
|
|
classes are listed in the following table.
|
|
|
|
.. table:: Surface types available in OpenMC.
|
|
|
|
+----------------------+------------------------------+---------------------------+
|
|
| Surface | Equation | Class |
|
|
+======================+==============================+===========================+
|
|
| Plane perpendicular | :math:`x - x_0 = 0` | :class:`openmc.XPlane` |
|
|
| to :math:`x`-axis | | |
|
|
+----------------------+------------------------------+---------------------------+
|
|
| Plane perpendicular | :math:`y - y_0 = 0` | :class:`openmc.YPlane` |
|
|
| to :math:`y`-axis | | |
|
|
+----------------------+------------------------------+---------------------------+
|
|
| Plane perpendicular | :math:`z - z_0 = 0` | :class:`openmc.ZPlane` |
|
|
| to :math:`z`-axis | | |
|
|
+----------------------+------------------------------+---------------------------+
|
|
| Arbitrary plane | :math:`Ax + By + Cz = D` | :class:`openmc.Plane` |
|
|
+----------------------+------------------------------+---------------------------+
|
|
| Infinite cylinder | :math:`(y-y_0)^2 + (z-z_0)^2 | :class:`openmc.XCylinder` |
|
|
| parallel to | - R^2 = 0` | |
|
|
| :math:`x`-axis | | |
|
|
+----------------------+------------------------------+---------------------------+
|
|
| Infinite cylinder | :math:`(x-x_0)^2 + (z-z_0)^2 | :class:`openmc.YCylinder` |
|
|
| parallel to | - R^2 = 0` | |
|
|
| :math:`y`-axis | | |
|
|
+----------------------+------------------------------+---------------------------+
|
|
| Infinite cylinder | :math:`(x-x_0)^2 + (y-y_0)^2 | :class:`openmc.ZCylinder` |
|
|
| parallel to | - R^2 = 0` | |
|
|
| :math:`z`-axis | | |
|
|
+----------------------+------------------------------+---------------------------+
|
|
| Sphere | :math:`(x-x_0)^2 + (y-y_0)^2 | :class:`openmc.Sphere` |
|
|
| | + (z-z_0)^2 - R^2 = 0` | |
|
|
+----------------------+------------------------------+---------------------------+
|
|
| Cone parallel to the | :math:`(y-y_0)^2 + (z-z_0)^2 | :class:`openmc.XCone` |
|
|
| :math:`x`-axis | - R^2(x-x_0)^2 = 0` | |
|
|
+----------------------+------------------------------+---------------------------+
|
|
| Cone parallel to the | :math:`(x-x_0)^2 + (z-z_0)^2 | :class:`openmc.YCone` |
|
|
| :math:`y`-axis | - R^2(y-y_0)^2 = 0` | |
|
|
+----------------------+------------------------------+---------------------------+
|
|
| Cone parallel to the | :math:`(x-x_0)^2 + (y-y_0)^2 | :class:`openmc.ZCone` |
|
|
| :math:`z`-axis | - R^2(z-z_0)^2 = 0` | |
|
|
+----------------------+------------------------------+---------------------------+
|
|
| General quadric | :math:`Ax^2 + By^2 + Cz^2 + | :class:`openmc.Quadric` |
|
|
| surface | Dxy + Eyz + Fxz + Gx + Hy + | |
|
|
| | Jz + K = 0` | |
|
|
+----------------------+------------------------------+---------------------------+
|
|
|
|
Each surface is characterized by several parameters. As one example, the
|
|
parameters for a sphere are the :math:`x,y,z` coordinates of the center of the
|
|
sphere and the radius of the sphere. All of these parameters can be set either
|
|
as optional keyword arguments to the class constructor or via attributes::
|
|
|
|
sphere = openmc.Sphere(R=10.0)
|
|
|
|
# This is equivalent
|
|
sphere = openmc.Sphere()
|
|
sphere.r = 10.0
|
|
|
|
Once a surface has been created, half-spaces can be obtained by applying the
|
|
unary ``-`` or ``+`` operators, corresponding to the negative and positive
|
|
half-spaces, respectively. For example::
|
|
|
|
>>> sphere = openmc.Sphere(R=10.0)
|
|
>>> inside_sphere = -sphere
|
|
>>> outside_sphere = +sphere
|
|
>>> type(inside_sphere)
|
|
<class 'openmc.surface.Halfspace'>
|
|
|
|
Instances of :class:`openmc.Halfspace` can be combined together using the
|
|
Boolean operators ``&`` (intersection), ``|`` (union), and ``~`` (complement)::
|
|
|
|
>>> inside_sphere = -openmc.Sphere()
|
|
>>> above_plane = +openmc.ZPlane()
|
|
>>> northern_hemisphere = inside_sphere & above_plane
|
|
>>> type(northern_hemisphere)
|
|
<class 'openmc.region.Intersection'>
|
|
|
|
For many regions, a bounding-box can be determined automatically::
|
|
|
|
>>> northern_hemisphere.bounding_box
|
|
(array([-1., -1., 0.]), array([1., 1., 1.]))
|
|
|
|
While a bounding box can be determined for regions involving half-spaces of
|
|
spheres, cylinders, and axis-aligned planes, it generally cannot be determined
|
|
if the region involves cones, non-axis-aligned planes, or other exotic
|
|
second-order surfaces. For example, the :func:`openmc.get_hexagonal_prism`
|
|
function returns the interior region of a hexagonal prism; because it is bounded
|
|
by a :class:`openmc.Plane`, trying to get its bounding box won't work::
|
|
|
|
>>> hex = openmc.get_hexagonal_prism()
|
|
>>> hex.bounding_box
|
|
(array([-0.8660254, -inf, -inf]),
|
|
array([ 0.8660254, inf, inf]))
|
|
|
|
Boundary Conditions
|
|
-------------------
|
|
|
|
When a surface is created, by default particles that pass through the surface
|
|
will consider it to be transmissive, i.e., they pass through the surface
|
|
freely. If your model does not extend to infinity in all spatial dimensions, you
|
|
may want to specify different behavior for particles passing through a
|
|
surface. To specify a vacuum boundary condition, simply change the
|
|
:attr:`Surface.boundary_type` attribute to 'vacuum'::
|
|
|
|
outer_surface = openmc.Sphere(R=100.0, boundary_type='vacuum')
|
|
|
|
# This is equivalent
|
|
outer_surface = openmc.Sphere(R=100.0)
|
|
outer_surface.boundary_type = 'vacuum'
|
|
|
|
Reflective and periodic boundary conditions can be set with the strings
|
|
'reflective' and 'periodic'. Vacuum and reflective boundary conditions can be
|
|
applied to any type of surface. Periodic boundary conditions can be applied to
|
|
pairs of planar surfaces. For axis-aligned planes, matching periodic surfaces
|
|
can be determined automatically. For non-axis-aligned planes, it is necessary to
|
|
specify pairs explicitly using the :attr:`Surface.periodic_surface` attribute as
|
|
in the following example::
|
|
|
|
p1 = openmc.Plane(A=0.3, B=5.0, D=1.0, boundary_type='periodic')
|
|
p2 = openmc.Plane(A=0.3, B=5.0, D=-1.0, boundary_type='periodic')
|
|
p1.periodic_surface = p2
|
|
|
|
Rotationally-periodic boundary conditions can be specified for a pair of
|
|
:class:`XPlane` and :class:`YPlane`; in that case, the
|
|
:attr:`Surface.periodic_surface` attribute must be specified manually as well.
|
|
|
|
.. caution:: When using rotationally-periodic boundary conditions, your geometry
|
|
must be defined in the first quadrant, i.e., above the y-plane and
|
|
to the right of the x-plane.
|
|
|
|
.. _usersguide_cells:
|
|
|
|
-----
|
|
Cells
|
|
-----
|
|
|
|
Once you have a material created and a region of space defined, you need to
|
|
define a *cell* that assigns the material to the region. Cells are created using
|
|
the :class:`openmc.Cell` class::
|
|
|
|
fuel = openmc.Cell(fill=uo2, region=pellet)
|
|
|
|
# This is equivalent
|
|
fuel = openmc.Cell()
|
|
fuel.fill = uo2
|
|
fuel.region = pellet
|
|
|
|
In this example, an instance of :class:`openmc.Material` is assigned to the
|
|
:attr:`Cell.fill` attribute. One can also fill a cell with a :ref:`universe
|
|
<usersguide_universes>` or :ref:`lattice <usersguide_lattices>`.
|
|
|
|
The classes :class:`Halfspace`, :class:`Intersection`, :class:`Union`, and
|
|
:class:`Complement` and all instances of :class:`openmc.Region` and can be
|
|
assigned to the :attr:`Cell.region` attribute.
|
|
|
|
.. _usersguide_universes:
|
|
|
|
---------
|
|
Universes
|
|
---------
|
|
|
|
Similar to MCNP and Serpent, OpenMC is capable of using *universes*, collections
|
|
of cells that can be used as repeatable units of geometry. At a minimum, there
|
|
must be one "root" universe present in the model. To define a universe, an
|
|
instance of :class:`openmc.Universe` is created and then cells can be added
|
|
using the :meth:`Universe.add_cells` or :meth:`Universe.add_cell`
|
|
methods. Alternatively, a list of cells can be specified in the constructor::
|
|
|
|
universe = openmc.Universe(cells=[cell1, cell2, cell3])
|
|
|
|
# This is equivalent
|
|
universe = openmc.Universe()
|
|
universe.add_cells([cell1, cell2])
|
|
universe.add_cell(cell3)
|
|
|
|
Universes are generally used in three ways:
|
|
|
|
1. To be assigned to a :class:`Geometry` object (see
|
|
:ref:`usersguide_geom_export`),
|
|
2. To be assigned as the fill for a cell via the :attr:`Cell.fill` attribute,
|
|
and
|
|
3. To be used in a regular arrangement of universes in a :ref:`lattice
|
|
<usersguide_lattices>`.
|
|
|
|
Once a universe is constructed, it can actually be used to determine what cell
|
|
or material is found at a given location by using the :meth:`Universe.find`
|
|
method, which returns a list of universes, cells, and lattices which are
|
|
traversed to find a given point. The last element of that list would contain the
|
|
lowest-level cell at that location::
|
|
|
|
>>> universe.find((0., 0., 0.))[-1]
|
|
Cell
|
|
ID = 10000
|
|
Name = cell 1
|
|
Fill = Material 10000
|
|
Region = -10000
|
|
Rotation = None
|
|
Temperature = None
|
|
Translation = None
|
|
|
|
As you are building a geometry, it is also possible to display a plot of single
|
|
universe using the :meth:`Universe.plot` method. This method requires that you
|
|
have `matplotlib <http://matplotlib.org/>`_ installed.
|
|
|
|
.. _usersguide_lattices:
|
|
|
|
--------
|
|
Lattices
|
|
--------
|
|
|
|
Many particle transport models involve repeated structures that occur in a
|
|
regular pattern such as a rectangular or hexagonal lattice. In such a case, it
|
|
would be cumbersome to have to define the boundaries of each of the cells to be
|
|
filled with a universe. OpenMC provides a means to define lattice structures
|
|
through the :class:`openmc.RectLattice` and :class:`openmc.HexLattice` classes.
|
|
|
|
Rectangular Lattices
|
|
--------------------
|
|
|
|
A rectangular lattice defines a two-dimensional or three-dimensional array of
|
|
universes that are filled into rectangular prisms (lattice elements) each of
|
|
which has the same width, length, and height. To completely define a rectangular
|
|
lattice, one needs to specify
|
|
|
|
- The coordinates of the lower-left corner of the lattice
|
|
(:attr:`RectLattice.lower_left`),
|
|
- The pitch of the lattice, i.e., the distance between the center of adjacent
|
|
lattice elements (:attr:`RectLattice.pitch`),
|
|
- What universes should fill each lattice element
|
|
(:attr:`RectLattice.universes`), and
|
|
- A universe that is used to fill any lattice position outside the well-defined
|
|
portion of the lattice (:attr:`RectLattice.outer`).
|
|
|
|
For example, to create a 3x3 lattice centered at the origin in which each
|
|
lattice element is 5cm by 5cm and is filled by a universe ``u``, one could run::
|
|
|
|
lattice = openmc.RectLattice()
|
|
lattice.lower_left = (-7.5, -7.5)
|
|
lattice.pitch = (5.0, 5.0)
|
|
lattice.universes = [[u, u, u],
|
|
[u, u, u],
|
|
[u, u, u]]
|
|
|
|
Note that because this is a two-dimensional lattice, the lower-left coordinates
|
|
and pitch only need to specify the :math:`x,y` values. The order that the
|
|
universes appear is such that the first row corresponds to lattice elements with
|
|
the highest :math:`y` -value. Note that the :attr:`RectLattice.universes`
|
|
attribute expects a doubly-nested iterable of type :class:`openmc.Universe` ---
|
|
this can be normal Python lists, as shown above, or a NumPy array can be used as
|
|
well::
|
|
|
|
lattice.universes = np.tile(u, (3, 3))
|
|
|
|
For a three-dimensional lattice, the :math:`x,y,z` coordinates of the lower-left
|
|
coordinate need to be given and the pitch should also give dimensions for all
|
|
three axes. For example, to make a 3x3x3 lattice where the bottom layer is
|
|
universe ``u``, the middle layer is universe ``q`` and the top layer is universe
|
|
``z`` would look like::
|
|
|
|
lat3d = openmc.RectLattice()
|
|
lat3d.lower_left = (-7.5, -7.5, -7.5)
|
|
lat3d.pitch = (5.0, 5.0, 5.0)
|
|
lat3d.universes = [
|
|
[[u, u, u],
|
|
[u, u, u],
|
|
[u, u, u]],
|
|
[[q, q, q],
|
|
[q, q, q],
|
|
[q, q, q]],
|
|
[[z, z, z],
|
|
[z, z, z]
|
|
[z, z, z]]]
|
|
|
|
Again, using NumPy can make things easier::
|
|
|
|
lat3d.universes = np.empty((3, 3, 3), dtype=openmc.Universe)
|
|
lat3d.universes[0, ...] = u
|
|
lat3d.universes[1, ...] = q
|
|
lat3d.universes[2, ...] = z
|
|
|
|
Finally, it's possible to specify that lattice positions that aren't normally
|
|
without the bounds of the lattice be filled with an "outer" universe. This
|
|
allows one to create a truly infinite lattice if desired. An outer universe is
|
|
set with the :attr:`RectLattice.outer` attribute.
|
|
|
|
Hexagonal Lattices
|
|
------------------
|
|
|
|
OpenMC also allows creation of 2D and 3D hexagonal lattices. Creating a
|
|
hexagonal lattice is similar to creating a rectangular lattice with a few
|
|
differences:
|
|
|
|
- The center of the lattice must be specified (:attr:`HexLattice.center`).
|
|
- For a 2D hexagonal lattice, a single value for the pitch should be specified,
|
|
although it still needs to appear in a list. For a 3D hexagonal lattice, the
|
|
pitch in the radial and axial directions should be given.
|
|
- For a hexagonal lattice, the :attr:`HexLattice.universes` attribute cannot be
|
|
given as a NumPy array for reasons explained below.
|
|
- As with rectangular lattices, the :attr:`HexLattice.outer` attribute will
|
|
specify an outer universe.
|
|
|
|
For a 2D hexagonal lattice, the :attr:`HexLattice.universes` attribute should be
|
|
set to a two-dimensional list of universes filling each lattice element. Each
|
|
sub-list corresponds to one ring of universes and is ordered from the outermost
|
|
ring to the innermost ring. The universes within each sub-list are ordered from
|
|
the "top" (position with greatest y value) and proceed in a clockwise fashion
|
|
around the ring. The :meth:`HexLattice.show_indices` static method can be used
|
|
to help figure out how to place universes::
|
|
|
|
>>> print(openmc.HexLattice.show_indices(3))
|
|
(0, 0)
|
|
(0,11) (0, 1)
|
|
(0,10) (1, 0) (0, 2)
|
|
(1, 5) (1, 1)
|
|
(0, 9) (2, 0) (0, 3)
|
|
(1, 4) (1, 2)
|
|
(0, 8) (1, 3) (0, 4)
|
|
(0, 7) (0, 5)
|
|
(0, 6)
|
|
|
|
|
|
Note that by default, hexagonal lattices are positioned such that each lattice
|
|
element has two faces that are parallel to the :math:`y` axis. As one example,
|
|
to create a three-ring lattice centered at the origin with a pitch of 10 cm
|
|
where all the lattice elements centered along the :math:`y` axis are filled with
|
|
universe ``u`` and the remainder are filled with universe ``q``, the following
|
|
code would work::
|
|
|
|
hexlat = openmc.HexLattice()
|
|
hexlat.center = (0, 0)
|
|
hexlat.pitch = [10]
|
|
|
|
outer_ring = [u, q, q, q, q, q, u, q, q, q, q, q]
|
|
middle_ring = [u, q, q, u, q, q]
|
|
inner_ring = [u]
|
|
hexlat.universes = [outer_ring, middle_ring, inner_ring]
|
|
|
|
If you need to create a hexagonal boundary (composed of six planar surfaces) for
|
|
a hexagonal lattice, :func:`openmc.get_hexagonal_prism` can be used.
|
|
|
|
.. _usersguide_geom_export:
|
|
|
|
--------------------------
|
|
Exporting a Geometry Model
|
|
--------------------------
|
|
|
|
Once you have finished building your geometry by creating surfaces, cell, and,
|
|
if needed, lattices, the last step is to create an instance of
|
|
:class:`openmc.Geometry` and export it to an XML file that the
|
|
:ref:`scripts_openmc` executable can read using the
|
|
:meth:`Geometry.export_to_xml` method. This can be done as follows::
|
|
|
|
geom = openmc.Geometry(root_univ)
|
|
geom.export_to_xml()
|
|
|
|
# This is equivalent
|
|
geom = openmc.Geometry()
|
|
geom.root_universe = root_univ
|
|
geom.export_to_xml()
|
|
|
|
.. _constructive solid geometry: http://en.wikipedia.org/wiki/Constructive_solid_geometry
|
|
.. _quadratic surfaces: http://en.wikipedia.org/wiki/Quadric
|