mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 06:05:58 -04:00
Adopt shorthand notation for half-spaces and regions in Python API
Surfaces now have __neg__ and __pos__ operators, and regions have __and__, __or__, and __invert__. This makes the syntax for complex regions much less bulky.
This commit is contained in:
parent
f0da87cde0
commit
71dfde8d12
5 changed files with 32 additions and 31 deletions
|
|
@ -896,7 +896,7 @@ Each ``<cell>`` element can have the following attributes or sub-elements:
|
|||
the cell occupies. Each half-space is identified by the unique ID of the
|
||||
surface prefixed by `-` or `+` to indicate that it is the negative or
|
||||
positive half-space, respectively. The `+` sign for a positive half-space
|
||||
can be omitted. Valid Boolean operators are parentheses, union `^`,
|
||||
can be omitted. Valid Boolean operators are parentheses, union `|`,
|
||||
complement `~`, and intersection. Intersection is implicit and indicated by
|
||||
the presence of whitespace. The order of operator precedence is parentheses,
|
||||
complement, intersection, and then union.
|
||||
|
|
@ -908,7 +908,7 @@ Each ``<cell>`` element can have the following attributes or sub-elements:
|
|||
|
||||
.. code-block:: xml
|
||||
|
||||
<cell id="1" material="1" region="-3 ^ ~(5 -2)" />
|
||||
<cell id="1" material="1" region="-3 | ~(5 -2)" />
|
||||
|
||||
.. note:: The ``region`` attribute/element can be omitted to make a cell
|
||||
fill its entire universe.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,15 @@ from openmc.checkvalue import check_type
|
|||
class Region(object):
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
def __and__(self, other):
|
||||
return Intersection(self, other)
|
||||
|
||||
def __or__(self, other):
|
||||
return Union(self, other)
|
||||
|
||||
def __invert__(self):
|
||||
return Complement(self)
|
||||
|
||||
@abstractmethod
|
||||
def __str__(self):
|
||||
return ''
|
||||
|
|
@ -19,8 +28,8 @@ class Region(object):
|
|||
----------
|
||||
expression : str
|
||||
Boolean expression relating surface half-spaces. The possible
|
||||
operators are union '^', intersection ' ', and complement '~'. For
|
||||
example, '(1 -2) ^ 3 ~(4 -5)'.
|
||||
operators are union '|', intersection ' ', and complement '~'. For
|
||||
example, '(1 -2) | 3 ~(4 -5)'.
|
||||
surfaces : dict
|
||||
Dictionary whose keys are suface IDs that appear in the Boolean
|
||||
expression and whose values are Surface objects.
|
||||
|
|
@ -37,7 +46,7 @@ class Region(object):
|
|||
i_start = -1
|
||||
tokens = []
|
||||
while i < len(expression):
|
||||
if expression[i] in '()^~ ':
|
||||
if expression[i] in '()|~ ':
|
||||
# If special character appears immediately after a non-operator,
|
||||
# create a token with the apporpriate half-space
|
||||
if i_start >= 0:
|
||||
|
|
@ -47,7 +56,7 @@ class Region(object):
|
|||
else:
|
||||
tokens.append(surfaces[abs(j)].positive)
|
||||
|
||||
if expression[i] in '()^~':
|
||||
if expression[i] in '()|~':
|
||||
# For everything other than intersection, add the operator
|
||||
# to the list of tokens
|
||||
tokens.append(expression[i])
|
||||
|
|
@ -60,7 +69,7 @@ class Region(object):
|
|||
# is not a left parenthese or union operator, that implies that the
|
||||
# whitespace is to be interpreted as an intersection operator
|
||||
if (i_start >= 0 or tokens[-1] == ')') and \
|
||||
expression[i+1] not in ')^':
|
||||
expression[i+1] not in ')|':
|
||||
tokens.append(' ')
|
||||
|
||||
i_start = -1
|
||||
|
|
@ -104,7 +113,7 @@ class Region(object):
|
|||
output.append(r1)
|
||||
else:
|
||||
output.append(Intersection(r1, r2))
|
||||
elif operator == '^':
|
||||
elif operator == '|':
|
||||
r1 = output.pop()
|
||||
if isinstance(r1, Union) and can_be_combined(r2):
|
||||
r1.nodes.append(r2)
|
||||
|
|
@ -124,10 +133,10 @@ class Region(object):
|
|||
# generate an abstract syntax tree for the region expression.
|
||||
output = []
|
||||
stack = []
|
||||
precedence = {'^': 1, ' ': 2, '~': 3}
|
||||
associativity = {'^': 'left', ' ': 'left', '~': 'right'}
|
||||
precedence = {'|': 1, ' ': 2, '~': 3}
|
||||
associativity = {'|': 'left', ' ': 'left', '~': 'right'}
|
||||
for token in tokens:
|
||||
if token in (' ', '^', '~'):
|
||||
if token in (' ', '|', '~'):
|
||||
# Normal operators
|
||||
while stack:
|
||||
op = stack[-1]
|
||||
|
|
@ -225,7 +234,7 @@ class Union(Region):
|
|||
self._nodes = nodes
|
||||
|
||||
def __str__(self):
|
||||
return '(' + ' ^ '.join(map(str, self.nodes)) + ')'
|
||||
return '(' + ' | '.join(map(str, self.nodes)) + ')'
|
||||
|
||||
|
||||
class Complement(Region):
|
||||
|
|
|
|||
|
|
@ -48,12 +48,6 @@ class Surface(object):
|
|||
Unique identifier for the surface
|
||||
name : str
|
||||
Name of the surface
|
||||
negative : Halfspace
|
||||
Negative half-space of the surface, i.e., if :math:`f(x,y,z) = 0` is the
|
||||
equation for the sufrace, the region for which :math:`f(x,y,z) < 0`.
|
||||
positive : Halfspace
|
||||
Positive half-space of the surface, i.e., if :math:`f(x,y,z) = 0` is the
|
||||
equation for the sufrace, the region for which :math:`f(x,y,z) > 0`.
|
||||
type : str
|
||||
Type of the surface, e.g. 'x-plane'
|
||||
|
||||
|
|
@ -75,6 +69,12 @@ class Surface(object):
|
|||
# proper order
|
||||
self._coeff_keys = []
|
||||
|
||||
def __neg__(self):
|
||||
return Halfspace(self, '-')
|
||||
|
||||
def __pos__(self):
|
||||
return Halfspace(self, '+')
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
|
@ -95,14 +95,6 @@ class Surface(object):
|
|||
def coeffs(self):
|
||||
return self._coeffs
|
||||
|
||||
@property
|
||||
def negative(self):
|
||||
return Halfspace(self, '-')
|
||||
|
||||
@property
|
||||
def positive(self):
|
||||
return Halfspace(self, '+')
|
||||
|
||||
@id.setter
|
||||
def id(self, surface_id):
|
||||
if surface_id is None:
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ contains
|
|||
!===============================================================================
|
||||
! TOKENIZE takes a string that includes logical expressions for a list of
|
||||
! bounding surfaces in a cell and splits it into separate tokens. The characters
|
||||
! (, ), ^, and ~ count as separate tokens since they represent operators.
|
||||
! (, ), |, and ~ count as separate tokens since they represent operators.
|
||||
!===============================================================================
|
||||
|
||||
subroutine tokenize(string, tokens)
|
||||
|
|
@ -85,7 +85,7 @@ contains
|
|||
i = 1
|
||||
do while (i <= len_trim(string_))
|
||||
! Check for special characters
|
||||
if (index('()^~ ', string_(i:i)) > 0) then
|
||||
if (index('()|~ ', string_(i:i)) > 0) then
|
||||
! If the special character appears immediately after a non-operator,
|
||||
! create a token with the surface half-space
|
||||
if (i_start > 0) then
|
||||
|
|
@ -98,7 +98,7 @@ contains
|
|||
call tokens%push_back(OP_LEFT_PAREN)
|
||||
case (')')
|
||||
call tokens%push_back(OP_RIGHT_PAREN)
|
||||
case ('^')
|
||||
case ('|')
|
||||
call tokens%push_back(OP_UNION)
|
||||
case ('~')
|
||||
call tokens%push_back(OP_COMPLEMENT)
|
||||
|
|
@ -112,7 +112,7 @@ contains
|
|||
! is not a left parenthese or union operator, that implies that the
|
||||
! whitespace is to be interpreted as an intersection operator
|
||||
if (i_start > 0 .or. tokens%data(tokens%size()) == OP_RIGHT_PAREN) then
|
||||
if (index(')^', string_(i+1:i+1)) == 0) then
|
||||
if (index(')|', string_(i+1:i+1)) == 0) then
|
||||
call tokens%push_back(OP_INTERSECTION)
|
||||
end if
|
||||
end if
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
<cell id="1" material="1" region="3 -4 13 -14" />
|
||||
<cell id="2" material="2" region="2 -5 12 -15 ~(3 -4 13 -14)" />
|
||||
<cell id="3" material="3" region="7 -6 11 -16 (-2 ^ 5 ^ -12 ^ 15)" />
|
||||
<cell id="3" material="3" region="7 -6 11 -16 (-2 | 5 | -12 | 15)" />
|
||||
<cell id="4" material="4" region="((1 -7) 11 -16) ~(2 -5 (12 -15))" />
|
||||
|
||||
</geometry>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue