Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow users to define flag names with numerical values #1966

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion armi/reactor/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -1454,7 +1454,7 @@ def breakFuelComponentsIntoIndividuals(self):
# Handle all other components that may be linked to the fuel multiplicity
# by unlinking them and setting them directly.
# TODO: What about other (actual) dimensions? This is a limitation in that only fuel
# compuents are duplicated, and not the entire pin. It is also a reasonable assumption with
# components are duplicated, and not the entire pin. It is also a reasonable assumption with
# current/historical usage of ARMI.
for comp, dim in self.getComponentsThatAreLinkedTo(fuel, "mult"):
comp.setDimension(dim, nPins)
Expand Down
45 changes: 23 additions & 22 deletions armi/reactor/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,16 @@ def __fromStringGeneral(cls, typeSpec, updateMethod):
result |= _CONVERSIONS[conversion]

for name in typeSpec.split():
# ignore numbers so we don't have to define flags up to 217+ (number of pins/assem)
typeSpecWithoutNumbers = "".join([c for c in name if not c.isdigit()])
if not typeSpecWithoutNumbers:
continue
result |= updateMethod(typeSpecWithoutNumbers)
try:
# check for an exact name match first
result |= cls[name]
except KeyError:
# ignore numbers so we don't have to define flags up to the number of pins/assem
typeSpecWithoutNumbers = "".join([c for c in name if not c.isdigit()])
if not typeSpecWithoutNumbers:
continue
result |= updateMethod(typeSpecWithoutNumbers)

return result


Expand All @@ -148,8 +153,7 @@ def _fromStringIgnoreErrors(cls, typeSpec):

Complications arise when:

a. multiple-word flags are used such as *grid plate* or
*inlet nozzle* so we use lookups.
a. multiple-word flags are used such as *grid plate* or *inlet nozzle* so we use lookups.
b. Some flags have digits in them. We just strip those off.
"""

Expand All @@ -170,8 +174,8 @@ def updateMethod(typeSpec):
return cls[typeSpec]
except KeyError:
raise InvalidFlagsError(
"The requested type specification `{}` is invalid. "
"See armi.reactor.flags documentation.".format(typeSpec)
f"The requested type specification `{typeSpec}` is invalid. "
"See armi.reactor.flags documentation."
)

return __fromStringGeneral(cls, typeSpec, updateMethod)
Expand Down Expand Up @@ -289,15 +293,14 @@ def fromString(cls, typeSpec):
:id: I_ARMI_FLAG_TO_STR0
:implements: R_ARMI_FLAG_TO_STR

For a string passed as ``typeSpec``, first converts the whole string
to uppercase. Then tries to parse the string for any special phrases, as
defined in the module dictionary ``_CONVERSIONS``, and converts those
phrases to flags directly.
For a string passed as ``typeSpec``, first converts the whole string to uppercase. Then
tries to parse the string for any special phrases, as defined in the module dictionary
``_CONVERSIONS``, and converts those phrases to flags directly.

Then it splits the remaining string into separate words based on the presence
of spaces. Looping over each of the words, any numbers are stripped out
and the remaining string is matched up to any class attribute names.
If any matches are found these are returned as flags.
Then it splits the remaining string into words based on spaces. Looping over each of the
words, if any word exactly matches a flag name. Otherwise, any numbers are stripped out
and the remaining string is matched up to any class attribute names. If any matches are
found these are returned as flags.
"""
return _fromString(cls, typeSpec)

Expand All @@ -310,11 +313,9 @@ def toString(cls, typeSpec):
:id: I_ARMI_FLAG_TO_STR1
:implements: R_ARMI_FLAG_TO_STR

This converts the representation of a bunch of flags from ``typeSpec``,
which might look like ``Flags.A|B``,
into a string with spaces in between the flag names, which would look
like ``'A B'``. This is done via nesting string splitting and replacement
actions.
This converts the representation of a bunch of flags from ``typeSpec``, which might look
like ``Flags.A|B``, into a string with spaces in between the flag names, which would
look like ``'A B'``. This is done via nesting string splitting and replacement actions.
"""
return _toString(cls, typeSpec)

Expand Down
46 changes: 44 additions & 2 deletions armi/reactor/tests/test_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,49 @@ def test_fromString(self):
self._help_fromString(flags.Flags.fromStringIgnoreErrors)
self.assertEqual(flags.Flags.fromStringIgnoreErrors("invalid"), flags.Flags(0))

def test_fromStringWithNumbers(self):
# testing pure numbers
self.assertEqual(flags.Flags.fromStringIgnoreErrors("1"), flags.Flags(0))
self.assertEqual(flags.Flags.fromStringIgnoreErrors("7"), flags.Flags(0))

# testing fuel naming logic
self.assertEqual(flags.Flags.fromStringIgnoreErrors("Fuel1"), flags.Flags.FUEL)
self.assertEqual(
flags.Flags.fromStringIgnoreErrors("Fuel123"), flags.Flags.FUEL
)
self.assertEqual(flags.Flags.fromStringIgnoreErrors("fuel 1"), flags.Flags.FUEL)
self.assertEqual(
flags.Flags.fromStringIgnoreErrors("fuel 123"), flags.Flags.FUEL
)

def test_flagsDefinedWithNumbers(self):
"""Test that if we DEFINE flags with numbers in them, those are treated as exceptions."""
# define flags TYPE1 and TYPE1B (arbitrary example)
flags.Flags.extend({"TYPE1": flags.auto(), "TYPE1B": flags.auto()})

# verify that these flags are correctly found
self.assertEqual(flags.Flags["TYPE1"], flags.Flags.TYPE1)
self.assertEqual(flags.Flags["TYPE1B"], flags.Flags.TYPE1B)
self.assertEqual(flags.Flags.fromStringIgnoreErrors("type1"), flags.Flags.TYPE1)
self.assertEqual(
flags.Flags.fromStringIgnoreErrors("Type1b"), flags.Flags.TYPE1B
)

# the more complicated situation where our exceptions are mixed with the usual flag logic
self.assertEqual(
flags.Flags.fromString("type1 fuel"), flags.Flags.TYPE1 | flags.Flags.FUEL
)

self.assertEqual(
flags.Flags.fromString("type1 fuel 123 bond"),
flags.Flags.TYPE1 | flags.Flags.FUEL | flags.Flags.BOND,
)

self.assertEqual(
flags.Flags.fromString("type1 fuel123 bond"),
flags.Flags.TYPE1 | flags.Flags.FUEL | flags.Flags.BOND,
)

def test_flagsToAndFromString(self):
"""
Convert flag to and from string for serialization.
Expand Down Expand Up @@ -58,8 +101,7 @@ def _help_fromString(self, method):
self.assertEqual(method("bond1"), flags.Flags.BOND)
self.assertEqual(method("bond 2"), flags.Flags.BOND)
self.assertEqual(method("fuel test"), flags.Flags.FUEL | flags.Flags.TEST)
# test the more strict GRID conversion, which can cause collisions with
# GRID_PLATE
# test the more strict GRID conversion, which can cause collisions with GRID_PLATE
self.assertEqual(
flags.Flags.fromStringIgnoreErrors("grid_plate"), flags.Flags.GRID_PLATE
)
Expand Down
1 change: 1 addition & 0 deletions doc/release/0.4.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ New Features
#. Allow merging a component with zero area into another component. (`PR#1858 <https://github.com/terrapower/armi/pull/1858>`_)
#. Provide ``Parameter.hasCategory`` for quickly checking if a parameter is defined with a given category. (`PR#1899 <https://github.com/terrapower/armi/pull/1899>`_)
#. Provide ``ParameterCollection.where`` for efficient iteration over parameters who's definition matches a given condition. (`PR#1899 <https://github.com/terrapower/armi/pull/1899>`_)
#. Flags can now be defined with letters and numbers. (`PR#1966 <https://github.com/terrapower/armi/pull/1966>`_)
#. Plugins can provide the ``getAxialExpansionChanger`` hook to customize axial expansion. (`PR#1870 <https://github.com/terrapower/armi/pull/1870`_)
#. Plugins can provide the ``getAxialExpansionChanger`` hook to customize axial expansion. (`PR#1870 <https://github.com/terrapower/armi/pull/1870>`_)
#. New plugin hook ``beforeReactorConstruction`` added to enable plugins to process case settings before reactor init. (`PR#1945 <https://github.com/terrapower/armi/pull/1945>`_)
Expand Down
Loading