from collections.abc import MutableSequence, Iterable
import io
import numpy as np
from numpy.polynomial import Polynomial
import pandas as pd
import openmc.checkvalue as cv
from .data import NEUTRON_MASS
from .endf import get_head_record, get_cont_record, get_tab1_record, get_list_record
try:
from .reconstruct import wave_number, penetration_shift, reconstruct_mlbw, \
reconstruct_slbw, reconstruct_rm
_reconstruct = True
except ImportError:
_reconstruct = False
[docs]class Resonances:
"""Resolved and unresolved resonance data
Parameters
----------
ranges : list of openmc.data.ResonanceRange
Distinct energy ranges for resonance data
Attributes
----------
ranges : list of openmc.data.ResonanceRange
Distinct energy ranges for resonance data
resolved : openmc.data.ResonanceRange or None
Resolved resonance range
unresolved : openmc.data.Unresolved or None
Unresolved resonance range
"""
def __init__(self, ranges):
self.ranges = ranges
def __iter__(self):
for r in self.ranges:
yield r
@property
def ranges(self):
return self._ranges
@ranges.setter
def ranges(self, ranges):
cv.check_type('resonance ranges', ranges, MutableSequence)
self._ranges = cv.CheckedList(ResonanceRange, 'resonance ranges',
ranges)
@property
def resolved(self):
resolved_ranges = [r for r in self.ranges
if not isinstance(r, Unresolved)]
if len(resolved_ranges) > 1:
raise ValueError('More than one resolved range present')
elif len(resolved_ranges) == 0:
return None
else:
return resolved_ranges[0]
@property
def unresolved(self):
for r in self.ranges:
if isinstance(r, Unresolved):
return r
else:
return None
[docs] @classmethod
def from_endf(cls, ev):
"""Generate resonance data from an ENDF evaluation.
Parameters
----------
ev : openmc.data.endf.Evaluation
ENDF evaluation
Returns
-------
openmc.data.Resonances
Resonance data
"""
file_obj = io.StringIO(ev.section[2, 151])
# Determine whether discrete or continuous representation
items = get_head_record(file_obj)
n_isotope = items[4] # Number of isotopes
ranges = []
for _ in range(n_isotope):
items = get_cont_record(file_obj)
fission_widths = (items[3] == 1) # fission widths are given?
n_ranges = items[4] # number of resonance energy ranges
for j in range(n_ranges):
items = get_cont_record(file_obj)
resonance_flag = items[2] # flag for resolved (1)/unresolved (2)
formalism = items[3] # resonance formalism
if resonance_flag in (0, 1):
# resolved resonance region
erange = _FORMALISMS[formalism].from_endf(ev, file_obj, items)
elif resonance_flag == 2:
# unresolved resonance region
erange = Unresolved.from_endf(file_obj, items, fission_widths)
# erange.material = self
ranges.append(erange)
return cls(ranges)
[docs]class ResonanceRange:
"""Resolved resonance range
Parameters
----------
target_spin : float
Intrinsic spin, :math:`I`, of the target nuclide
energy_min : float
Minimum energy of the resolved resonance range in eV
energy_max : float
Maximum energy of the resolved resonance range in eV
channel : dict
Dictionary whose keys are l-values and values are channel radii as a
function of energy
scattering : dict
Dictionary whose keys are l-values and values are scattering radii as a
function of energy
Attributes
----------
channel_radius : dict
Dictionary whose keys are l-values and values are channel radii as a
function of energy
energy_max : float
Maximum energy of the resolved resonance range in eV
energy_min : float
Minimum energy of the resolved resonance range in eV
scattering_radius : dict
Dictionary whose keys are l-values and values are scattering radii as a
function of energ
target_spin : float
Intrinsic spin, :math:`I`, of the target nuclide
"""
def __init__(self, target_spin, energy_min, energy_max, channel, scattering):
self.target_spin = target_spin
self.energy_min = energy_min
self.energy_max = energy_max
self.channel_radius = channel
self.scattering_radius = scattering
self._prepared = False
self._parameter_matrix = {}
def __copy__(self):
cls = type(self)
new_copy = cls.__new__(cls)
new_copy.__dict__.update(self.__dict__)
new_copy._prepared = False
return new_copy
[docs] @classmethod
def from_endf(cls, ev, file_obj, items):
"""Create resonance range from an ENDF evaluation.
This factory method is only used when LRU=0, indicating that only a
scattering radius appears in MF=2, MT=151. All subclasses of
ResonanceRange override this method with their own.
Parameters
----------
ev : openmc.data.endf.Evaluation
ENDF evaluation
file_obj : file-like object
ENDF file positioned at the second record of a resonance range
subsection in MF=2, MT=151
items : list
Items from the CONT record at the start of the resonance range
subsection
Returns
-------
openmc.data.ResonanceRange
Resonance range data
"""
energy_min, energy_max = items[0:2]
# For scattering radius-only, NRO must be zero
assert items[4] == 0
# Get energy-independent scattering radius
items = get_cont_record(file_obj)
target_spin = items[0]
ap = Polynomial((items[1],))
# Calculate channel radius from ENDF-102 equation D.14
a = Polynomial((0.123 * (NEUTRON_MASS*ev.target['mass'])**(1./3.) + 0.08,))
return cls(target_spin, energy_min, energy_max, {0: a}, {0: ap})
[docs] def reconstruct(self, energies):
"""Evaluate cross section at specified energies.
Parameters
----------
energies : float or Iterable of float
Energies at which the cross section should be evaluated
Returns
-------
3-tuple of float or numpy.ndarray
Elastic, capture, and fission cross sections at the specified
energies
"""
if not _reconstruct:
raise RuntimeError("Resonance reconstruction not available.")
# Pre-calculate penetrations and shifts for resonances
if not self._prepared:
self._prepare_resonances()
if isinstance(energies, Iterable):
elastic = np.zeros_like(energies)
capture = np.zeros_like(energies)
fission = np.zeros_like(energies)
for i, E in enumerate(energies):
xse, xsg, xsf = self._reconstruct(self, E)
elastic[i] = xse
capture[i] = xsg
fission[i] = xsf
else:
elastic, capture, fission = self._reconstruct(self, energies)
return {2: elastic, 102: capture, 18: fission}
[docs]class MultiLevelBreitWigner(ResonanceRange):
"""Multi-level Breit-Wigner resolved resonance formalism data.
Multi-level Breit-Wigner resolved resonance data is identified by LRF=2 in
the ENDF-6 format.
Parameters
----------
target_spin : float
Intrinsic spin, :math:`I`, of the target nuclide
energy_min : float
Minimum energy of the resolved resonance range in eV
energy_max : float
Maximum energy of the resolved resonance range in eV
channel : dict
Dictionary whose keys are l-values and values are channel radii as a
function of energy
scattering : dict
Dictionary whose keys are l-values and values are scattering radii as a
function of energy
Attributes
----------
atomic_weight_ratio : float
Atomic weight ratio of the target nuclide given as a function of
l-value. Note that this may be different than the value for the
evaluation as a whole.
channel_radius : dict
Dictionary whose keys are l-values and values are channel radii as a
function of energy
energy_max : float
Maximum energy of the resolved resonance range in eV
energy_min : float
Minimum energy of the resolved resonance range in eV
parameters : pandas.DataFrame
Energies, spins, and resonances widths for each resonance
q_value : dict
Q-value to be added to incident particle's center-of-mass energy to
determine the channel energy for use in the penetrability factor. The
keys of the dictionary are l-values.
scattering_radius : dict
Dictionary whose keys are l-values and values are scattering radii as a
function of energy
target_spin : float
Intrinsic spin, :math:`I`, of the target nuclide
"""
def __init__(self, target_spin, energy_min, energy_max, channel, scattering):
super().__init__(target_spin, energy_min, energy_max, channel,
scattering)
self.parameters = None
self.q_value = {}
self.atomic_weight_ratio = None
# Set resonance reconstruction function
if _reconstruct:
self._reconstruct = reconstruct_mlbw
else:
self._reconstruct = None
[docs] @classmethod
def from_endf(cls, ev, file_obj, items):
"""Create MLBW data from an ENDF evaluation.
Parameters
----------
ev : openmc.data.endf.Evaluation
ENDF evaluation
file_obj : file-like object
ENDF file positioned at the second record of a resonance range
subsection in MF=2, MT=151
items : list
Items from the CONT record at the start of the resonance range
subsection
Returns
-------
openmc.data.MultiLevelBreitWigner
Multi-level Breit-Wigner resonance parameters
"""
# Read energy-dependent scattering radius if present
energy_min, energy_max = items[0:2]
nro, naps = items[4:6]
if nro != 0:
params, ape = get_tab1_record(file_obj)
# Other scatter radius parameters
items = get_cont_record(file_obj)
target_spin = items[0]
ap = Polynomial((items[1],)) # energy-independent scattering-radius
NLS = items[4] # number of l-values
# Read resonance widths, J values, etc
channel_radius = {}
scattering_radius = {}
q_value = {}
records = []
for l in range(NLS):
items, values = get_list_record(file_obj)
l_value = items[2]
awri = items[0]
q_value[l_value] = items[1]
competitive = items[3]
# Calculate channel radius from ENDF-102 equation D.14
a = Polynomial((0.123 * (NEUTRON_MASS*awri)**(1./3.) + 0.08,))
# Construct scattering and channel radius
if nro == 0:
scattering_radius[l_value] = ap
if naps == 0:
channel_radius[l_value] = a
elif naps == 1:
channel_radius[l_value] = ap
elif nro == 1:
scattering_radius[l_value] = ape
if naps == 0:
channel_radius[l_value] = a
elif naps == 1:
channel_radius[l_value] = ape
elif naps == 2:
channel_radius[l_value] = ap
energy = values[0::6]
spin = values[1::6]
gt = np.asarray(values[2::6])
gn = np.asarray(values[3::6])
gg = np.asarray(values[4::6])
gf = np.asarray(values[5::6])
if competitive > 0:
gx = gt - (gn + gg + gf)
else:
gx = np.zeros_like(gt)
for i, E in enumerate(energy):
records.append([energy[i], l_value, spin[i], gt[i], gn[i],
gg[i], gf[i], gx[i]])
columns = ['energy', 'L', 'J', 'totalWidth', 'neutronWidth',
'captureWidth', 'fissionWidth', 'competitiveWidth']
parameters = pd.DataFrame.from_records(records, columns=columns)
# Create instance of class
mlbw = cls(target_spin, energy_min, energy_max,
channel_radius, scattering_radius)
mlbw.q_value = q_value
mlbw.atomic_weight_ratio = awri
mlbw.parameters = parameters
return mlbw
def _prepare_resonances(self):
df = self.parameters.copy()
# Penetration and shift factors
p = np.zeros(len(df))
s = np.zeros(len(df))
# Penetration and shift factors for competitive reaction
px = np.zeros(len(df))
sx = np.zeros(len(df))
l_values = []
competitive = []
A = self.atomic_weight_ratio
for i, E, l, J, gt, gn, gg, gf, gx in df.itertuples():
if l not in l_values:
l_values.append(l)
competitive.append(gx > 0)
# Determine penetration and shift corresponding to resonance energy
k = wave_number(A, E)
rho = k*self.channel_radius[l](E)
p[i], s[i] = penetration_shift(l, rho)
# Determine penetration at modified energy for competitive reaction
if gx > 0:
Ex = E + self.q_value[l]*(A + 1)/A
rho = k*self.channel_radius[l](Ex)
px[i], sx[i] = penetration_shift(l, rho)
else:
px[i] = sx[i] = 0.0
df['p'] = p
df['s'] = s
df['px'] = px
df['sx'] = sx
self._l_values = np.array(l_values)
self._competitive = np.array(competitive)
for l in l_values:
self._parameter_matrix[l] = df[df.L == l].values
self._prepared = True
[docs]class SingleLevelBreitWigner(MultiLevelBreitWigner):
"""Single-level Breit-Wigner resolved resonance formalism data.
Single-level Breit-Wigner resolved resonance data is is identified by LRF=1
in the ENDF-6 format.
Parameters
----------
target_spin : float
Intrinsic spin, :math:`I`, of the target nuclide
energy_min : float
Minimum energy of the resolved resonance range in eV
energy_max : float
Maximum energy of the resolved resonance range in eV
channel : dict
Dictionary whose keys are l-values and values are channel radii as a
function of energy
scattering : dict
Dictionary whose keys are l-values and values are scattering radii as a
function of energy
Attributes
----------
atomic_weight_ratio : float
Atomic weight ratio of the target nuclide given as a function of
l-value. Note that this may be different than the value for the
evaluation as a whole.
channel_radius : dict
Dictionary whose keys are l-values and values are channel radii as a
function of energy
energy_max : float
Maximum energy of the resolved resonance range in eV
energy_min : float
Minimum energy of the resolved resonance range in eV
parameters : pandas.DataFrame
Energies, spins, and resonances widths for each resonance
q_value : dict
Q-value to be added to incident particle's center-of-mass energy to
determine the channel energy for use in the penetrability factor. The
keys of the dictionary are l-values.
scattering_radius : dict
Dictionary whose keys are l-values and values are scattering radii as a
function of energy
target_spin : float
Intrinsic spin, :math:`I`, of the target nuclide
"""
def __init__(self, target_spin, energy_min, energy_max, channel, scattering):
super().__init__(target_spin, energy_min, energy_max, channel,
scattering)
# Set resonance reconstruction function
if _reconstruct:
self._reconstruct = reconstruct_slbw
else:
self._reconstruct = None
[docs]class ReichMoore(ResonanceRange):
"""Reich-Moore resolved resonance formalism data.
Reich-Moore resolved resonance data is identified by LRF=3 in the ENDF-6
format.
Parameters
----------
target_spin : float
Intrinsic spin, :math:`I`, of the target nuclide
energy_min : float
Minimum energy of the resolved resonance range in eV
energy_max : float
Maximum energy of the resolved resonance range in eV
channel : dict
Dictionary whose keys are l-values and values are channel radii as a
function of energy
scattering : dict
Dictionary whose keys are l-values and values are scattering radii as a
function of energy
Attributes
----------
angle_distribution : bool
Indicate whether parameters can be used to compute angular distributions
atomic_weight_ratio : float
Atomic weight ratio of the target nuclide given as a function of
l-value. Note that this may be different than the value for the
evaluation as a whole.
channel_radius : dict
Dictionary whose keys are l-values and values are channel radii as a
function of energy
energy_max : float
Maximum energy of the resolved resonance range in eV
energy_min : float
Minimum energy of the resolved resonance range in eV
num_l_convergence : int
Number of l-values which must be used to converge the calculation
scattering_radius : dict
Dictionary whose keys are l-values and values are scattering radii as a
function of energy
parameters : pandas.DataFrame
Energies, spins, and resonances widths for each resonance
target_spin : float
Intrinsic spin, :math:`I`, of the target nuclide
"""
def __init__(self, target_spin, energy_min, energy_max, channel, scattering):
super().__init__(target_spin, energy_min, energy_max, channel,
scattering)
self.parameters = None
self.angle_distribution = False
self.num_l_convergence = 0
# Set resonance reconstruction function
if _reconstruct:
self._reconstruct = reconstruct_rm
else:
self._reconstruct = None
[docs] @classmethod
def from_endf(cls, ev, file_obj, items):
"""Create Reich-Moore resonance data from an ENDF evaluation.
Parameters
----------
ev : openmc.data.endf.Evaluation
ENDF evaluation
file_obj : file-like object
ENDF file positioned at the second record of a resonance range
subsection in MF=2, MT=151
items : list
Items from the CONT record at the start of the resonance range
subsection
Returns
-------
openmc.data.ReichMoore
Reich-Moore resonance parameters
"""
# Read energy-dependent scattering radius if present
energy_min, energy_max = items[0:2]
nro, naps = items[4:6]
if nro != 0:
params, ape = get_tab1_record(file_obj)
# Other scatter radius parameters
items = get_cont_record(file_obj)
target_spin = items[0]
ap = Polynomial((items[1],))
angle_distribution = (items[3] == 1) # Flag for angular distribution
NLS = items[4] # Number of l-values
num_l_convergence = items[5] # Number of l-values for convergence
# Read resonance widths, J values, etc
channel_radius = {}
scattering_radius = {}
records = []
for i in range(NLS):
items, values = get_list_record(file_obj)
apl = Polynomial((items[1],)) if items[1] != 0.0 else ap
l_value = items[2]
awri = items[0]
# Calculate channel radius from ENDF-102 equation D.14
a = Polynomial((0.123 * (NEUTRON_MASS*awri)**(1./3.) + 0.08,))
# Construct scattering and channel radius
if nro == 0:
scattering_radius[l_value] = apl
if naps == 0:
channel_radius[l_value] = a
elif naps == 1:
channel_radius[l_value] = apl
elif nro == 1:
if naps == 0:
channel_radius[l_value] = a
scattering_radius[l_value] = ape
elif naps == 1:
channel_radius[l_value] = scattering_radius[l_value] = ape
elif naps == 2:
channel_radius[l_value] = apl
scattering_radius[l_value] = ape
energy = values[0::6]
spin = values[1::6]
gn = values[2::6]
gg = values[3::6]
gfa = values[4::6]
gfb = values[5::6]
for i, E in enumerate(energy):
records.append([energy[i], l_value, spin[i], gn[i], gg[i],
gfa[i], gfb[i]])
# Create pandas DataFrame with resonance data
columns = ['energy', 'L', 'J', 'neutronWidth', 'captureWidth',
'fissionWidthA', 'fissionWidthB']
parameters = pd.DataFrame.from_records(records, columns=columns)
# Create instance of ReichMoore
rm = cls(target_spin, energy_min, energy_max,
channel_radius, scattering_radius)
rm.parameters = parameters
rm.angle_distribution = angle_distribution
rm.num_l_convergence = num_l_convergence
rm.atomic_weight_ratio = awri
return rm
def _prepare_resonances(self):
df = self.parameters.copy()
# Penetration and shift factors
p = np.zeros(len(df))
s = np.zeros(len(df))
l_values = []
lj_values = []
A = self.atomic_weight_ratio
for i, E, l, J, gn, gg, gfa, gfb in df.itertuples():
if l not in l_values:
l_values.append(l)
if (l, abs(J)) not in lj_values:
lj_values.append((l, abs(J)))
# Determine penetration and shift corresponding to resonance energy
k = wave_number(A, E)
rho = k*self.channel_radius[l](E)
p[i], s[i] = penetration_shift(l, rho)
df['p'] = p
df['s'] = s
self._l_values = np.array(l_values)
for (l, J) in lj_values:
self._parameter_matrix[l, J] = df[(df.L == l) &
(abs(df.J) == J)].values
self._prepared = True
[docs]class RMatrixLimited(ResonanceRange):
"""R-matrix limited resolved resonance formalism data.
R-matrix limited resolved resonance data is identified by LRF=7 in the
ENDF-6 format.
Parameters
----------
energy_min : float
Minimum energy of the resolved resonance range in eV
energy_max : float
Maximum energy of the resolved resonance range in eV
particle_pairs : list of dict
List of particle pairs. Each particle pair is represented by a
dictionary that contains the mass, atomic number, spin, and parity of
each particle as well as other characteristics.
spin_groups : list of dict
List of spin groups. Each spin group is characterized by channels,
resonance energies, and resonance widths.
Attributes
----------
reduced_width : bool
Flag indicating whether channel widths in eV or reduced-width amplitudes
in eV^1/2 are given
formalism : int
Flag to specify which formulae for the R-matrix are to be used
particle_pairs : list of dict
List of particle pairs. Each particle pair is represented by a
dictionary that contains the mass, atomic number, spin, and parity of
each particle as well as other characteristics.
spin_groups : list of dict
List of spin groups. Each spin group is characterized by channels,
resonance energies, and resonance widths.
"""
def __init__(self, energy_min, energy_max, particle_pairs, spin_groups):
super().__init__(0.0, energy_min, energy_max, None, None)
self.reduced_width = False
self.formalism = 3
self.particle_pairs = particle_pairs
self.spin_groups = spin_groups
[docs] @classmethod
def from_endf(cls, ev, file_obj, items):
"""Read R-Matrix limited resonance data from an ENDF evaluation.
Parameters
----------
ev : openmc.data.endf.Evaluation
ENDF evaluation
file_obj : file-like object
ENDF file positioned at the second record of a resonance range
subsection in MF=2, MT=151
items : list
Items from the CONT record at the start of the resonance range
subsection
Returns
-------
openmc.data.RMatrixLimited
R-matrix limited resonance parameters
"""
energy_min, energy_max = items[0:2]
items = get_cont_record(file_obj)
reduced_width = (items[2] == 1) # reduced width amplitude?
formalism = items[3] # Specify which formulae are used
n_spin_groups = items[4] # Number of Jpi values (NJS)
particle_pairs = []
spin_groups = []
items, values = get_list_record(file_obj)
n_pairs = items[5]//2 # Number of particle pairs (NPP)
for i in range(n_pairs):
first = {'mass': values[12*i],
'z': int(values[12*i + 2]),
'spin': values[12*i + 4],
'parity': values[12*i + 10]}
second = {'mass': values[12*i + 1],
'z': int(values[12*i + 3]),
'spin': values[12*i + 5],
'parity': values[12*i + 11]}
q_value = values[12*i + 6]
penetrability = values[12*i + 7]
shift = values[12*i + 8]
mt = int(values[12*i + 9])
particle_pairs.append(ParticlePair(
first, second, q_value, penetrability, shift, mt))
# loop over spin groups
for i in range(n_spin_groups):
items, values = get_list_record(file_obj)
J = items[0]
if J == 0.0:
parity = '+' if items[1] == 1.0 else '-'
else:
parity = '+' if J > 0. else '-'
J = abs(J)
kbk = items[2]
kps = items[3]
n_channels = items[5]
channels = []
for j in range(n_channels):
channel = {}
channel['particle_pair'] = particle_pairs[
int(values[6*j]) - 1]
channel['l'] = values[6*j + 1]
channel['spin'] = values[6*j + 2]
channel['boundary'] = values[6*j + 3]
channel['effective_radius'] = values[6*j + 4]
channel['true_radius'] = values[6*j + 5]
channels.append(channel)
# Read resonance energies and widths
items, values = get_list_record(file_obj)
n_resonances = items[3]
records = []
m = n_channels//6 + 1
for j in range(n_resonances):
energy = values[6*m*j]
records.append([energy] + [values[6*m*j + k + 1]
for k in range(n_channels)])
# Determine column names
columns = ['energy']
for channel in channels:
mt = channel['particle_pair'].mt
if mt == 2:
columns.append('neutronWidth')
elif mt == 18:
columns.append('fissionWidth')
elif mt == 102:
columns.append('captureWidth')
else:
columns.append(f'width (MT={mt})')
# Create Pandas dataframe with resonance parameters
parameters = pd.DataFrame.from_records(records, columns=columns)
# Construct SpinGroup instance and add to list
sg = SpinGroup(J, parity, channels, parameters)
spin_groups.append(sg)
# Optional extension (Background R-Matrix)
if kbk > 0:
items, values = get_list_record(file_obj)
lbk = items[4]
if lbk == 1:
params, rbr = get_tab1_record(file_obj)
params, rbi = get_tab1_record(file_obj)
# Optional extension (Tabulated phase shifts)
if kps > 0:
items, values = get_list_record(file_obj)
lps = items[4]
if lps == 1:
params, psr = get_tab1_record(file_obj)
params, psi = get_tab1_record(file_obj)
rml = cls(energy_min, energy_max, particle_pairs, spin_groups)
rml.reduced_width = reduced_width
rml.formalism = formalism
return rml
[docs]class ParticlePair:
def __init__(self, first, second, q_value, penetrability,
shift, mt):
self.first = first
self.second = second
self.q_value = q_value
self.penetrability = penetrability
self.shift = shift
self.mt = mt
[docs]class SpinGroup:
"""Resonance spin group
Attributes
----------
spin : float
Total angular momentum (nuclear spin)
parity : {'+', '-'}
Even (+) or odd(-) parity
channels : list of openmc.data.Channel
Available channels
parameters : pandas.DataFrame
Energies/widths for each resonance/channel
"""
def __init__(self, spin, parity, channels, parameters):
self.spin = spin
self.parity = parity
self.channels = channels
self.parameters = parameters
def __repr__(self):
return f'<SpinGroup: Jpi={self.spin}{self.parity}>'
[docs]class Unresolved(ResonanceRange):
"""Unresolved resonance parameters as identified by LRU=2 in MF=2.
Parameters
----------
target_spin : float
Intrinsic spin, :math:`I`, of the target nuclide
energy_min : float
Minimum energy of the unresolved resonance range in eV
energy_max : float
Maximum energy of the unresolved resonance range in eV
channel : openmc.data.Function1D
Channel radii as a function of energy
scattering : openmc.data.Function1D
Scattering radii as a function of energy
Attributes
----------
add_to_background : bool
If True, file 3 contains partial cross sections to be added to the
average unresolved cross sections calculated from parameters.
atomic_weight_ratio : float
Atomic weight ratio of the target nuclide
channel_radius : openmc.data.Function1D
Channel radii as a function of energy
energies : Iterable of float
Energies at which parameters are tabulated
energy_max : float
Maximum energy of the unresolved resonance range in eV
energy_min : float
Minimum energy of the unresolved resonance range in eV
parameters : list of pandas.DataFrame
Average resonance parameters at each energy
scattering_radius : openmc.data.Function1D
Scattering radii as a function of energy
target_spin : float
Intrinsic spin, :math:`I`, of the target nuclide
"""
def __init__(self, target_spin, energy_min, energy_max, channel, scattering):
super().__init__(target_spin, energy_min, energy_max, channel,
scattering)
self.energies = None
self.parameters = None
self.add_to_background = False
self.atomic_weight_ratio = None
[docs] @classmethod
def from_endf(cls, file_obj, items, fission_widths):
"""Read unresolved resonance data from an ENDF evaluation.
Parameters
----------
file_obj : file-like object
ENDF file positioned at the second record of a resonance range
subsection in MF=2, MT=151
items : list
Items from the CONT record at the start of the resonance range
subsection
fission_widths : bool
Whether fission widths are given
Returns
-------
openmc.data.Unresolved
Unresolved resonance region parameters
"""
# Read energy-dependent scattering radius if present
energy_min, energy_max = items[0:2]
nro, naps = items[4:6]
if nro != 0:
params, ape = get_tab1_record(file_obj)
# Get SPI, AP, and LSSF
formalism = items[3]
if not (fission_widths and formalism == 1):
items = get_cont_record(file_obj)
target_spin = items[0]
if nro == 0:
ap = Polynomial((items[1],))
add_to_background = (items[2] == 0)
if not fission_widths and formalism == 1:
# Case A -- fission widths not given, all parameters are
# energy-independent
NLS = items[4]
columns = ['L', 'J', 'd', 'amun', 'gn0', 'gg']
records = []
for ls in range(NLS):
items, values = get_list_record(file_obj)
awri = items[0]
l = items[2]
NJS = items[5]
for j in range(NJS):
d, j, amun, gn0, gg = values[6*j:6*j + 5]
records.append([l, j, d, amun, gn0, gg])
parameters = pd.DataFrame.from_records(records, columns=columns)
energies = None
elif fission_widths and formalism == 1:
# Case B -- fission widths given, only fission widths are
# energy-dependent
items, energies = get_list_record(file_obj)
target_spin = items[0]
if nro == 0:
ap = Polynomial((items[1],))
add_to_background = (items[2] == 0)
NE, NLS = items[4:6]
records = []
columns = ['L', 'J', 'E', 'd', 'amun', 'amuf', 'gn0', 'gg', 'gf']
for ls in range(NLS):
items = get_cont_record(file_obj)
awri = items[0]
l = items[2]
NJS = items[4]
for j in range(NJS):
items, values = get_list_record(file_obj)
muf = items[3]
d = values[0]
j = values[1]
amun = values[2]
gn0 = values[3]
gg = values[4]
gfs = values[6:]
for E, gf in zip(energies, gfs):
records.append([l, j, E, d, amun, muf, gn0, gg, gf])
parameters = pd.DataFrame.from_records(records, columns=columns)
elif formalism == 2:
# Case C -- all parameters are energy-dependent
NLS = items[4]
columns = ['L', 'J', 'E', 'd', 'amux', 'amun', 'amuf', 'gx', 'gn0',
'gg', 'gf']
records = []
for ls in range(NLS):
items = get_cont_record(file_obj)
awri = items[0]
l = items[2]
NJS = items[4]
for j in range(NJS):
items, values = get_list_record(file_obj)
ne = items[5]
j = items[0]
amux = values[2]
amun = values[3]
amuf = values[5]
energies = []
for k in range(1, ne + 1):
E = values[6*k]
d = values[6*k + 1]
gx = values[6*k + 2]
gn0 = values[6*k + 3]
gg = values[6*k + 4]
gf = values[6*k + 5]
energies.append(E)
records.append([l, j, E, d, amux, amun, amuf, gx, gn0,
gg, gf])
parameters = pd.DataFrame.from_records(records, columns=columns)
# Calculate channel radius from ENDF-102 equation D.14
a = Polynomial((0.123 * (NEUTRON_MASS*awri)**(1./3.) + 0.08,))
# Determine scattering and channel radius
if nro == 0:
scattering_radius = ap
if naps == 0:
channel_radius = a
elif naps == 1:
channel_radius = ap
elif nro == 1:
scattering_radius = ape
if naps == 0:
channel_radius = a
elif naps == 1:
channel_radius = ape
elif naps == 2:
channel_radius = ap
urr = cls(target_spin, energy_min, energy_max, channel_radius,
scattering_radius)
urr.parameters = parameters
urr.add_to_background = add_to_background
urr.atomic_weight_ratio = awri
urr.energies = energies
return urr
_FORMALISMS = {0: ResonanceRange,
1: SingleLevelBreitWigner,
2: MultiLevelBreitWigner,
3: ReichMoore,
7: RMatrixLimited}
_RESOLVED = (SingleLevelBreitWigner, MultiLevelBreitWigner,
ReichMoore, RMatrixLimited)