OpenMC/openmc/surface.py
2015-12-03 07:31:02 -06:00

1441 lines
41 KiB
Python

from abc import ABCMeta
from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
import numpy as np
from openmc.checkvalue import check_type, check_value, check_greater_than
from openmc.region import Region
if sys.version_info[0] >= 3:
basestring = str
# A static variable for auto-generated Surface IDs
AUTO_SURFACE_ID = 10000
_BC_TYPES = ['transmission', 'vacuum', 'reflective', 'periodic']
def reset_auto_surface_id():
global AUTO_SURFACE_ID
AUTO_SURFACE_ID = 10000
class Surface(object):
"""A two-dimensional surface that can be used define regions of space with an
associated boundary condition.
Parameters
----------
surface_id : int, optional
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
name : str, optional
Name of the surface. If not specified, the name will be the empty
string.
Attributes
----------
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface.
coeffs : dict
Dictionary of surface coefficients
id : int
Unique identifier for the surface
name : str
Name of the surface
type : str
Type of the surface, e.g. 'x-plane'
"""
def __init__(self, surface_id=None, boundary_type='transmission', name=''):
# Initialize class attributes
self.id = surface_id
self.name = name
self._type = ''
self.boundary_type = boundary_type
# A dictionary of the quadratic surface coefficients
# Key - coefficeint name
# Value - coefficient value
self._coeffs = {}
# An ordered list of the coefficient names to export to XML in the
# proper order
self._coeff_keys = []
def __neg__(self):
return Halfspace(self, '-')
def __pos__(self):
return Halfspace(self, '+')
def __repr__(self):
string = 'Surface\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._type)
string += '{0: <16}{1}{2}\n'.format('\tBoundary', '=\t', self._boundary_type)
coeffs = '{0: <16}'.format('\tCoefficients') + '\n'
for coeff in self._coeffs:
coeffs += '{0: <16}{1}{2}\n'.format(coeff, '=\t', self._coeffs[coeff])
string += coeffs
return string
@property
def id(self):
return self._id
@property
def name(self):
return self._name
@property
def type(self):
return self._type
@property
def boundary_type(self):
return self._boundary_type
@property
def coeffs(self):
return self._coeffs
@id.setter
def id(self, surface_id):
if surface_id is None:
global AUTO_SURFACE_ID
self._id = AUTO_SURFACE_ID
AUTO_SURFACE_ID += 1
else:
check_type('surface ID', surface_id, Integral)
check_greater_than('surface ID', surface_id, 0, equality=True)
self._id = surface_id
@name.setter
def name(self, name):
if name is not None:
check_type('surface name', name, basestring)
self._name = name
else:
self._name = ''
@boundary_type.setter
def boundary_type(self, boundary_type):
check_type('boundary type', boundary_type, basestring)
check_value('boundary type', boundary_type, _BC_TYPES)
self._boundary_type = boundary_type
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. If the half-space is
unbounded in a particular direction, numpy.inf is used to represent
infinity.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
def create_xml_subelement(self):
element = ET.Element("surface")
element.set("id", str(self._id))
if len(self._name) > 0:
element.set("name", str(self._name))
element.set("type", self._type)
element.set("boundary", self._boundary_type)
element.set("coeffs", ' '.join([str(self._coeffs.setdefault(key, 0.0))
for key in self._coeff_keys]))
return element
class Plane(Surface):
"""An arbitrary plane of the form :math:`Ax + By + Cz = D`.
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
A : float
The 'A' parameter for the plane
B : float
The 'B' parameter for the plane
C : float
The 'C' parameter for the plane
D : float
The 'D' parameter for the plane
name : str
Name of the plane. If not specified, the name will be the empty string.
Attributes
----------
a : float
The 'A' parameter for the plane
b : float
The 'B' parameter for the plane
c : float
The 'C' parameter for the plane
d : float
The 'D' parameter for the plane
"""
def __init__(self, surface_id=None, boundary_type='transmission',
A=None, B=None, C=None, D=None, name=''):
# Initialize Plane class attributes
super(Plane, self).__init__(surface_id, boundary_type, name=name)
self._type = 'plane'
self._coeff_keys = ['A', 'B', 'C', 'D']
self._coeffs['A'] = 1.
self._coeffs['B'] = 0.
self._coeffs['C'] = 0.
self._coeffs['D'] = 0.
if A is not None:
self.a = A
if B is not None:
self.b = B
if C is not None:
self.c = C
if D is not None:
self.d = D
@property
def a(self):
return self.coeffs['A']
@property
def b(self):
return self.coeffs['B']
@property
def c(self):
return self.coeffs['C']
@property
def d(self):
return self.coeffs['D']
@a.setter
def a(self, A):
check_type('A coefficient', A, Real)
self._coeffs['A'] = A
@b.setter
def b(self, B):
check_type('B coefficient', B, Real)
self._coeffs['B'] = B
@c.setter
def c(self, C):
check_type('C coefficient', C, Real)
self._coeffs['C'] = C
@d.setter
def d(self, D):
check_type('D coefficient', D, Real)
self._coeffs['D'] = D
class XPlane(Plane):
"""A plane perpendicular to the x axis, i.e. a surface of the form :math:`x -
x_0 = 0`
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
x0 : float
Location of the plane
name : str
Name of the plane. If not specified, the name will be the empty string.
Attributes
----------
x0 : float
Location of the plane
"""
def __init__(self, surface_id=None, boundary_type='transmission',
x0=None, name=''):
# Initialize XPlane class attributes
super(XPlane, self).__init__(surface_id, boundary_type, name=name)
self._type = 'x-plane'
self._coeff_keys = ['x0']
self._coeffs['x0'] = 0.
if x0 is not None:
self.x0 = x0
@property
def x0(self):
return self.coeffs['x0']
@x0.setter
def x0(self, x0):
check_type('x0 coefficient', x0, Real)
self._coeffs['x0'] = x0
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. For the x-plane surface, the
half-spaces are unbounded in their y- and z- directions. To represent
infinity, numpy.inf is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([self.x0, np.inf, np.inf]))
elif side == '+':
return (np.array([self.x0, -np.inf, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
class YPlane(Plane):
"""A plane perpendicular to the y axis, i.e. a surface of the form :math:`y -
y_0 = 0`
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
y0 : float
Location of the plane
name : str
Name of the plane. If not specified, the name will be the empty string.
Attributes
----------
y0 : float
Location of the plane
"""
def __init__(self, surface_id=None, boundary_type='transmission',
y0=None, name=''):
# Initialize YPlane class attributes
super(YPlane, self).__init__(surface_id, boundary_type, name=name)
self._type = 'y-plane'
self._coeff_keys = ['y0']
self._coeffs['y0'] = 0.
if y0 is not None:
self.y0 = y0
@property
def y0(self):
return self.coeffs['y0']
@y0.setter
def y0(self, y0):
check_type('y0 coefficient', y0, Real)
self._coeffs['y0'] = y0
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. For the y-plane surface, the
half-spaces are unbounded in their x- and z- directions. To represent
infinity, numpy.inf is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, self.y0, np.inf]))
elif side == '+':
return (np.array([-np.inf, self.y0, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
class ZPlane(Plane):
"""A plane perpendicular to the z axis, i.e. a surface of the form :math:`z -
z_0 = 0`
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
z0 : float
Location of the plane
name : str
Name of the plane. If not specified, the name will be the empty string.
Attributes
----------
z0 : float
Location of the plane
"""
def __init__(self, surface_id=None, boundary_type='transmission',
z0=None, name=''):
# Initialize ZPlane class attributes
super(ZPlane, self).__init__(surface_id, boundary_type, name=name)
self._type = 'z-plane'
self._coeff_keys = ['z0']
self._coeffs['z0'] = 0.
if z0 is not None:
self.z0 = z0
@property
def z0(self):
return self.coeffs['z0']
@z0.setter
def z0(self, z0):
check_type('z0 coefficient', z0, Real)
self._coeffs['z0'] = z0
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. For the z-plane surface, the
half-spaces are unbounded in their x- and y- directions. To represent
infinity, numpy.inf is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, np.inf, self.z0]))
elif side == '+':
return (np.array([-np.inf, -np.inf, self.z0]),
np.array([np.inf, np.inf, np.inf]))
class Cylinder(Surface):
"""A cylinder whose length is parallel to the x-, y-, or z-axis.
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
R : float
Radius of the cylinder
name : str
Name of the cylinder. If not specified, the name will be the empty
string.
Attributes
----------
r : float
Radius of the cylinder
"""
__metaclass__ = ABCMeta
def __init__(self, surface_id=None, boundary_type='transmission',
R=None, name=''):
# Initialize Cylinder class attributes
super(Cylinder, self).__init__(surface_id, boundary_type, name=name)
self._coeff_keys = ['R']
self._coeffs['R'] = 1.
if R is not None:
self.r = R
@property
def r(self):
return self.coeffs['R']
@r.setter
def r(self, R):
check_type('R coefficient', R, Real)
self._coeffs['R'] = R
class XCylinder(Cylinder):
"""An infinite cylinder whose length is parallel to the x-axis. This is a
quadratic surface of the form :math:`(y - y_0)^2 + (z - z_0)^2 = R^2`.
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
y0 : float
y-coordinate of the center of the cylinder
z0 : float
z-coordinate of the center of the cylinder
R : float
Radius of the cylinder
name : str
Name of the cylinder. If not specified, the name will be the empty
string.
Attributes
----------
y0 : float
y-coordinate of the center of the cylinder
z0 : float
z-coordinate of the center of the cylinder
"""
def __init__(self, surface_id=None, boundary_type='transmission',
y0=None, z0=None, R=None, name=''):
# Initialize XCylinder class attributes
super(XCylinder, self).__init__(surface_id, boundary_type, R, name=name)
self._type = 'x-cylinder'
self._coeff_keys = ['y0', 'z0', 'R']
self._coeffs['y0'] = 0.
self._coeffs['z0'] = 0.
if y0 is not None:
self.y0 = y0
if z0 is not None:
self.z0 = z0
@property
def y0(self):
return self.coeffs['y0']
@property
def z0(self):
return self.coeffs['z0']
@y0.setter
def y0(self, y0):
check_type('y0 coefficient', y0, Real)
self._coeffs['y0'] = y0
@z0.setter
def z0(self, z0):
check_type('z0 coefficient', z0, Real)
self._coeffs['z0'] = z0
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. For the x-cylinder surface,
the negative half-space is unbounded in the x- direction and the
positive half-space is unbounded in all directions. To represent
infinity, numpy.inf is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([-np.inf, self.y0 - self.r, self.z0 - self.r]),
np.array([np.inf, self.y0 + self.r, self.z0 + self.r]))
elif side == '+':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
class YCylinder(Cylinder):
"""An infinite cylinder whose length is parallel to the y-axis. This is a
quadratic surface of the form :math:`(x - x_0)^2 + (z - z_0)^2 = R^2`.
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
x0 : float
x-coordinate of the center of the cylinder
z0 : float
z-coordinate of the center of the cylinder
R : float
Radius of the cylinder
name : str
Name of the cylinder. If not specified, the name will be the empty
string.
Attributes
----------
x0 : float
x-coordinate of the center of the cylinder
z0 : float
z-coordinate of the center of the cylinder
"""
def __init__(self, surface_id=None, boundary_type='transmission',
x0=None, z0=None, R=None, name=''):
# Initialize YCylinder class attributes
super(YCylinder, self).__init__(surface_id, boundary_type, R, name=name)
self._type = 'y-cylinder'
self._coeff_keys = ['x0', 'z0', 'R']
self._coeffs['x0'] = 0.
self._coeffs['z0'] = 0.
if x0 is not None:
self.x0 = x0
if z0 is not None:
self.z0 = z0
@property
def x0(self):
return self.coeffs['x0']
@property
def z0(self):
return self.coeffs['z0']
@x0.setter
def x0(self, x0):
check_type('x0 coefficient', x0, Real)
self._coeffs['x0'] = x0
@z0.setter
def z0(self, z0):
check_type('z0 coefficient', z0, Real)
self._coeffs['z0'] = z0
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. For the y-cylinder surface,
the negative half-space is unbounded in the y- direction and the
positive half-space is unbounded in all directions. To represent
infinity, numpy.inf is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([self.x0 - self.r, -np.inf, self.z0 - self.r]),
np.array([self.x0 + self.r, np.inf, self.z0 + self.r]))
elif side == '+':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
class ZCylinder(Cylinder):
"""An infinite cylinder whose length is parallel to the z-axis. This is a
quadratic surface of the form :math:`(x - x_0)^2 + (y - y_0)^2 = R^2`.
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
x0 : float
x-coordinate of the center of the cylinder
y0 : float
y-coordinate of the center of the cylinder
R : float
Radius of the cylinder
name : str
Name of the cylinder. If not specified, the name will be the empty
string.
Attributes
----------
x0 : float
x-coordinate of the center of the cylinder
y0 : float
y-coordinate of the center of the cylinder
"""
def __init__(self, surface_id=None, boundary_type='transmission',
x0=None, y0=None, R=None, name=''):
# Initialize ZCylinder class attributes
super(ZCylinder, self).__init__(surface_id, boundary_type, R, name=name)
self._type = 'z-cylinder'
self._coeff_keys = ['x0', 'y0', 'R']
self._coeffs['x0'] = 0.
self._coeffs['y0'] = 0.
if x0 is not None:
self.x0 = x0
if y0 is not None:
self.y0 = y0
@property
def x0(self):
return self.coeffs['x0']
@property
def y0(self):
return self.coeffs['y0']
@x0.setter
def x0(self, x0):
check_type('x0 coefficient', x0, Real)
self._coeffs['x0'] = x0
@y0.setter
def y0(self, y0):
check_type('y0 coefficient', y0, Real)
self._coeffs['y0'] = y0
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. For the z-cylinder surface,
the negative half-space is unbounded in the z- direction and the
positive half-space is unbounded in all directions. To represent
infinity, numpy.inf is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([self.x0 - self.r, self.y0 - self.r, -np.inf]),
np.array([self.x0 + self.r, self.y0 + self.r, np.inf]))
elif side == '+':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
class Sphere(Surface):
"""A sphere of the form :math:`(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 = R^2`.
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
x0 : float
x-coordinate of the center of the sphere
y0 : float
y-coordinate of the center of the sphere
z0 : float
z-coordinate of the center of the sphere
R : float
Radius of the sphere
name : str
Name of the sphere. If not specified, the name will be the empty string.
Attributes
----------
x0 : float
x-coordinate of the center of the sphere
y0 : float
y-coordinate of the center of the sphere
z0 : float
z-coordinate of the center of the sphere
R : float
Radius of the sphere
"""
def __init__(self, surface_id=None, boundary_type='transmission',
x0=None, y0=None, z0=None, R=None, name=''):
# Initialize Sphere class attributes
super(Sphere, self).__init__(surface_id, boundary_type, name=name)
self._type = 'sphere'
self._coeff_keys = ['x0', 'y0', 'z0', 'R']
self._coeffs['x0'] = 0.
self._coeffs['y0'] = 0.
self._coeffs['z0'] = 0.
self._coeffs['R'] = 1.
if x0 is not None:
self.x0 = x0
if y0 is not None:
self.y0 = y0
if z0 is not None:
self.z0 = z0
if R is not None:
self.r = R
@property
def x0(self):
return self.coeffs['x0']
@property
def y0(self):
return self.coeffs['y0']
@property
def z0(self):
return self.coeffs['z0']
@property
def r(self):
return self.coeffs['R']
@x0.setter
def x0(self, x0):
check_type('x0 coefficient', x0, Real)
self._coeffs['x0'] = x0
@y0.setter
def y0(self, y0):
check_type('y0 coefficient', y0, Real)
self._coeffs['y0'] = y0
@z0.setter
def z0(self, z0):
check_type('z0 coefficient', z0, Real)
self._coeffs['z0'] = z0
@r.setter
def r(self, R):
check_type('R coefficient', R, Real)
self._coeffs['R'] = R
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. The positive half-space of a
sphere is unbounded in all directions. To represent infinity, numpy.inf
is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([self.x0 - self.r, self.y0 - self.r,
self.z0 - self.r]),
np.array([self.x0 + self.r, self.y0 + self.r,
self.z0 + self.r]))
elif side == '+':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
class Cone(Surface):
"""A conical surface parallel to the x-, y-, or z-axis.
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
x0 : float
x-coordinate of the apex
y0 : float
y-coordinate of the apex
z0 : float
z-coordinate of the apex
R2 : float
Parameter related to the aperature
name : str
Name of the cone. If not specified, the name will be the empty string.
Attributes
----------
x0 : float
x-coordinate of the apex
y0 : float
y-coordinate of the apex
z0 : float
z-coordinate of the apex
R2 : float
Parameter related to the aperature
"""
__metaclass__ = ABCMeta
def __init__(self, surface_id=None, boundary_type='transmission',
x0=None, y0=None, z0=None, R2=None, name=''):
# Initialize Cone class attributes
super(Cone, self).__init__(surface_id, boundary_type, name=name)
self._coeff_keys = ['x0', 'y0', 'z0', 'R2']
self._coeffs['x0'] = 0.
self._coeffs['y0'] = 0.
self._coeffs['z0'] = 0.
self._coeffs['R2'] = 1.
if x0 is not None:
self.x0 = x0
if y0 is not None:
self.y0 = y0
if z0 is not None:
self.z0 = z0
if R2 is not None:
self.r2 = R2
@property
def x0(self):
return self.coeffs['x0']
@property
def y0(self):
return self.coeffs['y0']
@property
def z0(self):
return self.coeffs['z0']
@property
def r2(self):
return self.coeffs['r2']
@x0.setter
def x0(self, x0):
check_type('x0 coefficient', x0, Real)
self._coeffs['x0'] = x0
@y0.setter
def y0(self, y0):
check_type('y0 coefficient', y0, Real)
self._coeffs['y0'] = y0
@z0.setter
def z0(self, z0):
check_type('z0 coefficient', z0, Real)
self._coeffs['z0'] = z0
@r2.setter
def r2(self, R2):
check_type('R^2 coefficient', R2, Real)
self._coeffs['R2'] = R2
class XCone(Cone):
"""A cone parallel to the x-axis of the form :math:`(y - y_0)^2 + (z - z_0)^2 =
R^2 (x - x_0)^2`.
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
x0 : float
x-coordinate of the apex
y0 : float
y-coordinate of the apex
z0 : float
z-coordinate of the apex
R2 : float
Parameter related to the aperature
name : str
Name of the cone. If not specified, the name will be the empty string.
Attributes
----------
x0 : float
x-coordinate of the apex
y0 : float
y-coordinate of the apex
z0 : float
z-coordinate of the apex
R2 : float
Parameter related to the aperature
"""
def __init__(self, surface_id=None, boundary_type='transmission',
x0=None, y0=None, z0=None, R2=None, name=''):
# Initialize XCone class attributes
super(XCone, self).__init__(surface_id, boundary_type, x0, y0,
z0, R2, name=name)
self._type = 'x-cone'
class YCone(Cone):
"""A cone parallel to the y-axis of the form :math:`(x - x_0)^2 + (z - z_0)^2 =
R^2 (y - y_0)^2`.
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
x0 : float
x-coordinate of the apex
y0 : float
y-coordinate of the apex
z0 : float
z-coordinate of the apex
R2 : float
Parameter related to the aperature
name : str
Name of the cone. If not specified, the name will be the empty string.
Attributes
----------
x0 : float
x-coordinate of the apex
y0 : float
y-coordinate of the apex
z0 : float
z-coordinate of the apex
R2 : float
Parameter related to the aperature
"""
def __init__(self, surface_id=None, boundary_type='transmission',
x0=None, y0=None, z0=None, R2=None, name=''):
# Initialize YCone class attributes
super(YCone, self).__init__(surface_id, boundary_type, x0, y0, z0,
R2, name=name)
self._type = 'y-cone'
class ZCone(Cone):
"""A cone parallel to the x-axis of the form :math:`(x - x_0)^2 + (y - y_0)^2 =
R^2 (z - z_0)^2`.
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
x0 : float
x-coordinate of the apex
y0 : float
y-coordinate of the apex
z0 : float
z-coordinate of the apex
R2 : float
Parameter related to the aperature
name : str
Name of the cone. If not specified, the name will be the empty string.
Attributes
----------
x0 : float
x-coordinate of the apex
y0 : float
y-coordinate of the apex
z0 : float
z-coordinate of the apex
R2 : float
Parameter related to the aperature
"""
def __init__(self, surface_id=None, boundary_type='transmission',
x0=None, y0=None, z0=None, R2=None, name=''):
# Initialize ZCone class attributes
super(ZCone, self).__init__(surface_id, boundary_type, x0, y0, z0,
R2, name=name)
self._type = 'z-cone'
class Quadric(Surface):
"""A sphere of the form :math:`Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy +
Jz + K`.
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
a, b, c, d, e, f, g, h, j, k : float
coefficients for the surface
name : str
Name of the sphere. If not specified, the name will be the empty string.
Attributes
----------
a, b, c, d, e, f, g, h, j, k : float
coefficients for the surface
"""
def __init__(self, surface_id=None, boundary_type='transmission',
a=None, b=None, c=None, d=None, e=None, f=None, g=None,
h=None, j=None, k=None, name=''):
# Initialize Quadric class attributes
super(Quadric, self).__init__(surface_id, boundary_type, name=name)
self._type = 'quadric'
self._coeff_keys = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k']
for key in self._coeff_keys:
self._coeffs[key] = 0.
if a is not None:
self.a = a
if b is not None:
self.b = b
if c is not None:
self.c = c
if d is not None:
self.d = d
if e is not None:
self.e = e
if f is not None:
self.f = f
if g is not None:
self.g = g
if h is not None:
self.h = h
if j is not None:
self.j = j
if k is not None:
self.k = k
@property
def a(self):
return self.coeffs['a']
@property
def b(self):
return self.coeffs['b']
@property
def c(self):
return self.coeffs['c']
@property
def d(self):
return self.coeffs['d']
@property
def e(self):
return self.coeffs['e']
@property
def f(self):
return self.coeffs['f']
@property
def g(self):
return self.coeffs['g']
@property
def h(self):
return self.coeffs['h']
@property
def j(self):
return self.coeffs['j']
@property
def k(self):
return self.coeffs['k']
@a.setter
def a(self, a):
check_type('a coefficient', a, Real)
self._coeffs['a'] = a
@b.setter
def b(self, b):
check_type('b coefficient', b, Real)
self._coeffs['b'] = b
@c.setter
def c(self, c):
check_type('c coefficient', c, Real)
self._coeffs['c'] = c
@d.setter
def d(self, d):
check_type('d coefficient', d, Real)
self._coeffs['d'] = d
@e.setter
def e(self, e):
check_type('e coefficient', e, Real)
self._coeffs['e'] = e
@f.setter
def f(self, f):
check_type('f coefficient', f, Real)
self._coeffs['f'] = f
@g.setter
def g(self, g):
check_type('g coefficient', g, Real)
self._coeffs['g'] = g
@h.setter
def h(self, h):
check_type('h coefficient', h, Real)
self._coeffs['h'] = h
@j.setter
def j(self, j):
check_type('j coefficient', j, Real)
self._coeffs['j'] = j
@k.setter
def k(self, k):
check_type('k coefficient', k, Real)
self._coeffs['k'] = k
class Halfspace(Region):
"""A positive or negative half-space region.
A half-space is either of the two parts into which a two-dimension surface
divides the three-dimensional Euclidean space. If the equation of the
surface is :math:`f(x,y,z) = 0`, the region for which :math:`f(x,y,z) < 0`
is referred to as the negative half-space and the region for which
:math:`f(x,y,z) > 0` is referred to as the positive half-space.
Instances of Halfspace are generally not instantiated directly. Rather, they
can be created from an existing Surface through the __neg__ and __pos__
operators, as the following example demonstrates:
>>> sphere = openmc.surface.Sphere(surface_id=1, R=10.0)
>>> inside_sphere = -sphere
>>> outside_sphere = +sphere
>>> type(inside_sphere)
<class 'openmc.surface.Halfspace'>
Parameters
----------
surface : Surface
Surface which divides Euclidean space.
side : {'+', '-'}
Indicates whether the positive or negative half-space is used.
Attributes
----------
surface : Surface
Surface which divides Euclidean space.
side : {'+', '-'}
Indicates whether the positive or negative half-space is used.
bounding_box : tuple of numpy.array
Lower-left and upper-right coordinates of an axis-aligned bounding box
"""
def __init__(self, surface, side):
self.surface = surface
self.side = side
def __invert__(self):
return -self.surface if self.side == '+' else +self.surface
@property
def surface(self):
return self._surface
@surface.setter
def surface(self, surface):
check_type('surface', surface, Surface)
self._surface = surface
@property
def side(self):
return self._side
@side.setter
def side(self, side):
check_value('side', side, ('+', '-'))
self._side = side
@property
def bounding_box(self):
return self.surface.bounding_box(self.side)
def __str__(self):
return '-' + str(self.surface.id) if self.side == '-' \
else str(self.surface.id)