Add a vessel composite surface with ellipsoids on top and bottom. (#3168)

Co-authored-by: Patrick Shriwise <pshriwise@gmail.com>
Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
This commit is contained in:
Seda Yilmaz 2024-11-13 02:11:59 +03:00 committed by GitHub
parent 3e2a042eb1
commit 0ecd45c906
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 150 additions and 5 deletions

View file

@ -32,6 +32,7 @@ Composite Surfaces
openmc.model.RectangularParallelepiped
openmc.model.RectangularPrism
openmc.model.RightCircularCylinder
openmc.model.Vessel
openmc.model.XConeOneSided
openmc.model.YConeOneSided
openmc.model.ZConeOneSided

View file

@ -41,9 +41,11 @@ class CompositeSurface(ABC):
def boundary_type(self, boundary_type):
# Set boundary type on underlying surfaces, but not for ambiguity plane
# on one-sided cones
classes = (XConeOneSided, YConeOneSided, ZConeOneSided, Vessel)
for name in self._surface_names:
if name != 'plane':
getattr(self, name).boundary_type = boundary_type
if isinstance(self, classes) and name.startswith('plane'):
continue
getattr(self, name).boundary_type = boundary_type
def __repr__(self):
return f"<{type(self).__name__} at 0x{id(self):x}>"
@ -90,8 +92,8 @@ class CylinderSector(CompositeSurface):
counterclockwise direction with respect to the first basis axis
(+y, +z, or +x). Must be greater than :attr:`theta1`.
center : iterable of float
Coordinate for central axes of cylinders in the (y, z), (x, z), or (x, y)
basis. Defaults to (0,0).
Coordinate for central axes of cylinders in the (y, z), (x, z), or (x, y)
basis. Defaults to (0,0).
axis : {'x', 'y', 'z'}
Central axis of the cylinders defining the inner and outer surfaces of
the sector. Defaults to 'z'.
@ -119,7 +121,7 @@ class CylinderSector(CompositeSurface):
r2,
theta1,
theta2,
center=(0.,0.),
center=(0., 0.),
axis='z',
**kwargs):
@ -1856,3 +1858,96 @@ class ConicalFrustum(CompositeSurface):
def __neg__(self) -> openmc.Region:
return +self.plane_bottom & -self.plane_top & -self.cone
class Vessel(CompositeSurface):
"""Vessel composed of cylinder with semi-ellipsoid top and bottom.
This composite surface is represented by a finite cylinder with ellipsoidal
top and bottom surfaces. This surface is equivalent to the 'vesesl' surface
in Serpent.
.. versionadded:: 0.15.1
Parameters
----------
r : float
Radius of vessel.
p1 : float
Minimum coordinate for cylindrical part of vessel.
p2 : float
Maximum coordinate for cylindrical part of vessel.
h1 : float
Height of bottom ellipsoidal part of vessel.
h2 : float
Height of top ellipsoidal part of vessel.
center : 2-tuple of float
Coordinate for central axis of the cylinder in the (y, z), (x, z), or
(x, y) basis. Defaults to (0,0).
axis : {'x', 'y', 'z'}
Central axis of the cylinder.
"""
_surface_names = ('cyl', 'plane_bottom', 'plane_top', 'bottom', 'top')
def __init__(self, r: float, p1: float, p2: float, h1: float, h2: float,
center: Sequence[float] = (0., 0.), axis: str = 'z', **kwargs):
if p1 >= p2:
raise ValueError('p1 must be less than p2')
check_value('axis', axis, {'x', 'y', 'z'})
c1, c2 = center
cyl_class = getattr(openmc, f'{axis.upper()}Cylinder')
plane_class = getattr(openmc, f'{axis.upper()}Plane')
self.cyl = cyl_class(c1, c2, r, **kwargs)
self.plane_bottom = plane_class(p1)
self.plane_top = plane_class(p2)
# General equation for an ellipsoid:
# (x-x₀)²/r² + (y-y₀)²/r² + (z-z₀)²/h² = 1
# (x-x₀)² + (y-y₀)² + (z-z₀)²s² = r²
# Let s = r/h:
# (x² - 2x₀x + x₀²) + (y² - 2y₀y + y₀²) + (z² - 2z₀z + z₀²)s² = r²
# x² + y² + s²z² - 2x₀x - 2y₀y - 2s²z₀z + (x₀² + y₀² + z₀²s² - r²) = 0
sb = (r/h1)
st = (r/h2)
kwargs['a'] = kwargs['b'] = kwargs['c'] = 1.0
kwargs_bottom = kwargs
kwargs_top = kwargs.copy()
sb2 = sb*sb
st2 = st*st
kwargs_bottom['k'] = c1*c1 + c2*c2 + p1*p1*sb2 - r*r
kwargs_top['k'] = c1*c1 + c2*c2 + p2*p2*st2 - r*r
if axis == 'x':
kwargs_bottom['a'] *= sb2
kwargs_top['a'] *= st2
kwargs_bottom['g'] = -2*p1*sb2
kwargs_top['g'] = -2*p2*st2
kwargs_top['h'] = kwargs_bottom['h'] = -2*c1
kwargs_top['j'] = kwargs_bottom['j'] = -2*c2
elif axis == 'y':
kwargs_bottom['b'] *= sb2
kwargs_top['b'] *= st2
kwargs_top['g'] = kwargs_bottom['g'] = -2*c1
kwargs_bottom['h'] = -2*p1*sb2
kwargs_top['h'] = -2*p2*st2
kwargs_top['j'] = kwargs_bottom['j'] = -2*c2
elif axis == 'z':
kwargs_bottom['c'] *= sb2
kwargs_top['c'] *= st2
kwargs_top['g'] = kwargs_bottom['g'] = -2*c1
kwargs_top['h'] = kwargs_bottom['h'] = -2*c2
kwargs_bottom['j'] = -2*p1*sb2
kwargs_top['j'] = -2*p2*st2
self.bottom = openmc.Quadric(**kwargs_bottom)
self.top = openmc.Quadric(**kwargs_top)
def __neg__(self):
return ((-self.cyl & +self.plane_bottom & -self.plane_top) |
(-self.bottom & -self.plane_bottom) |
(-self.top & +self.plane_top))

View file

@ -599,3 +599,52 @@ def test_conical_frustum():
# Denegenerate case with r1 = r2
s = openmc.model.ConicalFrustum(center_base, axis, r1, r1)
assert (1., 1., -0.01) in -s
def test_vessel():
center = (3.0, 2.0)
r = 1.0
p1, p2 = -5.0, 5.0
h1 = h2 = 1.0
s = openmc.model.Vessel(r, p1, p2, h1, h2, center)
assert isinstance(s.cyl, openmc.Cylinder)
assert isinstance(s.plane_bottom, openmc.Plane)
assert isinstance(s.plane_top, openmc.Plane)
assert isinstance(s.bottom, openmc.Quadric)
assert isinstance(s.top, openmc.Quadric)
# Make sure boundary condition propagates (but not for planes)
s.boundary_type = 'reflective'
assert s.boundary_type == 'reflective'
assert s.cyl.boundary_type == 'reflective'
assert s.bottom.boundary_type == 'reflective'
assert s.top.boundary_type == 'reflective'
assert s.plane_bottom.boundary_type == 'transmission'
assert s.plane_top.boundary_type == 'transmission'
# Check bounding box
ll, ur = (+s).bounding_box
assert np.all(np.isinf(ll))
assert np.all(np.isinf(ur))
ll, ur = (-s).bounding_box
assert np.all(np.isinf(ll))
assert np.all(np.isinf(ur))
# __contains__ on associated half-spaces
assert (3., 2., 0.) in -s
assert (3., 2., -5.0) in -s
assert (3., 2., 5.0) in -s
assert (3., 2., -5.9) in -s
assert (3., 2., 5.9) in -s
assert (3., 2., -6.1) not in -s
assert (3., 2., 6.1) not in -s
assert (4.5, 2., 0.) in +s
assert (3., 3.2, 0.) in +s
assert (3., 2., 7.) in +s
# translate method
s_t = s.translate((0., 0., 1.))
assert (3., 2., 6.1) in -s_t
# Make sure repr works
repr(s)