Merge pull request #2134 from paulromano/thermal-mixed-elastic

Add support for mixed incoherent/coherent elastic thermal scattering
This commit is contained in:
Amelia J Trainer 2022-07-28 15:43:55 -04:00 committed by GitHub
commit a18abd4fd9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 476 additions and 48 deletions

View file

@ -339,6 +339,16 @@ Incoherent elastic scattering
[eV\ :math:`^{-1}`].
:Attributes: - **type** (*char[]*) -- 'IncoherentElastic'
Sum of functions
----------------
:Object type: Group
:Attributes: - **type** (*char[]*) -- "Sum"
- **n** (*int*) -- Number of functions
:Datasets:
- ***func_<i>** (:ref:`function <1d_functions>`) -- Dataset for the
i-th function (indexing starts at 1)
.. _angle_energy:
--------------------------
@ -501,6 +511,19 @@ equiprobable bins.
- **skewed** (*int8_t*) -- Whether discrete angles are equi-probable
(0) or have a skewed distribution (1).
Mixed Elastic
-------------
This angle-energy distribution is used when an evaluation specifies both
coherent and incoherent elastic thermal neutron scattering.
:Object type: Group
:Attributes: - **type** (*char[]*) -- "mixed_elastic"
:Groups: - **coherent** -- Distribution for coherent elastic scattering. The
format is given in :ref:`angle_energy`.
- **incoherent** -- Distribution for incoherent elastic scattering.
The format is given in :ref:`angle_energy`.
.. _energy_distribution:
--------------------

View file

@ -116,6 +116,7 @@ Angle-Energy Distributions
IncoherentElasticAE
IncoherentElasticAEDiscrete
IncoherentInelasticAEDiscrete
MixedElasticAE
Resonance Data
--------------

View file

@ -126,6 +126,26 @@ private:
debye_waller_; //!< Debye-Waller integral divided by atomic mass in [eV^-1]
};
//==============================================================================
//! Sum of multiple 1D functions
//==============================================================================
class Sum1D : public Function1D {
public:
// Constructors
explicit Sum1D(hid_t group);
//! Evaluate each function and sum results
//! \param[in] x independent variable
//! \return Function evaluated at x
double operator()(double E) const override;
const unique_ptr<Function1D>& functions(int i) const { return functions_[i]; }
private:
vector<unique_ptr<Function1D>> functions_; //!< individual functions
};
//! Read 1D function from HDF5 dataset
//! \param[in] group HDF5 group containing dataset
//! \param[in] name Name of dataset

View file

@ -61,6 +61,8 @@ void ensure_exists(hid_t obj_id, const char* name, bool attribute = false);
vector<std::string> group_names(hid_t group_id);
vector<hsize_t> object_shape(hid_t obj_id);
std::string object_name(hid_t obj_id);
hid_t open_object(hid_t group_id, const std::string& name);
void close_object(hid_t obj_id);
//==============================================================================
// Fortran compatibility functions

View file

@ -150,6 +150,34 @@ private:
//!< each incident energy
};
//==============================================================================
//! Mixed coherent/incoherent elastic angle-energy distribution
//==============================================================================
class MixedElasticAE : public AngleEnergy {
public:
//! Construct from HDF5 file
//
//! \param[in] group HDF5 group
explicit MixedElasticAE(
hid_t group, const CoherentElasticXS& coh_xs, const Function1D& incoh_xs);
//! Sample distribution for an angle and energy
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
//! \param[inout] seed Pseudorandom number seed pointer
void sample(
double E_in, double& E_out, double& mu, uint64_t* seed) const override;
private:
CoherentElasticAE coherent_dist_; //!< Coherent distribution
unique_ptr<AngleEnergy> incoherent_dist_; //!< Incoherent distribution
const CoherentElasticXS& coherent_xs_; //!< Ref. to coherent XS
const Function1D& incoherent_xs_; //!< Polymorphic ref. to incoherent XS
};
} // namespace openmc
#endif // OPENMC_SECONDARY_THERMAL_H

View file

@ -44,6 +44,8 @@ class AngleEnergy(EqualityMixin, ABC):
return openmc.data.IncoherentInelasticAEDiscrete.from_hdf5(group)
elif dist_type == 'incoherent_inelastic':
return openmc.data.IncoherentInelasticAE.from_hdf5(group)
elif dist_type == 'mixed_elastic':
return openmc.data.MixedElasticAE.from_hdf5(group)
@staticmethod
def from_ace(ace, location_dist, location_start, rx=None):

View file

@ -544,7 +544,7 @@ class Combination(EqualityMixin):
self._operations = operations
class Sum(EqualityMixin):
class Sum(Function1D):
"""Sum of multiple functions.
This class allows you to create a callable object which represents the sum
@ -578,6 +578,49 @@ class Sum(EqualityMixin):
cv.check_type('functions', functions, Iterable, Callable)
self._functions = functions
def to_hdf5(self, group, name='xy'):
"""Write sum of functions to an HDF5 group
.. versionadded:: 0.13.1
Parameters
----------
group : h5py.Group
HDF5 group to write to
name : str
Name of the dataset to create
"""
sum_group = group.create_group(name)
sum_group.attrs['type'] = np.string_(type(self).__name__)
sum_group.attrs['n'] = len(self.functions)
for i, f in enumerate(self.functions):
f.to_hdf5(sum_group, f'func_{i+1}')
@classmethod
def from_hdf5(cls, group):
"""Generate sum of functions from an HDF5 group
.. versionadded:: 0.13.1
Parameters
----------
group : h5py.Group
Group to read from
Returns
-------
openmc.data.Sum
Functions read from the group
"""
n = group.attrs['n']
functions = [
Function1D.from_hdf5(group[f'func_{i+1}'])
for i in range(n)
]
return cls(functions)
class Regions1D(EqualityMixin):
r"""Piecewise composition of multiple functions.

View file

@ -19,12 +19,12 @@ from . import HDF5_VERSION, HDF5_VERSION_MAJOR, endf
from .data import K_BOLTZMANN, ATOMIC_SYMBOL, EV_PER_MEV, isotopes
from .ace import Table, get_table, Library
from .angle_energy import AngleEnergy
from .function import Tabulated1D, Function1D
from .function import Tabulated1D, Function1D, Sum
from .njoy import make_ace_thermal
from .thermal_angle_energy import (CoherentElasticAE, IncoherentElasticAE,
IncoherentElasticAEDiscrete,
IncoherentInelasticAEDiscrete,
IncoherentInelasticAE)
IncoherentInelasticAE, MixedElasticAE)
_THERMAL_NAMES = {
@ -694,29 +694,53 @@ class ThermalScattering(EqualityMixin):
# Incoherent/coherent elastic scattering cross section
idx = ace.jxs[4]
n_mu = ace.nxs[6] + 1
if idx != 0:
n_energy = int(ace.xss[idx])
energy = ace.xss[idx + 1: idx + 1 + n_energy]*EV_PER_MEV
P = ace.xss[idx + 1 + n_energy: idx + 1 + 2 * n_energy]
if ace.nxs[5] == 4:
if ace.nxs[5] in (4, 5):
# Coherent elastic
xs = CoherentElastic(energy, P*EV_PER_MEV)
distribution = CoherentElasticAE(xs)
n_energy = int(ace.xss[idx])
energy = ace.xss[idx + 1: idx + 1 + n_energy]*EV_PER_MEV
P = ace.xss[idx + 1 + n_energy: idx + 1 + 2 * n_energy]
coherent_xs = CoherentElastic(energy, P*EV_PER_MEV)
coherent_dist = CoherentElasticAE(coherent_xs)
# Coherent elastic shouldn't have angular distributions listed
n_mu = ace.nxs[6] + 1
assert n_mu == 0
else:
# Incoherent elastic
xs = Tabulated1D(energy, P)
if ace.nxs[5] in (3, 5):
# Incoherent elastic scattering -- first determine if both
# incoherent and coherent are present (mixed)
mixed = (ace.nxs[5] == 5)
# Get cross section values
idx = ace.jxs[7] if mixed else ace.jxs[4]
n_energy = int(ace.xss[idx])
energy = ace.xss[idx + 1: idx + 1 + n_energy]*EV_PER_MEV
values = ace.xss[idx + 1 + n_energy: idx + 1 + 2 * n_energy]
incoherent_xs = Tabulated1D(energy, values)
# Angular distribution
n_mu = (ace.nxs[8] if mixed else ace.nxs[6]) + 1
assert n_mu > 0
idx = ace.jxs[6]
idx = ace.jxs[9] if mixed else ace.jxs[6]
mu_out = ace.xss[idx:idx + n_energy * n_mu]
mu_out.shape = (n_energy, n_mu)
distribution = IncoherentElasticAEDiscrete(mu_out)
incoherent_dist = IncoherentElasticAEDiscrete(mu_out)
if ace.nxs[5] == 3:
xs = incoherent_xs
dist = incoherent_dist
elif ace.nxs[5] == 4:
xs = coherent_xs
dist = coherent_dist
else:
# Create mixed cross section -- note that coherent must come
# first due to assumption on C++ side
xs = Sum([coherent_xs, incoherent_xs])
# Create mixed distribution
distribution = MixedElasticAE(coherent_dist, incoherent_dist)
table.elastic = ThermalScatteringReaction({T: xs}, {T: distribution})
@ -802,7 +826,7 @@ class ThermalScattering(EqualityMixin):
# Replace ACE data with ENDF data
rx, rx_endf = data.elastic, data_endf.elastic
for t in temperatures:
if isinstance(rx_endf.xs[t], IncoherentElastic):
if isinstance(rx_endf.xs[t], (IncoherentElastic, Sum)):
rx.xs[t] = rx_endf.xs[t]
rx.distribution[t] = rx_endf.distribution[t]
@ -832,20 +856,14 @@ class ThermalScattering(EqualityMixin):
# Read coherent/incoherent elastic data
elastic = None
if (7, 2) in ev.section:
xs = {}
distribution = {}
file_obj = StringIO(ev.section[7, 2])
lhtr = endf.get_head_record(file_obj)[2]
if lhtr == 1:
# coherent elastic
# Define helper functions to avoid duplication
def get_coherent_elastic(file_obj):
# Get structure factor at first temperature
params, S = endf.get_tab1_record(file_obj)
strT = _temperature_str(params[0])
n_temps = params[2]
bragg_edges = S.x
xs[strT] = CoherentElastic(bragg_edges, S.y)
xs = {strT: CoherentElastic(bragg_edges, S.y)}
distribution = {strT: CoherentElasticAE(xs[strT])}
# Get structure factor for subsequent temperatures
@ -854,15 +872,34 @@ class ThermalScattering(EqualityMixin):
strT = _temperature_str(params[0])
xs[strT] = CoherentElastic(bragg_edges, S)
distribution[strT] = CoherentElasticAE(xs[strT])
return xs, distribution
elif lhtr == 2:
# incoherent elastic
def get_incoherent_elastic(file_obj):
params, W = endf.get_tab1_record(file_obj)
bound_xs = params[0]
xs = {}
distribution = {}
for T, debye_waller in zip(W.x, W.y):
strT = _temperature_str(T)
xs[strT] = IncoherentElastic(bound_xs, debye_waller)
distribution[strT] = IncoherentElasticAE(debye_waller)
return xs, distribution
file_obj = StringIO(ev.section[7, 2])
lhtr = endf.get_head_record(file_obj)[2]
if lhtr == 1:
# coherent elastic
xs, distribution = get_coherent_elastic(file_obj)
elif lhtr == 2:
# incoherent elastic
xs, distribution = get_incoherent_elastic(file_obj)
elif lhtr == 3:
# mixed coherent / incoherent elastic
xs_c, dist_c = get_coherent_elastic(file_obj)
xs_i, dist_i = get_incoherent_elastic(file_obj)
assert sorted(xs_c) == sorted(xs_i)
xs = {T: Sum([xs_c[T], xs_i[T]]) for T in xs_c}
distribution = {T: MixedElasticAE(dist_c[T], dist_i[T]) for T in dist_c}
elastic = ThermalScatteringReaction(xs, distribution)

View file

@ -2,6 +2,7 @@ import numpy as np
from .angle_energy import AngleEnergy
from .correlated import CorrelatedAngleEnergy
import openmc.data
class CoherentElasticAE(AngleEnergy):
@ -43,7 +44,27 @@ class CoherentElasticAE(AngleEnergy):
"""
group.attrs['type'] = np.string_('coherent_elastic')
group['coherent_xs'] = group.parent['xs']
self.coherent_xs.to_hdf5(group, 'coherent_xs')
@classmethod
def from_hdf5(cls, group):
"""Generate coherent elastic distribution from HDF5 data
.. versionadded:: 0.13.1
Parameters
----------
group : h5py.Group
HDF5 group to read from
Returns
-------
openmc.data.CoherentElasticAE
Coherent elastic distribution
"""
coherent_xs = openmc.data.CoherentElastic.from_hdf5(group['coherent_xs'])
return cls(coherent_xs)
class IncoherentElasticAE(AngleEnergy):
@ -101,7 +122,7 @@ class IncoherentElasticAE(AngleEnergy):
Incoherent elastic distribution
"""
return cls(group['debye_waller'])
return cls(group['debye_waller'][()])
class IncoherentElasticAEDiscrete(AngleEnergy):
@ -210,3 +231,62 @@ class IncoherentInelasticAEDiscrete(AngleEnergy):
class IncoherentInelasticAE(CorrelatedAngleEnergy):
_name = 'incoherent_inelastic'
class MixedElasticAE(AngleEnergy):
"""Secondary distribution for mixed coherent/incoherent thermal elastic
.. versionadded:: 0.13.1
Parameters
----------
coherent : AngleEnergy
Secondary distribution for coherent elastic scattering
incoherent : AngleEnergy
Secondary distribution for incoherent elastic scattering
Attributes
----------
coherent : AngleEnergy
Secondary distribution for coherent elastic scattering
incoherent : AngleEnergy
Secondary distribution for incoherent elastic scattering
"""
def __init__(self, coherent, incoherent):
self.coherent = coherent
self.incoherent = incoherent
def to_hdf5(self, group):
"""Write mixed elastic distribution to an HDF5 group
Parameters
----------
group : h5py.Group
HDF5 group to write to
"""
group.attrs['type'] = np.string_('mixed_elastic')
coherent_group = group.create_group('coherent')
self.coherent.to_hdf5(coherent_group)
incoherent_group = group.create_group('incoherent')
self.incoherent.to_hdf5(incoherent_group)
@classmethod
def from_hdf5(cls, group):
"""Generate mixed thermal elastic distribution from HDF5 data
Parameters
----------
group : h5py.Group
HDF5 group to read from
Returns
-------
openmc.data.MixedElasticAE
Mixed thermal elastic distribution
"""
coherent = AngleEnergy.from_hdf5(group['coherent'])
incoherent = AngleEnergy.from_hdf5(group['incoherent'])
return cls(coherent, incoherent)

View file

@ -14,8 +14,11 @@ class EqualityMixin:
def __eq__(self, other):
if isinstance(other, type(self)):
for key, value in self.__dict__.items():
if not np.array_equal(value, other.__dict__.get(key)):
return False
if isinstance(value, np.ndarray):
if not np.array_equal(value, other.__dict__.get(key)):
return False
else:
return value == other.__dict__.get(key)
else:
return False

View file

@ -90,23 +90,25 @@ bool is_inelastic_scatter(int mt)
unique_ptr<Function1D> read_function(hid_t group, const char* name)
{
hid_t dset = open_dataset(group, name);
hid_t obj_id = open_object(group, name);
std::string func_type;
read_attribute(dset, "type", func_type);
read_attribute(obj_id, "type", func_type);
unique_ptr<Function1D> func;
if (func_type == "Tabulated1D") {
func = make_unique<Tabulated1D>(dset);
func = make_unique<Tabulated1D>(obj_id);
} else if (func_type == "Polynomial") {
func = make_unique<Polynomial>(dset);
func = make_unique<Polynomial>(obj_id);
} else if (func_type == "CoherentElastic") {
func = make_unique<CoherentElasticXS>(dset);
func = make_unique<CoherentElasticXS>(obj_id);
} else if (func_type == "IncoherentElastic") {
func = make_unique<IncoherentElasticXS>(dset);
func = make_unique<IncoherentElasticXS>(obj_id);
} else if (func_type == "Sum") {
func = make_unique<Sum1D>(obj_id);
} else {
throw std::runtime_error {"Unknown function type " + func_type +
" for dataset " + object_name(dset)};
" for dataset " + object_name(obj_id)};
}
close_dataset(dset);
close_object(obj_id);
return func;
}
@ -271,4 +273,30 @@ double IncoherentElasticXS::operator()(double E) const
return bound_xs_ / 2.0 * ((1 - std::exp(-4.0 * E * W)) / (2.0 * E * W));
}
//==============================================================================
// Sum1D implementation
//==============================================================================
Sum1D::Sum1D(hid_t group)
{
// Get number of functions
int n;
read_attribute(group, "n", n);
// Get each function
for (int i = 0; i < n; ++i) {
auto dset_name = fmt::format("func_{}", i + 1);
functions_.push_back(read_function(group, dset_name.c_str()));
}
}
double Sum1D::operator()(double x) const
{
double result = 0.0;
for (auto& func : functions_) {
result += (*func)(x);
}
return result;
}
} // namespace openmc

View file

@ -118,6 +118,12 @@ void close_group(hid_t group_id)
fatal_error("Failed to close group");
}
void close_object(hid_t obj_id)
{
if (H5Oclose(obj_id) < 0)
fatal_error("Failed to close object");
}
int dataset_ndims(hid_t dset)
{
hid_t dspace = H5Dget_space(dset);
@ -394,6 +400,12 @@ hid_t open_group(hid_t group_id, const char* name)
return H5Gopen(group_id, name, H5P_DEFAULT);
}
hid_t open_object(hid_t group_id, const std::string& name)
{
ensure_exists(group_id, name.c_str());
return H5Oopen(group_id, name.c_str(), H5P_DEFAULT);
}
void read_attr(hid_t obj_id, const char* name, hid_t mem_type_id, void* buffer)
{
hid_t attr = H5Aopen(obj_id, name, H5P_DEFAULT);

View file

@ -332,4 +332,40 @@ void IncoherentInelasticAE::sample(
mu += std::min(mu - mu_left, mu_right - mu) * (prn(seed) - 0.5);
}
//==============================================================================
// MixedElasticAE implementation
//==============================================================================
MixedElasticAE::MixedElasticAE(
hid_t group, const CoherentElasticXS& coh_xs, const Function1D& incoh_xs)
: coherent_dist_(coh_xs), coherent_xs_(coh_xs), incoherent_xs_(incoh_xs)
{
// Read incoherent elastic distribution
hid_t incoherent_group = open_group(group, "incoherent");
std::string temp;
read_attribute(incoherent_group, "type", temp);
if (temp == "incoherent_elastic") {
incoherent_dist_ = make_unique<IncoherentElasticAE>(incoherent_group);
} else if (temp == "incoherent_elastic_discrete") {
auto xs = dynamic_cast<const Tabulated1D*>(&incoh_xs);
incoherent_dist_ =
make_unique<IncoherentElasticAEDiscrete>(incoherent_group, xs->x());
}
close_group(incoherent_group);
}
void MixedElasticAE::sample(
double E_in, double& E_out, double& mu, uint64_t* seed) const
{
// Evaluate coherent and incoherent elastic cross sections
double xs_coh = coherent_xs_(E_in);
double xs_incoh = incoherent_xs_(E_in);
if (prn(seed) * (xs_coh + xs_incoh) < xs_coh) {
coherent_dist_.sample(E_in, E_out, mu, seed);
} else {
incoherent_dist_->sample(E_in, E_out, mu, seed);
}
}
} // namespace openmc

View file

@ -210,14 +210,22 @@ ThermalData::ThermalData(hid_t group)
if (temp == "coherent_elastic") {
auto xs = dynamic_cast<CoherentElasticXS*>(elastic_.xs.get());
elastic_.distribution = make_unique<CoherentElasticAE>(*xs);
} else {
if (temp == "incoherent_elastic") {
elastic_.distribution = make_unique<IncoherentElasticAE>(dgroup);
} else if (temp == "incoherent_elastic_discrete") {
auto xs = dynamic_cast<Tabulated1D*>(elastic_.xs.get());
elastic_.distribution =
make_unique<IncoherentElasticAEDiscrete>(dgroup, xs->x());
}
} else if (temp == "incoherent_elastic") {
elastic_.distribution = make_unique<IncoherentElasticAE>(dgroup);
} else if (temp == "incoherent_elastic_discrete") {
auto xs = dynamic_cast<Tabulated1D*>(elastic_.xs.get());
elastic_.distribution =
make_unique<IncoherentElasticAEDiscrete>(dgroup, xs->x());
} else if (temp == "mixed_elastic") {
// Get coherent/incoherent cross sections
auto mixed_xs = dynamic_cast<Sum1D*>(elastic_.xs.get());
const auto& coh_xs =
dynamic_cast<const CoherentElasticXS*>(mixed_xs->functions(0).get());
const auto& incoh_xs = mixed_xs->functions(1).get();
// Create mixed elastic distribution
elastic_.distribution =
make_unique<MixedElasticAE>(dgroup, *coh_xs, *incoh_xs);
}
close_group(elastic_group);

View file

@ -262,3 +262,108 @@ def test_get_thermal_name():
# Names that don't remotely match anything
assert f('boogie_monster') == 'c_boogie_monster'
@pytest.fixture
def fake_mixed_elastic():
fake_tsl = openmc.data.ThermalScattering("c_D_in_7LiD", 1.9968, 4.9, [0.0253])
fake_tsl.nuclides = ['H2']
# Create elastic reaction
bragg_edges = [0.00370672, 0.00494229, 0.00988458, 0.01359131, 0.01482688,
0.01976918, 0.02347589, 0.02471147, 0.02965376, 0.03336048,
0.03953834, 0.04324506, 0.04448063, 0.04942292, 0.05312964,
0.05436522, 0.05930751, 0.06301423, 0.0642498 , 0.06919209,
0.07289881, 0.07907667, 0.08278339, 0.08401896, 0.08896126,
0.09266798, 0.09390355, 0.09884584, 0.1025526 , 0.1037882 ,
0.1087305 , 0.1124372 , 0.1186151 , 0.1223218 , 0.1235574 ,
0.1284997 , 0.1322064 , 0.133442 , 0.142091 , 0.1433266 ,
0.1482688 , 0.1519756 , 0.1581534 , 0.1618601 , 0.1630957 ,
0.168038 , 0.1717447 , 0.1729803 , 0.1779226 , 0.1816293 ,
0.1828649 , 0.1878072 , 0.1915139 , 0.1976918 , 0.2026341 ,
0.2075763 , 0.2125186 , 0.2174609 , 0.2224032 , 0.2273455 ,
0.2421724 , 0.2471147 , 0.252057 , 0.2569993 , 0.2619415 ,
0.2668838 , 0.2767684 , 0.2817107 , 0.2915953 , 0.3064222 ,
0.3261913 , 0.366965]
factors = [0.00375735, 0.01386287, 0.02595574, 0.02992438, 0.03549502,
0.03855745, 0.04058831, 0.04986305, 0.05703106, 0.05855471,
0.06078031, 0.06212291, 0.06656602, 0.06930339, 0.0697072 ,
0.07201456, 0.07263853, 0.07313129, 0.07465531, 0.07714482,
0.07759976, 0.077809 , 0.07790282, 0.07927957, 0.08013058,
0.08026637, 0.08073475, 0.08112202, 0.08123039, 0.08187171,
0.08213756, 0.08218236, 0.08236572, 0.08240729, 0.08259795,
0.08297893, 0.08300455, 0.08314566, 0.08315611, 0.08337715,
0.08350026, 0.08350663, 0.08352815, 0.08353776, 0.0836098 ,
0.08367017, 0.08367361, 0.0837242 , 0.08375069, 0.08375227,
0.08377006, 0.08381488, 0.08381644, 0.08382698, 0.08386266,
0.08387756, 0.08388445, 0.08388974, 0.08390341, 0.08391088,
0.08391695, 0.08392361, 0.08392684, 0.08392818, 0.08393161,
0.08393546, 0.08393685, 0.08393801, 0.08393976, 0.08394167,
0.08394288, 0.08394398]
coherent_xs = openmc.data.CoherentElastic(bragg_edges, factors)
incoherent_xs = openmc.data.Tabulated1D([0.00370672, 0.00370672], [0.00370672, 0.00370672])
elastic_xs = {'294K': openmc.data.Sum((coherent_xs, incoherent_xs))}
coherent_dist = openmc.data.CoherentElasticAE(coherent_xs)
incoherent_dist = openmc.data.IncoherentElasticAEDiscrete([
[-0.6, -0.18, 0.18, 0.6], [-0.6, -0.18, 0.18, 0.6]
])
elastic_dist = {'294K': openmc.data.MixedElasticAE(coherent_dist, incoherent_dist)}
fake_tsl.elastic = openmc.data.ThermalScatteringReaction(elastic_xs, elastic_dist)
# Create inelastic reaction
inelastic_xs = {'294K': openmc.data.Tabulated1D([1.0e-5, 4.9], [13.4, 3.35])}
breakpoints = [3]
interpolation = [2]
energy = [1.0e-5, 4.3e-2, 4.9]
energy_out = [
openmc.data.Tabular([0.0002, 0.067, 0.146, 0.366], [0.25, 0.25, 0.25, 0.25]),
openmc.data.Tabular([0.0001, 0.009, 0.137, 0.277], [0.25, 0.25, 0.25, 0.25]),
openmc.data.Tabular([0.0579, 4.555, 4.803, 4.874], [0.25, 0.25, 0.25, 0.25]),
]
for eout in energy_out:
eout.normalize()
eout.c = eout.cdf()
discrete = openmc.stats.Discrete([-0.9, -0.6, -0.3, -0.1, 0.1, 0.3, 0.6, 0.9], [1/8]*8)
discrete.c = discrete.cdf()[1:]
mu = [[discrete]*4]*3
inelastic_dist = {'294K': openmc.data.IncoherentInelasticAE(
breakpoints, interpolation, energy, energy_out, mu)}
inelastic = openmc.data.ThermalScatteringReaction(inelastic_xs, inelastic_dist)
fake_tsl.inelastic = inelastic
return fake_tsl
def test_mixed_elastic(fake_mixed_elastic, run_in_tmpdir):
# Write data to HDF5 and then read back
original = fake_mixed_elastic
original.export_to_hdf5('c_D_in_7LiD.h5')
copy = openmc.data.ThermalScattering.from_hdf5('c_D_in_7LiD.h5')
# Make sure data did not change as a result of HDF5 writing/reading
assert original == copy
# Create modified cross_sections.xml file that includes the above data
xs = openmc.data.DataLibrary.from_xml()
xs.register_file('c_D_in_7LiD.h5')
xs.export_to_xml('cross_sections_mixed.xml')
# Create a minimal model that includes the new data and run it
mat = openmc.Material()
mat.add_nuclide('H2', 1.0)
mat.add_nuclide('Li7', 1.0)
mat.set_density('g/cm3', 1.0)
mat.add_s_alpha_beta('c_D_in_7LiD')
sph = openmc.Sphere(r=10.0, boundary_type="vacuum")
cell = openmc.Cell(fill=mat, region=-sph)
model = openmc.Model()
model.geometry = openmc.Geometry([cell])
model.materials = openmc.Materials([mat])
model.materials.cross_sections = "cross_sections_mixed.xml"
model.settings.particles = 1000
model.settings.batches = 10
model.settings.run_mode = 'fixed source'
model.settings.source = openmc.Source(
energy=openmc.stats.Discrete([3.0], [1.0]) # 3 eV source
)
model.run()