From 6b3ed544ff038dd5424878f312368ca7572d6092 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 4 Oct 2017 14:43:51 -0500 Subject: [PATCH 1/7] Fix error in creating Fortran-backed objects from C API Python bindings when no objects exist. --- openmc/capi/filter.py | 5 ++++- openmc/capi/material.py | 5 ++++- openmc/capi/tally.py | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index e171e05bde..b7cc42fb0d 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -147,7 +147,10 @@ class MaterialFilterView(FilterView): def new(cls, bins=None, filter_id=None): # Determine filter ID to assign if filter_id is None: - filter_id = max(filters) + 1 + try: + filter_id = max(filters) + 1 + except ValueError: + filter_id = 1 index = c_int32() _dll.openmc_extend_filters(1, index, None) diff --git a/openmc/capi/material.py b/openmc/capi/material.py index ba4b650899..56040bdef1 100644 --- a/openmc/capi/material.py +++ b/openmc/capi/material.py @@ -127,7 +127,10 @@ class MaterialView(_View): def new(cls, material_id=None): # Determine ID to assign if material_id is None: - material_id = max(materials) + 1 + try: + material_id = max(materials) + 1 + except ValueError: + material_id = 1 index = c_int32() _dll.openmc_extend_materials(1, index, None) diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index 335cbb8e77..a4c0b6d4a5 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -143,7 +143,10 @@ class TallyView(_View): def new(cls, tally_id=None): # Determine ID to assign if tally_id is None: - tally_id = max(tallies) + 1 + try: + tally_id = max(tallies) + 1 + except ValueError: + tally_id = 1 index = c_int32() _dll.openmc_extend_tallies(1, index, None) From 95a4f4c0f580edcafc4270433966f91933ab089b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 4 Oct 2017 22:43:09 -0500 Subject: [PATCH 2/7] Allow C API View objects to be created (new) using normal constructor --- openmc/capi/__init__.py | 3 + openmc/capi/core.py | 12 +++- openmc/capi/filter.py | 121 ++++++++++++++++++++++++++-------------- openmc/capi/material.py | 68 ++++++++++++++-------- openmc/capi/nuclide.py | 11 +++- openmc/capi/tally.py | 58 +++++++++++++++---- 6 files changed, 191 insertions(+), 82 deletions(-) diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index 88a69df341..e0f1c8b270 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -38,3 +38,6 @@ from .cell import * from .filter import * from .tally import * from .settings import settings + +warn("The Python bindings to OpenMC's C API are still unstable " + "and may change substantially in future releases.", FutureWarning) diff --git a/openmc/capi/core.py b/openmc/capi/core.py index 2234fac640..3d74ad5cd0 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -173,8 +173,14 @@ class _DLLGlobal(object): class _View(object): - def __init__(self, index): - self._index = index - def __repr__(self): return "{}[{}]".format(type(self).__name__, self._index) + + +class _ViewWithID(_View): + def __init__(self, uid=None, new=True, index=None): + # Creating the object has already been handled by __new__. In the + # initializer, all we do is make sure that the object returned has an ID + # assigned. If the array index of the object is out of bounds, an + # OutOfBoundsError will be raised here by virtue of referencing self.id + self.id diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index b7cc42fb0d..1fce757028 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -7,8 +7,8 @@ import numpy as np from numpy.ctypeslib import as_array from . import _dll -from .core import _View -from .error import _error_handler +from .core import _ViewWithID +from .error import _error_handler, AllocationError, InvalidIDError from .material import MaterialView @@ -57,14 +57,39 @@ _dll.openmc_mesh_filter_set_mesh.restype = c_int _dll.openmc_mesh_filter_set_mesh.errcheck = _error_handler -class FilterView(_View): +class FilterView(_ViewWithID): __instances = WeakValueDictionary() - def __new__(cls, *args): - if args not in cls.__instances: + def __new__(cls, filter_type, uid=None, new=True, index=None): + mapping = filters + if index is None: + if new: + # Determine ID to assign + if uid is None: + try: + uid = max(mapping) + 1 + except ValueError: + uid = 1 + else: + if uid in mapping: + raise AllocationError('A filter with ID={} has already ' + 'been allocated.'.format(uid)) + + index = c_int32() + _dll.openmc_extend_filters(1, index, None) + _dll.openmc_filter_set_type(index, filter_type) + index = index.value + else: + index = mapping[uid]._index + + if index not in cls.__instances: instance = super().__new__(cls) - cls.__instances[args] = instance - return cls.__instances[args] + instance._index = index + if uid is not None: + instance.id = uid + cls.__instances[index] = instance + + return cls.__instances[index] @property def id(self): @@ -78,6 +103,9 @@ class FilterView(_View): class EnergyFilterView(FilterView): + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'energy', uid, new, index) + @property def bins(self): energies = POINTER(c_double)() @@ -96,44 +124,60 @@ class EnergyFilterView(FilterView): class EnergyoutFilterView(FilterView): - pass + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'energyout', uid, new, index) class AzimuthalFilterView(FilterView): - pass + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'azimuthal', uid, new, index) class CellFilterView(FilterView): - pass + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'cell', uid, new, index) class CellbornFilterView(FilterView): - pass + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'cellborn', uid, new, index) class CellfromFilterView(FilterView): - pass + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'cellfrom', uid, new, index) class DelayedGroupFilterView(FilterView): - pass + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'delayedgroup', uid, new, index) class DistribcellFilterView(FilterView): - pass + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'distribcell', uid, new, index) class EnergyFunctionFilterView(FilterView): - pass + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'energyfunction', uid, new, index) class MaterialFilterView(FilterView): + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'material', uid, new, index) + + def __init__(self, bins=None, uid=None, new=True, index=None): + super().__init__(uid, new, index) + if bins is not None: + self.bins = bins + @property def bins(self): materials = POINTER(c_int32)() n = c_int32() _dll.openmc_material_filter_get_bins(self._index, materials, n) - return [MaterialView(materials[i]) for i in range(n.value)] + return [MaterialView(index=materials[i]) for i in range(n.value)] @bins.setter def bins(self, materials): @@ -143,46 +187,33 @@ class MaterialFilterView(FilterView): _dll.openmc_material_filter_set_bins(self._index, n, bins) - @classmethod - def new(cls, bins=None, filter_id=None): - # Determine filter ID to assign - if filter_id is None: - try: - filter_id = max(filters) + 1 - except ValueError: - filter_id = 1 - - index = c_int32() - _dll.openmc_extend_filters(1, index, None) - _dll.openmc_filter_set_type(index, b'material') - f = cls(index.value) - f.id = filter_id - if bins is not None: - f.bins = bins - return f - class MeshFilterView(FilterView): - pass + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'mesh', uid, new, index) class MuFilterView(FilterView): - pass + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'mu', uid, new, index) class PolarFilterView(FilterView): - pass + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'polar', uid, new, index) class SurfaceFilterView(FilterView): - pass + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'surface', uid, new, index) class UniverseFilterView(FilterView): - pass + def __new__(cls, bins=None, uid=None, new=True, index=None): + return super().__new__(cls, b'universe', uid, new, index) -_filter_type_map = { +_FILTER_TYPE_MAP = { 'azimuthal': AzimuthalFilterView, 'cell': CellFilterView, 'cellborn': CellbornFilterView, @@ -205,18 +236,22 @@ def _get_filter(index): filter_type = create_string_buffer(20) _dll.openmc_filter_get_type(index, filter_type) filter_type = filter_type.value.decode() - return _filter_type_map[filter_type](index) + return _FILTER_TYPE_MAP[filter_type](index=index) class _FilterMapping(Mapping): def __getitem__(self, key): index = c_int32() - _dll.openmc_get_filter_index(key, index) + try: + _dll.openmc_get_filter_index(key, index) + except (AllocationError, InvalidIDError) as e: + # __contains__ expects a KeyError to work correctly + raise KeyError(str(e)) return _get_filter(index.value) def __iter__(self): for i in range(len(self)): - yield TallyView(i + 1).id + yield _get_filter(i + 1).id def __len__(self): return c_int32.in_dll(_dll, 'n_filters').value diff --git a/openmc/capi/material.py b/openmc/capi/material.py index 56040bdef1..2f2f7b3f8d 100644 --- a/openmc/capi/material.py +++ b/openmc/capi/material.py @@ -6,8 +6,8 @@ import numpy as np from numpy.ctypeslib import as_array from . import _dll, NuclideView -from .core import _View -from .error import _error_handler +from .core import _ViewWithID +from .error import _error_handler, AllocationError, InvalidIDError __all__ = ['MaterialView', 'materials'] @@ -43,7 +43,7 @@ _dll.openmc_material_set_id.restype = c_int _dll.openmc_material_set_id.errcheck = _error_handler -class MaterialView(_View): +class MaterialView(_ViewWithID): """View of a material. This class exposes a material that is stored internally in the OpenMC @@ -52,7 +52,12 @@ class MaterialView(_View): Parameters ---------- - index : int + uid : int or None + Unique ID of the tally + new : bool + When `index` is None, this argument controls whether a new object is + created or a view to an existing object is returned. + index : int or None Index in the `materials` array. Attributes @@ -67,6 +72,36 @@ class MaterialView(_View): """ __instances = WeakValueDictionary() + def __new__(cls, uid=None, new=True, index=None): + mapping = materials + if index is None: + if new: + # Determine ID to assign + if uid is None: + try: + uid = max(mapping) + 1 + except ValueError: + uid = 1 + else: + if uid in mapping: + raise AllocationError('A material with ID={} has already ' + 'been allocated.'.format(uid)) + + index = c_int32() + _dll.openmc_extend_materials(1, index, None) + index = index.value + else: + index = mapping[uid]._index + + if index not in cls.__instances: + instance = super().__new__(cls) + instance._index = index + if uid is not None: + instance.id = uid + cls.__instances[index] = instance + + return cls.__instances[index] + @property def id(self): mat_id = c_int32() @@ -123,21 +158,6 @@ class MaterialView(_View): """ _dll.openmc_material_add_nuclide(self._index, name.encode(), density) - @classmethod - def new(cls, material_id=None): - # Determine ID to assign - if material_id is None: - try: - material_id = max(materials) + 1 - except ValueError: - material_id = 1 - - index = c_int32() - _dll.openmc_extend_materials(1, index, None) - mat = cls(index.value) - mat.id = material_id - return mat - def set_density(self, density): """Set density of a material. @@ -174,12 +194,16 @@ class MaterialView(_View): class _MaterialMapping(Mapping): def __getitem__(self, key): index = c_int32() - _dll.openmc_get_material_index(key, index) - return MaterialView(index.value) + try: + _dll.openmc_get_material_index(key, index) + except InvalidIDError as e: + # __contains__ expects a KeyError to work correctly + raise KeyError(str(e)) + return MaterialView(index=index.value) def __iter__(self): for i in range(len(self)): - yield MaterialView(i + 1).id + yield MaterialView(index=i + 1).id def __len__(self): return c_int32.in_dll(_dll, 'n_materials').value diff --git a/openmc/capi/nuclide.py b/openmc/capi/nuclide.py index 16bb71accd..c5b61a6236 100644 --- a/openmc/capi/nuclide.py +++ b/openmc/capi/nuclide.py @@ -7,7 +7,7 @@ from numpy.ctypeslib import as_array from . import _dll from .core import _View -from .error import _error_handler +from .error import _error_handler, DataError, AllocationError __all__ = ['NuclideView', 'nuclides', 'load_nuclide'] @@ -62,6 +62,9 @@ class NuclideView(_View): cls.__instances[args] = instance return cls.__instances[args] + def __init__(self, index): + self._index = index + @property def name(self): name = c_char_p() @@ -78,7 +81,11 @@ class _NuclideMapping(Mapping): """Provide mapping from nuclide name to index in nuclides array.""" def __getitem__(self, key): index = c_int() - _dll.openmc_get_nuclide_index(key.encode(), index) + try: + _dll.openmc_get_nuclide_index(key.encode(), index) + except (DataError, AllocationError) as e: + # __contains__ expects a KeyError to work correctly + raise KeyError(str(e)) return NuclideView(index.value) def __iter__(self): diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index a4c0b6d4a5..89816e039f 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -5,8 +5,8 @@ from weakref import WeakValueDictionary from numpy.ctypeslib import as_array from . import _dll, NuclideView -from .core import _View -from .error import _error_handler +from .core import _ViewWithID +from .error import _error_handler, AllocationError, InvalidIDError from .filter import _get_filter @@ -51,7 +51,7 @@ _dll.openmc_tally_set_type.restype = c_int _dll.openmc_tally_set_type.errcheck = _error_handler -class TallyView(_View): +class TallyView(_ViewWithID): """View of a tally. This class exposes a tally that is stored internally in the OpenMC @@ -60,8 +60,13 @@ class TallyView(_View): Parameters ---------- - index : int - Index in the `tallies` array. + uid : int or None + Unique ID of the tally + new : bool + When `index` is None, this argument controls whether a new object is + created or a view to an existing object is returned. + index : int or None + Index in the `tallies` array. Attributes ---------- @@ -77,11 +82,36 @@ class TallyView(_View): """ __instances = WeakValueDictionary() - def __new__(cls, *args): - if args not in cls.__instances: + def __new__(cls, uid=None, new=True, index=None): + mapping = tallies + if index is None: + if new: + # Determine ID to assign + if uid is None: + try: + uid = max(mapping) + 1 + except ValueError: + uid = 1 + else: + if uid in mapping: + raise AllocationError('A tally with ID={} has already ' + 'been allocated.'.format(uid)) + + index = c_int32() + _dll.openmc_extend_tallies(1, index, None) + _dll.openmc_tally_set_type(index, b'generic') + index = index.value + else: + index = mapping[uid]._index + + if index not in cls.__instances: instance = super().__new__(cls) - cls.__instances[args] = instance - return cls.__instances[args] + instance._index = index + if uid is not None: + instance.id = uid + cls.__instances[index] = instance + + return cls.__instances[index] @property def id(self): @@ -159,12 +189,16 @@ class TallyView(_View): class _TallyMapping(Mapping): def __getitem__(self, key): index = c_int32() - _dll.openmc_get_tally_index(key, index) - return TallyView(index.value) + try: + _dll.openmc_get_tally_index(key, index) + except InvalidIDError as e: + # __contains__ expects a KeyError to work correctly + raise KeyError(str(e)) + return TallyView(index=index.value) def __iter__(self): for i in range(len(self)): - yield TallyView(i + 1).id + yield TallyView(index=i + 1).id def __len__(self): return c_int32.in_dll(_dll, 'n_tallies').value From 02b8f4ec098ba4d09ea48cf3a1b259019c27132b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 5 Oct 2017 10:52:29 -0500 Subject: [PATCH 3/7] Change names of openmc.capi classes. Hopefully fix RTD docs build. --- docs/source/pythonapi/capi.rst | 10 +++-- openmc/arithmetic.py | 12 ++--- openmc/capi/__init__.py | 20 +++++++-- openmc/capi/cell.py | 30 +++++++------ openmc/capi/core.py | 4 +- openmc/capi/error.py | 6 +-- openmc/capi/filter.py | 80 +++++++++++++++++----------------- openmc/capi/material.py | 20 ++++----- openmc/capi/nuclide.py | 12 ++--- openmc/capi/tally.py | 24 +++++----- openmc/mgxs/library.py | 6 +-- openmc/mgxs/mdgxs.py | 14 +++--- openmc/mgxs/mgxs.py | 32 +++++++------- openmc/tallies.py | 2 +- 14 files changed, 144 insertions(+), 128 deletions(-) diff --git a/docs/source/pythonapi/capi.rst b/docs/source/pythonapi/capi.rst index 6e4e860fad..457f053204 100644 --- a/docs/source/pythonapi/capi.rst +++ b/docs/source/pythonapi/capi.rst @@ -33,7 +33,9 @@ Classes :nosignatures: :template: myclass.rst - openmc.capi.CellView - openmc.capi.MaterialView - openmc.capi.NuclideView - openmc.capi.TallyView + openmc.capi.Cell + openmc.capi.EnergyFilter + openmc.capi.MaterialFilter + openmc.capi.Material + openmc.capi.Nuclide + openmc.capi.Tally diff --git a/openmc/arithmetic.py b/openmc/arithmetic.py index 57afacac33..6a781cb5a2 100644 --- a/openmc/arithmetic.py +++ b/openmc/arithmetic.py @@ -108,9 +108,9 @@ class CrossNuclide(object): Parameters ---------- - left_nuclide : Nuclide or CrossNuclide + left_nuclide : openmc.Nuclide or CrossNuclide The left nuclide in the outer product - right_nuclide : Nuclide or CrossNuclide + right_nuclide : openmc.Nuclide or CrossNuclide The right nuclide in the outer product binary_op : str The tally arithmetic binary operator (e.g., '+', '-', etc.) used to @@ -118,9 +118,9 @@ class CrossNuclide(object): Attributes ---------- - left_nuclide : Nuclide or CrossNuclide + left_nuclide : openmc.Nuclide or CrossNuclide The left nuclide in the outer product - right_nuclide : Nuclide or CrossNuclide + right_nuclide : openmc.Nuclide or CrossNuclide The right nuclide in the outer product binary_op : str The tally arithmetic binary operator (e.g., '+', '-', etc.) used to @@ -510,7 +510,7 @@ class AggregateNuclide(object): Parameters ---------- - nuclides : Iterable of str or Nuclide or CrossNuclide + nuclides : Iterable of str or openmc.Nuclide or CrossNuclide The nuclides included in the aggregation aggregate_op : str The tally aggregation operator (e.g., 'sum', 'avg', etc.) used @@ -518,7 +518,7 @@ class AggregateNuclide(object): Attributes ---------- - nuclides : Iterable of str or Nuclide or CrossNuclide + nuclides : Iterable of str or openmc.Nuclide or CrossNuclide The nuclides included in the aggregation aggregate_op : str The tally aggregation operator (e.g., 'sum', 'avg', etc.) used diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index e0f1c8b270..d302001c99 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -13,6 +13,7 @@ objects in the :mod:`openmc.capi` subpackage, for example: """ from ctypes import CDLL +import os import sys from warnings import warn @@ -25,10 +26,21 @@ if sys.platform == 'darwin': else: _suffix = 'so' -# Open shared library -_filename = pkg_resources.resource_filename( - __name__, 'libopenmc.{}'.format(_suffix)) -_dll = CDLL(_filename) +if os.environ.get('READTHEDOCS', None) != 'True': + # Open shared library + _filename = pkg_resources.resource_filename( + __name__, 'libopenmc.{}'.format(_suffix)) + _dll = CDLL(_filename) +else: + # For documentation builds, we don't actually have the shared library + # available. Instead, we create a mock object so that when the modules + # within the openmc.capi package try to configure arguments and return + # values for symbols, no errors occur + try: + from unittest.mock import Mock + except ImportError: + from mock import Mock + _dll = Mock() from .error import * from .core import * diff --git a/openmc/capi/cell.py b/openmc/capi/cell.py index 11b0e2199d..62823c0ba9 100644 --- a/openmc/capi/cell.py +++ b/openmc/capi/cell.py @@ -6,11 +6,11 @@ import numpy as np from numpy.ctypeslib import as_array from . import _dll -from .core import _View +from .core import _FortranObjectWithID from .error import _error_handler -from .material import MaterialView +from .material import Material -__all__ = ['CellView', 'cells'] +__all__ = ['Cell', 'cells'] # Cell functions _dll.openmc_cell_get_id.argtypes = [c_int32, POINTER(c_int32)] @@ -31,11 +31,11 @@ _dll.openmc_get_cell_index.restype = c_int _dll.openmc_get_cell_index.errcheck = _error_handler -class CellView(_View): - """View of a cell. +class Cell(_FortranObjectWithID): + """Cell stored internally. - This class exposes a cell that is stored internally in the OpenMC solver. To - obtain a view of a cell with a given ID, use the + This class exposes a cell that is stored internally in the OpenMC + library. To obtain a view of a cell with a given ID, use the :data:`openmc.capi.nuclides` mapping. Parameters @@ -72,9 +72,9 @@ class CellView(_View): if fill_type.value == 1: if n.value > 1: - return [MaterialView(i) for i in indices[:n.value]] + return [Material(index=i) for i in indices[:n.value]] else: - return MaterialView(indices[0]) + return Material(index=indices[0]) else: raise NotImplementedError @@ -84,7 +84,7 @@ class CellView(_View): n = len(fill) indices = (c_int*n)(*(m._index for m in fill)) _dll.openmc_cell_set_fill(self._index, 1, 1, indices) - elif isinstance(fill, MaterialView): + elif isinstance(fill, Material): materials = [fill] indices = (c_int*1)(fill._index) _dll.openmc_cell_set_fill(self._index, 1, 1, indices) @@ -108,12 +108,16 @@ class CellView(_View): class _CellMapping(Mapping): def __getitem__(self, key): index = c_int32() - _dll.openmc_get_cell_index(key, index) - return CellView(index.value) + try: + _dll.openmc_get_cell_index(key, index) + except (AllocationError, InvalidIDError) as e: + # __contains__ expects a KeyError to work correctly + raise KeyError(str(e)) + return Cell(index.value) def __iter__(self): for i in range(len(self)): - yield CellView(i + 1).id + yield Cell(i + 1).id def __len__(self): return c_int32.in_dll(_dll, 'n_cells').value diff --git a/openmc/capi/core.py b/openmc/capi/core.py index 3d74ad5cd0..0d8ff5c436 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -172,12 +172,12 @@ class _DLLGlobal(object): self.ctype.in_dll(_dll, self.name).value = value -class _View(object): +class _FortranObject(object): def __repr__(self): return "{}[{}]".format(type(self).__name__, self._index) -class _ViewWithID(_View): +class _FortranObjectWithID(_FortranObject): def __init__(self, uid=None, new=True, index=None): # Creating the object has already been handled by __new__. In the # initializer, all we do is make sure that the object returned has an ID diff --git a/openmc/capi/error.py b/openmc/capi/error.py index 7ebf2cb64c..98d43ae462 100644 --- a/openmc/capi/error.py +++ b/openmc/capi/error.py @@ -39,9 +39,6 @@ class InvalidTypeError(Error): """Tried to perform an operation on the wrong type.""" -_errmsg = (c_char*256).in_dll(_dll, 'openmc_err_msg') - - def _error_handler(err, func, args): """Raise exception according to error code.""" @@ -50,7 +47,8 @@ def _error_handler(err, func, args): return c_int.in_dll(_dll, s).value # Get error message set by OpenMC library - msg = _errmsg.value.decode() + errmsg = (c_char*256).in_dll(_dll, 'openmc_err_msg') + msg = errmsg.value.decode() # Raise exception type corresponding to error code if err == errcode('e_allocate'): diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index 1fce757028..ac9de8a433 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -7,17 +7,17 @@ import numpy as np from numpy.ctypeslib import as_array from . import _dll -from .core import _ViewWithID +from .core import _FortranObjectWithID from .error import _error_handler, AllocationError, InvalidIDError -from .material import MaterialView +from .material import Material -__all__ = ['FilterView', 'AzimuthalFilterView', 'CellFilterView', - 'CellbornFilterView', 'CellfromFilterView', 'DistribcellFilterView', - 'DelayedGroupFilterView', 'EnergyFilterView', 'EnergyoutFilterView', - 'EnergyFunctionFilterView', 'MaterialFilterView', 'MeshFilterView', - 'MuFilterView', 'PolarFilterView', 'SurfaceFilterView', - 'UniverseFilterView', 'filters'] +__all__ = ['Filter', 'AzimuthalFilter', 'CellFilter', + 'CellbornFilter', 'CellfromFilter', 'DistribcellFilter', + 'DelayedGroupFilter', 'EnergyFilter', 'EnergyoutFilter', + 'EnergyFunctionFilter', 'MaterialFilter', 'MeshFilter', + 'MuFilter', 'PolarFilter', 'SurfaceFilter', + 'UniverseFilter', 'filters'] # Tally functions _dll.openmc_energy_filter_get_bins.argtypes = [ @@ -57,7 +57,7 @@ _dll.openmc_mesh_filter_set_mesh.restype = c_int _dll.openmc_mesh_filter_set_mesh.errcheck = _error_handler -class FilterView(_ViewWithID): +class Filter(_FortranObjectWithID): __instances = WeakValueDictionary() def __new__(cls, filter_type, uid=None, new=True, index=None): @@ -102,7 +102,7 @@ class FilterView(_ViewWithID): _dll.openmc_filter_set_id(self._index, filter_id) -class EnergyFilterView(FilterView): +class EnergyFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'energy', uid, new, index) @@ -123,47 +123,47 @@ class EnergyFilterView(FilterView): self._index, len(energies), energies_p) -class EnergyoutFilterView(FilterView): +class EnergyoutFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'energyout', uid, new, index) -class AzimuthalFilterView(FilterView): +class AzimuthalFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'azimuthal', uid, new, index) -class CellFilterView(FilterView): +class CellFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'cell', uid, new, index) -class CellbornFilterView(FilterView): +class CellbornFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'cellborn', uid, new, index) -class CellfromFilterView(FilterView): +class CellfromFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'cellfrom', uid, new, index) -class DelayedGroupFilterView(FilterView): +class DelayedGroupFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'delayedgroup', uid, new, index) -class DistribcellFilterView(FilterView): +class DistribcellFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'distribcell', uid, new, index) -class EnergyFunctionFilterView(FilterView): +class EnergyFunctionFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'energyfunction', uid, new, index) -class MaterialFilterView(FilterView): +class MaterialFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'material', uid, new, index) @@ -177,7 +177,7 @@ class MaterialFilterView(FilterView): materials = POINTER(c_int32)() n = c_int32() _dll.openmc_material_filter_get_bins(self._index, materials, n) - return [MaterialView(index=materials[i]) for i in range(n.value)] + return [Material(index=materials[i]) for i in range(n.value)] @bins.setter def bins(self, materials): @@ -188,47 +188,47 @@ class MaterialFilterView(FilterView): _dll.openmc_material_filter_set_bins(self._index, n, bins) -class MeshFilterView(FilterView): +class MeshFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'mesh', uid, new, index) -class MuFilterView(FilterView): +class MuFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'mu', uid, new, index) -class PolarFilterView(FilterView): +class PolarFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'polar', uid, new, index) -class SurfaceFilterView(FilterView): +class SurfaceFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'surface', uid, new, index) -class UniverseFilterView(FilterView): +class UniverseFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super().__new__(cls, b'universe', uid, new, index) _FILTER_TYPE_MAP = { - 'azimuthal': AzimuthalFilterView, - 'cell': CellFilterView, - 'cellborn': CellbornFilterView, - 'cellfrom': CellfromFilterView, - 'delayedgroup': DelayedGroupFilterView, - 'distribcell': DistribcellFilterView, - 'energy': EnergyFilterView, - 'energyout': EnergyoutFilterView, - 'energyfunction': EnergyFunctionFilterView, - 'material': MaterialFilterView, - 'mesh': MeshFilterView, - 'mu': MuFilterView, - 'polar': PolarFilterView, - 'surface': SurfaceFilterView, - 'universe': UniverseFilterView, + 'azimuthal': AzimuthalFilter, + 'cell': CellFilter, + 'cellborn': CellbornFilter, + 'cellfrom': CellfromFilter, + 'delayedgroup': DelayedGroupFilter, + 'distribcell': DistribcellFilter, + 'energy': EnergyFilter, + 'energyout': EnergyoutFilter, + 'energyfunction': EnergyFunctionFilter, + 'material': MaterialFilter, + 'mesh': MeshFilter, + 'mu': MuFilter, + 'polar': PolarFilter, + 'surface': SurfaceFilter, + 'universe': UniverseFilter, } diff --git a/openmc/capi/material.py b/openmc/capi/material.py index 2f2f7b3f8d..a9cc9c1481 100644 --- a/openmc/capi/material.py +++ b/openmc/capi/material.py @@ -5,12 +5,12 @@ from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array -from . import _dll, NuclideView -from .core import _ViewWithID +from . import _dll, Nuclide +from .core import _FortranObjectWithID from .error import _error_handler, AllocationError, InvalidIDError -__all__ = ['MaterialView', 'materials'] +__all__ = ['Material', 'materials'] # Material functions _dll.openmc_extend_materials.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)] @@ -43,11 +43,11 @@ _dll.openmc_material_set_id.restype = c_int _dll.openmc_material_set_id.errcheck = _error_handler -class MaterialView(_ViewWithID): - """View of a material. +class Material(_FortranObjectWithID): + """Material stored internally. This class exposes a material that is stored internally in the OpenMC - solver. To obtain a view of a material with a given ID, use the + library. To obtain a view of a material with a given ID, use the :data:`openmc.capi.materials` mapping. Parameters @@ -141,7 +141,7 @@ class MaterialView(_ViewWithID): _dll.openmc_material_get_densities(self._index, nuclides, densities, n) # Convert to appropriate types and return - nuclide_list = [NuclideView(nuclides[i]).name for i in range(n.value)] + nuclide_list = [Nuclide(nuclides[i]).name for i in range(n.value)] density_array = as_array(densities, (n.value,)) return nuclide_list, density_array @@ -196,14 +196,14 @@ class _MaterialMapping(Mapping): index = c_int32() try: _dll.openmc_get_material_index(key, index) - except InvalidIDError as e: + except (AllocationError, InvalidIDError) as e: # __contains__ expects a KeyError to work correctly raise KeyError(str(e)) - return MaterialView(index=index.value) + return Material(index=index.value) def __iter__(self): for i in range(len(self)): - yield MaterialView(index=i + 1).id + yield Material(index=i + 1).id def __len__(self): return c_int32.in_dll(_dll, 'n_materials').value diff --git a/openmc/capi/nuclide.py b/openmc/capi/nuclide.py index c5b61a6236..54d131498f 100644 --- a/openmc/capi/nuclide.py +++ b/openmc/capi/nuclide.py @@ -6,11 +6,11 @@ import numpy as np from numpy.ctypeslib import as_array from . import _dll -from .core import _View +from .core import _FortranObject from .error import _error_handler, DataError, AllocationError -__all__ = ['NuclideView', 'nuclides', 'load_nuclide'] +__all__ = ['Nuclide', 'nuclides', 'load_nuclide'] # Nuclide functions _dll.openmc_get_nuclide_index.argtypes = [c_char_p, POINTER(c_int)] @@ -36,8 +36,8 @@ def load_nuclide(name): _dll.openmc_load_nuclide(name.encode()) -class NuclideView(_View): - """View of a nuclide. +class Nuclide(_FortranObject): + """Nuclide stored internally. This class exposes a nuclide that is stored internally in the OpenMC solver. To obtain a view of a nuclide with a given name, use the @@ -86,11 +86,11 @@ class _NuclideMapping(Mapping): except (DataError, AllocationError) as e: # __contains__ expects a KeyError to work correctly raise KeyError(str(e)) - return NuclideView(index.value) + return Nuclide(index.value) def __iter__(self): for i in range(len(self)): - yield NuclideView(i + 1).name + yield Nuclide(i + 1).name def __len__(self): return c_int.in_dll(_dll, 'n_nuclides').value diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index 89816e039f..cb37e69713 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -4,13 +4,13 @@ from weakref import WeakValueDictionary from numpy.ctypeslib import as_array -from . import _dll, NuclideView -from .core import _ViewWithID +from . import _dll, Nuclide +from .core import _FortranObjectWithID from .error import _error_handler, AllocationError, InvalidIDError from .filter import _get_filter -__all__ = ['TallyView', 'tallies'] +__all__ = ['Tally', 'tallies'] # Tally functions _dll.openmc_get_tally_index.argtypes = [c_int32, POINTER(c_int32)] @@ -51,11 +51,11 @@ _dll.openmc_tally_set_type.restype = c_int _dll.openmc_tally_set_type.errcheck = _error_handler -class TallyView(_ViewWithID): - """View of a tally. +class Tally(_FortranObjectWithID): + """Tally stored internally. This class exposes a tally that is stored internally in the OpenMC - solver. To obtain a view of a tally with a given ID, use the + library. To obtain a view of a tally with a given ID, use the :data:`openmc.capi.tallies` mapping. Parameters @@ -64,7 +64,7 @@ class TallyView(_ViewWithID): Unique ID of the tally new : bool When `index` is None, this argument controls whether a new object is - created or a view to an existing object is returned. + created or a view of an existing object is returned. index : int or None Index in the `tallies` array. @@ -73,7 +73,7 @@ class TallyView(_ViewWithID): id : int ID of the tally filters : list - List of views to tally filters + List of tally filters nuclides : list of str List of nuclides to score results for results : numpy.ndarray @@ -135,7 +135,7 @@ class TallyView(_ViewWithID): nucs = POINTER(c_int)() n = c_int() _dll.openmc_tally_get_nuclides(self._index, nucs, n) - return [NuclideView(nucs[i]).name if nucs[i] > 0 else 'total' + return [Nuclide(nucs[i]).name if nucs[i] > 0 else 'total' for i in range(n.value)] @property @@ -191,14 +191,14 @@ class _TallyMapping(Mapping): index = c_int32() try: _dll.openmc_get_tally_index(key, index) - except InvalidIDError as e: + except (AllocationError, InvalidIDError) as e: # __contains__ expects a KeyError to work correctly raise KeyError(str(e)) - return TallyView(index=index.value) + return Tally(index=index.value) def __iter__(self): for i in range(len(self)): - yield TallyView(index=i + 1).id + yield Tally(index=i + 1).id def __len__(self): return c_int32.in_dll(_dll, 'n_tallies').value diff --git a/openmc/mgxs/library.py b/openmc/mgxs/library.py index 7ebe6cb8cf..3cdd0fbf54 100644 --- a/openmc/mgxs/library.py +++ b/openmc/mgxs/library.py @@ -606,7 +606,7 @@ class Library(object): Parameters ---------- - domain : Material or Cell or Universe or Integral + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh or Integral The material, cell, or universe object of interest (or its ID) mgxs_type : {'total', 'transport', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'multiplicity matrix', 'nu-fission matrix', chi', 'chi-prompt', 'inverse-velocity', 'prompt-nu-fission', 'prompt-nu-fission matrix', 'delayed-nu-fission', 'delayed-nu-fission matrix', 'chi-delayed', 'beta'} The type of multi-group cross section object to return @@ -668,7 +668,7 @@ class Library(object): Returns ------- - Library + openmc.mgxs.Library A new multi-group cross section library condensed to the group structure of interest @@ -880,7 +880,7 @@ class Library(object): Returns ------- - Library + openmc.mgxs.Library A Library object loaded from the pickle binary file See also diff --git a/openmc/mgxs/mdgxs.py b/openmc/mgxs/mdgxs.py index 055bf267d2..d3f1a0646b 100644 --- a/openmc/mgxs/mdgxs.py +++ b/openmc/mgxs/mdgxs.py @@ -71,7 +71,7 @@ class MDGXS(MGXS): Reaction type (e.g., 'chi-delayed', 'beta', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -948,7 +948,7 @@ class ChiDelayed(MDGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -1462,7 +1462,7 @@ class DelayedNuFissionXS(MDGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -1598,7 +1598,7 @@ class Beta(MDGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -1782,7 +1782,7 @@ class DecayRate(MDGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -1959,7 +1959,7 @@ class MatrixMDGXS(MDGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -2551,7 +2551,7 @@ class DelayedNuFissionMatrixXS(MatrixMDGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 3325e0b157..192f484a5e 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -154,7 +154,7 @@ class MGXS(object): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -2018,7 +2018,7 @@ class MatrixMGXS(MGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -2512,7 +2512,7 @@ class TotalXS(MGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -2649,7 +2649,7 @@ class TransportXS(MGXS): If True, the cross section data will include neutron multiplication by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -2858,7 +2858,7 @@ class AbsorptionXS(MGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -2986,7 +2986,7 @@ class CaptureXS(MGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -3140,7 +3140,7 @@ class FissionXS(MGXS): If true, computes cross sections which only includes prompt neutrons by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -3309,7 +3309,7 @@ class KappaFissionXS(MGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -3441,7 +3441,7 @@ class ScatterXS(MGXS): If True, the cross section data will include neutron multiplication by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -3658,7 +3658,7 @@ class ScatterMatrixXS(MatrixMGXS): If True, the cross section data will include neutron multiplication by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -4020,7 +4020,7 @@ class ScatterMatrixXS(MatrixMGXS): # Override the nuclides for tally arithmetic correction.nuclides = scatter_p1.nuclides self._xs_tally -= correction - + self._compute_xs() return self._xs_tally @@ -4749,7 +4749,7 @@ class MultiplicityMatrixXS(MatrixMGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -4915,7 +4915,7 @@ class ScatterProbabilityMatrix(MatrixMGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -5088,7 +5088,7 @@ class NuFissionMatrixXS(MatrixMGXS): If true, computes cross sections which only includes prompt neutrons by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -5245,7 +5245,7 @@ class Chi(MGXS): If true, computes cross sections which only includes prompt neutrons by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -5823,7 +5823,7 @@ class InverseVelocity(MGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization diff --git a/openmc/tallies.py b/openmc/tallies.py index 6853db494e..9c656d56d4 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -502,7 +502,7 @@ class Tally(IDManagerMixin): Parameters ---------- - nuclide : str, Nuclide, CrossNuclide or AggregateNuclide + nuclide : str, openmc.Nuclide, CrossNuclide or AggregateNuclide Nuclide to add to the tally. The nuclide should be a Nuclide object when a user is adding nuclides to a Tally for input file generation. The nuclide is a str when a Tally is created from a StatePoint file From 12841f11afa303a77432d5361dae575c39cbd36b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 5 Oct 2017 11:34:43 -0500 Subject: [PATCH 4/7] Make super() Py2 compatible --- openmc/capi/cell.py | 2 +- openmc/capi/filter.py | 44 +++++++++++++++++++++++++---------------- openmc/capi/material.py | 2 +- openmc/capi/nuclide.py | 2 +- openmc/capi/tally.py | 2 +- 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/openmc/capi/cell.py b/openmc/capi/cell.py index 62823c0ba9..fe33825894 100644 --- a/openmc/capi/cell.py +++ b/openmc/capi/cell.py @@ -53,7 +53,7 @@ class Cell(_FortranObjectWithID): def __new__(cls, *args): if args not in cls.__instances: - instance = super().__new__(cls) + instance = super(Cell, self).__new__(cls) cls.__instances[args] = instance return cls.__instances[args] diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index ac9de8a433..d15e4dc146 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -83,7 +83,7 @@ class Filter(_FortranObjectWithID): index = mapping[uid]._index if index not in cls.__instances: - instance = super().__new__(cls) + instance = super(Filter, cls).__new__(cls) instance._index = index if uid is not None: instance.id = uid @@ -104,7 +104,7 @@ class Filter(_FortranObjectWithID): class EnergyFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'energy', uid, new, index) + return super(EnergyFilter, cls).__new__(cls, b'energy', uid, new, index) @property def bins(self): @@ -125,50 +125,58 @@ class EnergyFilter(Filter): class EnergyoutFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'energyout', uid, new, index) + return super(Energyoutfilter, cls).__new__(cls, b'energyout', + uid, new, index) class AzimuthalFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'azimuthal', uid, new, index) + return super(AzimuthalFilter, cls).__new__(cls, b'azimuthal', + uid, new, index) class CellFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'cell', uid, new, index) + return super(CellFilter, cls).__new__(cls, b'cell', uid, new, index) class CellbornFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'cellborn', uid, new, index) + return super(CellbornFilter, cls).__new__(cls, b'cellborn', uid, + new, index) class CellfromFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'cellfrom', uid, new, index) + return super(CellfromFilter, cls).__new__(cls, b'cellfrom', uid, + new, index) class DelayedGroupFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'delayedgroup', uid, new, index) + return super(DelayedGroupFilter, cls).__new__(cls, b'delayedgroup', + uid, new, index) class DistribcellFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'distribcell', uid, new, index) + return super(DistribcellFilter, cls).__new__(cls, b'distribcell', + uid, new, index) class EnergyFunctionFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'energyfunction', uid, new, index) + return super(EnergyFunctionFilter, cls).__new__( + cls, b'energyfunction', uid, new, index) class MaterialFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'material', uid, new, index) + return super(MaterialFilter, cls).__new__(cls, b'material', + uid, new, index) def __init__(self, bins=None, uid=None, new=True, index=None): - super().__init__(uid, new, index) + super(MaterialFilter, self).__init__(uid, new, index) if bins is not None: self.bins = bins @@ -190,27 +198,29 @@ class MaterialFilter(Filter): class MeshFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'mesh', uid, new, index) + return super(MeshFilter, cls).__new__(cls, b'mesh', uid, new, index) class MuFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'mu', uid, new, index) + return super(MuFilter, cls).__new__(cls, b'mu', uid, new, index) class PolarFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'polar', uid, new, index) + return super(PolarFilter, cls).__new__(cls, b'polar', uid, new, index) class SurfaceFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'surface', uid, new, index) + return super(SurfaceFilter, cls).__new__(cls, b'surface', uid, + new, index) class UniverseFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super().__new__(cls, b'universe', uid, new, index) + return super(UniverseFilter, cls).__new__(cls, b'universe', uid, + new, index) _FILTER_TYPE_MAP = { diff --git a/openmc/capi/material.py b/openmc/capi/material.py index a9cc9c1481..af0e9893e4 100644 --- a/openmc/capi/material.py +++ b/openmc/capi/material.py @@ -94,7 +94,7 @@ class Material(_FortranObjectWithID): index = mapping[uid]._index if index not in cls.__instances: - instance = super().__new__(cls) + instance = super(Material, cls).__new__(cls) instance._index = index if uid is not None: instance.id = uid diff --git a/openmc/capi/nuclide.py b/openmc/capi/nuclide.py index 54d131498f..e07872e583 100644 --- a/openmc/capi/nuclide.py +++ b/openmc/capi/nuclide.py @@ -58,7 +58,7 @@ class Nuclide(_FortranObject): def __new__(cls, *args): if args not in cls.__instances: - instance = super().__new__(cls) + instance = super(Nuclide, cls).__new__(cls) cls.__instances[args] = instance return cls.__instances[args] diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index cb37e69713..799cb42ee8 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -105,7 +105,7 @@ class Tally(_FortranObjectWithID): index = mapping[uid]._index if index not in cls.__instances: - instance = super().__new__(cls) + instance = super(Tally, cls).__new__(cls) instance._index = index if uid is not None: instance.id = uid From 4d5ca78ba64c3494bf69a05698f299ea0c9c1bca Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 5 Oct 2017 12:33:12 -0500 Subject: [PATCH 5/7] Improve how PRN seed is exposed to Python --- openmc/capi/settings.py | 12 +++++ src/api.F90 | 7 +-- src/initialize.F90 | 6 ++- src/input_xml.F90 | 5 +- src/random_lcg.F90 | 109 ++++++++++++++++++++-------------------- 5 files changed, 77 insertions(+), 62 deletions(-) diff --git a/openmc/capi/settings.py b/openmc/capi/settings.py index 11b0eacef5..eede6cf47b 100644 --- a/openmc/capi/settings.py +++ b/openmc/capi/settings.py @@ -10,6 +10,10 @@ _RUN_MODES = {1: 'fixed source', 4: 'particle restart', 5: 'volume'} +_dll.openmc_set_seed.argtypes = [c_int64] +_dll.openmc_set_seed.restype = c_int +_dll.openmc_set_seed.errcheck = _error_handler + class _Settings(object): # Attributes that are accessed through a descriptor @@ -37,5 +41,13 @@ class _Settings(object): else: raise ValueError('Invalid run mode: {}'.format(mode)) + @property + def seed(self): + return c_int64.in_dll(_dll, 'seed').value + + @seed.setter + def seed(self, seed): + _dll.openmc_set_seed(seed) + settings = _Settings() diff --git a/src/api.F90 b/src/api.F90 index 3b2fdacad8..4410f2fe45 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -17,7 +17,7 @@ module openmc_api use initialize, only: openmc_init use particle_header, only: Particle use plot, only: openmc_plot_geometry - use random_lcg, only: seed, initialize_prng + use random_lcg, only: seed, openmc_set_seed use settings use simulation_header use tally_header @@ -212,6 +212,8 @@ contains !=============================================================================== subroutine openmc_hard_reset() bind(C) + integer :: err + ! Reset all tallies and timers call openmc_reset() @@ -220,8 +222,7 @@ contains total_gen = 0 ! Reset the random number generator state - seed = 1_8 - call initialize_prng() + err = openmc_set_seed(1_8) end subroutine openmc_hard_reset !=============================================================================== diff --git a/src/initialize.F90 b/src/initialize.F90 index 1720f2adf9..a5ead1e835 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -21,7 +21,7 @@ module initialize use message_passing use mgxs_data, only: read_mgxs, create_macro_xs use output, only: print_version, write_message, print_usage - use random_lcg, only: initialize_prng + use random_lcg, only: openmc_set_seed, seed use settings #ifdef _OPENMP use simulation_header, only: n_threads @@ -45,6 +45,8 @@ contains subroutine openmc_init(intracomm) bind(C) integer, intent(in), optional :: intracomm ! MPI intracommunicator + integer :: err + ! Copy the communicator to a new variable. This is done to avoid changing ! the signature of this subroutine. If MPI is being used but no communicator ! was passed, assume MPI_COMM_WORLD. @@ -83,7 +85,7 @@ contains ! Initialize random number generator -- if the user specifies a seed, it ! will be re-initialized later - call initialize_prng() + err = openmc_set_seed(seed) ! Read XML input files call read_input_xml() diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 545c7e3510..402e030c41 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -25,7 +25,7 @@ module input_xml use nuclide_header use output, only: write_message, title, header, print_plot use plot_header - use random_lcg, only: prn, seed, initialize_prng + use random_lcg, only: prn, openmc_set_seed use surface_header use set_header, only: SetChar use settings @@ -151,6 +151,7 @@ contains integer :: temp_int integer :: temp_int_array3(3) integer(C_INT32_T) :: i_start, i_end + integer(C_INT64_T) :: seed integer(C_INT) :: err integer, allocatable :: temp_int_array(:) integer :: n_tracks @@ -340,7 +341,7 @@ contains ! Copy random number seed if specified if (check_for_node(root, "seed")) then call get_node_value(root, "seed", seed) - call initialize_prng() + err = openmc_set_seed(seed) end if ! Number of bins for logarithmic grid diff --git a/src/random_lcg.F90 b/src/random_lcg.F90 index 287bbba6d9..685557f070 100644 --- a/src/random_lcg.F90 +++ b/src/random_lcg.F90 @@ -1,5 +1,7 @@ module random_lcg + use, intrinsic :: ISO_C_BINDING + use constants implicit none @@ -7,28 +9,29 @@ module random_lcg private save - ! Random number seed - integer(8), public :: seed = 1_8 + ! Starting seed + integer(C_INT64_T), public, bind(C) :: seed = 1_8 - integer(8) :: prn_seed0 ! original seed - integer(8) :: prn_seed(N_STREAMS) ! current seed - integer(8) :: prn_mult ! multiplication factor, g - integer(8) :: prn_add ! additive factor, c - integer :: prn_bits ! number of bits, M - integer(8) :: prn_mod ! 2^M - integer(8) :: prn_mask ! 2^M - 1 - integer(8) :: prn_stride ! stride between particles - real(8) :: prn_norm ! 2^(-M) - integer :: stream ! current RNG stream + ! LCG parameters + integer(C_INT64_T), parameter :: prn_mult = 2806196910506780709_8 ! multiplication factor, g + integer(C_INT64_T), parameter :: prn_add = 1_8 ! additive factor, c + integer, parameter :: prn_bits = 63 ! number of bits, M + integer(C_INT64_T), parameter :: prn_mod = ibset(0_8, prn_bits) ! 2^M + integer(C_INT64_T), parameter :: prn_mask = not(prn_mod) ! 2^M - 1 + integer(C_INT64_T), parameter :: prn_stride = 152917_8 ! stride between particles + real(C_DOUBLE), parameter :: prn_norm = 2._8**(-prn_bits) ! 2^(-M) + ! Current PRNG state + integer(C_INT64_T) :: prn_seed(N_STREAMS) ! current seed + integer :: stream ! current RNG stream !$omp threadprivate(prn_seed, stream) public :: prn public :: future_prn - public :: initialize_prng public :: set_particle_seed public :: advance_prn_seed public :: prn_set_stream + public :: openmc_set_seed contains @@ -38,10 +41,10 @@ contains function prn() result(pseudo_rn) - real(8) :: pseudo_rn + real(C_DOUBLE) :: pseudo_rn - ! This algorithm uses bit-masking to find the next integer(8) value to be - ! used to calculate the random number + ! This algorithm uses bit-masking to find the next integer(C_INT64_T) value + ! to be used to calculate the random number prn_seed(stream) = iand(prn_mult*prn_seed(stream) + prn_add, prn_mask) @@ -59,40 +62,14 @@ contains function future_prn(n) result(pseudo_rn) - integer(8), intent(in) :: n ! number of prns to skip + integer(C_INT64_T), intent(in) :: n ! number of prns to skip - real(8) :: pseudo_rn + real(C_DOUBLE) :: pseudo_rn pseudo_rn = future_seed(n, prn_seed(stream)) * prn_norm end function future_prn -!=============================================================================== -! INITIALIZE_PRNG sets up the random number generator, determining the seed and -! values for g, c, and m. -!=============================================================================== - - subroutine initialize_prng() - - integer :: i - - prn_seed0 = seed -!$omp parallel - do i = 1, N_STREAMS - prn_seed(i) = prn_seed0 + i - 1 - end do - stream = STREAM_TRACKING -!$omp end parallel - prn_mult = 2806196910506780709_8 - prn_add = 1_8 - prn_bits = 63 - prn_mod = ibset(0_8, prn_bits) ! clever way of calculating 2**bits - prn_mask = prn_mod - 1_8 - prn_stride = 152917_8 - prn_norm = 2._8**(-prn_bits) - - end subroutine initialize_prng - !=============================================================================== ! SET_PARTICLE_SEED sets the seed to a unique value based on the ID of the ! particle @@ -100,12 +77,12 @@ contains subroutine set_particle_seed(id) - integer(8), intent(in) :: id + integer(C_INT64_T), intent(in) :: id integer :: i do i = 1, N_STREAMS - prn_seed(i) = future_seed(id*prn_stride, prn_seed0 + i - 1) + prn_seed(i) = future_seed(id*prn_stride, seed + i - 1) end do end subroutine set_particle_seed @@ -117,7 +94,7 @@ contains subroutine advance_prn_seed(n) - integer(8), intent(in) :: n ! number of seeds to skip + integer(C_INT64_T), intent(in) :: n ! number of seeds to skip prn_seed(stream) = future_seed(n, prn_seed(stream)) @@ -132,15 +109,15 @@ contains function future_seed(n, seed) result(new_seed) - integer(8), intent(in) :: n ! number of seeds to skip - integer(8), intent(in) :: seed ! original seed - integer(8) :: new_seed ! new seed + integer(C_INT64_T), intent(in) :: n ! number of seeds to skip + integer(C_INT64_T), intent(in) :: seed ! original seed + integer(C_INT64_T) :: new_seed ! new seed - integer(8) :: nskip ! positive number of seeds to skip - integer(8) :: g ! original multiplicative constant - integer(8) :: c ! original additive constnat - integer(8) :: g_new ! new effective multiplicative constant - integer(8) :: c_new ! new effective additive constant + integer(C_INT64_T) :: nskip ! positive number of seeds to skip + integer(C_INT64_T) :: g ! original multiplicative constant + integer(C_INT64_T) :: c ! original additive constnat + integer(C_INT64_T) :: g_new ! new effective multiplicative constant + integer(C_INT64_T) :: c_new ! new effective additive constant ! In cases where we want to skip backwards, we add the period of the random ! number generator until the number of PRNs to skip is positive since @@ -198,4 +175,26 @@ contains end subroutine prn_set_stream +!=============================================================================== +! C API FUNCTIONS +!=============================================================================== + + function openmc_set_seed(new_seed) result(err) bind(C) + ! Saves the starting seed and sets up the PRNG thread state + integer(C_INT64_T), value, intent(in) :: new_seed + integer(C_INT) :: err + + integer :: i + + err = 0 + seed = new_seed +!$omp parallel + do i = 1, N_STREAMS + prn_seed(i) = seed + i - 1 + end do + stream = STREAM_TRACKING +!$omp end parallel + + end function openmc_set_seed + end module random_lcg From 645b740d12496ec633d4817fa29526dc319671f8 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 11 Oct 2017 13:47:30 -0500 Subject: [PATCH 6/7] Fix typo, add __init__ for capi.EnergyFilter --- openmc/capi/filter.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index d15e4dc146..402ac4d688 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -106,6 +106,11 @@ class EnergyFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): return super(EnergyFilter, cls).__new__(cls, b'energy', uid, new, index) + def __init__(self, bins=None, uid=None, new=True, index=None): + super(EnergyFilter, self).__init__(uid, new, index) + if bins is not None: + self.bins = bins + @property def bins(self): energies = POINTER(c_double)() @@ -125,7 +130,7 @@ class EnergyFilter(Filter): class EnergyoutFilter(Filter): def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(Energyoutfilter, cls).__new__(cls, b'energyout', + return super(EnergyoutFilter, cls).__new__(cls, b'energyout', uid, new, index) From e27e1f3003a430ea6943962da1bfb3df2cfdc7ad Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 11 Oct 2017 14:05:28 -0500 Subject: [PATCH 7/7] Use class attribute in capi.Filter.__new__ to avoid the need to define __new__ in subclasses. --- openmc/capi/filter.py | 63 +++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index 402ac4d688..1b52af6db9 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -60,7 +60,7 @@ _dll.openmc_mesh_filter_set_mesh.errcheck = _error_handler class Filter(_FortranObjectWithID): __instances = WeakValueDictionary() - def __new__(cls, filter_type, uid=None, new=True, index=None): + def __new__(cls, obj=None, uid=None, new=True, index=None): mapping = filters if index is None: if new: @@ -75,9 +75,13 @@ class Filter(_FortranObjectWithID): raise AllocationError('A filter with ID={} has already ' 'been allocated.'.format(uid)) + # Resize internal array index = c_int32() _dll.openmc_extend_filters(1, index, None) - _dll.openmc_filter_set_type(index, filter_type) + + # Set the filter type -- note that the filter_type attribute + # only exists on subclasses! + _dll.openmc_filter_set_type(index, cls.filter_type.encode()) index = index.value else: index = mapping[uid]._index @@ -103,8 +107,7 @@ class Filter(_FortranObjectWithID): class EnergyFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(EnergyFilter, cls).__new__(cls, b'energy', uid, new, index) + filter_type = 'energy' def __init__(self, bins=None, uid=None, new=True, index=None): super(EnergyFilter, self).__init__(uid, new, index) @@ -129,56 +132,39 @@ class EnergyFilter(Filter): class EnergyoutFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(EnergyoutFilter, cls).__new__(cls, b'energyout', - uid, new, index) + filter_type = 'energyout' class AzimuthalFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(AzimuthalFilter, cls).__new__(cls, b'azimuthal', - uid, new, index) + filter_type = 'azimuthal' class CellFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(CellFilter, cls).__new__(cls, b'cell', uid, new, index) + filter_type = 'cell' class CellbornFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(CellbornFilter, cls).__new__(cls, b'cellborn', uid, - new, index) + filter_type = 'cellborn' class CellfromFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(CellfromFilter, cls).__new__(cls, b'cellfrom', uid, - new, index) + filter_type = 'cellfrom' class DelayedGroupFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(DelayedGroupFilter, cls).__new__(cls, b'delayedgroup', - uid, new, index) + filter_type = 'delayedgroup' class DistribcellFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(DistribcellFilter, cls).__new__(cls, b'distribcell', - uid, new, index) + filter_type = 'distribcell' class EnergyFunctionFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(EnergyFunctionFilter, cls).__new__( - cls, b'energyfunction', uid, new, index) + filter_type = 'energyfunction' class MaterialFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(MaterialFilter, cls).__new__(cls, b'material', - uid, new, index) + filter_type = 'material' def __init__(self, bins=None, uid=None, new=True, index=None): super(MaterialFilter, self).__init__(uid, new, index) @@ -202,30 +188,23 @@ class MaterialFilter(Filter): class MeshFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(MeshFilter, cls).__new__(cls, b'mesh', uid, new, index) + filter_type = 'mesh' class MuFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(MuFilter, cls).__new__(cls, b'mu', uid, new, index) + filter_type = 'mu' class PolarFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(PolarFilter, cls).__new__(cls, b'polar', uid, new, index) + filter_type = 'polar' class SurfaceFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(SurfaceFilter, cls).__new__(cls, b'surface', uid, - new, index) + filter_type = 'surface' class UniverseFilter(Filter): - def __new__(cls, bins=None, uid=None, new=True, index=None): - return super(UniverseFilter, cls).__new__(cls, b'universe', uid, - new, index) + filter_type = 'universe' _FILTER_TYPE_MAP = {