diff --git a/docs/source/usersguide/cross_sections.rst b/docs/source/usersguide/data.rst
similarity index 64%
rename from docs/source/usersguide/cross_sections.rst
rename to docs/source/usersguide/data.rst
index 0cbb581bd..48003ecb7 100644
--- a/docs/source/usersguide/cross_sections.rst
+++ b/docs/source/usersguide/data.rst
@@ -1,55 +1,73 @@
-.. _usersguide_cross_sections:
+.. _usersguide_data:
-===========================
-Cross Section Configuration
-===========================
+==================
+Data Configuration
+==================
-In order to run a simulation with OpenMC, you will need cross section data for
-each nuclide or material in your problem. OpenMC can be run in continuous-energy
-or multi-group mode.
+OpenMC relies on a variety of physical data in order to carry out transport
+simulations, depletion simulations, and other common tasks. As a user, you are
+responsible for specifying one or more of the following:
-In continuous-energy mode, OpenMC uses a native `HDF5
-`_ format (see :ref:`io_nuclear_data`) to
-store all nuclear data. Pregenerated HDF5 libraries can be found at
-https://openmc.org; unless you have specific data needs, it is highly
-recommended to use one of the pregenerated libraries. Alternatively, if you have
-ACE format data that was produced with NJOY_, such as that distributed with
-MCNP_ or Serpent_, it can be converted to the HDF5 format using the :ref:`using
-the Python API `. Several sources provide openly available
-ACE data including the `ENDF/B`_, JEFF_, and TENDL_ libraries as well as the
-`LANL Nuclear Data Team `_. In addition to
-tabulated cross sections in the HDF5 files, OpenMC relies on :ref:`windowed
-multipole ` data to perform on-the-fly Doppler broadening.
+- **Cross sections (XML)** -- A :ref:`cross sections XML `
+ file (commonly named ``cross_sections.xml``) contains a listing of other data
+ files, in particular neutron cross sections, photon cross sections, and
+ windowed multipole data. Each of those files, in turn, uses a `HDF5
+ `_ format (see :ref:`io_nuclear_data`). In
+ order to run transport simulations with continuous-energy cross sections, you
+ need to specify this file.
-In multi-group mode, OpenMC utilizes an HDF5-based library format which can be
-used to describe nuclide- or material-specific quantities.
+- **Depletion chain (XML)** -- A :ref:`depletion chain XML `
+ file contains decay data, fission product yields, and information on what
+ neutron reactions can result in transmutation. This file is needed for
+ depletion/activation calculations as well as some basic functions in the
+ :mod:`openmc.data` module.
+
+- **Multigroup cross sections (HDF5)** -- OpenMC can also perform transport
+ simulations using multigroup data. In this case, multigroup cross sections are
+ stored in a single :ref:`HDF5 file `. Thus, in order to run a
+ multigroup transport simulation, this file needs to be specified.
+
+Each of the above files can specified in several ways. In the Python API, a
+:ref:`runtime configuration variable `
+:data:`openmc.config` can be used to specify any of the above and is initialized
+using a set of environment variables.
+
+.. _usersguide_data_runtime:
---------------------
-Environment Variables
+Runtime Configuration
---------------------
-When :ref:`scripts_openmc` is run, it will look for several environment
-variables that indicate where cross sections can be found. While the location of
-cross sections can also be indicated through the
-:attr:`openmc.Materials.cross_sections` attribute (or in the :ref:`materials.xml
-` file), if you always use the same set of cross section data, it
-is often easier to just set an environment variable that will be picked up by
-default every time OpenMC is run. The following environment variables are used:
+Data sources for OpenMC can be specified at runtime in Python using the
+:data:`openmc.config` variable. This variable acts like a dictionary and stores
+key-values pairs, where the values are file paths (strings or path-like objects)
+and the key can be one of the following:
-:envvar:`OPENMC_CROSS_SECTIONS`
- Indicates the path to the :ref:`cross_sections.xml `
- summary file that is used to locate HDF5 format cross section libraries if the
- user has not specified :attr:`openmc.Materials.cross_sections` (equivalently,
- the :ref:`cross_sections` in :ref:`materials.xml `).
+``"cross_sections"``
+ Indicates the path to the :ref:`cross sections XML ` file
+ that lists HDF5 format neutron cross sections, photon cross sections, and
+ windowed multipole data. At startup, this is initialized with the value of the
+ :envvar:`OPENMC_CROSS_SECTIONS` environment variable. Note that the
+ :attr:`openmc.Materials.cross_sections` attribute will override this, if
+ specified.
-:envvar:`OPENMC_MG_CROSS_SECTIONS`
+``"chain_file"``
+ Indicates the path to the :ref:`depletion chain XML ` file
+ that contains decay data, fission product yields, and what neutron reactions
+ may result in transmutation of a target nuclide. At startup, this is
+ initialized with the value of the :envvar:`OPENMC_CHAIN_FILE` environment
+ variable.
+
+``"mg_cross_sections"``
Indicates the path to an :ref:`HDF5 file ` that contains
- multi-group cross sections if the user has not specified
- :attr:`openmc.Materials.cross_sections` (equivalently, the
- :ref:`cross_sections` in :ref:`materials.xml `).
+ multigroup cross sections. At startup, this is initialized with the value of
+ the :envvar:`OPENMC_MG_CROSS_SECTIONS` environment variable. Note that the
+ :attr:`openmc.Materials.cross_sections` attribute will override this if
+ specified.
-To set these environment variables persistently, export them from your shell
-profile (``.profile`` or ``.bashrc`` in bash_).
+If you want to persistently set the environment variables used to initialized
+the configuration, export them from your shell profile (``.profile`` or
+``.bashrc`` in bash_).
.. _bash: http://www.linuxfromscratch.org/blfs/view/6.3/postlfs/profile.html
@@ -61,12 +79,13 @@ Using Pregenerated Libraries
----------------------------
Various evaluated nuclear data libraries have been processed into the HDF5
-format required by OpenMC and can be found at https://openmc.org. You
-can find both libraries generated by the OpenMC development team as well as
-libraries based on ACE files distributed elsewhere. To use these libraries,
-download the archive file, unpack it, and then set your
-:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of
-the ``cross_sections.xml`` file contained in the unpacked directory.
+format required by OpenMC and can be found at https://openmc.org. Unless you
+have specific data needs, it is highly recommended to use one of the
+pregenerated libraries. You can find both libraries generated by the OpenMC
+development team as well as libraries based on ACE files distributed elsewhere.
+To use these libraries, download the archive file, unpack it, and then specify
+the path of the ``cross_sections.xml`` file contained in the unpacked directory
+as described in :ref:`usersguide_data_runtime`.
.. _create_xs_library:
@@ -75,6 +94,12 @@ Manually Creating a Library from ACE files
.. currentmodule:: openmc.data
+If you have ACE format data that was produced with NJOY_, such as that
+distributed with MCNP_ or Serpent_, it can be converted to the HDF5 format using
+the using the Python API. Several sources provide openly available ACE data
+including the `ENDF/B`_, JEFF_, and TENDL_ libraries as well as the `LANL
+Nuclear Data Team `_.
+
The :mod:`openmc.data` module in the Python API enables users to directly
convert ACE data to OpenMC's HDF5 format and create a corresponding
:ref:`cross_sections.xml ` file. For those who prefer to use
@@ -224,6 +249,19 @@ relaxation sublibrary files are required:
Once the HDF5 files have been generated, a library can be created using the
:class:`DataLibrary` class as described in :ref:`create_xs_library`.
+-----------
+Chain Files
+-----------
+
+Pregenerated depletion chain XML files can be found at https://openmc.org.
+Additionally, depletion chains can be generated using the
+:class:`openmc.deplete.Chain` class. In particular, the
+:meth:`~openmc.deplete.Chain.from_endf` method allows a chain to be generated
+starting from a set of ENDF incident neutron, decay, and fission product yield
+sublibrary files. Once you've downloaded or generated a depletion chain XML
+file, make sure to specify its path as described in
+:ref:`usersguide_data_runtime`.
+
-----------------------
Windowed Multipole Data
-----------------------
@@ -241,18 +279,16 @@ The `official ENDF/B-VII.1 HDF5 library
multipole library, so if you are using this library, the windowed multipole data
will already be available to you.
---------------------------
-Multi-Group Cross Sections
---------------------------
+-------------------------
+Multigroup Cross Sections
+-------------------------
-Multi-group cross section libraries are generally tailored to the specific
+Multigroup cross section libraries are generally tailored to the specific
calculation to be performed. Therefore, at this point in time, OpenMC is not
-distributed with any pre-existing multi-group cross section libraries.
-However, if obtained or generated their own library, the user
-should set the :envvar:`OPENMC_MG_CROSS_SECTIONS` environment variable
-to the absolute path of the file library expected to used most frequently.
-
-For an example of how to create a multi-group library, see the `example notebook
+distributed with any pre-existing multigroup cross section libraries. However,
+if a multigroup library file is downloaded or generated, the path to the file
+needs to be specified as described in :ref:`usersguide_data_runtime`. For an
+example of how to create a multigroup library, see the `example notebook
<../examples/mg-mode-part-i.ipynb>`__.
.. _NJOY: http://www.njoy21.io/
diff --git a/docs/source/usersguide/index.rst b/docs/source/usersguide/index.rst
index fab353c77..511bdc6ce 100644
--- a/docs/source/usersguide/index.rst
+++ b/docs/source/usersguide/index.rst
@@ -13,7 +13,7 @@ essential aspects of using OpenMC to perform simulations.
beginners
install
- cross_sections
+ data
basics
materials
geometry
diff --git a/openmc/__init__.py b/openmc/__init__.py
index abcac899f..214695e67 100644
--- a/openmc/__init__.py
+++ b/openmc/__init__.py
@@ -32,6 +32,7 @@ from openmc.search import *
from openmc.polynomial import *
from openmc.tracks import *
from . import examples
+from .config import *
# Import a few names from the model module
from openmc.model import rectangular_prism, hexagonal_prism, Model
diff --git a/openmc/config.py b/openmc/config.py
new file mode 100644
index 000000000..628df7edc
--- /dev/null
+++ b/openmc/config.py
@@ -0,0 +1,83 @@
+from collections.abc import MutableMapping
+import os
+from pathlib import Path
+import warnings
+
+from openmc.data import DataLibrary
+
+__all__ = ["config"]
+
+
+class _Config(MutableMapping):
+ def __init__(self, data=()):
+ self._mapping = {}
+ self.update(data)
+
+ def __getitem__(self, key):
+ return self._mapping[key]
+
+ def __delitem__(self, key):
+ del self._mapping[key]
+ if key == 'cross_sections':
+ del os.environ['OPENMC_CROSS_SECTIONS']
+ elif key == 'mg_cross_sections':
+ del os.environ['OPENMC_MG_CROSS_SECTIONS']
+
+ def __setitem__(self, key, value):
+ if key == 'cross_sections':
+ # Force environment variable to match
+ self._set_path(key, value)
+ os.environ['OPENMC_CROSS_SECTIONS'] = str(value)
+ elif key == 'mg_cross_sections':
+ self._set_path(key, value)
+ os.environ['OPENMC_MG_CROSS_SECTIONS'] = str(value)
+ elif key == 'chain_file':
+ self._set_path(key, value)
+ os.environ['OPENMC_CHAIN_FILE'] = str(value)
+ else:
+ raise KeyError(f'Unrecognized config key: {key}')
+
+ def __iter__(self):
+ return iter(self._mapping)
+
+ def __len__(self):
+ return len(self._mapping)
+
+ def __repr__(self):
+ return repr(self._mapping)
+
+ def _set_path(self, key, value):
+ self._mapping[key] = p = Path(value)
+ if not p.exists():
+ warnings.warn(f"'{value}' does not exist.")
+
+
+def _default_config():
+ """Return default configuration"""
+ config = _Config()
+
+ # Set cross sections using environment variable
+ if "OPENMC_CROSS_SECTIONS" in os.environ:
+ config['cross_sections'] = os.environ["OPENMC_CROSS_SECTIONS"]
+ if "OPENMC_MG_CROSS_SECTIONS" in os.environ:
+ config['mg_cross_sections'] = os.environ["OPENMC_MG_CROSS_SECTIONS"]
+
+ # Set depletion chain
+ chain_file = os.environ.get("OPENMC_CHAIN_FILE")
+ if (chain_file is None and
+ config.get('cross_sections') is not None and
+ config['cross_sections'].exists()
+ ):
+ # Check for depletion chain in cross_sections.xml
+ data = DataLibrary.from_xml(config['cross_sections'])
+ for lib in reversed(data.libraries):
+ if lib['type'] == 'depletion_chain':
+ chain_file = lib['path']
+ break
+ if chain_file is not None:
+ config['chain_file'] = chain_file
+
+ return config
+
+
+config = _default_config()
diff --git a/openmc/data/library.py b/openmc/data/library.py
index bf937e57a..ad6c65fb6 100644
--- a/openmc/data/library.py
+++ b/openmc/data/library.py
@@ -4,6 +4,7 @@ import pathlib
import h5py
+import openmc
from openmc.mixin import EqualityMixin
from openmc._xml import clean_indentation, reorder_attributes
@@ -124,8 +125,8 @@ class DataLibrary(EqualityMixin):
Parameters
----------
path : str, optional
- Path to XML file to read. If not provided, the
- :envvar:`OPENMC_CROSS_SECTIONS` environment variable will be used.
+ Path to XML file to read. If not provided,
+ openmc.config['cross_sections'] will be used.
Returns
-------
@@ -136,15 +137,14 @@ class DataLibrary(EqualityMixin):
data = cls()
- # If path is None, get the cross sections from the
- # OPENMC_CROSS_SECTIONS environment variable
+ # If path is None, get the cross sections from the global configuration
if path is None:
- path = os.environ.get('OPENMC_CROSS_SECTIONS')
+ path = openmc.config.get('cross_sections')
- # Check to make sure there was an environmental variable.
+ # Check to make sure we picked up cross sections
if path is None:
- raise ValueError("Either path or OPENMC_CROSS_SECTIONS "
- "environmental variable must be set")
+ raise ValueError("Either path or openmc.config['cross_sections'] "
+ "must be set")
tree = ET.parse(path)
root = tree.getroot()
@@ -162,7 +162,6 @@ class DataLibrary(EqualityMixin):
data.libraries.append(library)
# get depletion chain data
-
dep_node = root.find("depletion_chain")
if dep_node is not None:
filename = os.path.join(directory, dep_node.attrib['path'])
diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py
index d63bc6bfb..217c9cac0 100644
--- a/openmc/deplete/chain.py
+++ b/openmc/deplete/chain.py
@@ -239,24 +239,6 @@ def replace_missing_fpy(actinide, fpy_data, decay_data):
return 'U235'
-def _find_chain_file(cross_sections=None):
- # First check deprecated OPENMC_DEPLETE_CHAIN environment variable
- chain_file = os.environ.get("OPENMC_DEPLETE_CHAIN")
- if chain_file is not None:
- warn("Use of OPENMC_DEPLETE_CHAIN is deprecated in favor of adding "
- "depletion_chain to OPENMC_CROSS_SECTIONS", FutureWarning)
- return chain_file
-
- # Check for depletion chain in cross_sections.xml
- data = DataLibrary.from_xml(cross_sections)
- for lib in reversed(data.libraries):
- if lib['type'] == 'depletion_chain':
- return lib['path']
-
- raise DataError("No depletion chain specified and could not find depletion "
- f"chain in {cross_sections}")
-
-
class Chain:
"""Full representation of a depletion chain.
@@ -265,9 +247,8 @@ class Chain:
yield sublibrary files. The depletion chain used during a depletion
simulation is indicated by either an argument to
:class:`openmc.deplete.CoupledOperator` or
- :class:`openmc.deplete.IndependentOperator`, or through the
- ``depletion_chain`` item in the :envvar:`OPENMC_CROSS_SECTIONS`
- environment variable.
+ :class:`openmc.deplete.IndependentOperator`, or through
+ openmc.config['chain_file'].
Attributes
----------
diff --git a/openmc/deplete/coupled_operator.py b/openmc/deplete/coupled_operator.py
index dd14f282f..a8c1fa156 100644
--- a/openmc/deplete/coupled_operator.py
+++ b/openmc/deplete/coupled_operator.py
@@ -9,7 +9,6 @@ filesystem.
"""
import copy
-import os
from warnings import warn
import numpy as np
@@ -22,7 +21,6 @@ from openmc.exceptions import DataError
import openmc.lib
from openmc.mpi import comm
from .abc import OperatorResult
-from .chain import _find_chain_file
from .openmc_operator import OpenMCOperator, _distribute
from .results import Results
from .helpers import (
@@ -48,11 +46,11 @@ def _find_cross_sections(model):
return model.materials.cross_sections
# otherwise fallback to environment variable
- cross_sections = os.environ.get("OPENMC_CROSS_SECTIONS")
+ cross_sections = openmc.config.get("cross_sections")
if cross_sections is None:
raise DataError(
"Cross sections were not specified in Model.materials and "
- "the OPENMC_CROSS_SECTIONS environment variable is not set."
+ "openmc.config['cross_sections'] is not set."
)
return cross_sections
@@ -103,9 +101,8 @@ class CoupledOperator(OpenMCOperator):
model : openmc.model.Model
OpenMC model object
chain_file : str, optional
- Path to the depletion chain XML file. Defaults to the file
- listed under ``depletion_chain`` in
- :envvar:`OPENMC_CROSS_SECTIONS` environment variable.
+ Path to the depletion chain XML file. Defaults to
+ ``openmc.config['chain_file']``.
prev_results : Results, optional
Results from a previous depletion calculation. If this argument is
specified, the depletion calculation will start from the latest state
@@ -231,10 +228,8 @@ class CoupledOperator(OpenMCOperator):
" model with which to generate the transport Operator."
raise TypeError(msg)
- # Determine cross sections / depletion chain
+ # Determine cross sections
cross_sections = _find_cross_sections(model)
- if chain_file is None:
- chain_file = _find_chain_file(cross_sections)
check_value('fission yield mode', fission_yield_mode,
self._fission_helpers.keys())
diff --git a/openmc/deplete/independent_operator.py b/openmc/deplete/independent_operator.py
index 7f0bf5fb2..f628a7db1 100644
--- a/openmc/deplete/independent_operator.py
+++ b/openmc/deplete/independent_operator.py
@@ -40,7 +40,8 @@ class IndependentOperator(OpenMCOperator):
micro_xs : MicroXS
One-group microscopic cross sections in [b] .
chain_file : str
- Path to the depletion chain XML file.
+ Path to the depletion chain XML file. Defaults to
+ ``openmc.config['chain_file']``.
keff : 2-tuple of float, optional
keff eigenvalue and uncertainty from transport calculation.
Default is None.
@@ -111,7 +112,7 @@ class IndependentOperator(OpenMCOperator):
def __init__(self,
materials,
micro_xs,
- chain_file,
+ chain_file=None,
keff=None,
normalization_mode='fission-q',
fission_q=None,
diff --git a/openmc/deplete/microxs.py b/openmc/deplete/microxs.py
index 72d87d3b7..7f3a84bf6 100644
--- a/openmc/deplete/microxs.py
+++ b/openmc/deplete/microxs.py
@@ -5,18 +5,16 @@ nuclide names as row indices and reaction names as column indices.
"""
import tempfile
-from pathlib import Path
from copy import deepcopy
-from pandas import DataFrame, read_csv, concat
+from pandas import DataFrame, read_csv
import numpy as np
from openmc.checkvalue import check_type, check_value, check_iterable_type
+from openmc.exceptions import DataError
from openmc.mgxs import EnergyGroups, ArbitraryXS, FissionXS
-from openmc.data import DataLibrary
-from openmc import Tallies, StatePoint, Materials, Material
-
-
+from openmc import Tallies, StatePoint, Materials
+import openmc
from .chain import Chain, REACTIONS
from .coupled_operator import _find_cross_sections, _get_nuclides_with_data
@@ -35,7 +33,7 @@ class MicroXS(DataFrame):
def from_model(cls,
model,
reaction_domain,
- chain_file,
+ chain_file=None,
dilute_initial=1.0e3,
energy_bounds=(0, 20e6),
run_kwargs=None):
@@ -48,11 +46,12 @@ class MicroXS(DataFrame):
OpenMC model object. Must contain geometry, materials, and settings.
reaction_domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh
Domain in which to tally reaction rates.
- chain_file : str
+ chain_file : str, optional
Path to the depletion chain XML file that will be used in depletion
simulation. Used to determine cross sections for materials not
- present in the inital composition.
- dilute_initial : float
+ present in the inital composition. Defaults to
+ ``openmc.config['chain_file']``.
+ dilute_initial : float, optional
Initial atom density [atoms/cm^3] to add for nuclides that
are zero in initial condition to ensure they exist in the cross
section data. Only done for nuclides with reaction rates.
@@ -144,6 +143,13 @@ class MicroXS(DataFrame):
:class:`openmc.Materials` object with nuclides added to burnable
materials.
"""
+ if chain_file is None:
+ chain_file = openmc.config.get('chain_file')
+ if chain_file is None:
+ raise DataError(
+ "No depletion chain specified and could not find depletion "
+ "chain in openmc.config['chain_file']"
+ )
chain = Chain.from_xml(chain_file)
reactions = chain.reactions
cross_sections = _find_cross_sections(model)
diff --git a/openmc/deplete/openmc_operator.py b/openmc/deplete/openmc_operator.py
index 76f8e964e..ca1516803 100644
--- a/openmc/deplete/openmc_operator.py
+++ b/openmc/deplete/openmc_operator.py
@@ -11,6 +11,7 @@ from collections import OrderedDict
import numpy as np
import openmc
+from openmc.exceptions import DataError
from openmc.mpi import comm
from .abc import TransportOperator, OperatorResult
from .atom_number import AtomNumber
@@ -57,9 +58,8 @@ class OpenMCOperator(TransportOperator):
Path to continuous energy cross section library, or object containing
one-group cross-sections.
chain_file : str, optional
- Path to the depletion chain XML file. Defaults to the file
- listed under ``depletion_chain`` in
- :envvar:`OPENMC_CROSS_SECTIONS` environment variable.
+ Path to the depletion chain XML file. Defaults to
+ openmc.config['chain_file'].
prev_results : Results, optional
Results from a previous depletion calculation. If this argument is
specified, the depletion calculation will start from the latest state
@@ -83,7 +83,6 @@ class OpenMCOperator(TransportOperator):
if ``reduce_chain`` evaluates to true. The default value of
``None`` implies no limit on the depth.
-
Attributes
----------
materials : openmc.Materials
@@ -133,6 +132,15 @@ class OpenMCOperator(TransportOperator):
reduce_chain=False,
reduce_chain_level=None):
+ # If chain file was not specified, try to get it from global config
+ if chain_file is None:
+ chain_file = openmc.config.get('chain_file')
+ if chain_file is None:
+ raise DataError(
+ "No depletion chain specified and could not find depletion "
+ "chain in openmc.config['chain_file']"
+ )
+
super().__init__(chain_file, fission_q, dilute_initial, prev_results)
self.round_number = False
self.materials = materials
diff --git a/openmc/deplete/results.py b/openmc/deplete/results.py
index 15a65e98a..b2c8e7733 100644
--- a/openmc/deplete/results.py
+++ b/openmc/deplete/results.py
@@ -392,7 +392,7 @@ class Results(list):
as such cannot be used in subsequent transport calculations.
If not provided, nuclides from the cross_sections element of
materials.xml will be used. If that element is not present,
- nuclides from OPENMC_CROSS_SECTIONS will be used.
+ nuclides from openmc.config['cross_sections'] will be used.
Returns
-------
@@ -412,7 +412,7 @@ class Results(list):
# the new materials XML file. The precedence of nuclides to select
# is first ones provided as a kwarg here, then ones specified
# in the materials.xml file if provided, then finally from
- # the environment variable OPENMC_CROSS_SECTIONS.
+ # openmc.config['cross_sections'].
if nuc_with_data:
cv.check_iterable_type('nuclide names', nuc_with_data, str)
available_cross_sections = nuc_with_data
diff --git a/openmc/element.py b/openmc/element.py
index 1473bd63e..49aeaf464 100644
--- a/openmc/element.py
+++ b/openmc/element.py
@@ -1,9 +1,9 @@
from collections import OrderedDict
-import os
import re
from xml.etree import ElementTree as ET
import openmc.checkvalue as cv
+import openmc
from openmc.data import NATURAL_ABUNDANCE, atomic_mass, \
isotopes as natural_isotopes
@@ -40,10 +40,10 @@ class Element(str):
cross_sections=None):
"""Expand natural element into its naturally-occurring isotopes.
- An optional cross_sections argument or the :envvar:`OPENMC_CROSS_SECTIONS`
- environment variable is used to specify a cross_sections.xml file.
- If the cross_sections.xml file is found, the element is expanded only
- into the isotopes/nuclides present in cross_sections.xml. If no
+ An optional cross_sections argument or the ``cross_sections``
+ configuration value is used to specify a cross_sections.xml file. If the
+ cross_sections.xml file is found, the element is expanded only into the
+ isotopes/nuclides present in cross_sections.xml. If no
cross_sections.xml file is found, the element is expanded based on its
naturally occurring isotopes.
@@ -54,12 +54,13 @@ class Element(str):
percent_type : {'ao', 'wo'}
'ao' for atom percent and 'wo' for weight percent
enrichment : float, optional
- Enrichment of an enrichment_target nuclide in percent (ao or wo).
- If enrichment_target is not supplied then it is enrichment for U235
- in weight percent. For example, input 4.95 for 4.95 weight percent
+ Enrichment of an enrichment_target nuclide in percent (ao or wo). If
+ enrichment_target is not supplied then it is enrichment for U235 in
+ weight percent. For example, input 4.95 for 4.95 weight percent
enriched U. Default is None (natural composition).
enrichment_target: str, optional
- Single nuclide name to enrich from a natural composition (e.g., 'O16')
+ Single nuclide name to enrich from a natural composition (e.g.,
+ 'O16')
.. versionadded:: 0.12
enrichment_type: {'ao', 'wo'}, optional
@@ -82,8 +83,8 @@ class Element(str):
ValueError
No data is available for any of natural isotopes of the element
ValueError
- If only some natural isotopes are available in the cross-section data
- library and the element is not O, W, or Ta
+ If only some natural isotopes are available in the cross-section
+ data library and the element is not O, W, or Ta
ValueError
If a non-naturally-occurring isotope is requested
ValueError
@@ -101,8 +102,8 @@ class Element(str):
`ORNL/CSD/TM-244 `_ is used to
calculate the weight fractions of U234, U235, U236, and U238. Namely,
the weight fraction of U234 and U236 are taken to be 0.89% and 0.46%,
- respectively, of the U235 weight fraction. The remainder of the
- isotopic weight is assigned to U238.
+ respectively, of the U235 weight fraction. The remainder of the isotopic
+ weight is assigned to U238.
When the `enrichment` argument is specified with `enrichment_target`, a
general enrichment procedure is used for elements composed of exactly
@@ -125,10 +126,10 @@ class Element(str):
# Create dict to store the expanded nuclides and abundances
abundances = OrderedDict()
- # If cross_sections is None, get the cross sections from the
- # OPENMC_CROSS_SECTIONS environment variable
+ # If cross_sections is None, get the cross sections from the global
+ # configuration
if cross_sections is None:
- cross_sections = os.environ.get('OPENMC_CROSS_SECTIONS')
+ cross_sections = openmc.config.get('cross_sections')
# If a cross_sections library is present, check natural nuclides
# against the nuclides in the library
diff --git a/openmc/material.py b/openmc/material.py
index ece70df88..1b2b46a73 100644
--- a/openmc/material.py
+++ b/openmc/material.py
@@ -1310,9 +1310,8 @@ class Materials(cv.CheckedList):
"""Collection of Materials used for an OpenMC simulation.
This class corresponds directly to the materials.xml input file. It can be
- thought of as a normal Python list where each member is a
- :class:`Material`. It behaves like a list as the following example
- demonstrates:
+ thought of as a normal Python list where each member is a :class:`Material`.
+ It behaves like a list as the following example demonstrates:
>>> fuel = openmc.Material()
>>> clad = openmc.Material()
@@ -1332,9 +1331,9 @@ class Materials(cv.CheckedList):
Indicates the path to an XML cross section listing file (usually named
cross_sections.xml). If it is not set, the
:envvar:`OPENMC_CROSS_SECTIONS` environment variable will be used for
- continuous-energy calculations and
- :envvar:`OPENMC_MG_CROSS_SECTIONS` will be used for multi-group
- calculations to find the path to the HDF5 cross section file.
+ continuous-energy calculations and :envvar:`OPENMC_MG_CROSS_SECTIONS`
+ will be used for multi-group calculations to find the path to the HDF5
+ cross section file.
"""
diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py
index 67ad151cc..98a99196e 100644
--- a/openmc/mgxs_library.py
+++ b/openmc/mgxs_library.py
@@ -1,6 +1,5 @@
import copy
from numbers import Real, Integral
-import os
import h5py
import numpy as np
@@ -2536,8 +2535,7 @@ class MGXSLibrary:
----------
filename : str, optional
Name of HDF5 file containing MGXS data. Default is None.
- If not provided, the value of the OPENMC_MG_CROSS_SECTIONS
- environmental variable will be used
+ If not provided, openmc.config['mg_cross_sections'] will be used.
Returns
-------
@@ -2545,15 +2543,14 @@ class MGXSLibrary:
Multi-group cross section data object.
"""
- # If filename is None, get the cross sections from the
- # OPENMC_CROSS_SECTIONS environment variable
+ # If filename is None, get the cross sections from openmc.config
if filename is None:
- filename = os.environ.get('OPENMC_MG_CROSS_SECTIONS')
+ filename = openmc.config.get('mg_cross_sections')
# Check to make sure there was an environmental variable.
if filename is None:
- raise ValueError("Either path or OPENMC_MG_CROSS_SECTIONS "
- "environmental variable must be set")
+ raise ValueError("Either path or openmc.config['mg_cross_sections']"
+ "must be set")
check_type('filename', filename, str)
file = h5py.File(filename, 'r')
diff --git a/tests/unit_tests/test_config.py b/tests/unit_tests/test_config.py
new file mode 100644
index 000000000..1d3c0f173
--- /dev/null
+++ b/tests/unit_tests/test_config.py
@@ -0,0 +1,43 @@
+from collections.abc import Mapping
+import os
+
+import openmc
+import pytest
+
+
+@pytest.fixture(autouse=True, scope='module')
+def reset_config():
+ config = dict(openmc.config)
+ try:
+ yield
+ finally:
+ openmc.config.clear()
+ openmc.config.update(config)
+
+
+def test_config_basics():
+ assert isinstance(openmc.config, Mapping)
+ for key, value in openmc.config.items():
+ assert isinstance(key, str)
+ assert isinstance(value, os.PathLike)
+
+ # Set and delete
+ openmc.config['cross_sections'] = '/path/to/cross_sections.xml'
+ del openmc.config['cross_sections']
+ assert 'cross_sections' not in openmc.config
+ assert 'OPENMC_CROSS_SECTIONS' not in os.environ
+
+ # Can't use any key
+ with pytest.raises(KeyError):
+ openmc.config['🐖'] = '/like/to/eat/bacon'
+
+
+def test_config_set_envvar():
+ openmc.config['cross_sections'] = '/path/to/cross_sections.xml'
+ assert os.environ['OPENMC_CROSS_SECTIONS'] == '/path/to/cross_sections.xml'
+
+ openmc.config['mg_cross_sections'] = '/path/to/mg_cross_sections.h5'
+ assert os.environ['OPENMC_MG_CROSS_SECTIONS'] == '/path/to/mg_cross_sections.h5'
+
+ openmc.config['chain_file'] = '/path/to/chain_file.xml'
+ assert os.environ['OPENMC_CHAIN_FILE'] == '/path/to/chain_file.xml'
diff --git a/tests/unit_tests/test_deplete_operator.py b/tests/unit_tests/test_deplete_operator.py
index 5fe8715ac..6ea89fc4a 100644
--- a/tests/unit_tests/test_deplete_operator.py
+++ b/tests/unit_tests/test_deplete_operator.py
@@ -1,37 +1,15 @@
"""Basic unit tests for openmc.deplete.Operator instantiation
-Modifies and resets environment variable OPENMC_CROSS_SECTIONS
-to a custom file with new depletion_chain node
"""
from pathlib import Path
-import pytest
from openmc.deplete.abc import TransportOperator
-from openmc.deplete.chain import Chain, _find_chain_file
+from openmc.deplete.chain import Chain
-BARE_XS_FILE = "bare_cross_sections.xml"
CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml"
-@pytest.fixture()
-def bare_xs(run_in_tmpdir):
- """Create a very basic cross_sections file, return simple Chain.
-
- """
-
- bare_xs_contents = """
-
-
-
-""".format(CHAIN_PATH)
-
- with open(BARE_XS_FILE, "w") as out:
- out.write(bare_xs_contents)
-
- yield BARE_XS_FILE
-
-
class BareDepleteOperator(TransportOperator):
"""Very basic class for testing the initialization."""
@@ -52,10 +30,10 @@ class BareDepleteOperator(TransportOperator):
pass
-def test_operator_init(bare_xs):
+def test_operator_init():
"""The test uses a temporary dummy chain. This file will be removed
at the end of the test, and only contains a depletion_chain node."""
- bare_op = BareDepleteOperator(_find_chain_file(bare_xs))
+ bare_op = BareDepleteOperator(CHAIN_PATH)
act_chain = bare_op.chain
ref_chain = Chain.from_xml(CHAIN_PATH)
assert len(act_chain) == len(ref_chain)
@@ -73,8 +51,7 @@ def test_operator_init(bare_xs):
def test_operator_fiss_q():
"""Make sure fission q values can be set"""
new_q = {"U235": 2.0E8, "U238": 2.0E8, "U234": 5.0E7}
- chain_file = Path(__file__).parents[1] / "chain_simple.xml"
- operator = BareDepleteOperator(chain_file=chain_file, fission_q=new_q)
+ operator = BareDepleteOperator(chain_file=CHAIN_PATH, fission_q=new_q)
mod_chain = operator.chain
for name, q in new_q.items():
chain_nuc = mod_chain[name]