1212import openmc .checkvalue as cv
1313from .._xml import get_elem_list , get_text
1414from ..mesh import MeshBase
15- from .univariate import PowerLaw , Uniform , Univariate
15+ from .univariate import PowerLaw , Uniform , Univariate , delta_function
1616
1717
1818class UnitSphere (ABC ):
@@ -610,6 +610,10 @@ class CylindricalIndependent(Spatial):
610610 origin: Iterable of float, optional
611611 coordinates (x0, y0, z0) of the center of the cylindrical reference
612612 frame. Defaults to (0.0, 0.0, 0.0)
613+ r_dir : Iterable of float, optional
614+ Unit vector of the cylinder r axis at phi=0.
615+ z_dir : Iterable of float, optional
616+ Unit vector of the cylinder z axis direction.
613617
614618 Attributes
615619 ----------
@@ -623,14 +627,21 @@ class CylindricalIndependent(Spatial):
623627 origin: Iterable of float, optional
624628 coordinates (x0, y0, z0) of the center of the cylindrical reference
625629 frame. Defaults to (0.0, 0.0, 0.0)
630+ r_dir : Iterable of float, optional
631+ Unit vector of the cylinder r axis at phi=0.
632+ z_dir : Iterable of float, optional
633+ Unit vector of the cylinder z axis direction.
626634
627635 """
628636
629- def __init__ (self , r , phi , z , origin = (0.0 , 0.0 , 0.0 )):
637+ def __init__ (self , r , phi , z , origin = (0.0 , 0.0 , 0.0 ), r_dir = (1.0 , 0.0 , 0.0 ),
638+ z_dir = (0.0 , 0.0 , 1.0 )):
630639 self .r = r
631640 self .phi = phi
632641 self .z = z
633642 self .origin = origin
643+ self .z_dir = z_dir
644+ self .r_dir = r_dir
634645
635646 @property
636647 def r (self ):
@@ -669,6 +680,33 @@ def origin(self, origin):
669680 origin = np .asarray (origin )
670681 self ._origin = origin
671682
683+ @property
684+ def z_dir (self ):
685+ return self ._z_dir
686+
687+ @z_dir .setter
688+ def z_dir (self , z_dir ):
689+ cv .check_type ('z-axis direction' , z_dir , Iterable , Real )
690+ z_dir = np .array (z_dir )
691+ norm = np .linalg .norm (z_dir )
692+ cv .check_greater_than ('z-axis direction magnitude' , norm , 0.0 )
693+ z_dir /= norm
694+ self ._z_dir = z_dir
695+
696+ @property
697+ def r_dir (self ):
698+ return self ._r_dir
699+
700+ @r_dir .setter
701+ def r_dir (self , r_dir ):
702+ cv .check_type ('r-axis direction' , r_dir , Iterable , Real )
703+ r_dir = np .array (r_dir )
704+ r_dir -= np .dot (r_dir , self .z_dir ) * self .z_dir
705+ norm = np .linalg .norm (r_dir )
706+ cv .check_greater_than ('r-axis direction magnitude' , norm , 0.0 )
707+ r_dir /= norm
708+ self ._r_dir = r_dir
709+
672710 def to_xml_element (self ):
673711 """Return XML representation of the spatial distribution
674712
@@ -683,7 +721,12 @@ def to_xml_element(self):
683721 element .append (self .r .to_xml_element ('r' ))
684722 element .append (self .phi .to_xml_element ('phi' ))
685723 element .append (self .z .to_xml_element ('z' ))
686- element .set ("origin" , ' ' .join (map (str , self .origin )))
724+ if not np .allclose (self .origin , [0. , 0. , 0. ]):
725+ element .set ("origin" , ' ' .join (map (str , self .origin )))
726+ if not np .allclose (self .r_dir , [1. , 0. , 0. ]):
727+ element .set ("r_dir" , ' ' .join (map (str , self .r_dir )))
728+ if not np .allclose (self .z_dir , [0. , 0. , 1. ]):
729+ element .set ("z_dir" , ' ' .join (map (str , self .z_dir )))
687730 return element
688731
689732 @classmethod
@@ -704,8 +747,10 @@ def from_xml_element(cls, elem: ET.Element):
704747 r = Univariate .from_xml_element (elem .find ('r' ))
705748 phi = Univariate .from_xml_element (elem .find ('phi' ))
706749 z = Univariate .from_xml_element (elem .find ('z' ))
707- origin = get_elem_list (elem , "origin" , float )
708- return cls (r , phi , z , origin = origin )
750+ origin = get_elem_list (elem , "origin" , float ) or [0.0 , 0.0 , 0.0 ]
751+ r_dir = get_elem_list (elem , "r_dir" , float ) or [1.0 , 0.0 , 0.0 ]
752+ z_dir = get_elem_list (elem , "z_dir" , float ) or [0.0 , 0.0 , 1.0 ]
753+ return cls (r , phi , z , origin = origin , r_dir = r_dir , z_dir = z_dir )
709754
710755
711756class MeshSpatial (Spatial ):
@@ -1219,3 +1264,49 @@ def spherical_uniform(
12191264 phis_dist = Uniform (phis [0 ], phis [1 ])
12201265
12211266 return SphericalIndependent (r_dist , cos_thetas_dist , phis_dist , origin )
1267+
1268+
1269+ def cylindrical_uniform (
1270+ r_outer : float ,
1271+ height : float ,
1272+ r_inner : float = 0.0 ,
1273+ phis : Sequence [float ] = (0. , 2 * pi ),
1274+ ** kwargs ,
1275+ ):
1276+ """Return a uniform spatial distribution over a cylindrical shell.
1277+
1278+ This function provides a uniform spatial distribution over a cylindrical
1279+ shell between `r_inner` and `r_outer`. When `height` is zero, a delta
1280+ function is used for the z-distribution, giving a uniform distribution over
1281+ a flat ring (annulus) at z=0 in the local coordinate frame. Optionally, the
1282+ range of angles can be restricted by the `phis` argument.
1283+
1284+ .. versionadded:: 0.15.4
1285+
1286+ Parameters
1287+ ----------
1288+ r_outer : float
1289+ Outer radius of the cylindrical shell in [cm]
1290+ height : float
1291+ Height of the cylindrical shell in [cm]. When 0, the distribution is a
1292+ flat ring at z=0 in the local frame.
1293+ r_inner : float
1294+ Inner radius of the cylindrical shell in [cm]
1295+ phis : iterable of float
1296+ Starting and ending phi coordinates (azimuthal angle) in radians in a
1297+ reference frame centered at `origin`.
1298+ **kwargs
1299+ Keyword arguments passed directly to
1300+ :class:`~openmc.stats.CylindricalIndependent` (e.g., ``origin``,
1301+ ``r_dir``, ``z_dir``).
1302+
1303+ Returns
1304+ -------
1305+ openmc.stats.CylindricalIndependent
1306+ Uniform distribution over the cylindrical shell
1307+ """
1308+
1309+ r_dist = PowerLaw (r_inner , r_outer , 1 )
1310+ phis_dist = Uniform (phis [0 ], phis [1 ])
1311+ z_dist = delta_function (0.0 ) if height == 0.0 else Uniform (- height / 2 , height / 2 )
1312+ return CylindricalIndependent (r_dist , phis_dist , z_dist , ** kwargs )
0 commit comments