mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-24 03:55:38 -04:00
969 lines
28 KiB
Python
969 lines
28 KiB
Python
from __future__ import annotations
|
|
import typing
|
|
from abc import ABC, abstractmethod
|
|
from collections.abc import Iterable
|
|
from math import cos, pi
|
|
from numbers import Real
|
|
|
|
import lxml.etree as ET
|
|
import numpy as np
|
|
|
|
import openmc.checkvalue as cv
|
|
from .._xml import get_text
|
|
from ..mesh import MeshBase
|
|
from .univariate import PowerLaw, Uniform, Univariate
|
|
|
|
|
|
class UnitSphere(ABC):
|
|
"""Distribution of points on the unit sphere.
|
|
|
|
This abstract class is used for angular distributions, since a direction is
|
|
represented as a unit vector (i.e., vector on the unit sphere).
|
|
|
|
Parameters
|
|
----------
|
|
reference_uvw : Iterable of float
|
|
Direction from which polar angle is measured
|
|
|
|
Attributes
|
|
----------
|
|
reference_uvw : Iterable of float
|
|
Direction from which polar angle is measured
|
|
|
|
"""
|
|
def __init__(self, reference_uvw=None):
|
|
self._reference_uvw = None
|
|
if reference_uvw is not None:
|
|
self.reference_uvw = reference_uvw
|
|
|
|
@property
|
|
def reference_uvw(self):
|
|
return self._reference_uvw
|
|
|
|
@reference_uvw.setter
|
|
def reference_uvw(self, uvw):
|
|
cv.check_type('reference direction', uvw, Iterable, Real)
|
|
uvw = np.asarray(uvw)
|
|
self._reference_uvw = uvw/np.linalg.norm(uvw)
|
|
|
|
@abstractmethod
|
|
def to_xml_element(self):
|
|
return ''
|
|
|
|
@classmethod
|
|
@abstractmethod
|
|
def from_xml_element(cls, elem):
|
|
distribution = get_text(elem, 'type')
|
|
if distribution == 'mu-phi':
|
|
return PolarAzimuthal.from_xml_element(elem)
|
|
elif distribution == 'isotropic':
|
|
return Isotropic.from_xml_element(elem)
|
|
elif distribution == 'monodirectional':
|
|
return Monodirectional.from_xml_element(elem)
|
|
|
|
|
|
class PolarAzimuthal(UnitSphere):
|
|
"""Angular distribution represented by polar and azimuthal angles
|
|
|
|
This distribution allows one to specify the distribution of the cosine of
|
|
the polar angle and the azimuthal angle independently of one another. The
|
|
polar angle is measured relative to the reference angle.
|
|
|
|
Parameters
|
|
----------
|
|
mu : openmc.stats.Univariate
|
|
Distribution of the cosine of the polar angle
|
|
phi : openmc.stats.Univariate
|
|
Distribution of the azimuthal angle in radians
|
|
reference_uvw : Iterable of float
|
|
Direction from which polar angle is measured. Defaults to the positive
|
|
z-direction.
|
|
|
|
Attributes
|
|
----------
|
|
mu : openmc.stats.Univariate
|
|
Distribution of the cosine of the polar angle
|
|
phi : openmc.stats.Univariate
|
|
Distribution of the azimuthal angle in radians
|
|
|
|
"""
|
|
|
|
def __init__(self, mu=None, phi=None, reference_uvw=(0., 0., 1.)):
|
|
super().__init__(reference_uvw)
|
|
if mu is not None:
|
|
self.mu = mu
|
|
else:
|
|
self.mu = Uniform(-1., 1.)
|
|
|
|
if phi is not None:
|
|
self.phi = phi
|
|
else:
|
|
self.phi = Uniform(0., 2*pi)
|
|
|
|
@property
|
|
def mu(self):
|
|
return self._mu
|
|
|
|
@mu.setter
|
|
def mu(self, mu):
|
|
cv.check_type('cosine of polar angle', mu, Univariate)
|
|
self._mu = mu
|
|
|
|
@property
|
|
def phi(self):
|
|
return self._phi
|
|
|
|
@phi.setter
|
|
def phi(self, phi):
|
|
cv.check_type('azimuthal angle', phi, Univariate)
|
|
self._phi = phi
|
|
|
|
def to_xml_element(self):
|
|
"""Return XML representation of the angular distribution
|
|
|
|
Returns
|
|
-------
|
|
element : lxml.etree._Element
|
|
XML element containing angular distribution data
|
|
|
|
"""
|
|
element = ET.Element('angle')
|
|
element.set("type", "mu-phi")
|
|
if self.reference_uvw is not None:
|
|
element.set("reference_uvw", ' '.join(map(str, self.reference_uvw)))
|
|
element.append(self.mu.to_xml_element('mu'))
|
|
element.append(self.phi.to_xml_element('phi'))
|
|
return element
|
|
|
|
@classmethod
|
|
def from_xml_element(cls, elem):
|
|
"""Generate angular distribution from an XML element
|
|
|
|
Parameters
|
|
----------
|
|
elem : lxml.etree._Element
|
|
XML element
|
|
|
|
Returns
|
|
-------
|
|
openmc.stats.PolarAzimuthal
|
|
Angular distribution generated from XML element
|
|
|
|
"""
|
|
mu_phi = cls()
|
|
uvw = get_text(elem, 'reference_uvw')
|
|
if uvw is not None:
|
|
mu_phi.reference_uvw = [float(x) for x in uvw.split()]
|
|
mu_phi.mu = Univariate.from_xml_element(elem.find('mu'))
|
|
mu_phi.phi = Univariate.from_xml_element(elem.find('phi'))
|
|
return mu_phi
|
|
|
|
|
|
class Isotropic(UnitSphere):
|
|
"""Isotropic angular distribution."""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
def to_xml_element(self):
|
|
"""Return XML representation of the isotropic distribution
|
|
|
|
Returns
|
|
-------
|
|
element : lxml.etree._Element
|
|
XML element containing isotropic distribution data
|
|
|
|
"""
|
|
element = ET.Element('angle')
|
|
element.set("type", "isotropic")
|
|
return element
|
|
|
|
@classmethod
|
|
def from_xml_element(cls, elem: ET.Element):
|
|
"""Generate isotropic distribution from an XML element
|
|
|
|
Parameters
|
|
----------
|
|
elem : lxml.etree._Element
|
|
XML element
|
|
|
|
Returns
|
|
-------
|
|
openmc.stats.Isotropic
|
|
Isotropic distribution generated from XML element
|
|
|
|
"""
|
|
return cls()
|
|
|
|
|
|
class Monodirectional(UnitSphere):
|
|
"""Monodirectional angular distribution.
|
|
|
|
A monodirectional angular distribution is one for which the polar and
|
|
azimuthal angles are always the same. It is completely specified by the
|
|
reference direction vector.
|
|
|
|
Parameters
|
|
----------
|
|
reference_uvw : Iterable of float
|
|
Direction from which polar angle is measured. Defaults to the positive
|
|
x-direction.
|
|
|
|
"""
|
|
|
|
def __init__(self, reference_uvw: typing.Sequence[float] = [1., 0., 0.]):
|
|
super().__init__(reference_uvw)
|
|
|
|
def to_xml_element(self):
|
|
"""Return XML representation of the monodirectional distribution
|
|
|
|
Returns
|
|
-------
|
|
element : lxml.etree._Element
|
|
XML element containing monodirectional distribution data
|
|
|
|
"""
|
|
element = ET.Element('angle')
|
|
element.set("type", "monodirectional")
|
|
if self.reference_uvw is not None:
|
|
element.set("reference_uvw", ' '.join(map(str, self.reference_uvw)))
|
|
return element
|
|
|
|
@classmethod
|
|
def from_xml_element(cls, elem: ET.Element):
|
|
"""Generate monodirectional distribution from an XML element
|
|
|
|
Parameters
|
|
----------
|
|
elem : lxml.etree._Element
|
|
XML element
|
|
|
|
Returns
|
|
-------
|
|
openmc.stats.Monodirectional
|
|
Monodirectional distribution generated from XML element
|
|
|
|
"""
|
|
monodirectional = cls()
|
|
uvw = get_text(elem, 'reference_uvw')
|
|
if uvw is not None:
|
|
monodirectional.reference_uvw = [float(x) for x in uvw.split()]
|
|
return monodirectional
|
|
|
|
|
|
class Spatial(ABC):
|
|
"""Distribution of locations in three-dimensional Euclidean space.
|
|
|
|
Classes derived from this abstract class can be used for spatial
|
|
distributions of source sites.
|
|
|
|
"""
|
|
@abstractmethod
|
|
def to_xml_element(self):
|
|
return ''
|
|
|
|
@classmethod
|
|
@abstractmethod
|
|
def from_xml_element(cls, elem, meshes=None):
|
|
distribution = get_text(elem, 'type')
|
|
if distribution == 'cartesian':
|
|
return CartesianIndependent.from_xml_element(elem)
|
|
elif distribution == 'cylindrical':
|
|
return CylindricalIndependent.from_xml_element(elem)
|
|
elif distribution == 'spherical':
|
|
return SphericalIndependent.from_xml_element(elem)
|
|
elif distribution == 'box' or distribution == 'fission':
|
|
return Box.from_xml_element(elem)
|
|
elif distribution == 'point':
|
|
return Point.from_xml_element(elem)
|
|
elif distribution == 'mesh':
|
|
return MeshSpatial.from_xml_element(elem, meshes)
|
|
|
|
|
|
class CartesianIndependent(Spatial):
|
|
"""Spatial distribution with independent x, y, and z distributions.
|
|
|
|
This distribution allows one to specify coordinates whose x-, y-, and z-
|
|
components are sampled independently from one another.
|
|
|
|
Parameters
|
|
----------
|
|
x : openmc.stats.Univariate
|
|
Distribution of x-coordinates
|
|
y : openmc.stats.Univariate
|
|
Distribution of y-coordinates
|
|
z : openmc.stats.Univariate
|
|
Distribution of z-coordinates
|
|
|
|
Attributes
|
|
----------
|
|
x : openmc.stats.Univariate
|
|
Distribution of x-coordinates
|
|
y : openmc.stats.Univariate
|
|
Distribution of y-coordinates
|
|
z : openmc.stats.Univariate
|
|
Distribution of z-coordinates
|
|
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
x: openmc.stats.Univariate,
|
|
y: openmc.stats.Univariate,
|
|
z: openmc.stats.Univariate
|
|
):
|
|
self.x = x
|
|
self.y = y
|
|
self.z = z
|
|
|
|
@property
|
|
def x(self):
|
|
return self._x
|
|
|
|
@x.setter
|
|
def x(self, x):
|
|
cv.check_type('x coordinate', x, Univariate)
|
|
self._x = x
|
|
|
|
@property
|
|
def y(self):
|
|
return self._y
|
|
|
|
@y.setter
|
|
def y(self, y):
|
|
cv.check_type('y coordinate', y, Univariate)
|
|
self._y = y
|
|
|
|
@property
|
|
def z(self):
|
|
return self._z
|
|
|
|
@z.setter
|
|
def z(self, z):
|
|
cv.check_type('z coordinate', z, Univariate)
|
|
self._z = z
|
|
|
|
def to_xml_element(self):
|
|
"""Return XML representation of the spatial distribution
|
|
|
|
Returns
|
|
-------
|
|
element : lxml.etree._Element
|
|
XML element containing spatial distribution data
|
|
|
|
"""
|
|
element = ET.Element('space')
|
|
element.set('type', 'cartesian')
|
|
element.append(self.x.to_xml_element('x'))
|
|
element.append(self.y.to_xml_element('y'))
|
|
element.append(self.z.to_xml_element('z'))
|
|
return element
|
|
|
|
@classmethod
|
|
def from_xml_element(cls, elem: ET.Element):
|
|
"""Generate spatial distribution from an XML element
|
|
|
|
Parameters
|
|
----------
|
|
elem : lxml.etree._Element
|
|
XML element
|
|
|
|
Returns
|
|
-------
|
|
openmc.stats.CartesianIndependent
|
|
Spatial distribution generated from XML element
|
|
|
|
"""
|
|
x = Univariate.from_xml_element(elem.find('x'))
|
|
y = Univariate.from_xml_element(elem.find('y'))
|
|
z = Univariate.from_xml_element(elem.find('z'))
|
|
return cls(x, y, z)
|
|
|
|
|
|
class SphericalIndependent(Spatial):
|
|
r"""Spatial distribution represented in spherical coordinates.
|
|
|
|
This distribution allows one to specify coordinates whose :math:`r`,
|
|
:math:`\theta`, and :math:`\phi` components are sampled independently
|
|
from one another and centered on the coordinates (x0, y0, z0).
|
|
|
|
.. versionadded:: 0.12
|
|
|
|
.. versionchanged:: 0.13.1
|
|
Accepts ``cos_theta`` instead of ``theta``
|
|
|
|
Parameters
|
|
----------
|
|
r : openmc.stats.Univariate
|
|
Distribution of r-coordinates in a reference frame specified by
|
|
the origin parameter
|
|
cos_theta : openmc.stats.Univariate
|
|
Distribution of the cosine of the theta-coordinates (angle relative to
|
|
the z-axis) in a reference frame specified by the origin parameter
|
|
phi : openmc.stats.Univariate
|
|
Distribution of phi-coordinates (azimuthal angle) in a reference frame
|
|
specified by the origin parameter
|
|
origin: Iterable of float, optional
|
|
coordinates (x0, y0, z0) of the center of the spherical reference frame
|
|
for the source. Defaults to (0.0, 0.0, 0.0)
|
|
|
|
Attributes
|
|
----------
|
|
r : openmc.stats.Univariate
|
|
Distribution of r-coordinates in the local reference frame
|
|
cos_theta : openmc.stats.Univariate
|
|
Distribution of the cosine of the theta-coordinates (angle relative to
|
|
the z-axis) in the local reference frame
|
|
phi : openmc.stats.Univariate
|
|
Distribution of phi-coordinates (azimuthal angle) in the local
|
|
reference frame
|
|
origin: Iterable of float, optional
|
|
coordinates (x0, y0, z0) of the center of the spherical reference
|
|
frame. Defaults to (0.0, 0.0, 0.0)
|
|
|
|
"""
|
|
|
|
def __init__(self, r, cos_theta, phi, origin=(0.0, 0.0, 0.0)):
|
|
self.r = r
|
|
self.cos_theta = cos_theta
|
|
self.phi = phi
|
|
self.origin = origin
|
|
|
|
@property
|
|
def r(self):
|
|
return self._r
|
|
|
|
@r.setter
|
|
def r(self, r):
|
|
cv.check_type('r coordinate', r, Univariate)
|
|
self._r = r
|
|
|
|
@property
|
|
def cos_theta(self):
|
|
return self._cos_theta
|
|
|
|
@cos_theta.setter
|
|
def cos_theta(self, cos_theta):
|
|
cv.check_type('cos_theta coordinate', cos_theta, Univariate)
|
|
self._cos_theta = cos_theta
|
|
|
|
@property
|
|
def phi(self):
|
|
return self._phi
|
|
|
|
@phi.setter
|
|
def phi(self, phi):
|
|
cv.check_type('phi coordinate', phi, Univariate)
|
|
self._phi = phi
|
|
|
|
@property
|
|
def origin(self):
|
|
return self._origin
|
|
|
|
@origin.setter
|
|
def origin(self, origin):
|
|
cv.check_type('origin coordinates', origin, Iterable, Real)
|
|
origin = np.asarray(origin)
|
|
self._origin = origin
|
|
|
|
def to_xml_element(self):
|
|
"""Return XML representation of the spatial distribution
|
|
|
|
Returns
|
|
-------
|
|
element : lxml.etree._Element
|
|
XML element containing spatial distribution data
|
|
|
|
"""
|
|
element = ET.Element('space')
|
|
element.set('type', 'spherical')
|
|
element.append(self.r.to_xml_element('r'))
|
|
element.append(self.cos_theta.to_xml_element('cos_theta'))
|
|
element.append(self.phi.to_xml_element('phi'))
|
|
element.set("origin", ' '.join(map(str, self.origin)))
|
|
return element
|
|
|
|
@classmethod
|
|
def from_xml_element(cls, elem: ET.Element):
|
|
"""Generate spatial distribution from an XML element
|
|
|
|
Parameters
|
|
----------
|
|
elem : lxml.etree._Element
|
|
XML element
|
|
|
|
Returns
|
|
-------
|
|
openmc.stats.SphericalIndependent
|
|
Spatial distribution generated from XML element
|
|
|
|
"""
|
|
r = Univariate.from_xml_element(elem.find('r'))
|
|
cos_theta = Univariate.from_xml_element(elem.find('cos_theta'))
|
|
phi = Univariate.from_xml_element(elem.find('phi'))
|
|
origin = [float(x) for x in elem.get('origin').split()]
|
|
return cls(r, cos_theta, phi, origin=origin)
|
|
|
|
|
|
class CylindricalIndependent(Spatial):
|
|
r"""Spatial distribution represented in cylindrical coordinates.
|
|
|
|
This distribution allows one to specify coordinates whose :math:`r`,
|
|
:math:`\phi`, and :math:`z` components are sampled independently from
|
|
one another and in a reference frame whose origin is specified by the
|
|
coordinates (x0, y0, z0).
|
|
|
|
.. versionadded:: 0.12
|
|
|
|
Parameters
|
|
----------
|
|
r : openmc.stats.Univariate
|
|
Distribution of r-coordinates in a reference frame specified by the
|
|
origin parameter
|
|
phi : openmc.stats.Univariate
|
|
Distribution of phi-coordinates (azimuthal angle) in a reference frame
|
|
specified by the origin parameter
|
|
z : openmc.stats.Univariate
|
|
Distribution of z-coordinates in a reference frame specified by the
|
|
origin parameter
|
|
origin: Iterable of float, optional
|
|
coordinates (x0, y0, z0) of the center of the cylindrical reference
|
|
frame. Defaults to (0.0, 0.0, 0.0)
|
|
|
|
Attributes
|
|
----------
|
|
r : openmc.stats.Univariate
|
|
Distribution of r-coordinates in the local reference frame
|
|
phi : openmc.stats.Univariate
|
|
Distribution of phi-coordinates (azimuthal angle) in the local
|
|
reference frame
|
|
z : openmc.stats.Univariate
|
|
Distribution of z-coordinates in the local reference frame
|
|
origin: Iterable of float, optional
|
|
coordinates (x0, y0, z0) of the center of the cylindrical reference
|
|
frame. Defaults to (0.0, 0.0, 0.0)
|
|
|
|
"""
|
|
|
|
def __init__(self, r, phi, z, origin=(0.0, 0.0, 0.0)):
|
|
self.r = r
|
|
self.phi = phi
|
|
self.z = z
|
|
self.origin = origin
|
|
|
|
@property
|
|
def r(self):
|
|
return self._r
|
|
|
|
@r.setter
|
|
def r(self, r):
|
|
cv.check_type('r coordinate', r, Univariate)
|
|
self._r = r
|
|
|
|
@property
|
|
def phi(self):
|
|
return self._phi
|
|
|
|
@phi.setter
|
|
def phi(self, phi):
|
|
cv.check_type('phi coordinate', phi, Univariate)
|
|
self._phi = phi
|
|
|
|
@property
|
|
def z(self):
|
|
return self._z
|
|
|
|
@z.setter
|
|
def z(self, z):
|
|
cv.check_type('z coordinate', z, Univariate)
|
|
self._z = z
|
|
|
|
@property
|
|
def origin(self):
|
|
return self._origin
|
|
|
|
@origin.setter
|
|
def origin(self, origin):
|
|
cv.check_type('origin coordinates', origin, Iterable, Real)
|
|
origin = np.asarray(origin)
|
|
self._origin = origin
|
|
|
|
def to_xml_element(self):
|
|
"""Return XML representation of the spatial distribution
|
|
|
|
Returns
|
|
-------
|
|
element : lxml.etree._Element
|
|
XML element containing spatial distribution data
|
|
|
|
"""
|
|
element = ET.Element('space')
|
|
element.set('type', 'cylindrical')
|
|
element.append(self.r.to_xml_element('r'))
|
|
element.append(self.phi.to_xml_element('phi'))
|
|
element.append(self.z.to_xml_element('z'))
|
|
element.set("origin", ' '.join(map(str, self.origin)))
|
|
return element
|
|
|
|
@classmethod
|
|
def from_xml_element(cls, elem: ET.Element):
|
|
"""Generate spatial distribution from an XML element
|
|
|
|
Parameters
|
|
----------
|
|
elem : lxml.etree._Element
|
|
XML element
|
|
|
|
Returns
|
|
-------
|
|
openmc.stats.CylindricalIndependent
|
|
Spatial distribution generated from XML element
|
|
|
|
"""
|
|
r = Univariate.from_xml_element(elem.find('r'))
|
|
phi = Univariate.from_xml_element(elem.find('phi'))
|
|
z = Univariate.from_xml_element(elem.find('z'))
|
|
origin = [float(x) for x in elem.get('origin').split()]
|
|
return cls(r, phi, z, origin=origin)
|
|
|
|
|
|
class MeshSpatial(Spatial):
|
|
"""Spatial distribution for a mesh.
|
|
|
|
This distribution specifies a mesh to sample over with source strengths
|
|
specified for each mesh element.
|
|
|
|
.. versionadded:: 0.13.3
|
|
|
|
Parameters
|
|
----------
|
|
mesh : openmc.MeshBase
|
|
The mesh instance used for sampling
|
|
strengths : iterable of float, optional
|
|
An iterable of values that represents the weights of each element. If no
|
|
source strengths are specified, they will be equal for all mesh
|
|
elements.
|
|
volume_normalized : bool, optional
|
|
Whether or not the strengths will be multiplied by element volumes at
|
|
runtime. Default is True.
|
|
|
|
Attributes
|
|
----------
|
|
mesh : openmc.MeshBase
|
|
The mesh instance used for sampling
|
|
strengths : numpy.ndarray or None
|
|
An array of source strengths for each mesh element
|
|
volume_normalized : bool
|
|
Whether or not the strengths will be multiplied by element volumes at
|
|
runtime.
|
|
"""
|
|
|
|
def __init__(self, mesh, strengths=None, volume_normalized=True):
|
|
self.mesh = mesh
|
|
self.strengths = strengths
|
|
self.volume_normalized = volume_normalized
|
|
|
|
@property
|
|
def mesh(self):
|
|
return self._mesh
|
|
|
|
@mesh.setter
|
|
def mesh(self, mesh):
|
|
if mesh is not None:
|
|
cv.check_type('mesh instance', mesh, MeshBase)
|
|
self._mesh = mesh
|
|
|
|
@property
|
|
def volume_normalized(self):
|
|
return self._volume_normalized
|
|
|
|
@volume_normalized.setter
|
|
def volume_normalized(self, volume_normalized):
|
|
cv.check_type('Multiply strengths by element volumes', volume_normalized, bool)
|
|
self._volume_normalized = volume_normalized
|
|
|
|
@property
|
|
def strengths(self):
|
|
return self._strengths
|
|
|
|
@strengths.setter
|
|
def strengths(self, given_strengths):
|
|
if given_strengths is not None:
|
|
cv.check_type('strengths array passed in', given_strengths, Iterable, Real)
|
|
self._strengths = np.asarray(given_strengths, dtype=float).flatten()
|
|
else:
|
|
self._strengths = None
|
|
|
|
@property
|
|
def num_strength_bins(self):
|
|
if self.strengths is None:
|
|
raise ValueError('Strengths are not set')
|
|
return self.strengths.size
|
|
|
|
def to_xml_element(self):
|
|
"""Return XML representation of the spatial distribution
|
|
|
|
Returns
|
|
-------
|
|
element : lxml.etree._Element
|
|
XML element containing spatial distribution data
|
|
|
|
"""
|
|
element = ET.Element('space')
|
|
|
|
element.set('type', 'mesh')
|
|
element.set("mesh_id", str(self.mesh.id))
|
|
element.set("volume_normalized", str(self.volume_normalized))
|
|
|
|
if self.strengths is not None:
|
|
subelement = ET.SubElement(element, 'strengths')
|
|
subelement.text = ' '.join(str(e) for e in self.strengths)
|
|
|
|
return element
|
|
|
|
@classmethod
|
|
def from_xml_element(cls, elem, meshes):
|
|
"""Generate spatial distribution from an XML element
|
|
|
|
Parameters
|
|
----------
|
|
elem : lxml.etree._Element
|
|
XML element
|
|
meshes : dict
|
|
A dictionary with mesh IDs as keys and openmc.MeshBase instances as
|
|
values
|
|
|
|
Returns
|
|
-------
|
|
openmc.stats.MeshSpatial
|
|
Spatial distribution generated from XML element
|
|
|
|
"""
|
|
|
|
mesh_id = int(elem.get('mesh_id'))
|
|
|
|
# check if this mesh has been read in from another location already
|
|
if mesh_id not in meshes:
|
|
raise ValueError(f'Could not locate mesh with ID "{mesh_id}"')
|
|
|
|
volume_normalized = elem.get("volume_normalized")
|
|
volume_normalized = get_text(elem, 'volume_normalized').lower() == 'true'
|
|
strengths = get_text(elem, 'strengths')
|
|
if strengths is not None:
|
|
strengths = [float(b) for b in get_text(elem, 'strengths').split()]
|
|
|
|
return cls(meshes[mesh_id], strengths, volume_normalized)
|
|
|
|
|
|
class Box(Spatial):
|
|
"""Uniform distribution of coordinates in a rectangular cuboid.
|
|
|
|
Parameters
|
|
----------
|
|
lower_left : Iterable of float
|
|
Lower-left coordinates of cuboid
|
|
upper_right : Iterable of float
|
|
Upper-right coordinates of cuboid
|
|
only_fissionable : bool, optional
|
|
Whether spatial sites should only be accepted if they occur in
|
|
fissionable materials
|
|
|
|
Attributes
|
|
----------
|
|
lower_left : Iterable of float
|
|
Lower-left coordinates of cuboid
|
|
upper_right : Iterable of float
|
|
Upper-right coordinates of cuboid
|
|
only_fissionable : bool, optional
|
|
Whether spatial sites should only be accepted if they occur in
|
|
fissionable materials
|
|
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
lower_left: typing.Sequence[float],
|
|
upper_right: typing.Sequence[float],
|
|
only_fissionable: bool = False
|
|
):
|
|
self.lower_left = lower_left
|
|
self.upper_right = upper_right
|
|
self.only_fissionable = only_fissionable
|
|
|
|
@property
|
|
def lower_left(self):
|
|
return self._lower_left
|
|
|
|
@lower_left.setter
|
|
def lower_left(self, lower_left):
|
|
cv.check_type('lower left coordinate', lower_left, Iterable, Real)
|
|
cv.check_length('lower left coordinate', lower_left, 3)
|
|
self._lower_left = lower_left
|
|
|
|
@property
|
|
def upper_right(self):
|
|
return self._upper_right
|
|
|
|
@upper_right.setter
|
|
def upper_right(self, upper_right):
|
|
cv.check_type('upper right coordinate', upper_right, Iterable, Real)
|
|
cv.check_length('upper right coordinate', upper_right, 3)
|
|
self._upper_right = upper_right
|
|
|
|
@property
|
|
def only_fissionable(self):
|
|
return self._only_fissionable
|
|
|
|
@only_fissionable.setter
|
|
def only_fissionable(self, only_fissionable):
|
|
cv.check_type('only fissionable', only_fissionable, bool)
|
|
self._only_fissionable = only_fissionable
|
|
|
|
def to_xml_element(self):
|
|
"""Return XML representation of the box distribution
|
|
|
|
Returns
|
|
-------
|
|
element : lxml.etree._Element
|
|
XML element containing box distribution data
|
|
|
|
"""
|
|
element = ET.Element('space')
|
|
if self.only_fissionable:
|
|
element.set("type", "fission")
|
|
else:
|
|
element.set("type", "box")
|
|
params = ET.SubElement(element, "parameters")
|
|
params.text = ' '.join(map(str, self.lower_left)) + ' ' + \
|
|
' '.join(map(str, self.upper_right))
|
|
return element
|
|
|
|
@classmethod
|
|
def from_xml_element(cls, elem: ET.Element):
|
|
"""Generate box distribution from an XML element
|
|
|
|
Parameters
|
|
----------
|
|
elem : lxml.etree._Element
|
|
XML element
|
|
|
|
Returns
|
|
-------
|
|
openmc.stats.Box
|
|
Box distribution generated from XML element
|
|
|
|
"""
|
|
only_fissionable = get_text(elem, 'type') == 'fission'
|
|
params = [float(x) for x in get_text(elem, 'parameters').split()]
|
|
lower_left = params[:len(params)//2]
|
|
upper_right = params[len(params)//2:]
|
|
return cls(lower_left, upper_right, only_fissionable)
|
|
|
|
|
|
class Point(Spatial):
|
|
"""Delta function in three dimensions.
|
|
|
|
This spatial distribution can be used for a point source where sites are
|
|
emitted at a specific location given by its Cartesian coordinates.
|
|
|
|
Parameters
|
|
----------
|
|
xyz : Iterable of float, optional
|
|
Cartesian coordinates of location. Defaults to (0., 0., 0.).
|
|
|
|
Attributes
|
|
----------
|
|
xyz : Iterable of float
|
|
Cartesian coordinates of location
|
|
|
|
"""
|
|
|
|
def __init__(self, xyz: typing.Sequence[float] = (0., 0., 0.)):
|
|
self.xyz = xyz
|
|
|
|
@property
|
|
def xyz(self):
|
|
return self._xyz
|
|
|
|
@xyz.setter
|
|
def xyz(self, xyz):
|
|
cv.check_type('coordinate', xyz, Iterable, Real)
|
|
cv.check_length('coordinate', xyz, 3)
|
|
self._xyz = xyz
|
|
|
|
def to_xml_element(self):
|
|
"""Return XML representation of the point distribution
|
|
|
|
Returns
|
|
-------
|
|
element : lxml.etree._Element
|
|
XML element containing point distribution location
|
|
|
|
"""
|
|
element = ET.Element('space')
|
|
element.set("type", "point")
|
|
params = ET.SubElement(element, "parameters")
|
|
params.text = ' '.join(map(str, self.xyz))
|
|
return element
|
|
|
|
@classmethod
|
|
def from_xml_element(cls, elem: ET.Element):
|
|
"""Generate point distribution from an XML element
|
|
|
|
Parameters
|
|
----------
|
|
elem : lxml.etree._Element
|
|
XML element
|
|
|
|
Returns
|
|
-------
|
|
openmc.stats.Point
|
|
Point distribution generated from XML element
|
|
|
|
"""
|
|
xyz = [float(x) for x in get_text(elem, 'parameters').split()]
|
|
return cls(xyz)
|
|
|
|
|
|
def spherical_uniform(
|
|
r_outer: float,
|
|
r_inner: float = 0.0,
|
|
thetas: typing.Sequence[float] = (0., pi),
|
|
phis: typing.Sequence[float] = (0., 2*pi),
|
|
origin: typing.Sequence[float] = (0., 0., 0.)
|
|
):
|
|
"""Return a uniform spatial distribution over a spherical shell.
|
|
|
|
This function provides a uniform spatial distribution over a spherical
|
|
shell between `r_inner` and `r_outer`. Optionally, the range of angles
|
|
can be restricted by the `thetas` and `phis` arguments.
|
|
|
|
.. versionadded:: 0.13.1
|
|
|
|
Parameters
|
|
----------
|
|
r_outer : float
|
|
Outer radius of the spherical shell in [cm]
|
|
r_inner : float
|
|
Inner radius of the spherical shell in [cm]
|
|
thetas : iterable of float
|
|
Starting and ending theta coordinates (angle relative to
|
|
the z-axis) in radius in a reference frame centered at `origin`
|
|
phis : iterable of float
|
|
Starting and ending phi coordinates (azimuthal angle) in
|
|
radians in a reference frame centered at `origin`
|
|
origin: iterable of float
|
|
Coordinates (x0, y0, z0) of the center of the spherical
|
|
reference frame for the distribution.
|
|
|
|
Returns
|
|
-------
|
|
openmc.stats.SphericalIndependent
|
|
Uniform distribution over the spherical shell
|
|
"""
|
|
|
|
r_dist = PowerLaw(r_inner, r_outer, 2)
|
|
cos_thetas_dist = Uniform(cos(thetas[1]), cos(thetas[0]))
|
|
phis_dist = Uniform(phis[0], phis[1])
|
|
|
|
return SphericalIndependent(r_dist, cos_thetas_dist, phis_dist, origin)
|