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) 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)