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:
Paul Romano 2015-10-03 09:26:50 +07:00
parent f0da87cde0
commit 71dfde8d12
5 changed files with 32 additions and 31 deletions

View file

@ -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.

View file

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

View file

@ -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:

View file

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

View file

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