mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-27 21:55:41 -04:00
make changes from @paulromano's 3rd review
- syntax fixes and adjustments - change name of FluxDepletionOperator to IndependentOperator - flux_operator.py -> independent_operator.py - new class, MicroXS, for creating (for now) one-group microscopic cross section DataFrames. This class takes the functionality that was previously in static functions in IndependentOperator - Associated changes to the test suite and online docs
This commit is contained in:
parent
4818a296d4
commit
3e827530ba
15 changed files with 437 additions and 319 deletions
|
|
@ -49,9 +49,9 @@ specific to OpenMC are available using the following classes:
|
|||
:template: mycallable.rst
|
||||
|
||||
Operator
|
||||
FluxDepletionOperator
|
||||
IndependentOperator
|
||||
|
||||
The :class:`Operator` and :class:`FluxDepletionOperator` classes must also have
|
||||
The :class:`Operator` and :class:`IndependentOperator` classes must also have
|
||||
some knowledge of how nuclides transmute and decay. This is handled by the
|
||||
:class:`Chain`.
|
||||
|
||||
|
|
@ -134,6 +134,7 @@ data, such as number densities and reaction rates for each material.
|
|||
:template: myclass.rst
|
||||
|
||||
AtomNumber
|
||||
MicroXS
|
||||
OperatorResult
|
||||
ReactionRates
|
||||
Results
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ Energy Deposition
|
|||
-----------------
|
||||
|
||||
The default energy deposition mode, ``"fission-q"``, instructs the
|
||||
:class:`openmc.deplete.Operator` to normalize reaction rates using the product
|
||||
:class:`~openmc.deplete.Operator` to normalize reaction rates using the product
|
||||
of fission reaction rates and fission Q values taken from the depletion chain.
|
||||
This approach does not consider indirect contributions to energy deposition,
|
||||
such as neutron heating and energy from secondary photons. In doing this, the
|
||||
|
|
@ -113,7 +113,7 @@ should be, including indirect components. Some examples are provided below::
|
|||
fission_q = {"U235": 202e+6} # energy in eV
|
||||
|
||||
# create a Model object
|
||||
model = openmc.Model(geometry, settings)
|
||||
model = openmc.Model(geometry, settings)
|
||||
|
||||
# create a modified chain and write it to a new file
|
||||
chain = openmc.deplete.Chain.from_xml("chain.xml", fission_q)
|
||||
|
|
@ -133,7 +133,7 @@ to normalize reaction rates instead of using the fission reaction rates with::
|
|||
normalization_mode="energy-deposition")
|
||||
|
||||
These modified heating libraries can be generated by running the latest version
|
||||
of :meth:`openmc.data.IncidentNeutron.from_njoy`, and will eventually be bundled
|
||||
of :meth:`openmc.data.IncidentNeutron.from_njoy()`, and will eventually be bundled
|
||||
into the distributed libraries.
|
||||
|
||||
Local Spectra and Repeated Materials
|
||||
|
|
@ -188,31 +188,57 @@ Transport-independent depletion
|
|||
possible and likely in the near future.
|
||||
|
||||
OpenMC supports running depletion calculations independent of the OpenMC
|
||||
transport solver using the :class:`~openmc.deplete.FluxDepletionOperator` class.
|
||||
transport solver using the :class:`~openmc.deplete.IndependentOperator` class.
|
||||
This class supports both constant-flux (``source-rate`` normalization) and
|
||||
constant-power depletion (``fission-q`` normalization).
|
||||
|
||||
.. important::
|
||||
|
||||
Make sure you set the correct parameter in the :class:`openmc.abc.Integrator` class. Use the ``source_rates`` parameter when ``normalization_mode == source-rate``, and use ``power`` or ``power_density`` when ``normalization_mode == fission-q``.
|
||||
Make sure you set the correct parameter in the :class:`openmc.abc.Integrator`
|
||||
class. Use the ``source_rates`` parameter when
|
||||
``normalization_mode == source-rate``, and use ``power`` or ``power_density``
|
||||
when ``normalization_mode == fission-q``.
|
||||
|
||||
.. warning::
|
||||
|
||||
The accuracy of results when using ``fission-q`` is entirely dependent on your depletion chain. Make sure it has sufficient data to resolve the dynamics of your particular scenario.
|
||||
The accuracy of results when using ``fission-q`` is entirely dependent on
|
||||
your depletion chain. Make sure it has sufficient data to resolve the
|
||||
dynamics of your particular scenario.
|
||||
|
||||
This class has two ways to initialize it: the default constructor accepts an
|
||||
:class:`openmc.Materials` object and one-group microscopic
|
||||
cross sections as a :class:`pandas.DataFrame`, while the ``from_nuclides``
|
||||
method accepts a volume and dictionary of nuclide concentrations in place of
|
||||
the :class:`openmc.Materials` object in addition to the other parameters.
|
||||
The class includes helper functions to construct the dataframe from a csv file
|
||||
or from data arrays::
|
||||
:class:`~openmc.deplete.IndependentOperator` class uses one-group microscopic cross sections to calculate reaction
|
||||
rates. Users can generate one-group microscopic cross sections using the
|
||||
:class:`~openmc.deplete.MicroXS` class::
|
||||
|
||||
import openmc
|
||||
from openmc.deplete import MicroXS
|
||||
|
||||
model = openmc.Model.from_xml()
|
||||
|
||||
micro_xs = MicroXS.from_model(model, model.materials[0])
|
||||
|
||||
micro_xs.to_csv(micro_xs_path)
|
||||
|
||||
:class:`~openmc.deplete.MicroXS` also includes functions to read in cross
|
||||
section data directly from a ``.csv`` file or from data arrays::
|
||||
|
||||
micro_xs = MicroXS.from_csv(micro_xs_path)
|
||||
|
||||
nuclides = ['U234', 'U235', 'U238']
|
||||
reactions = ['fission', '(n,gamma)']
|
||||
data = np.array([[0.1, 0.2],
|
||||
[0.3, 0.4],
|
||||
[0.01, 0.5]])
|
||||
micro_xs = MicroXS.from_array(nuclides, reactions, data)
|
||||
|
||||
:class:`~openmc.deplete.IndependentOperator` has two ways to initialize it:
|
||||
the default constructor accepts an :class:`openmc.Materials` object and
|
||||
one-group microscopic cross sections as a :class:`~openmc.deplete.MicroXS`
|
||||
object, while the :meth:`~openmc.deplete.IndependentOperator.from_nuclides`
|
||||
method accepts a volume and dictionary of nuclide concentrations in place of the
|
||||
:class:`openmc.Materials` object in addition to the other parameters::
|
||||
|
||||
...
|
||||
# load in the microscopic cross sections
|
||||
micro_xs = FluxDepletionOperator.create_micro_xs_from_csv(micro_xs_path)
|
||||
flux = 1.16e15
|
||||
op = FluxDepletionOperator(materials, micro_xs, chain_file)
|
||||
op = IndependentOperator(materials, micro_xs, chain_file)
|
||||
|
||||
# alternate construtor
|
||||
nuclides = {'U234': 8.92e18,
|
||||
|
|
@ -222,8 +248,8 @@ or from data arrays::
|
|||
'O16': 4.64e22,
|
||||
'O17': 1.76e19}
|
||||
volume = 0.5
|
||||
op = FluxDepletionOperator.from_nuclides(volume, nuclides, 'atom/cm3',
|
||||
micro_xs, flux, chain_file)
|
||||
op = IndependentOperator.from_nuclides(volume, nuclides, micro_xs,
|
||||
chain_file, nuc_units='atom/cm3')
|
||||
|
||||
A user can then define an integrator class as they would for a coupled
|
||||
transport-depletion calculation and follow the same steps from there.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ from .nuclide import *
|
|||
from .chain import *
|
||||
from .openmc_operator import *
|
||||
from .operator import *
|
||||
from .flux_operator import *
|
||||
from .independent_operator import *
|
||||
from .microxs import *
|
||||
from .reaction_rates import *
|
||||
from .atom_number import *
|
||||
from .stepresult import *
|
||||
|
|
|
|||
|
|
@ -526,7 +526,7 @@ class Integrator(ABC):
|
|||
initial heavy metal inventory to get total power if ``power``
|
||||
is not specified.
|
||||
source_rates : float or iterable of float, optional
|
||||
Source rate in [neutron/sec] or neutron flux in [neut/s-cm^2] for each
|
||||
Source rate in [neutron/sec] or neutron flux in [neut/cm^2-s] for each
|
||||
interval in :attr:`timesteps`
|
||||
|
||||
.. versionadded:: 0.12.1
|
||||
|
|
@ -864,7 +864,7 @@ class SIIntegrator(Integrator):
|
|||
initial heavy metal inventory to get total power if ``power``
|
||||
is not specified.
|
||||
source_rates : float or iterable of float, optional
|
||||
Source rate in [neutron/sec] or neutron flux in [neut/s-cm^2] for each
|
||||
Source rate in [neutron/sec] or neutron flux in [neutron/s-cm^2] for each
|
||||
interval in :attr:`timesteps`
|
||||
|
||||
.. versionadded:: 0.12.1
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"""Pure depletion operator
|
||||
"""Independent depletion operator
|
||||
|
||||
This module implements a pure depletion operator that user-provided one-group
|
||||
cross sections.
|
||||
This module implements a depletion operator that runs independently of any
|
||||
transport solver by using user-provided one-group cross sections.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -11,110 +11,33 @@ from warnings import warn
|
|||
from itertools import product
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from uncertainties import ufloat
|
||||
|
||||
import openmc
|
||||
from openmc.checkvalue import check_type, check_value, check_iterable_type
|
||||
from openmc.checkvalue import check_type
|
||||
from openmc.mpi import comm
|
||||
from openmc.mgxs import EnergyGroups, ArbitraryXS, FissionXS
|
||||
from .abc import ReactionRateHelper, OperatorResult
|
||||
from .chain import REACTIONS
|
||||
from .openmc_operator import OpenMCOperator, _distribute
|
||||
from .microxs import MicroXS
|
||||
from .results import Results
|
||||
from .helpers import ChainFissionHelper, ConstantFissionYieldHelper, SourceRateHelper
|
||||
|
||||
_valid_rxns = list(REACTIONS)
|
||||
_valid_rxns.append('fission')
|
||||
|
||||
def generate_1g_cross_sections(model,
|
||||
reaction_domain,
|
||||
reactions=['(n,gamma)',
|
||||
'(n,2n)',
|
||||
'(n,p)',
|
||||
'(n,a)',
|
||||
'(n,3n)',
|
||||
'(n,4n)',
|
||||
'fission'],
|
||||
energy_bounds=(0, 20e6),
|
||||
write_to_csv=False,
|
||||
filename='micro_xs.csv'):
|
||||
"""Helper function to generate a one-group cross-section dataframe using
|
||||
OpenMC. Note that the ``openmc`` executable must be compiled.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
model : openmc.model.Model
|
||||
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.
|
||||
reactions : list of str, optional
|
||||
Reaction names to tally
|
||||
energy_bound : 2-tuple of float, optional
|
||||
Bounds for the energy group.
|
||||
write_to_csv : bool, optional
|
||||
Option to write the DataFrame to a `.csv` file.
|
||||
filename : str
|
||||
Name for csv file. Only applicable if ``write_to_csv == True``
|
||||
|
||||
Returns
|
||||
-------
|
||||
None or pandas.DataFrame
|
||||
|
||||
"""
|
||||
groups = EnergyGroups(energy_bounds)
|
||||
|
||||
# Set up the reaction tallies
|
||||
original_tallies = model.tallies
|
||||
tallies = openmc.Tallies()
|
||||
xs = {}
|
||||
for rxn in reactions:
|
||||
if rxn == 'fission':
|
||||
xs[rxn] = FissionXS(domain=reaction_domain, groups=groups, by_nuclide=True)
|
||||
else:
|
||||
xs[rxn] = ArbitraryXS(rxn, domain=reaction_domain, groups=groups, by_nuclide=True)
|
||||
tallies += xs[rxn].tallies.values()
|
||||
|
||||
model.tallies = tallies
|
||||
statepoint_path = model.run()
|
||||
|
||||
# Revert to the original tallies
|
||||
model.tallies = old_tallies
|
||||
|
||||
with openmc.StatePoint(statepoint_path) as sp:
|
||||
for rxn in xs:
|
||||
xs[rxn].load_from_statepoint(sp)
|
||||
|
||||
# Build the DataFrame
|
||||
micro_xs = pd.DataFrame()
|
||||
for rxn in xs:
|
||||
df = xs[rxn].get_pandas_dataframe(xs_type='micro')
|
||||
df.index = df['nuclide']
|
||||
df.drop(['nuclide', xs[rxn].domain_type, 'group in', 'std. dev.'], axis=1, inplace=True)
|
||||
df.rename({'mean':rxn}, axis=1, inplace=True)
|
||||
micro_xs = pd.concat([micro_xs, df], axis=1)
|
||||
|
||||
if write_to_csv:
|
||||
micro_xs.to_csv(filename)
|
||||
|
||||
return micro_xs
|
||||
|
||||
class FluxDepletionOperator(OpenMCOperator):
|
||||
"""Depletion operator that uses one-group
|
||||
cross sections to calculate reaction rates.
|
||||
class IndependentOperator(OpenMCOperator):
|
||||
"""Depletion operator that uses one-group cross sections to calculate
|
||||
reaction rates.
|
||||
|
||||
Instances of this class can be used to perform depletion using one-group
|
||||
cross sections and constant flux or constant power. Normally, a user needn't call methods of
|
||||
this class directly. Instead, an instance of this class is passed to an
|
||||
integrator class, such as :class:`openmc.deplete.CECMIntegrator`.
|
||||
cross sections and constant flux or constant power. Normally, a user needn't
|
||||
call methods of this class directly. Instead, an instance of this class is
|
||||
passed to an integrator class, such as
|
||||
:class:`openmc.deplete.CECMIntegrator`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
materials : openmc.Materials
|
||||
Materials to deplete.
|
||||
micro_xs : pandas.DataFrame
|
||||
DataFrame with nuclides names as index and microscopic cross section
|
||||
data in the columns. Cross section units are [cm^-2].
|
||||
micro_xs : MicroXS
|
||||
One-group microscopic cross sections in [b] .
|
||||
chain_file : str
|
||||
Path to the depletion chain XML file.
|
||||
keff : 2-tuple of float, optional
|
||||
|
|
@ -134,23 +57,23 @@ class FluxDepletionOperator(OpenMCOperator):
|
|||
if ``"normalization_mode" == "fission-q"``.
|
||||
reduce_chain : bool, optional
|
||||
If True, use :meth:`openmc.deplete.Chain.reduce` to reduce the
|
||||
depletion chain up to ``reduce_chain_level``. Default is False.
|
||||
depletion chain up to ``reduce_chain_level``.
|
||||
reduce_chain_level : int, optional
|
||||
Depth of the search when reducing the depletion chain. Only used
|
||||
if ``reduce_chain`` evaluates to true. The default value of
|
||||
``None`` implies no limit on the depth.
|
||||
fission_yield_opts : dict of str to option, optional
|
||||
Optional arguments to pass to the `FissionYieldHelper`. Will be
|
||||
Optional arguments to pass to the
|
||||
:class:`openmc.deplete.helpers.FissionYieldHelper` object. Will be
|
||||
passed directly on to the helper. Passing a value of None will use
|
||||
the defaults for the associated helper.
|
||||
|
||||
|
||||
Attributes
|
||||
----------
|
||||
materials : openmc.Materials
|
||||
All materials present in the model
|
||||
cross_sections : pandas.DataFrame
|
||||
Object containing one-group cross-sections.
|
||||
cross_sections : MicroXS
|
||||
Object containing one-group cross-sections in [cm^2].
|
||||
dilute_initial : float
|
||||
Initial atom density [atoms/cm^3] to add for nuclides that
|
||||
are zero in initial condition to ensure they exist in the decay
|
||||
|
|
@ -193,7 +116,7 @@ class FluxDepletionOperator(OpenMCOperator):
|
|||
fission_yield_opts=None):
|
||||
# Validate micro-xs parameters
|
||||
check_type('materials', materials, openmc.Materials)
|
||||
check_type('micro_xs', micro_xs, pd.DataFrame)
|
||||
check_type('micro_xs', micro_xs, MicroXS)
|
||||
if keff is not None:
|
||||
check_type('keff', keff, tuple, float)
|
||||
keff = ufloat(*keff)
|
||||
|
|
@ -205,9 +128,11 @@ class FluxDepletionOperator(OpenMCOperator):
|
|||
helper_kwargs = {'normalization_mode': normalization_mode,
|
||||
'fission_yield_opts': fission_yield_opts}
|
||||
|
||||
cross_sections = micro_xs * 1e-24
|
||||
cross_sections._units = 'cm^2'
|
||||
super().__init__(
|
||||
materials,
|
||||
micro_xs,
|
||||
cross_sections,
|
||||
chain_file,
|
||||
prev_results,
|
||||
fission_q=fission_q,
|
||||
|
|
@ -216,9 +141,10 @@ class FluxDepletionOperator(OpenMCOperator):
|
|||
reduce_chain_level=reduce_chain_level)
|
||||
|
||||
@classmethod
|
||||
def from_nuclides(cls, volume, nuclides, nuc_units,
|
||||
def from_nuclides(cls, volume, nuclides,
|
||||
micro_xs,
|
||||
chain_file,
|
||||
nuc_units='atom/b-cm',
|
||||
keff=None,
|
||||
normalization_mode='source-rate',
|
||||
fission_q=None,
|
||||
|
|
@ -234,13 +160,12 @@ class FluxDepletionOperator(OpenMCOperator):
|
|||
nuclides : dict of str to float
|
||||
Dictionary with nuclide names as keys and nuclide concentrations as
|
||||
values.
|
||||
nuc_units : {'atom/cm3', 'atom/b-cm'}
|
||||
Units for nuclide concentration.
|
||||
micro_xs : pandas.DataFrame
|
||||
DataFrame with nuclides names as index and microscopic cross section
|
||||
data in the columns. Cross section units are [cm^-2].
|
||||
micro_xs : MicroXS
|
||||
One-group microscopic cross sections.
|
||||
chain_file : str
|
||||
Path to the depletion chain XML file.
|
||||
nuc_units : {'atom/cm3', 'atom/b-cm'}
|
||||
Units for nuclide concentration.
|
||||
keff : 2-tuple of float, optional
|
||||
keff eigenvalue and uncertainty from transport calculation.
|
||||
Default is None.
|
||||
|
|
@ -325,17 +250,16 @@ class FluxDepletionOperator(OpenMCOperator):
|
|||
"""Finds nuclides with cross section data"""
|
||||
return set(cross_sections.index)
|
||||
|
||||
class _FluxDepletionNormalizationHelper(ChainFissionHelper):
|
||||
"""Class for calculating one-group flux
|
||||
based on a power.
|
||||
class _IndependentNormalizationHelper(ChainFissionHelper):
|
||||
"""Class for calculating one-group flux based on a power.
|
||||
|
||||
flux = Power / X, where X = volume * sum_i(Q_i * fission_micro_xs_i * density_i)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
op : openmc.deplete.FluxDepletionOperator
|
||||
Reference to the object encapsulate _FluxDepletionNormalizationHelper.
|
||||
We pass this so we don't have to duplicate the ``number`` object.
|
||||
op : openmc.deplete.IndependentOperator
|
||||
Reference to the object encapsulating _IndependentNormalizationHelper.
|
||||
We pass this so we don't have to duplicate :attr:`IndependentOperator.number`.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -367,7 +291,7 @@ class FluxDepletionOperator(OpenMCOperator):
|
|||
|
||||
super().update(fission_rates)
|
||||
|
||||
class _FluxDepletionRateHelper(ReactionRateHelper):
|
||||
class _IndependentRateHelper(ReactionRateHelper):
|
||||
"""Class for generating one-group reaction rates with flux and
|
||||
one-group cross sections.
|
||||
|
||||
|
|
@ -378,9 +302,9 @@ class FluxDepletionOperator(OpenMCOperator):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
op : openmc.deplete.FluxDepletionOperator
|
||||
Reference to the object encapsulate _FluxDepletionRateHelper.
|
||||
We pass this so we don't have to duplicate the ``number`` object.
|
||||
op : openmc.deplete.IndependentOperator
|
||||
Reference to the object encapsulate _IndependentRateHelper.
|
||||
We pass this so we don't have to duplicate the :attr:`IndependentOperator.number` object.
|
||||
|
||||
|
||||
Attributes
|
||||
|
|
@ -441,9 +365,9 @@ class FluxDepletionOperator(OpenMCOperator):
|
|||
normalization_mode = helper_kwargs['normalization_mode']
|
||||
fission_yield_opts = helper_kwargs['fission_yield_opts']
|
||||
|
||||
self._rate_helper = self._FluxDepletionRateHelper(self)
|
||||
self._rate_helper = self._IndependentRateHelper(self)
|
||||
if normalization_mode == "fission-q":
|
||||
self._normalization_helper = self._FluxDepletionNormalizationHelper(self)
|
||||
self._normalization_helper = self._IndependentNormalizationHelper(self)
|
||||
else:
|
||||
self._normalization_helper = SourceRateHelper()
|
||||
|
||||
|
|
@ -472,7 +396,7 @@ class FluxDepletionOperator(OpenMCOperator):
|
|||
vec : list of numpy.ndarray
|
||||
Total atoms to be used in function.
|
||||
source_rate : float
|
||||
Power in [W] or flux in [neut/s-cm^2]
|
||||
Power in [W] or flux in [neutron/cm^2-s]
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -523,79 +447,3 @@ class FluxDepletionOperator(OpenMCOperator):
|
|||
|
||||
' atom/b-cm)')
|
||||
number_i[mat, nuc] = 0.0
|
||||
|
||||
@staticmethod
|
||||
def create_micro_xs_from_data_array(nuclides, reactions, data):
|
||||
"""
|
||||
Creates a ``micro_xs`` parameter from a dictionary.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nuclides : list of str
|
||||
List of nuclide symbols for that have data for at least one
|
||||
reaction.
|
||||
reactions : list of str
|
||||
List of reactions. All reactions must match those in ``chain.REACTIONS``
|
||||
data : ndarray of floats
|
||||
Array containing one-group microscopic cross section values, in
|
||||
[barn], for each nuclide and reaction.
|
||||
|
||||
Returns
|
||||
-------
|
||||
micro_xs : pandas.DataFrame
|
||||
A DataFrame object correctly formatted for use in ``FluxOperator``.
|
||||
Cross section data is in [cm^2]
|
||||
"""
|
||||
|
||||
# Validate inputs
|
||||
if data.shape != (len(nuclides), len(reactions)):
|
||||
raise ValueError(
|
||||
f'Nuclides list of length {len(nuclides)} and '
|
||||
f'reactions array of length {len(reactions)} do not '
|
||||
f'match dimensions of data array of shape {data.shape}')
|
||||
|
||||
FluxDepletionOperator._validate_micro_xs_inputs(
|
||||
nuclides, reactions, data)
|
||||
|
||||
# Convert to cm^2
|
||||
data *= 1e-24
|
||||
|
||||
return pd.DataFrame(index=nuclides, columns=reactions, data=data)
|
||||
|
||||
@staticmethod
|
||||
def create_micro_xs_from_csv(csv_file, units='barn'):
|
||||
"""
|
||||
Create the ``micro_xs`` parameter from a ``.csv`` file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
csv_file : str
|
||||
Relative path to csv-file containing microscopic cross section
|
||||
data. Cross section values should be in [barn].
|
||||
|
||||
Returns
|
||||
-------
|
||||
micro_xs : pandas.DataFrame
|
||||
A DataFrame object correctly formatted for use in ``FluxOperator``.
|
||||
Cross section data is in [cm^2]
|
||||
|
||||
"""
|
||||
micro_xs = pd.read_csv(csv_file, index_col=0)
|
||||
|
||||
FluxDepletionOperator._validate_micro_xs_inputs(list(micro_xs.index),
|
||||
list(micro_xs.columns),
|
||||
micro_xs.to_numpy())
|
||||
|
||||
micro_xs *= 1e-24
|
||||
|
||||
return micro_xs
|
||||
|
||||
# Convenience function for the micro_xs static methods
|
||||
@staticmethod
|
||||
def _validate_micro_xs_inputs(nuclides, reactions, data):
|
||||
check_iterable_type('nuclides', nuclides, str)
|
||||
check_iterable_type('reactions', reactions, str)
|
||||
check_type('data', data, np.ndarray, expected_iter_type=float)
|
||||
for reaction in reactions:
|
||||
check_value('reactions', reaction, _valid_rxns)
|
||||
|
||||
178
openmc/deplete/microxs.py
Normal file
178
openmc/deplete/microxs.py
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
"""MicroXS module
|
||||
|
||||
A pandas.DataFrame storing microscopic cross section data with
|
||||
nuclides names as row indices and reaction names as column indices.
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from os import chdir
|
||||
|
||||
from pandas import DataFrame, read_csv, concat
|
||||
from numpy import ndarray
|
||||
|
||||
from openmc.checkvalue import check_type, check_value, check_iterable_type
|
||||
from openmc.mgxs import EnergyGroups, ArbitraryXS, FissionXS
|
||||
from openmc import Tallies, StatePoint
|
||||
|
||||
from .chain import REACTIONS
|
||||
|
||||
_valid_rxns = list(REACTIONS)
|
||||
_valid_rxns.append('fission')
|
||||
|
||||
|
||||
class MicroXS(DataFrame):
|
||||
"""Stores microscopic cross section data for use in
|
||||
independent depletion.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_model(cls,
|
||||
model,
|
||||
reaction_domain,
|
||||
reactions=['(n,gamma)',
|
||||
'(n,2n)',
|
||||
'(n,p)',
|
||||
'(n,a)',
|
||||
'(n,3n)',
|
||||
'(n,4n)',
|
||||
'fission'],
|
||||
energy_bounds=(0, 20e6)):
|
||||
"""Generate a one-group cross-section dataframe using
|
||||
OpenMC. Note that the ``openmc`` executable must be compiled.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
model : openmc.Model
|
||||
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.
|
||||
reactions : list of str, optional
|
||||
Reaction names to tally
|
||||
energy_bound : 2-tuple of float, optional
|
||||
Bounds for the energy group.
|
||||
|
||||
Returns
|
||||
-------
|
||||
MicroXS, cross section data in [b]
|
||||
|
||||
"""
|
||||
groups = EnergyGroups(energy_bounds)
|
||||
|
||||
# Set up the reaction tallies
|
||||
original_tallies = model.tallies
|
||||
tallies = Tallies()
|
||||
xs = {}
|
||||
for rxn in reactions:
|
||||
if rxn == 'fission':
|
||||
xs[rxn] = FissionXS(domain=reaction_domain, groups=groups, by_nuclide=True)
|
||||
else:
|
||||
xs[rxn] = ArbitraryXS(rxn, domain=reaction_domain, groups=groups, by_nuclide=True)
|
||||
tallies += xs[rxn].tallies.values()
|
||||
|
||||
model.tallies = tallies
|
||||
|
||||
# create temporary run
|
||||
original_dir = Path.cwd()
|
||||
temp_dir = tempfile.TemporaryDirectory()
|
||||
chdir(tempfile.gettempdir())
|
||||
statepoint_path = model.run()
|
||||
chdir(original_dir)
|
||||
|
||||
with StatePoint(statepoint_path) as sp:
|
||||
for rxn in xs:
|
||||
xs[rxn].load_from_statepoint(sp)
|
||||
|
||||
# Build the DataFrame
|
||||
micro_xs = cls()
|
||||
for rxn in xs:
|
||||
df = xs[rxn].get_pandas_dataframe(xs_type='micro')
|
||||
df.index = df['nuclide']
|
||||
df.drop(['nuclide', xs[rxn].domain_type, 'group in', 'std. dev.'], axis=1, inplace=True)
|
||||
df.rename({'mean':rxn}, axis=1, inplace=True)
|
||||
micro_xs = concat([micro_xs, df], axis=1)
|
||||
|
||||
micro_xs._units = 'b'
|
||||
|
||||
# Revert to the original tallies
|
||||
model.tallies = original_tallies
|
||||
|
||||
return micro_xs
|
||||
|
||||
@classmethod
|
||||
def from_array(cls, nuclides, reactions, data, units='b'):
|
||||
"""
|
||||
Creates a ``MicroXS`` object from arrays.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nuclides : list of str
|
||||
List of nuclide symbols for that have data for at least one
|
||||
reaction.
|
||||
reactions : list of str
|
||||
List of reactions. All reactions must match those in
|
||||
:data:`openmc.deplete.chain.REACTIONS`
|
||||
data : ndarray of floats
|
||||
Array containing one-group microscopic cross section values for
|
||||
each nuclide and reaction
|
||||
units : {'b', 'cm^2'}
|
||||
Units of the cross section values in ``data``
|
||||
|
||||
Returns
|
||||
-------
|
||||
MicroXS
|
||||
"""
|
||||
|
||||
# Validate inputs
|
||||
if data.shape != (len(nuclides), len(reactions)):
|
||||
raise ValueError(
|
||||
f'Nuclides list of length {len(nuclides)} and '
|
||||
f'reactions array of length {len(reactions)} do not '
|
||||
f'match dimensions of data array of shape {data.shape}')
|
||||
|
||||
cls._validate_micro_xs_inputs(
|
||||
nuclides, reactions, data)
|
||||
micro_xs = cls(index=nuclides, columns=reactions, data=data)
|
||||
micro_xs._units = units
|
||||
|
||||
return micro_xs
|
||||
|
||||
@classmethod
|
||||
def from_csv(cls, csv_file, units='b', **kwargs):
|
||||
"""
|
||||
Load a ``MicroXS`` object from a ``.csv`` file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
csv_file : str
|
||||
Relative path to csv-file containing microscopic cross section
|
||||
data.
|
||||
units : {'b', 'cm^2'}
|
||||
Units of the cross section values in the ``.csv`` file
|
||||
**kwargs : dict
|
||||
Keyword arguments to pass to :func:`pandas.read_csv()`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
MicroXS
|
||||
|
||||
"""
|
||||
if 'float_precision' not in kwargs:
|
||||
kwargs['float_precision'] = 'round_trip'
|
||||
|
||||
micro_xs = cls(read_csv(csv_file, index_col=0, **kwargs))
|
||||
|
||||
cls._validate_micro_xs_inputs(list(micro_xs.index),
|
||||
list(micro_xs.columns),
|
||||
micro_xs.to_numpy())
|
||||
micro_xs._units = units
|
||||
|
||||
return micro_xs
|
||||
|
||||
@staticmethod
|
||||
def _validate_micro_xs_inputs(nuclides, reactions, data):
|
||||
check_iterable_type('nuclides', nuclides, str)
|
||||
check_iterable_type('reactions', reactions, str)
|
||||
check_type('data', data, ndarray, expected_iter_type=float)
|
||||
for reaction in reactions:
|
||||
check_value('reactions', reaction, _valid_rxns)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
"""OpenMC transport operator
|
||||
|
||||
This module implements functions used by both OpenMC transport operators as well as pure depletion operators.
|
||||
This module implements functions shared by both OpenMC transport operators as well as indepenedent depletion operators.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -75,8 +75,8 @@ class OpenMCOperator(TransportOperator):
|
|||
helper_kwargs : dict
|
||||
Keyword arguments for helper classes
|
||||
reduce_chain : bool, optional
|
||||
If True, use :meth:`openmc.deplete.Chain.reduce` to reduce the
|
||||
depletion chain up to ``reduce_chain_level``. Default is False.
|
||||
If True, use :meth:`openmc.deplete.Chain.reduce()` to reduce the
|
||||
depletion chain up to ``reduce_chain_level``.
|
||||
reduce_chain_level : int, optional
|
||||
Depth of the search when reducing the depletion chain. Only used
|
||||
if ``reduce_chain`` evaluates to true. The default value of
|
||||
|
|
@ -87,7 +87,7 @@ class OpenMCOperator(TransportOperator):
|
|||
----------
|
||||
materials : openmc.Materials
|
||||
All materials present in the model
|
||||
cross_sections : str or pandas.DataFrame
|
||||
cross_sections : str or MicroXS
|
||||
Path to continuous energy cross section library, or object
|
||||
containing one-group cross-sections.
|
||||
dilute_initial : float
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ class Operator(OpenMCOperator):
|
|||
.. versionadded:: 0.12.1
|
||||
reduce_chain : bool, optional
|
||||
If True, use :meth:`openmc.deplete.Chain.reduce` to reduce the
|
||||
depletion chain up to ``reduce_chain_level``. Default is False.
|
||||
depletion chain up to ``reduce_chain_level``.
|
||||
|
||||
.. versionadded:: 0.12
|
||||
reduce_chain_level : int, optional
|
||||
|
|
@ -226,7 +226,7 @@ class Operator(OpenMCOperator):
|
|||
self.cleanup_when_done = True
|
||||
|
||||
if reaction_rate_opts is None:
|
||||
reaction_rate_opts = {}
|
||||
reaction_rate_opts = {}
|
||||
if fission_yield_opts is None:
|
||||
fission_yield_opts = {}
|
||||
helper_kwargs = {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,12 @@
|
|||
""" Transport-free depletion test suite """
|
||||
|
||||
from math import floor
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from difflib import unified_diff
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
import openmc
|
||||
from openmc.data import JOULE_PER_EV
|
||||
import openmc.deplete
|
||||
from openmc.deplete import FluxDepletionOperator
|
||||
|
||||
from tests.regression_tests import config
|
||||
|
||||
from openmc.deplete import IndependentOperator, MicroXS
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def fuel():
|
||||
|
|
@ -27,7 +20,6 @@ def fuel():
|
|||
|
||||
return fuel
|
||||
|
||||
|
||||
@pytest.mark.parametrize("multiproc, from_nuclides, normalization_mode, power, flux", [
|
||||
(True, True,'source-rate', None, 1164719970082145.0),
|
||||
(False, True, 'source-rate', None, 1164719970082145.0),
|
||||
|
|
@ -46,7 +38,7 @@ def test_no_transport_from_nuclides(run_in_tmpdir, fuel, multiproc, from_nuclide
|
|||
"""
|
||||
# Create operator
|
||||
micro_xs_file = Path(__file__).parents[2] / 'micro_xs_simple.csv'
|
||||
micro_xs = FluxDepletionOperator.create_micro_xs_from_csv(micro_xs_file)
|
||||
micro_xs = MicroXS.from_csv(micro_xs_file)
|
||||
chain_file = Path(__file__).parents[2] / 'chain_simple.xml'
|
||||
|
||||
if from_nuclides:
|
||||
|
|
@ -54,11 +46,11 @@ def test_no_transport_from_nuclides(run_in_tmpdir, fuel, multiproc, from_nuclide
|
|||
for nuc, dens in fuel.get_nuclide_atom_densities().items():
|
||||
nuclides[nuc] = dens
|
||||
|
||||
op = FluxDepletionOperator.from_nuclides(
|
||||
fuel.volume, nuclides,'atom/b-cm', micro_xs, chain_file, normalization_mode=normalization_mode)
|
||||
op = IndependentOperator.from_nuclides(
|
||||
fuel.volume, nuclides, micro_xs, chain_file, normalization_mode=normalization_mode)
|
||||
|
||||
else:
|
||||
op = FluxDepletionOperator(openmc.Materials([fuel]), micro_xs, chain_file, normalization_mode=normalization_mode)
|
||||
op = IndependentOperator(openmc.Materials([fuel]), micro_xs, chain_file, normalization_mode=normalization_mode)
|
||||
|
||||
# Power and timesteps
|
||||
dt = [30] # single step
|
||||
|
|
|
|||
0
tests/regression_tests/microxs/__init.py__
Normal file
0
tests/regression_tests/microxs/__init.py__
Normal file
52
tests/regression_tests/microxs/test.py
Normal file
52
tests/regression_tests/microxs/test.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
"""Test one-group cross section generation"""
|
||||
import numpy as np
|
||||
import pytest
|
||||
import openmc
|
||||
|
||||
from openmc.deplete import MicroXS
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def model():
|
||||
fuel = openmc.Material(name="uo2")
|
||||
fuel.add_element("U", 1, percent_type="ao", enrichment=4.25)
|
||||
fuel.add_element("O", 2)
|
||||
fuel.set_density("g/cc", 10.4)
|
||||
|
||||
clad = openmc.Material(name="clad")
|
||||
clad.add_element("Zr", 1)
|
||||
clad.set_density("g/cc", 6)
|
||||
|
||||
water = openmc.Material(name="water")
|
||||
water.add_element("O", 1)
|
||||
water.add_element("H", 2)
|
||||
water.set_density("g/cc", 1.0)
|
||||
water.add_s_alpha_beta("c_H_in_H2O")
|
||||
|
||||
radii = [0.42, 0.45]
|
||||
fuel.volume = np.pi * radii[0] ** 2
|
||||
clad.volume = np.pi * (radii[1]**2 - radii[0]**2)
|
||||
water.volume = 1.24**2 - (np.pi * radii[1]**2)
|
||||
|
||||
materials = openmc.Materials([fuel, clad, water])
|
||||
|
||||
pin_surfaces = [openmc.ZCylinder(r=r) for r in radii]
|
||||
pin_univ = openmc.model.pin(pin_surfaces, materials)
|
||||
bound_box = openmc.rectangular_prism(1.24, 1.24, boundary_type="reflective")
|
||||
root_cell = openmc.Cell(fill=pin_univ, region=bound_box)
|
||||
geometry = openmc.Geometry([root_cell])
|
||||
|
||||
settings = openmc.Settings()
|
||||
settings.particles = 1000
|
||||
settings.inactive = 10
|
||||
settings.batches = 50
|
||||
|
||||
return openmc.Model(geometry, materials, settings)
|
||||
|
||||
|
||||
def test_from_model(model):
|
||||
ref_xs = MicroXS.from_csv('test_reference.csv')
|
||||
test_xs = MicroXS.from_model(model, model.materials[0])
|
||||
|
||||
assert ref_xs._units == test_xs._units
|
||||
np.testing.assert_allclose(test_xs, ref_xs, rtol=1e-11)
|
||||
|
||||
7
tests/regression_tests/microxs/test_reference.csv
Normal file
7
tests/regression_tests/microxs/test_reference.csv
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
nuclide,"(n,gamma)","(n,2n)","(n,p)","(n,a)","(n,3n)","(n,4n)",fission
|
||||
U234,23.518634203050674,0.0008255330840536984,0.0,0.0,9.404554397521455e-07,0.0,0.49531930067650964
|
||||
U235,10.621118186344795,0.004359401013759254,0.0,0.0,7.2974901306692395e-06,0.0,49.10955932965902
|
||||
U238,0.8652742788116055,0.005661917442209096,0.0,0.0,4.92273921631416e-05,0.0,0.10579281644765708
|
||||
U236,9.095623870006163,0.0024373322002926834,0.0,0.0,1.966889146690413e-05,0.0,0.3231539233923791
|
||||
O16,7.511380881289377e-05,0.0,1.3764104470470622e-05,0.002862620940027927,0.0,0.0,0.0
|
||||
O17,0.00041221042693945085,1.9826700699084533e-05,7.764409141772239e-06,0.05938517754333328,0.0,0.0,0.0
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
"""Basic unit tests for openmc.deplete.FluxDepletionOperator 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.flux_operator import FluxDepletionOperator
|
||||
from openmc import Material, Materials
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml"
|
||||
ONE_GROUP_XS = Path(__file__).parents[1] / "micro_xs_simple.csv"
|
||||
|
||||
|
||||
def test_create_micro_xs_from_data_array():
|
||||
nuclides = [
|
||||
'U234',
|
||||
'U235',
|
||||
'U238',
|
||||
'U236',
|
||||
'O16',
|
||||
'O17',
|
||||
'I135',
|
||||
'Xe135',
|
||||
'Xe136',
|
||||
'Cs135',
|
||||
'Gd157',
|
||||
'Gd156']
|
||||
reactions = ['fission', '(n,gamma)']
|
||||
# These values are placeholders and are not at all
|
||||
# physically meaningful.
|
||||
data = np.array([[0.1, 0.],
|
||||
[0.1, 0.],
|
||||
[0.9, 0.],
|
||||
[0.4, 0.],
|
||||
[0., 0.],
|
||||
[0., 0.],
|
||||
[0., 0.1],
|
||||
[0., 0.9],
|
||||
[0., 0.],
|
||||
[0., 0.],
|
||||
[0., 0.1],
|
||||
[0., 0.1]])
|
||||
|
||||
FluxDepletionOperator.create_micro_xs_from_data_array(
|
||||
nuclides, reactions, data)
|
||||
with pytest.raises(ValueError, match=r'Nuclides list of length \d* and '
|
||||
r'reactions array of length \d* do not '
|
||||
r'match dimensions of data array of shape \(\d*\,d*\)'):
|
||||
FluxDepletionOperator.create_micro_xs_from_data_array(
|
||||
nuclides, reactions, data[:, 0])
|
||||
|
||||
|
||||
def test_create_micro_xs_from_csv():
|
||||
FluxDepletionOperator.create_micro_xs_from_csv(ONE_GROUP_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."""
|
||||
volume = 1
|
||||
nuclides = {'U234': 8.922411359424315e+18,
|
||||
'U235': 9.98240191860822e+20,
|
||||
'U238': 2.2192386373095893e+22,
|
||||
'U236': 4.5724195495061115e+18,
|
||||
'O16': 4.639065406771322e+22,
|
||||
'O17': 1.7588724018066158e+19}
|
||||
micro_xs = FluxDepletionOperator.create_micro_xs_from_csv(ONE_GROUP_XS)
|
||||
nuclide_flux_operator = FluxDepletionOperator.from_nuclides(
|
||||
volume, nuclides, 'atom/cm3', micro_xs, CHAIN_PATH)
|
||||
|
||||
fuel = Material(name="uo2")
|
||||
fuel.add_element("U", 1, percent_type="ao", enrichment=4.25)
|
||||
fuel.add_element("O", 2)
|
||||
fuel.set_density("g/cc", 10.4)
|
||||
fuel.depletable=True
|
||||
fuel.volume = 1
|
||||
materials = Materials([fuel])
|
||||
nuclide_flux_operator = FluxDepletionOperator(materials, micro_xs, CHAIN_PATH)
|
||||
36
tests/unit_tests/test_deplete_independent_operator.py
Normal file
36
tests/unit_tests/test_deplete_independent_operator.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
"""Basic unit tests for openmc.deplete.IndependentOperator instantiation
|
||||
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from openmc.deplete import IndependentOperator, MicroXS
|
||||
from openmc import Material, Materials
|
||||
import numpy as np
|
||||
|
||||
CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml"
|
||||
ONE_GROUP_XS = Path(__file__).parents[1] / "micro_xs_simple.csv"
|
||||
|
||||
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."""
|
||||
volume = 1
|
||||
nuclides = {'U234': 8.922411359424315e+18,
|
||||
'U235': 9.98240191860822e+20,
|
||||
'U238': 2.2192386373095893e+22,
|
||||
'U236': 4.5724195495061115e+18,
|
||||
'O16': 4.639065406771322e+22,
|
||||
'O17': 1.7588724018066158e+19}
|
||||
micro_xs = MicroXS.from_csv(ONE_GROUP_XS)
|
||||
IndependentOperator.from_nuclides(
|
||||
volume, nuclides, micro_xs, CHAIN_PATH, nuc_units='atom/cm3')
|
||||
|
||||
fuel = Material(name="uo2")
|
||||
fuel.add_element("U", 1, percent_type="ao", enrichment=4.25)
|
||||
fuel.add_element("O", 2)
|
||||
fuel.set_density("g/cc", 10.4)
|
||||
fuel.depletable=True
|
||||
fuel.volume = 1
|
||||
materials = Materials([fuel])
|
||||
IndependentOperator(materials, micro_xs, CHAIN_PATH)
|
||||
60
tests/unit_tests/test_deplete_microxs.py
Normal file
60
tests/unit_tests/test_deplete_microxs.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
"""Basic unit tests for openmc.deplete.IndependentOperator instantiation
|
||||
|
||||
Modifies and resets environment variable OPENMC_CROSS_SECTIONS
|
||||
to a custom file with new depletion_chain node
|
||||
"""
|
||||
|
||||
from os import remove
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from openmc.deplete import MicroXS
|
||||
import numpy as np
|
||||
|
||||
ONE_GROUP_XS = Path(__file__).parents[1] / "micro_xs_simple.csv"
|
||||
|
||||
|
||||
def test_from_array():
|
||||
nuclides = [
|
||||
'U234',
|
||||
'U235',
|
||||
'U238',
|
||||
'U236',
|
||||
'O16',
|
||||
'O17',
|
||||
'I135',
|
||||
'Xe135',
|
||||
'Xe136',
|
||||
'Cs135',
|
||||
'Gd157',
|
||||
'Gd156']
|
||||
reactions = ['fission', '(n,gamma)']
|
||||
# These values are placeholders and are not at all
|
||||
# physically meaningful.
|
||||
data = np.array([[0.1, 0.],
|
||||
[0.1, 0.],
|
||||
[0.9, 0.],
|
||||
[0.4, 0.],
|
||||
[0., 0.],
|
||||
[0., 0.],
|
||||
[0., 0.1],
|
||||
[0., 0.9],
|
||||
[0., 0.],
|
||||
[0., 0.],
|
||||
[0., 0.1],
|
||||
[0., 0.1]])
|
||||
|
||||
MicroXS.from_array(nuclides, reactions, data)
|
||||
with pytest.raises(ValueError, match=r'Nuclides list of length \d* and '
|
||||
r'reactions array of length \d* do not '
|
||||
r'match dimensions of data array of shape \(\d*\,d*\)'):
|
||||
MicroXS.from_array(nuclides, reactions, data[:, 0])
|
||||
|
||||
def test_csv():
|
||||
ref_xs = MicroXS.from_csv(ONE_GROUP_XS)
|
||||
ref_xs.to_csv('temp_xs.csv')
|
||||
temp_xs = MicroXS.from_csv('temp_xs.csv')
|
||||
assert ref_xs._units == temp_xs._units
|
||||
assert np.all(ref_xs == temp_xs)
|
||||
remove('temp_xs.csv')
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue