Allowing CylindricalMesh and SphericalMesh to be fully made via constructor+ type hints (#2619)

Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
This commit is contained in:
Jonathan Shimwell 2023-08-05 21:16:42 +01:00 committed by GitHub
parent cd13d3471d
commit 557880aefc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 365 additions and 206 deletions

View file

@ -1,19 +1,20 @@
import typing
import warnings
from abc import ABC, abstractmethod
from collections.abc import Iterable
from math import pi
from numbers import Real, Integral
from numbers import Integral, Real
from pathlib import Path
import typing
import warnings
import lxml.etree as ET
from typing import Optional, Sequence, Tuple
import h5py
import lxml.etree as ET
import numpy as np
import openmc.checkvalue as cv
import openmc
from ._xml import get_text
import openmc.checkvalue as cv
from openmc.checkvalue import PathLike
from ._xml import get_text
from .mixin import IDManagerMixin
from .surface import _BOUNDARY_TYPES
@ -40,7 +41,7 @@ class MeshBase(IDManagerMixin, ABC):
next_id = 1
used_ids = set()
def __init__(self, mesh_id: typing.Optional[int] = None, name: str = ''):
def __init__(self, mesh_id: Optional[int] = None, name: str = ''):
# Initialize Mesh class attributes
self.id = mesh_id
self.name = name
@ -264,7 +265,7 @@ class StructuredMesh(MeshBase):
def write_data_to_vtk(self,
filename: PathLike,
datasets: typing.Optional[dict] = None,
datasets: Optional[dict] = None,
volume_normalization: bool = True,
curvilinear: bool = False):
"""Creates a VTK object of the mesh
@ -512,7 +513,7 @@ class RegularMesh(StructuredMesh):
"""
def __init__(self, mesh_id: typing.Optional[int] = None, name: str = ''):
def __init__(self, mesh_id: Optional[int] = None, name: str = ''):
super().__init__(mesh_id, name)
self._dimension = None
@ -597,7 +598,7 @@ class RegularMesh(StructuredMesh):
@property
def cartesian_vertices(self):
"""Returns vertices in cartesian coordiantes. Identical to ``vertices`` for RegularMesh and RectilinearMesh
"""Returns vertices in cartesian coordinates. Identical to ``vertices`` for RegularMesh and RectilinearMesh
"""
return self.vertices
@ -697,7 +698,7 @@ class RegularMesh(StructuredMesh):
cls,
lattice: 'openmc.RectLattice',
division: int = 1,
mesh_id: typing.Optional[int] = None,
mesh_id: Optional[int] = None,
name: str = ''
):
"""Create mesh from an existing rectangular lattice
@ -725,7 +726,7 @@ class RegularMesh(StructuredMesh):
shape = np.array(lattice.shape)
width = lattice.pitch*shape
mesh = cls(mesh_id, name)
mesh = cls(mesh_id=mesh_id, name=name)
mesh.lower_left = lattice.lower_left
mesh.upper_right = lattice.lower_left + width
mesh.dimension = shape*division
@ -736,8 +737,8 @@ class RegularMesh(StructuredMesh):
def from_domain(
cls,
domain: typing.Union['openmc.Cell', 'openmc.Region', 'openmc.Universe', 'openmc.Geometry'],
dimension: typing.Sequence[int] = (10, 10, 10),
mesh_id: typing.Optional[int] = None,
dimension: Sequence[int] = (10, 10, 10),
mesh_id: Optional[int] = None,
name: str = ''
):
"""Create mesh from an existing openmc cell, region, universe or
@ -748,7 +749,7 @@ class RegularMesh(StructuredMesh):
domain : {openmc.Cell, openmc.Region, openmc.Universe, openmc.Geometry}
The object passed in will be used as a template for this mesh. The
bounding box of the property of the object passed will be used to
set the lower_left and upper_right of the mesh instance
set the lower_left and upper_right and of the mesh instance
dimension : Iterable of int
The number of mesh cells in each direction (x, y, z).
mesh_id : int
@ -768,7 +769,7 @@ class RegularMesh(StructuredMesh):
(openmc.Cell, openmc.Region, openmc.Universe, openmc.Geometry),
)
mesh = cls(mesh_id, name)
mesh = cls(mesh_id=mesh_id, name=name)
mesh.lower_left = domain.bounding_box[0]
mesh.upper_right = domain.bounding_box[1]
mesh.dimension = dimension
@ -820,7 +821,7 @@ class RegularMesh(StructuredMesh):
"""
mesh_id = int(get_text(elem, 'id'))
mesh = cls(mesh_id)
mesh = cls(mesh_id=mesh_id)
mesh_type = get_text(elem, 'type')
if mesh_type is not None:
@ -844,7 +845,7 @@ class RegularMesh(StructuredMesh):
return mesh
def build_cells(self, bc: typing.Optional[str] = None):
def build_cells(self, bc: Optional[str] = None):
"""Generates a lattice of universes with the same dimensionality
as the mesh object. The individual cells/universes produced
will not have material definitions applied and so downstream code
@ -1119,7 +1120,7 @@ class RectilinearMesh(StructuredMesh):
mesh_id = int(group.name.split('/')[-1].lstrip('mesh '))
# Read and assign mesh properties
mesh = cls(mesh_id)
mesh = cls(mesh_id=mesh_id)
mesh.x_grid = group['x_grid'][()]
mesh.y_grid = group['y_grid'][()]
mesh.z_grid = group['z_grid'][()]
@ -1141,8 +1142,8 @@ class RectilinearMesh(StructuredMesh):
Rectilinear mesh object
"""
id = int(get_text(elem, 'id'))
mesh = cls(id)
mesh_id = int(get_text(elem, 'id'))
mesh = cls(mesh_id=mesh_id)
mesh.x_grid = [float(x) for x in get_text(elem, 'x_grid').split()]
mesh.y_grid = [float(y) for y in get_text(elem, 'y_grid').split()]
mesh.z_grid = [float(z) for z in get_text(elem, 'z_grid').split()]
@ -1180,6 +1181,17 @@ class CylindricalMesh(StructuredMesh):
Parameters
----------
r_grid : numpy.ndarray
1-D array of mesh boundary points along the r-axis.
Requirement is r >= 0.
z_grid : numpy.ndarray
1-D array of mesh boundary points along the z-axis.
phi_grid : numpy.ndarray
1-D array of mesh boundary points along the phi-axis in radians.
The default value is [0, 2π], i.e. the full phi range.
origin : numpy.ndarray
1-D array of length 3 the (x,y,z) origin of the mesh in
cartesian coordinates
mesh_id : int
Unique identifier for the mesh
name : str
@ -1213,13 +1225,21 @@ class CylindricalMesh(StructuredMesh):
"""
def __init__(self, mesh_id: int = None, name: str = ''):
def __init__(
self,
r_grid: Sequence[float],
z_grid: Sequence[float],
phi_grid: Sequence[float] = (0, 2*pi),
origin: Sequence[float] = (0., 0., 0.),
mesh_id: Optional[int] = None,
name: str = '',
):
super().__init__(mesh_id, name)
self._r_grid = None
self._phi_grid = [0.0, 2*pi]
self._z_grid = None
self.origin = (0., 0., 0.)
self._r_grid = r_grid
self._phi_grid = phi_grid
self._z_grid = z_grid
self.origin = origin
@property
def dimension(self):
@ -1309,10 +1329,12 @@ class CylindricalMesh(StructuredMesh):
mesh_id = int(group.name.split('/')[-1].lstrip('mesh '))
# Read and assign mesh properties
mesh = cls(mesh_id)
mesh.r_grid = group['r_grid'][()]
mesh.phi_grid = group['phi_grid'][()]
mesh.z_grid = group['z_grid'][()]
mesh = cls(
mesh_id=mesh_id,
r_grid = group['r_grid'][()],
phi_grid = group['phi_grid'][()],
z_grid = group['z_grid'][()],
)
if 'origin' in group:
mesh.origin = group['origin'][()]
@ -1322,10 +1344,10 @@ class CylindricalMesh(StructuredMesh):
def from_domain(
cls,
domain: typing.Union['openmc.Cell', 'openmc.Region', 'openmc.Universe', 'openmc.Geometry'],
dimension: typing.Sequence[int] = (10, 10, 10),
mesh_id: typing.Optional[int] = None,
phi_grid_bounds: typing.Sequence[float] = (0.0, 2*pi),
name=''
dimension: Sequence[int] = (10, 10, 10),
mesh_id: Optional[int] = None,
phi_grid_bounds: Sequence[float] = (0.0, 2*pi),
name: str = ''
):
"""Creates a regular CylindricalMesh from an existing openmc domain.
@ -1358,7 +1380,6 @@ class CylindricalMesh(StructuredMesh):
(openmc.Cell, openmc.Region, openmc.Universe, openmc.Geometry),
)
mesh = cls(mesh_id, name)
# loaded once to avoid reading h5m file repeatedly
cached_bb = domain.bounding_box
@ -1370,21 +1391,24 @@ class CylindricalMesh(StructuredMesh):
cached_bb[1][1],
]
)
mesh.r_grid = np.linspace(
r_grid = np.linspace(
0,
max_bounding_box_radius,
num=dimension[0]+1
)
mesh.phi_grid = np.linspace(
phi_grid = np.linspace(
phi_grid_bounds[0],
phi_grid_bounds[1],
num=dimension[1]+1
)
mesh.z_grid = np.linspace(
z_grid = np.linspace(
cached_bb[0][2],
cached_bb[1][2],
num=dimension[2]+1
)
mesh = cls(
r_grid=r_grid, z_grid=z_grid, phi_grid=phi_grid, mesh_id=mesh_id, name=name
)
return mesh
@ -1433,11 +1457,13 @@ class CylindricalMesh(StructuredMesh):
"""
mesh_id = int(get_text(elem, 'id'))
mesh = cls(mesh_id)
mesh.r_grid = [float(x) for x in get_text(elem, "r_grid").split()]
mesh.phi_grid = [float(x) for x in get_text(elem, "phi_grid").split()]
mesh.z_grid = [float(x) for x in get_text(elem, "z_grid").split()]
mesh.origin = [float(x) for x in get_text(elem, "origin", default=[0., 0., 0.]).split()]
mesh = cls(
r_grid = [float(x) for x in get_text(elem, "r_grid").split()],
phi_grid = [float(x) for x in get_text(elem, "phi_grid").split()],
z_grid = [float(x) for x in get_text(elem, "z_grid").split()],
origin = [float(x) for x in get_text(elem, "origin", default=[0., 0., 0.]).split()],
mesh_id=mesh_id,
)
return mesh
@ -1463,7 +1489,7 @@ class CylindricalMesh(StructuredMesh):
return self._convert_to_cartesian(self.vertices, self.origin)
@staticmethod
def _convert_to_cartesian(arr, origin: typing.Sequence[float]):
def _convert_to_cartesian(arr, origin: Sequence[float]):
"""Converts an array with xyz values in the first dimension (shape (3, ...))
to Cartesian coordinates.
"""
@ -1480,6 +1506,18 @@ class SphericalMesh(StructuredMesh):
Parameters
----------
r_grid : numpy.ndarray
1-D array of mesh boundary points along the r-axis.
Requirement is r >= 0.
phi_grid : numpy.ndarray
1-D array of mesh boundary points along the phi-axis in radians.
The default value is [0, 2π], i.e. the full phi range.
theta_grid : numpy.ndarray
1-D array of mesh boundary points along the theta-axis in radians.
The default value is [0, π], i.e. the full theta range.
origin : numpy.ndarray
1-D array of length 3 the (x,y,z) origin of the mesh in
cartesian coordinates
mesh_id : int
Unique identifier for the mesh
name : str
@ -1514,13 +1552,21 @@ class SphericalMesh(StructuredMesh):
"""
def __init__(self, mesh_id=None, name=''):
def __init__(
self,
r_grid: Sequence[float],
phi_grid: Sequence[float] = (0, 2*pi),
theta_grid: Sequence[float] = (0, pi),
origin: Sequence[float] = (0., 0., 0.),
mesh_id: Optional[int] = None,
name: str = '',
):
super().__init__(mesh_id, name)
self._r_grid = None
self._theta_grid = [0, pi]
self._phi_grid = [0, 2*pi]
self.origin = (0., 0., 0.)
self._r_grid = r_grid
self._theta_grid = theta_grid
self._phi_grid = phi_grid
self.origin = origin
@property
def dimension(self):
@ -1610,10 +1656,12 @@ class SphericalMesh(StructuredMesh):
mesh_id = int(group.name.split('/')[-1].lstrip('mesh '))
# Read and assign mesh properties
mesh = cls(mesh_id)
mesh.r_grid = group['r_grid'][()]
mesh.theta_grid = group['theta_grid'][()]
mesh.phi_grid = group['phi_grid'][()]
mesh = cls(
r_grid = group['r_grid'][()],
theta_grid = group['theta_grid'][()],
phi_grid = group['phi_grid'][()],
mesh_id=mesh_id,
)
if 'origin' in group:
mesh.origin = group['origin'][()]
@ -1663,11 +1711,13 @@ class SphericalMesh(StructuredMesh):
"""
mesh_id = int(get_text(elem, 'id'))
mesh = cls(mesh_id)
mesh.r_grid = [float(x) for x in get_text(elem, "r_grid").split()]
mesh.theta_grid = [float(x) for x in get_text(elem, "theta_grid").split()]
mesh.phi_grid = [float(x) for x in get_text(elem, "phi_grid").split()]
mesh.origin = [float(x) for x in get_text(elem, "origin", default=[0., 0., 0.]).split()]
mesh = cls(
mesh_id=mesh_id,
r_grid = [float(x) for x in get_text(elem, "r_grid").split()],
theta_grid = [float(x) for x in get_text(elem, "theta_grid").split()],
phi_grid = [float(x) for x in get_text(elem, "phi_grid").split()],
origin = [float(x) for x in get_text(elem, "origin", default=[0., 0., 0.]).split()],
)
return mesh
@ -1693,7 +1743,7 @@ class SphericalMesh(StructuredMesh):
return self._convert_to_cartesian(self.vertices, self.origin)
@staticmethod
def _convert_to_cartesian(arr, origin: typing.Sequence[float]):
def _convert_to_cartesian(arr, origin: Sequence[float]):
"""Converts an array with xyz values in the first dimension (shape (3, ...))
to Cartesian coordinates.
"""
@ -1768,7 +1818,7 @@ class UnstructuredMesh(MeshBase):
_LINEAR_TET = 0
_LINEAR_HEX = 1
def __init__(self, filename: PathLike, library: str, mesh_id: typing.Optional[int] = None,
def __init__(self, filename: PathLike, library: str, mesh_id: Optional[int] = None,
name: str = '', length_multiplier: float = 1.0):
super().__init__(mesh_id, name)
self.filename = filename
@ -1940,8 +1990,8 @@ class UnstructuredMesh(MeshBase):
def write_data_to_vtk(
self,
filename: typing.Optional[PathLike] = None,
datasets: typing.Optional[dict] = None,
filename: Optional[PathLike] = None,
datasets: Optional[dict] = None,
volume_normalization: bool = True
):
"""Map data to unstructured VTK mesh elements.
@ -2040,7 +2090,7 @@ class UnstructuredMesh(MeshBase):
filename = group['filename'][()].decode()
library = group['library'][()].decode()
mesh = cls(filename, library, mesh_id=mesh_id)
mesh = cls(filename=filename, library=library, mesh_id=mesh_id)
vol_data = group['volumes'][()]
mesh.volumes = np.reshape(vol_data, (vol_data.shape[0],))
mesh.n_elements = mesh.volumes.size

View file

@ -1,15 +1,17 @@
from __future__ import annotations
import typing
from abc import ABC, abstractmethod
from collections.abc import Iterable
from math import pi, cos
from math import cos, pi
from numbers import Real
import lxml.etree as ET
import lxml.etree as ET
import numpy as np
import openmc.checkvalue as cv
from .._xml import get_text
from .univariate import Univariate, Uniform, PowerLaw
from ..mesh import MeshBase
from .univariate import PowerLaw, Uniform, Univariate
class UnitSphere(ABC):
@ -177,7 +179,7 @@ class Isotropic(UnitSphere):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate isotropic distribution from an XML element
Parameters
@ -209,7 +211,7 @@ class Monodirectional(UnitSphere):
"""
def __init__(self, reference_uvw=[1., 0., 0.]):
def __init__(self, reference_uvw: typing.Sequence[float] = [1., 0., 0.]):
super().__init__(reference_uvw)
def to_xml_element(self):
@ -228,7 +230,7 @@ class Monodirectional(UnitSphere):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate monodirectional distribution from an XML element
Parameters
@ -304,7 +306,12 @@ class CartesianIndependent(Spatial):
"""
def __init__(self, x, y, z):
def __init__(
self,
x: openmc.stats.Univariate,
y: openmc.stats.Univariate,
z: openmc.stats.Univariate
):
self.x = x
self.y = y
self.z = z
@ -353,7 +360,7 @@ class CartesianIndependent(Spatial):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate spatial distribution from an XML element
Parameters
@ -477,7 +484,7 @@ class SphericalIndependent(Spatial):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate spatial distribution from an XML element
Parameters
@ -599,7 +606,7 @@ class CylindricalIndependent(Spatial):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate spatial distribution from an XML element
Parameters
@ -772,7 +779,12 @@ class Box(Spatial):
"""
def __init__(self, lower_left, upper_right, only_fissionable=False):
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
@ -826,7 +838,7 @@ class Box(Spatial):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate box distribution from an XML element
Parameters
@ -865,7 +877,7 @@ class Point(Spatial):
"""
def __init__(self, xyz=(0., 0., 0.)):
def __init__(self, xyz: typing.Sequence[float] = (0., 0., 0.)):
self.xyz = xyz
@property
@ -894,7 +906,7 @@ class Point(Spatial):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate point distribution from an XML element
Parameters
@ -912,8 +924,13 @@ class Point(Spatial):
return cls(xyz)
def spherical_uniform(r_outer, r_inner=0.0, thetas=(0., pi), phis=(0., 2*pi),
origin=(0., 0., 0.)):
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
@ -926,15 +943,15 @@ def spherical_uniform(r_outer, r_inner=0.0, thetas=(0., pi), phis=(0., 2*pi),
----------
r_outer : float
Outer radius of the spherical shell in [cm]
r_inner : float, optional
r_inner : float
Inner radius of the spherical shell in [cm]
thetas : iterable of float, optional
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, optional
phis : iterable of float
Starting and ending phi coordinates (azimuthal angle) in
radians in a reference frame centered at `origin`
origin: iterable of float, optional
origin: iterable of float
Coordinates (x0, y0, z0) of the center of the spherical
reference frame for the distribution.

View file

@ -1,19 +1,19 @@
import math
import typing
from abc import ABC, abstractmethod
from collections import defaultdict
from collections.abc import Iterable
from copy import deepcopy
import math
from numbers import Real
from warnings import warn
import lxml.etree as ET
import lxml.etree as ET
import numpy as np
import openmc.checkvalue as cv
from .._xml import get_text
from ..mixin import EqualityMixin
_INTERPOLATION_SCHEMES = [
'histogram',
'linear-linear',
@ -66,7 +66,7 @@ class Univariate(EqualityMixin, ABC):
return Mixture.from_xml_element(elem)
@abstractmethod
def sample(n_samples=1, seed=None):
def sample(n_samples: int = 1, seed: typing.Optional[int] = None):
"""Sample the univariate distribution
Parameters
@ -186,7 +186,7 @@ class Discrete(Univariate):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate discrete distribution from an XML element
Parameters
@ -206,7 +206,11 @@ class Discrete(Univariate):
return cls(x, p)
@classmethod
def merge(cls, dists, probs):
def merge(
cls,
dists: typing.Sequence['openmc.stats.Discrete'],
probs: typing.Sequence[int]
):
"""Merge multiple discrete distributions into a single distribution
.. versionadded:: 0.13.1
@ -271,7 +275,7 @@ class Uniform(Univariate):
"""
def __init__(self, a=0.0, b=1.0):
def __init__(self, a: float = 0.0, b: float = 1.0):
self.a = a
self.b = b
@ -306,7 +310,7 @@ class Uniform(Univariate):
np.random.seed(seed)
return np.random.uniform(self.a, self.b, n_samples)
def to_xml_element(self, element_name):
def to_xml_element(self, element_name: str):
"""Return XML representation of the uniform distribution
Parameters
@ -326,7 +330,7 @@ class Uniform(Univariate):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate uniform distribution from an XML element
Parameters
@ -372,7 +376,7 @@ class PowerLaw(Univariate):
"""
def __init__(self, a=0.0, b=1.0, n=0):
def __init__(self, a: float = 0.0, b: float = 1.0, n: float = 0.):
self.a = a
self.b = b
self.n = n
@ -415,7 +419,7 @@ class PowerLaw(Univariate):
span = self.b**pwr - offset
return np.power(offset + xi * span, 1/pwr)
def to_xml_element(self, element_name):
def to_xml_element(self, element_name: str):
"""Return XML representation of the power law distribution
Parameters
@ -435,7 +439,7 @@ class PowerLaw(Univariate):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate power law distribution from an XML element
Parameters
@ -493,12 +497,12 @@ class Maxwell(Univariate):
return self.sample_maxwell(self.theta, n_samples)
@staticmethod
def sample_maxwell(t, n_samples):
def sample_maxwell(t, n_samples: int):
r1, r2, r3 = np.random.rand(3, n_samples)
c = np.cos(0.5 * np.pi * r3)
return -t * (np.log(r1) + np.log(r2) * c * c)
def to_xml_element(self, element_name):
def to_xml_element(self, element_name: str):
"""Return XML representation of the Maxwellian distribution
Parameters
@ -518,7 +522,7 @@ class Maxwell(Univariate):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate Maxwellian distribution from an XML element
Parameters
@ -593,7 +597,7 @@ class Watt(Univariate):
aab = self.a * self.a * self.b
return w + 0.25*aab + u*np.sqrt(aab*w)
def to_xml_element(self, element_name):
def to_xml_element(self, element_name: str):
"""Return XML representation of the Watt distribution
Parameters
@ -613,7 +617,7 @@ class Watt(Univariate):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate Watt distribution from an XML element
Parameters
@ -683,7 +687,7 @@ class Normal(Univariate):
np.random.seed(seed)
return np.random.normal(self.mean_value, self.std_dev, n_samples)
def to_xml_element(self, element_name):
def to_xml_element(self, element_name: str):
"""Return XML representation of the Normal distribution
Parameters
@ -703,7 +707,7 @@ class Normal(Univariate):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate Normal distribution from an XML element
Parameters
@ -721,7 +725,7 @@ class Normal(Univariate):
return cls(*map(float, params))
def muir(e0, m_rat, kt):
def muir(e0: float, m_rat: float, kt: float):
"""Generate a Muir energy spectrum
The Muir energy spectrum is a normal distribution, but for convenience
@ -793,8 +797,13 @@ class Tabular(Univariate):
"""
def __init__(self, x, p, interpolation='linear-linear',
ignore_negative=False):
def __init__(
self,
x: typing.Sequence[float],
p: typing.Sequence[float],
interpolation: str = 'linear-linear',
ignore_negative: bool = False
):
self._ignore_negative = ignore_negative
self.x = x
self.p = p
@ -886,7 +895,7 @@ class Tabular(Univariate):
"""Normalize the probabilities stored on the distribution"""
self.p /= self.cdf().max()
def sample(self, n_samples=1, seed=None):
def sample(self, n_samples: int = 1, seed: typing.Optional[int] = None):
np.random.seed(seed)
xi = np.random.rand(n_samples)
@ -947,7 +956,7 @@ class Tabular(Univariate):
assert all(samples_out < self.x[-1])
return samples_out
def to_xml_element(self, element_name):
def to_xml_element(self, element_name: str):
"""Return XML representation of the tabular distribution
Parameters
@ -971,7 +980,7 @@ class Tabular(Univariate):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate tabular distribution from an XML element
Parameters
@ -1028,7 +1037,7 @@ class Legendre(Univariate):
"""
def __init__(self, coefficients):
def __init__(self, coefficients: typing.Sequence[float]):
self.coefficients = coefficients
self._legendre_poly = None
@ -1082,7 +1091,11 @@ class Mixture(Univariate):
"""
def __init__(self, probability, distribution):
def __init__(
self,
probability: typing.Sequence[float],
distribution: typing.Sequence['openmc.Univariate']
):
self.probability = probability
self.distribution = distribution
@ -1140,7 +1153,7 @@ class Mixture(Univariate):
norm = sum(self.probability)
self.probability = [val / norm for val in self.probability]
def to_xml_element(self, element_name):
def to_xml_element(self, element_name: str):
"""Return XML representation of the mixture distribution
.. versionadded:: 0.13.0
@ -1167,7 +1180,7 @@ class Mixture(Univariate):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate mixture distribution from an XML element
.. versionadded:: 0.13.0
@ -1207,7 +1220,10 @@ class Mixture(Univariate):
])
def combine_distributions(dists, probs):
def combine_distributions(
dists: typing.Sequence['openmc.Univariate'],
probs: typing.Sequence[float]
):
"""Combine distributions with specified probabilities
This function can be used to combine multiple instances of

View file

@ -29,7 +29,7 @@ class Trigger(EqualityMixin):
"""
def __init__(self, trigger_type, threshold):
def __init__(self, trigger_type: str, threshold: float):
self.trigger_type = trigger_type
self.threshold = threshold
self._scores = []
@ -92,7 +92,7 @@ class Trigger(EqualityMixin):
return element
@classmethod
def from_xml_element(cls, elem):
def from_xml_element(cls, elem: ET.Element):
"""Generate trigger object from an XML element
Parameters

View file

@ -589,13 +589,19 @@ def wwinp_to_wws(path: PathLike) -> List[WeightWindows]:
mesh = RectilinearMesh()
mesh.x_grid, mesh.y_grid, mesh.z_grid = grids
elif nwg == 2:
mesh = CylindricalMesh()
mesh.r_grid, mesh.z_grid, mesh.phi_grid = grids
mesh.origin = xyz0
mesh = CylindricalMesh(
r_grid=grids[0],
z_grid=grids[1],
phi_grid=grids[2],
origin = xyz0,
)
elif nwg == 3:
mesh = SphericalMesh()
mesh.r_grid, mesh.theta_grid, mesh.phi_grid = grids
mesh.origin = xyz0
mesh = SphericalMesh(
r_grid=grids[0],
theta_grid=grids[1],
phi_grid=grids[2],
origin = xyz0
)
# extract weight window values from array
wws = []

View file

@ -60,11 +60,11 @@ def model():
recti_mesh_exp_vols = np.multiply.outer(dxdy, dz)
np.testing.assert_allclose(recti_mesh.volumes, recti_mesh_exp_vols)
cyl_mesh = openmc.CylindricalMesh()
cyl_mesh.r_grid = np.linspace(0, 7.5, 18)
cyl_mesh.phi_grid = np.linspace(0, 2*pi, 19)
cyl_mesh.z_grid = np.linspace(-7.5, 7.5, 17)
cyl_mesh = openmc.CylindricalMesh(
r_grid=np.linspace(0, 7.5, 18),
phi_grid=np.linspace(0, 2*pi, 19),
z_grid=np.linspace(-7.5, 7.5, 17),
)
dr = 0.5 * np.diff(np.linspace(0, 7.5, 18)**2)
dp = np.full(cyl_mesh.dimension[1], 2*pi / 18)
dz = np.full(cyl_mesh.dimension[2], 15 / 16)
@ -72,10 +72,11 @@ def model():
cyl_mesh_exp_vols = np.multiply.outer(drdp, dz)
np.testing.assert_allclose(cyl_mesh.volumes, cyl_mesh_exp_vols)
sph_mesh = openmc.SphericalMesh()
sph_mesh.r_grid = np.linspace(0, 7.5, 18)
sph_mesh.theta_grid = np.linspace(0, pi, 9)
sph_mesh.phi_grid = np.linspace(0, 2*pi, 19)
sph_mesh = openmc.SphericalMesh(
r_grid=np.linspace(0, 7.5, 18),
theta_grid=np.linspace(0, pi, 9),
phi_grid=np.linspace(0, 2*pi, 19)
)
dr = np.diff(np.linspace(0, 7.5, 18)**3) / 3
dt = np.diff(-np.cos(np.linspace(0, pi, 9)))
dp = np.full(sph_mesh.dimension[2], 2*pi / 18)

View file

@ -32,17 +32,19 @@ rect_mesh.x_grid = np.linspace(0, 10, 5)
rect_mesh.y_grid = np.geomspace(5., 20., 10)
rect_mesh.z_grid = np.linspace(1, 100, 20)
cyl_mesh = openmc.CylindricalMesh()
cyl_mesh.origin = (10, 10, -10)
cyl_mesh.r_grid = np.linspace(0, 5, 5)
cyl_mesh.phi_grid = np.linspace(0, 2 * np.pi, 4)
cyl_mesh.z_grid = np.linspace(0, 2, 4)
cyl_mesh = openmc.CylindricalMesh(
origin=(10, 10, -10),
r_grid=np.linspace(0, 5, 5),
phi_grid=np.linspace(0, 2 * np.pi, 4),
z_grid=np.linspace(0, 2, 4),
)
sphere_mesh = openmc.SphericalMesh()
sphere_mesh.origin = (10, 10, -10)
sphere_mesh.r_grid = np.linspace(0, 5, 3)
sphere_mesh.theta_grid = np.linspace(0, 0.5 * np.pi, 4)
sphere_mesh.phi_grid = np.linspace(0, 2*np.pi, 8)
sphere_mesh = openmc.SphericalMesh(
origin=(10, 10, -10),
r_grid=np.linspace(0, 5, 3),
theta_grid=np.linspace(0, 0.5 * np.pi, 4),
phi_grid=np.linspace(0, 2*np.pi, 8),
)
def mesh_data(mesh_dims):

View file

@ -49,15 +49,17 @@ rectilinear_mesh.y_grid = \
np.concatenate((-rectilinear_mesh.y_grid[::-1], rectilinear_mesh.y_grid))
rectilinear_mesh.z_grid = np.linspace(-10, 10, 11)
cylinder_mesh = openmc.CylindricalMesh()
cylinder_mesh.r_grid = np.linspace(0, 10, 23)
cylinder_mesh = openmc.CylindricalMesh(
r_grid=np.linspace(0, 10, 23),
z_grid=np.linspace(0, 1, 15)
)
cylinder_mesh.phi_grid = np.linspace(0, np.pi, 21)
cylinder_mesh.z_grid = np.linspace(0, 1, 15)
spherical_mesh = openmc.SphericalMesh()
spherical_mesh.r_grid = np.linspace(1, 10, 30)
spherical_mesh.phi_grid = np.linspace(0, 0.8*np.pi, 25)
spherical_mesh.theta_grid = np.linspace(0, np.pi / 2, 15)
spherical_mesh = openmc.SphericalMesh(
r_grid=np.linspace(1, 10, 30),
phi_grid=np.linspace(0, 0.8*np.pi, 25),
theta_grid=np.linspace(0, np.pi / 2, 15),
)
MESHES = [cylinder_mesh, regular_mesh, rectilinear_mesh, spherical_mesh]
@ -143,16 +145,17 @@ def test_write_data_to_vtk_size_mismatch(mesh):
mesh.write_data_to_vtk(filename="out.vtk", datasets={"label": data})
def test_write_data_to_vtk_round_trip(run_in_tmpdir):
cmesh = openmc.CylindricalMesh()
cmesh.r_grid = (0.0, 1.0, 2.0)
cmesh.z_grid = (0.0, 2.0, 4.0, 5.0)
cmesh.phi_grid = (0.0, 3.0, 6.0)
smesh = openmc.SphericalMesh()
smesh.r_grid = (0.0, 1.0, 2.0)
smesh.theta_grid = (0.0, 2.0, 4.0, 5.0)
smesh.phi_grid = (0.0, 3.0, 6.0)
cmesh = openmc.CylindricalMesh(
r_grid=(0.0, 1.0, 2.0),
z_grid=(0.0, 2.0, 4.0, 5.0),
phi_grid=(0.0, 3.0, 6.0),
)
smesh = openmc.SphericalMesh(
r_grid=(0.0, 1.0, 2.0),
theta_grid=(0.0, 2.0, 4.0, 5.0),
phi_grid=(0.0, 3.0, 6.0),
)
rmesh = openmc.RegularMesh()
rmesh.lower_left = (0.0, 0.0, 0.0)
rmesh.upper_right = (1.0, 3.0, 5.0)
@ -276,11 +279,11 @@ def test_vtk_write_ordering(run_in_tmpdir, model, mesh, surface):
def test_sphere_mesh_coordinates(run_in_tmpdir):
mesh = openmc.SphericalMesh()
mesh.r_grid = np.linspace(0.1, 10, 30)
mesh.phi_grid = np.linspace(0, 1.5*np.pi, 25)
mesh.theta_grid = np.linspace(0, np.pi / 2, 15)
mesh = openmc.SphericalMesh(
r_grid=np.linspace(0.1, 10, 30),
phi_grid=np.linspace(0, 1.5*np.pi, 25),
theta_grid=np.linspace(0, np.pi / 2, 15),
)
# write the data to a VTK file (no data)
vtk_filename = 'test.vtk'
mesh.write_data_to_vtk(vtk_filename, {})

View file

@ -33,11 +33,11 @@ def model():
settings.run_mode = 'fixed source'
# build
mesh = openmc.CylindricalMesh()
mesh.phi_grid = np.linspace(0, 2*np.pi, 21)
mesh.z_grid = np.linspace(-geom_size, geom_size, 11)
mesh.r_grid = np.linspace(0, geom_size, geom_size)
mesh = openmc.CylindricalMesh(
phi_grid=np.linspace(0, 2*np.pi, 21),
z_grid=np.linspace(-geom_size, geom_size, 11),
r_grid=np.linspace(0, geom_size, geom_size)
)
tally = openmc.Tally()
mesh_filter = openmc.MeshFilter(mesh)
@ -127,10 +127,11 @@ def void_coincident_geom_model():
settings.particles = 1000
model.settings = settings
mesh = openmc.CylindricalMesh()
mesh.r_grid = np.linspace(0, 250, 501)
mesh.z_grid = [-250, 250]
mesh.phi_grid = np.linspace(0, 2*np.pi, 2)
mesh = openmc.CylindricalMesh(
r_grid=np.linspace(0, 250, 501),
z_grid=[-250, 250],
phi_grid=np.linspace(0, 2*np.pi, 2),
)
mesh_filter = openmc.MeshFilter(mesh)
tally = openmc.Tally()

View file

@ -22,15 +22,17 @@ def test_spherical_mesh_estimators(run_in_tmpdir):
model.settings.inactive = 10
model.settings.batches = 20
sph_mesh = openmc.SphericalMesh()
sph_mesh.r_grid = np.linspace(0.0, 5.0**3, 20)**(1/3)
sph_mesh = openmc.SphericalMesh(
r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3)
)
tally1 = openmc.Tally()
tally1.filters = [openmc.MeshFilter(sph_mesh)]
tally1.scores = ['flux']
tally1.estimator = 'collision'
sph_mesh = openmc.SphericalMesh()
sph_mesh.r_grid = np.linspace(0.0, 5.0**3, 20)**(1/3)
sph_mesh = openmc.SphericalMesh(
r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3)
)
tally2 = openmc.Tally()
tally2.filters = [openmc.MeshFilter(sph_mesh)]
tally2.scores = ['flux']
@ -75,17 +77,19 @@ def test_cylindrical_mesh_estimators(run_in_tmpdir):
model.settings.inactive = 10
model.settings.batches = 20
cyl_mesh = openmc.CylindricalMesh()
cyl_mesh.r_grid = np.linspace(0.0, 5.0**3, 20)**(1/3)
cyl_mesh.z_grid = [-5., 5.]
cyl_mesh = openmc.CylindricalMesh(
r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3),
z_grid=[-5., 5.]
)
tally1 = openmc.Tally()
tally1.filters = [openmc.MeshFilter(cyl_mesh)]
tally1.scores = ['flux']
tally1.estimator = 'collision'
cyl_mesh = openmc.CylindricalMesh()
cyl_mesh.r_grid = np.linspace(0.0, 5.0**3, 20)**(1/3)
cyl_mesh.z_grid = [-5., 5.]
cyl_mesh = openmc.CylindricalMesh(
r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3),
z_grid=[-5., 5.]
)
tally2 = openmc.Tally()
tally2.filters = [openmc.MeshFilter(cyl_mesh)]
tally2.scores = ['flux']
@ -133,10 +137,11 @@ def test_cylindrical_mesh_coincident(scale, run_in_tmpdir):
model.settings.batches = 10
model.settings.inactive = 0
cyl_mesh = openmc.CylindricalMesh()
cyl_mesh.r_grid = [0., 1.25*scale]
cyl_mesh.phi_grid = [0., 2*math.pi]
cyl_mesh.z_grid = [-1e10, 1e10]
cyl_mesh = openmc.CylindricalMesh(
r_grid=[0., 1.25*scale],
phi_grid=[0., 2*math.pi],
z_grid=[-1e10, 1e10]
)
cyl_mesh_filter = openmc.MeshFilter(cyl_mesh)
cell_filter = openmc.CellFilter([cell1])
@ -183,10 +188,12 @@ def test_spherical_mesh_coincident(scale, run_in_tmpdir):
model.settings.batches = 10
model.settings.inactive = 0
sph_mesh = openmc.SphericalMesh()
sph_mesh.r_grid = [0., 1.25*scale]
sph_mesh.phi_grid = [0., 2*math.pi]
sph_mesh.theta_grid = [0., math.pi]
sph_mesh = openmc.SphericalMesh(
r_grid=[0., 1.25*scale],
phi_grid=[0., 2*math.pi],
theta_grid=[0., math.pi],
)
sph_mesh_filter = openmc.MeshFilter(sph_mesh)
cell_filter = openmc.CellFilter([cell1])
@ -227,10 +234,11 @@ def test_get_reshaped_data(run_in_tmpdir):
model.settings.inactive = 10
model.settings.batches = 20
sph_mesh = openmc.SphericalMesh()
sph_mesh.r_grid = np.linspace(0.0, 5.0**3, 20)**(1/3)
sph_mesh.theta_grid = np.linspace(0, math.pi, 4)
sph_mesh.phi_grid = np.linspace(0, 2*math.pi, 3)
sph_mesh = openmc.SphericalMesh(
r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3),
theta_grid=np.linspace(0, math.pi, 4),
phi_grid=np.linspace(0, 2*math.pi, 3)
)
tally1 = openmc.Tally()
efilter = openmc.EnergyFilter([0, 1e5, 1e8])
meshfilter = openmc.MeshFilter(sph_mesh)

View file

@ -1,6 +1,9 @@
import openmc
import pytest
from math import pi
import numpy as np
import pytest
import openmc
@pytest.mark.parametrize("val_left,val_right", [(0, 0), (-1., -1.), (2.0, 2)])
def test_raises_error_when_flat(val_left, val_right):
@ -37,10 +40,63 @@ def test_raises_error_when_flat(val_left, val_right):
def test_mesh_bounding_box():
mesh = openmc.RegularMesh()
mesh.lower_left = [-2, -3 ,-5]
mesh.lower_left = [-2, -3, -5]
mesh.upper_right = [2, 3, 5]
bb = mesh.bounding_box
assert isinstance(bb, openmc.BoundingBox)
np.testing.assert_array_equal(bb.lower_left, np.array([-2, -3 ,-5]))
np.testing.assert_array_equal(bb.upper_right, np.array([2, 3, 5]))
def test_SphericalMesh_initiation():
# test defaults
mesh = openmc.SphericalMesh(r_grid=(0, 10))
assert (mesh.origin == np.array([0, 0, 0])).all()
assert mesh.r_grid == (0, 10)
assert (mesh.theta_grid == np.array([0, pi])).all()
assert (mesh.phi_grid == np.array([0, 2*pi])).all()
# test setting on creation
mesh = openmc.SphericalMesh(
origin=(1, 2, 3),
r_grid=(0, 2),
theta_grid=(1, 3),
phi_grid=(2, 4)
)
assert (mesh.origin == np.array([1, 2, 3])).all()
assert mesh.r_grid == (0., 2.)
assert mesh.theta_grid == (1, 3)
assert mesh.phi_grid == (2, 4)
# test attribute changing
mesh.r_grid = (0, 11)
assert (mesh.r_grid == np.array([0., 11.])).all()
def test_CylindricalMesh_initiation():
# test defaults
mesh = openmc.CylindricalMesh(r_grid=(0, 10), z_grid=(0, 10))
assert (mesh.origin == np.array([0, 0, 0])).all()
assert mesh.r_grid == (0, 10)
assert mesh.phi_grid == (0, 2*pi)
assert mesh.z_grid == (0, 10)
# test setting on creation
mesh = openmc.CylindricalMesh(
origin=(1, 2, 3),
r_grid=(0, 2),
z_grid=(1, 3),
phi_grid=(2, 4)
)
assert (mesh.origin == np.array([1, 2, 3])).all()
assert (mesh.r_grid == np.array([0., 2.])).all()
assert mesh.z_grid == (1, 3)
assert (mesh.phi_grid == np.array([2, 4])).all()
# test attribute changing
mesh.r_grid = (0., 10.)
assert (mesh.r_grid == np.array([0, 10.])).all()
mesh.z_grid = (0., 4.)
assert (mesh.z_grid == np.array([0, 4.])).all()

View file

@ -9,7 +9,7 @@ def test_reg_mesh_from_cell():
surface = openmc.Sphere(r=10, x0=2, y0=3, z0=5)
cell = openmc.Cell(region=-surface)
mesh = openmc.RegularMesh.from_domain(cell, dimension=[7, 11, 13])
mesh = openmc.RegularMesh.from_domain(domain=cell, dimension=[7, 11, 13])
assert isinstance(mesh, openmc.RegularMesh)
assert np.array_equal(mesh.dimension, (7, 11, 13))
assert np.array_equal(mesh.lower_left, cell.bounding_box[0])
@ -23,7 +23,7 @@ def test_cylindrical_mesh_from_cell():
z_surface_1 = openmc.ZPlane(z0=30)
z_surface_2 = openmc.ZPlane(z0=0)
cell = openmc.Cell(region=-cy_surface & -z_surface_1 & +z_surface_2)
mesh = openmc.CylindricalMesh.from_domain(cell, dimension=[2, 4, 3])
mesh = openmc.CylindricalMesh.from_domain(domain=cell, dimension=[2, 4, 3])
assert isinstance(mesh, openmc.CylindricalMesh)
assert np.array_equal(mesh.dimension, (2, 4, 3))
@ -38,7 +38,7 @@ def test_reg_mesh_from_region():
surface = openmc.Sphere(r=1, x0=-5, y0=-3, z0=-2)
region = -surface
mesh = openmc.RegularMesh.from_domain(region)
mesh = openmc.RegularMesh.from_domain(domain=region)
assert isinstance(mesh, openmc.RegularMesh)
assert np.array_equal(mesh.dimension, (10, 10, 10)) # default values
assert np.array_equal(mesh.lower_left, region.bounding_box[0])
@ -53,7 +53,7 @@ def test_cylindrical_mesh_from_region():
z_surface_2 = openmc.ZPlane(z0=-30)
cell = openmc.Cell(region=-cy_surface & -z_surface_1 & +z_surface_2)
mesh = openmc.CylindricalMesh.from_domain(
cell,
domain=cell,
dimension=(6, 2, 3),
phi_grid_bounds=(0., np.pi)
)

View file

@ -33,11 +33,11 @@ def model():
settings.run_mode = 'fixed source'
# build
mesh = openmc.SphericalMesh()
mesh.phi_grid = np.linspace(0, 2*np.pi, 13)
mesh.theta_grid = np.linspace(0, np.pi, 7)
mesh.r_grid = np.linspace(0, geom_size, geom_size)
mesh = openmc.SphericalMesh(
phi_grid=np.linspace(0, 2*np.pi, 13),
theta_grid=np.linspace(0, np.pi, 7),
r_grid=np.linspace(0, geom_size, geom_size),
)
tally = openmc.Tally()
mesh_filter = openmc.MeshFilter(mesh)
@ -135,8 +135,7 @@ def void_coincident_geom_model():
settings.particles = 5000
model.settings = settings
mesh = openmc.SphericalMesh()
mesh.r_grid = np.linspace(0, 250, 501)
mesh = openmc.SphericalMesh(r_grid=np.linspace(0, 250, 501))
mesh_filter = openmc.MeshFilter(mesh)
tally = openmc.Tally()