Initial implementation of MGXS C++ code

This commit is contained in:
Adam G Nelson 2018-06-04 19:00:07 -04:00
parent 890a1b4f55
commit 79b473fa5b
9 changed files with 2175 additions and 0 deletions

60
src/constants.h Normal file
View file

@ -0,0 +1,60 @@
#ifndef CONSTANTS_H
#define CONSTANTS_H
#include <array>
#include <vector>
namespace openmc {
typedef std::array<double, 3> dir_arr;
typedef std::vector<double> double_1dvec;
typedef std::vector<std::vector<double> > double_2dvec;
typedef std::vector<std::vector<std::vector<double> > > double_3dvec;
typedef std::vector<std::vector<std::vector<std::vector<double> > > > double_4dvec;
typedef std::vector<std::vector<std::vector<std::vector<std::vector<double> > > > > double_5dvec;
typedef std::vector<std::vector<std::vector<std::vector<std::vector<std::vector<double> > > > > > double_6dvec;
typedef std::vector<int> int_1dvec;
typedef std::vector<std::vector<int> > int_2dvec;
typedef std::vector<std::vector<std::vector<int> > > int_3dvec;
int constexpr MAX_SAMPLE {10000};
constexpr std::array<int, 3> VERSION {0, 10, 0};
constexpr std::array<int, 2> VERSION_PARTICLE_RESTART {2, 0};
// Maximum number of words in a single line, length of line, and length of
// single word
constexpr int MAX_WORDS {500};
constexpr int MAX_LINE_LEN {250};
constexpr int MAX_WORD_LEN {150};
constexpr int MAX_FILE_LEN {255};
// Physical Constants
constexpr double K_BOLTZMANN {8.6173303e-5}; // Boltzmann constant in eV/K
// Angular distribution type
constexpr int ANGLE_ISOTROPIC {1};
constexpr int ANGLE_32_EQUI {2};
constexpr int ANGLE_TABULAR {3};
constexpr int ANGLE_LEGENDRE {4};
constexpr int ANGLE_HISTOGRAM {5};
// MGXS Table Types
constexpr int MGXS_ISOTROPIC {1}; // Isotroically weighted data
constexpr int MGXS_ANGLE {2}; // Data by angular bins
// Flag to denote this was a macroscopic data object
constexpr double MACROSCOPIC_AWR {-2.};
// Number of mu bins to use when converting Legendres to tabular type
constexpr int DEFAULT_NMU {33};
// Temperature treatment method
constexpr int TEMPERATURE_NEAREST {1};
constexpr int TEMPERATURE_INTERPOLATION {2};
} // namespace openmc
#endif // CONSTANTS_H

View file

@ -447,6 +447,152 @@ read_complex(hid_t obj_id, const char* name, double _Complex* buffer, bool indep
}
void
read_nd_vector(hid_t obj_id, const char* name, std::vector<double>& result,
bool must_have)
{
if (object_exists(obj_id, name)) {
read_double(obj_id, name, &result[0], true);
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<double> >& result, bool must_have)
{
if (object_exists(obj_id, name)) {
int dim1 = result.size();
int dim2 = result[0].size();
std::vector<double> temp_arr = std::vector<double>(dim1 * dim2);
read_double(obj_id, name, &temp_arr[0], true);
int temp_idx = 0;
for (int i = 0; i < dim1; i++) {
for (int j = 0; j < dim2; j++) {
result[i][j] = temp_arr[temp_idx++];
}
}
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<double> > >& result,
bool must_have)
{
if (object_exists(obj_id, name)) {
dim1 = result.size();
dim2 = result[0].size();
dim3 = result[0][0].size();
std::vector<double> temp_arr = std::vector<double>(dim1 * dim2 * dim3);
read_double(obj_id, name, &temp_arr[0], true);
int temp_idx = 0;
for (int i = 0; i < dim1; i++) {
for (int j = 0; j < dim2; j++) {
for (int k = 0; k < dim3; k++) {
result[i][j][k] = temp_arr[temp_idx++];
}
}
}
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<int> > >& result,
bool must_have)
{
if (object_exists(obj_id, name)) {
dim1 = result.size();
dim2 = result[0].size();
dim3 = result[0][0].size();
std::vector<int> temp_arr = std::vector<int>(dim1 * dim2 * dim3);
read_int(obj_id, name, &temp_arr[0], true);
int temp_idx = 0;
for (int i = 0; i < dim1; i++) {
for (int j = 0; j < dim2; j++) {
for (int k = 0; k < dim3; k++) {
result[i][j][k] = temp_arr[temp_idx++];
}
}
}
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<std::vector<double> > > >& result,
bool must_have)
{
if (object_exists(obj_id, name)) {
dim1 = result.size();
dim2 = result[0].size();
dim3 = result[0][0].size();
dim4 = result[0][0][0].size();
std::vector<double> temp_arr = std::vector<double>(
dim1 * dim2 * dim3 * dim4);
read_double(obj_id, name, &temp_arr[0], true);
int temp_idx = 0;
for (int i = 0; i < dim1; i++) {
for (int j = 0; j < dim2; j++) {
for (int k = 0; k < dim3; k++) {
for (int l = 0; l < dim4; l++) {
result[i][j][k][l] = temp_arr[temp_idx++];
}
}
}
}
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<std::vector<std::vector<double> > > > >& result,
bool must_have)
{
if (object_exists(obj_id, name)) {
dim1 = result.size();
dim2 = result[0].size();
dim3 = result[0][0].size();
dim4 = result[0][0][0].size();
dim5 = result[0][0][0][0].size();
std::vector<double> temp_arr = std::vector<double>(
dim1 * dim2 * dim3 * dim4 * dim5);
read_double(obj_id, name, &temp_arr[0], true);
int temp_idx = 0;
for (int i = 0; i < dim1; i++) {
for (int j = 0; j < dim2; j++) {
for (int k = 0; k < dim3; k++) {
for (int l = 0; l < dim4; l++) {
for (int m = 0; m < dim5; m++) {
result[i][j][k][l][m] = temp_arr[temp_idx++];
}
}
}
}
}
} else if (must_have) {
fatal_error(std::string("Must provide " + std::string(name) + "!"));
}
}
void
read_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, double* results)
{

View file

@ -55,6 +55,35 @@ extern "C" void read_string(hid_t obj_id, const char* name, size_t slen,
extern "C" void read_complex(hid_t obj_id, const char* name,
double _Complex* buffer, bool indep);
void
read_nd_vector(hid_t obj_id, const char* name, std::vector<double>& result,
bool must_have = false);
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<double> >& result,
bool must_have = false);
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<double> > >& result,
bool must_have = false);
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<int> > >& result,
bool must_have = false);
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<std::vector<double> > > >& result,
bool must_have = false);
void
read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<std::vector<std::vector<double> > > > >& result,
bool must_have = false);
extern "C" void read_tally_results(hid_t group_id, hsize_t n_filter,
hsize_t n_score, double* results);

95
src/mgxs.h Normal file
View file

@ -0,0 +1,95 @@
//! \file mgxs.h
//! A collection of classes for Multi-Group Cross Section data
#ifndef MGXS_H
#define MGXS_H
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <string>
#include <valarray>
#include <vector>
#include "constants.h"
#include "hdf5_interface.h"
#include "math_functions.h"
#include "random_lcg.h"
#include "scattdata.h"
#include "string_functions.h"
#include "xsdata.h"
namespace openmc {
//==============================================================================
// MGXS contains the mgxs data for a nuclide/material
//==============================================================================
class Mgxs {
private:
std::string name; // name of dataset, e.g., UO2
double awr; // atomic weight ratio
double_1dvec kTs; // temperature in eV (k * T)
int scatter_format; // flag for if this is legendre, histogram, or tabular
int num_delayed_groups; // number of delayed neutron groups
int num_groups; // number of energy groups
int index_temp; // cache of temperature index
double last_sqrtkT; // cache of the temperature corresponding to index_temp
std::vector<XsData> xs; // Cross section data
int n_pol;
int n_azi;
int index_pol; // cache fof the angle indices
int index_azi;
double_1dvec polar;
double_1dvec azimuthal;
dir_arr last_uvw;
void _metadata_from_hdf5(hid_t xs_id, int in_num_groups,
int in_num_delayed_groups, double_1dvec temperature, int& method,
double tolerance, double_1dvec& temps_to_read, int& order_dim);
public:
bool fissionable; // Is this fissionable
void init(const std::string& in_name, double in_awr, double_1dvec& in_kTs,
bool in_fissionable, int in_scatter_format, int in_num_groups,
int in_num_delayed_groups, double_1dvec& in_polar,
double_1dvec& in_azimuthal);
void build_macro(const std::string& in_name, double_1dvec& mat_kTs,
std::vector<Mgxs>& micros, double_1dvec& atom_densities,
int& method, double tolerance);
void combine(std::vector<Mgxs>& micros, double_1dvec& scalars,
int_1dvec& micro_ts, int this_t);
void from_hdf5(hid_t xs_id, int energy_groups, int delayed_groups,
double_1dvec temperature, int& method,
double tolerance, int max_order,
bool legendre_to_tabular,
int legendre_to_tabular_points);
double get_xs(const char* xstype, int gin, int* gout, double* mu,
int* dg);
void sample_fission_energy(int gin, double nu_fission, int& dg, int& gout);
void sample_scatter(dir_arr& uvw, int gin, int& gout, double& mu,
double& wgt);
void calculate_xs(int gin, double sqrtkT, dir_arr& uvw, double& total_xs,
double& abs_xs, double& nu_fiss_xs);
bool equiv(const Mgxs& that);
inline void set_temperature_index(double sqrtkT);
inline void set_angle_index(dir_arr& uvw);
};
extern "C" void read_mgxs_library(hid_t file_id, int n_nuclides, char** names,
int energy_groups, int delayed_groups, int n_temps, double temps[],
int& method, double tolerance, int max_order, bool legendre_to_tabular,
int legendre_to_tabular_points);
extern "C" bool query_fissionable(const int i_nuclides[], const int n_nuclides);
void create_macro_xs(int n_materials, double_2dvec& mat_kTs,
std::vector<std::string>& mat_names, double_1dvec& atom_densities,
int& method, double tolerance);
// Storage for the MGXS data
std::vector<Mgxs> nuclides_MG;
std::vector<Mgxs> macro_xs;
} // namespace openmc
#endif // MGXS_H

831
src/scattdata.cpp Normal file
View file

@ -0,0 +1,831 @@
#include "scattdata.h"
namespace openmc {
//==============================================================================
// Methods for use by all extended types
//==============================================================================
//==============================================================================
// ScattData base-class methods
//==============================================================================
void ScattData::generic_init(int order, int_1dvec in_gmin,
int_1dvec in_gmax, double_2dvec in_energy, double_2dvec in_mult)
{
int groups = in_energy.size();
gmin = in_gmin;
gmax = in_gmax;
energy.resize(groups);
mult.resize(groups);
dist.resize(groups);
for (int gin = 0; gin < groups; gin++) {
// Make sure the energy is normalized
double norm = std::accumulate(in_energy[gin].begin(),
in_energy[gin].end(), 0.);
if (norm != 0.) {
for (auto& n : in_energy[gin]) n /= norm;
}
// Store the inputted data
energy[gin] = in_energy[gin];
mult[gin] = in_mult[gin];
// Initialize the distribution data
dist[gin].resize(in_gmax[gin] - in_gmin[gin] + 1);
for (auto& v : dist[gin]) {
v.resize(order);
for (auto& n : v) n = 0.;
}
}
}
void ScattData::sample_energy(int gin, int& gout, int& i_gout)
{
// Sample the outgoing group
double xi = prn();
i_gout = 0; //TODO: + 1?
gout = gmin[gin];
double prob = energy[gin][i_gout];
while((prob < xi) && (gout < gmax[gin])) {
gout++;
i_gout++;
prob += energy[gin][i_gout];
}
}
double ScattData::get_xs(const char* xstype, int gin, int* gout, double* mu)
{
// Set the outgoing group offset index as needed
int i_gout = 0;
if (gout != nullptr) {
// short circuit the function if gout is from a zero portion of the
// scattering matrix
if ((*gout < gmin[gin]) || (*gout >= gmax[gin])) { // > gmax?
return 0.;
}
i_gout = *gout - gmin[gin];
}
double val = 0.;
if (std::strcmp(xstype, "scatter")) {
if (gout != nullptr) {
val = scattxs[gin] * energy[gin][i_gout];
} else {
val = scattxs[gin];
}
} else if (std::strcmp(xstype, "scatter/mult")) {
if (gout != nullptr) {
val = scattxs[gin] * energy[gin][i_gout] / mult[gin][i_gout];
} else {
val = scattxs[gin] / std::inner_product(mult[gin].begin(),
mult[gin].end(),
energy[gin].begin(), 0.0);
}
} else if (std::strcmp(xstype, "scatter*f_mu/mult")) {
if ((gout != nullptr) && (mu != nullptr)) {
val = scattxs[gin] * energy[gin][i_gout] * calc_f(gin, *gout, *mu);
} else {
// This is not an expected path (asking for f_mu without asking for a
// group or mu is not useful
fatal_error("Invalid call to get_xs");
}
} else if (std::strcmp(xstype, "scatter*f_mu")) {
if ((gout != nullptr) && (mu != nullptr)) {
val = scattxs[gin] * energy[gin][i_gout] * calc_f(gin, *gout, *mu) /
mult[gin][i_gout];
} else {
// This is not an expected path (asking for f_mu without asking for a
// group or mu is not useful
fatal_error("Invalid call to get_xs");
}
}
return val;
}
//==============================================================================
// ScattDataLegendre methods
//==============================================================================
void ScattDataLegendre::init(int_1dvec in_gmin, int_1dvec in_gmax,
double_2dvec in_mult, double_3dvec coeffs)
{
int groups = coeffs.size();
int order = coeffs[0].size();
// make a copy of coeffs that we can use to both extract data and normalize
double_3dvec matrix = coeffs;
// Get the scattering cross section value by summing the un-normalized P0
// coefficient in the variable matrix over all outgoing groups.
scattxs.resize(groups);
for (int gin = 0; gin < groups; gin++) {
int num_groups = gmax[gin] - gmin[gin] + 1;
scattxs[gin] = 0.;
for (int i_gout = 0; i_gout < num_groups; i_gout++) {
scattxs[gin] = std::accumulate(matrix[gin][i_gout].begin(),
matrix[gin][i_gout].end(),
scattxs[gin]);
}
}
// Build the energy transfer matrix from data in the variable matrix while
// also normalizing the variable matrix itself
// (forcing the CDF of f(mu=1) == 1)
double_2dvec in_energy;
in_energy.resize(groups);
for (int gin = 0; gin < groups; gin++) {
int num_groups = gmax[gin] - gmin[gin] + 1;
in_energy[gin].resize(num_groups);
for (int i_gout = 0; i_gout < num_groups; i_gout++) {
double norm = matrix[gin][i_gout][0];
in_energy[gin][i_gout] = norm;
if (norm != 0.) {
for (auto& n : matrix[gin][i_gout]) n /= norm;
}
}
}
// Initialize the base class attributes
ScattData::generic_init(order, in_gmin, in_gmax, in_energy, in_mult);
// Set the distribution (sdata.dist) values and initialize max_val
max_val.resize(groups);
for (int gin = 0; gin < groups; gin++) {
int num_groups = gmax[gin] - gmin[gin] + 1;
for (int i_gout = 0; i_gout < num_groups; i_gout++) {
dist[gin][i_gout] = matrix[gin][i_gout];
}
max_val[gin].resize(num_groups);
for (auto& n : max_val[gin]) n = 0.;
}
// Now update the maximum value
update_max_val();
}
void ScattDataLegendre::update_max_val()
{
int groups = max_val.size();
// Step through the polynomial with fixed number of points to identify the
// maximal value
int Nmu = 1001;
double dmu = 2. / (Nmu - 1);
for (int gin = 0; gin < groups; gin++) {
int num_groups = gmax[gin] - gmin[gin] + 1;
for (int i_gout = 0; i_gout < num_groups; i_gout++) {
for (int imu = 0; imu < Nmu; imu++) {
double mu;
if (imu == 0) {
mu = -1.;
} else if (imu == (Nmu - 1)) {
mu = 1.;
} else {
mu = -1. + (imu - 1) * dmu;
}
// Calculate probability
double f = evaluate_legendre_c(dist[gin][i_gout].size() - 1,
dist[gin][i_gout].data(), mu);
// if this is a new maximum, store it
if (f > max_val[gin][i_gout]) max_val[gin][i_gout] = f;
} // end imu loop
// Since we may not have caught the true max, add 10% margin
max_val[gin][i_gout] *= 1.1;
}
}
}
double ScattDataLegendre::calc_f(int gin, int gout, double mu)
{
// TODO: gout >= or gout >?
double f;
if ((gout < gmin[gin]) || (gout >= gmax[gin])) {
f = 0.;
} else {
// TODO: size() -1 or just size?
int i_gout = gout - gmin[gin]; //TODO: + 1?
f = evaluate_legendre_c(dist[gin][i_gout].size() - 1,
dist[gin][i_gout].data(), mu);
}
return f;
}
void ScattDataLegendre::sample(int gin, int& gout, double& mu, double& wgt)
{
// Sample the outgoing energy using the base-class method
int i_gout;
sample_energy(gin, gout, i_gout);
// Now we can sample mu using the scattering kernel using rejection
// sampling from a rectangular bounding box
double M = max_val[gin][i_gout];
int samples = 0;
while(true) {
double mu = 2. * prn() - 1.;
double f = calc_f(gin, gout, mu);
if (f > 0.) {
double u = prn() * M;
if (u <= f) break;
}
samples++;
if (samples > MAX_SAMPLE) {
fatal_error("Maximum number of Legendre expansion samples reached");
}
};
// Update the weight to reflect neutron multiplicity
wgt *= mult[gin][i_gout];
}
void ScattDataLegendre::combine(std::vector<ScattData*> those_scatts,
double_1dvec& scalars)
{
int groups = energy.size();
// Find the maximum order in the data set
int max_order = get_order();
for (int i = 0; i < those_scatts.size(); i++) {
// Lets also make sure these items are combineable
ScattDataLegendre* that = dynamic_cast<ScattDataLegendre*>(those_scatts[i]);
if (!equiv(*that)) {
fatal_error("Cannot combine the ScattData objects!");
}
int that_order = that->get_order();
if (that_order > max_order) max_order = that_order;
}
max_order++; // Add one since this is a Legendre
// Now allocate and zero our storage spaces
double_3dvec this_matrix = get_matrix(max_order);
double_2dvec mult_numer(groups, double_1dvec(groups, 0.));
double_2dvec mult_denom(groups, double_1dvec(groups, 0.));
// Build the dense scattering and multiplicity matrices
// Get the multiplicity_matrix
// To combine from nuclidic data we need to use the final relationship
// mult_{gg'} = sum_i(N_i*nuscatt_{i,g,g'}) /
// sum_i(N_i*(nuscatt_{i,g,g'} / mult_{i,g,g'}))
// Developed as follows:
// mult_{gg'} = nuScatt{g,g'} / Scatt{g,g'}
// mult_{gg'} = sum_i(N_i*nuscatt_{i,g,g'}) / sum(N_i*scatt_{i,g,g'})
// mult_{gg'} = sum_i(N_i*nuscatt_{i,g,g'}) /
// sum_i(N_i*(nuscatt_{i,g,g'} / mult_{i,g,g'}))
// nuscatt_{i,g,g'} can be reconstructed from the energy and scattxs member
// variables
for (int i = 0; i < those_scatts.size(); i++) {
ScattDataLegendre* that = dynamic_cast<ScattDataLegendre*>(those_scatts[i]);
// Build the dense matrix for that object
double_3dvec that_matrix = that->get_matrix(max_order);
// Now add that to this for the scattering and multiplicity
for (int gin = 0; gin < groups; gin++) {
// Only spend time adding that's gmin to gmax data since the rest will
// be zeros
for (int gout = that->gmin[gin]; gout <= that->gmax[gin]; gout++) {
// Do the scattering matrix
for (int l = 0; l < max_order; l++) {
this_matrix[gin][gout][l] += scalars[i] * that_matrix[gin][gout][l];
}
// Incorporate that's contribution to the multiplicity matrix data
double nuscatt = that->scattxs[gin] * that->energy[gin][gout];
mult_numer[gin][gout] += scalars[i] * nuscatt;
if (that->mult[gin][gout] > 0.) {
mult_denom[gin][gout] += scalars[i] * nuscatt / that->mult[gin][gout];
} else {
mult_denom[gin][gout] += scalars[i];
}
}
}
}
// Combine mult_numer and mult_denom into the combined multiplicity matrix
double_2dvec this_mult(groups, double_1dvec(groups, 1.));
for (int gin = 0; gin < groups; gin++) {
for (int gout = 0; gout < groups; gout++) {
if (mult_denom[gin][gout] > 0.) {
this_mult[gin][gout] = mult_numer[gin][gout] / mult_denom[gin][gout];
}
}
}
mult_numer.clear();
mult_denom.clear();
// We have the data, now we need to convert to a jagged array and then use
// the initialize function to store it on the object.
int_1dvec in_gmin(groups);
int_1dvec in_gmax(groups);
double_3dvec sparse_scatter(groups);
double_2dvec sparse_mult(groups);
for (int gin = 0; gin < groups; gin++) {
// Find the minimum and maximum group boundaries
int gmin_;
for (gmin_ = 0; gmin_ < groups; gmin_++) {
bool non_zero = std::all_of(this_matrix[gin][gmin_].begin(),
this_matrix[gin][gmin_].end(),
[](double val){return val > 0.;});
if (non_zero) break;
}
int gmax_;
for (gmax_ = groups - 1; gmax_ >= 0; gmax_--) {
bool non_zero = std::all_of(this_matrix[gin][gmax_].begin(),
this_matrix[gin][gmax_].end(),
[](double val){return val > 0.;});
if (non_zero) break;
}
// treat the case of all values being 0
if (gmin_ > gmax_) {
gmin_ = gin;
gmax_ = gin;
}
// Store the group bounds
in_gmin[gin] = gmin_;
in_gmax[gin] = gmax_;
// Store the data in the compressed format
sparse_scatter[gin].resize(gmax_ - gmin_ + 1);
sparse_mult[gin].resize(gmax_ - gmin_ + 1);
int i_gout = 0;
for (int gout = gmin_; gout <= gmax_; gout++) {
sparse_scatter[gin][i_gout] = this_matrix[gin][gout];
sparse_mult[gin][i_gout] = this_mult[gin][gout];
}
}
// Got everything we need, store it.
init(in_gmin, in_gmax, sparse_mult, sparse_scatter);
}
bool ScattDataLegendre::equiv(const ScattDataLegendre& that)
{
// ensure that the number of groups match
return (this->energy.size() == that.energy.size());
}
double_3dvec ScattDataLegendre::get_matrix(int max_order)
{
// Get the sizes and initialize the data to 0
int groups = energy.size();
int order_dim = max_order + 1;
double_3dvec matrix = double_3dvec(groups, double_2dvec(order_dim,
double_1dvec(order_dim, 0.)));
for (int gin = 0; gin < groups; gin++) {
for (int i_gout = 0; i_gout < energy[0].size(); i_gout++) {
int gout = i_gout + gmin[gin];
for (int l = 0; l < order_dim; l++) {
matrix[gin][gout][l] = scattxs[gin] * energy[gin][i_gout] *
dist[gin][i_gout][l];
}
}
}
return matrix;
}
//==============================================================================
// ScattDataHistogram methods
//==============================================================================
void ScattDataHistogram::init(int_1dvec in_gmin, int_1dvec in_gmax,
double_2dvec in_mult, double_3dvec coeffs)
{
int groups = coeffs.size();
int order = coeffs[0].size();
// make a copy of coeffs that we can use to both extract data and normalize
double_3dvec matrix = coeffs;
// Get the scattering cross section value by summing the distribution
// over all the histogram bins in angle and outgoing energy groups
scattxs.resize(groups);
for (int gin = 0; gin < groups; gin++) {
scattxs[gin] = 0.;
for (int i_gout = 0; i_gout < matrix[gin].size(); i_gout++) {
scattxs[gin] += std::accumulate(matrix[gin][i_gout].begin(),
matrix[gin][i_gout].end(), 0.);
}
}
// Build the energy transfer matrix from data in the variable matrix
double_2dvec in_energy;
in_energy.resize(groups);
for (int gin = 0; gin < groups; gin++) {
int num_groups = gmax[gin] - gmin[gin] + 1;
in_energy[gin].resize(num_groups);
for (int i_gout = 0; i_gout < num_groups; i_gout++) {
double norm = std::accumulate(matrix[gin][i_gout].begin(),
matrix[gin][i_gout].end(), 0.);
if (norm != 0.) {
for (auto& n : matrix[gin][i_gout]) n /= norm;
}
}
}
// Initialize the base class attributes
ScattData::generic_init(order, in_gmin, in_gmax, in_energy,
in_mult);
// Build the angular distributio mu values
mu = double_1dvec(order);
dmu = 2. / order;
mu[0] = -1.;
for (int imu = 1; imu < order; imu++) {
mu[imu] = -1. + (imu - 1) * dmu;
}
// Calculate f(mu) and integrate it so we can avoid rejection sampling
fmu.resize(groups);
for (int gin = 0; gin < groups; gin++) {
int num_groups = gmax[gin] - gmin[gin] + 1;
fmu[gin].resize(num_groups);
for (int i_gout = 0; i_gout < num_groups; i_gout) {
fmu[gin][i_gout].resize(order);
// The variable matrix contains f(mu); so directly assign it
fmu[gin][i_gout] = matrix[gin][i_gout];
// Integrate the histogram
dist[gin][i_gout][0] = dmu * matrix[gin][i_gout][0];
for (int imu = 1; imu < order; imu++) {
dist[gin][i_gout][imu] = dmu * matrix[gin][i_gout][imu] +
dist[gin][i_gout][imu - 1];
}
// Now re-normalize for integral to unity
double norm = dist[gin][i_gout][order - 1];
if (norm > 0.) {
for (int imu = 0; imu < order; imu++) {
fmu[gin][i_gout][imu] /= norm;
dist[gin][i_gout][imu] /= norm;
}
}
}
}
}
double ScattDataHistogram::calc_f(int gin, int gout, double mu)
{
// TODO: gout >= or gout >?
double f;
if ((gout < gmin[gin]) || (gout >= gmax[gin])) {
f = 0.;
} else {
// Find mu bin
int i_gout = gout - gmin[gin]; //TODO: + 1?
int imu;
if (mu == 1.) {
// use size -2 to have the index one before the end
imu = this->mu.size() - 2;
} else {
imu = std::floor((mu + 1.) / dmu + 1.);
}
f = fmu[gin][i_gout][imu];
}
return f;
}
void ScattDataHistogram::sample(int gin, int& gout, double& mu, double& wgt)
{
// Sample the outgoing energy using the base-class method
int i_gout;
sample_energy(gin, gout, i_gout);
// Determine the outgoing cosine bin
double xi = prn();
int imu;
if (xi < dist[gin][i_gout][0]) {
imu = 1;
} else {
// TODO lower_bound? + 1?
imu = std::upper_bound(dist[gin][i_gout].begin(),
dist[gin][i_gout].end(), xi) -
dist[gin][i_gout].begin();
}
// Randomly select mu within the imu bin
mu = prn() * dmu + this->mu[imu];
if (mu < -1.) {
mu = -1.;
} else if (mu > 1.) {
mu = 1.;
}
// Update the weight to reflect neutron multiplicity
wgt *= mult[gin][i_gout];
}
bool ScattDataHistogram::equiv(const ScattDataHistogram& that)
{
bool match = false;
if (this->energy.size() == that.energy.size() &&
this->dmu == that.dmu &&
std::equal(this->mu.begin(), this->mu.end(), that.mu.begin())) {
match = true;
}
return match;
}
double_3dvec ScattDataHistogram::get_matrix(int max_order)
{
// Get the sizes and initialize the data to 0
int groups = energy.size();
// We ignore the requested order for Histogram and Tabular representations
int order_dim = get_order();
double_3dvec matrix = double_3dvec(groups, double_2dvec(order_dim,
double_1dvec(order_dim, 0.)));
for (int gin = 0; gin < groups; gin++) {
for (int i_gout = 0; i_gout < energy[0].size(); i_gout++) {
int gout = i_gout + gmin[gin];
for (int l = 0; l < order_dim; l++) {
matrix[gin][gout][l] = scattxs[gin] * energy[gin][i_gout] *
fmu[gin][i_gout][l];
}
}
}
return matrix;
}
//==============================================================================
// ScattDataTabular methods
//==============================================================================
void ScattDataTabular::init(int_1dvec in_gmin, int_1dvec in_gmax,
double_2dvec in_mult, double_3dvec coeffs)
{
int groups = coeffs.size();
int order = coeffs[0].size();
// make a copy of coeffs that we can use to both extract data and normalize
double_3dvec matrix = coeffs;
// Build the angular distribution mu values
mu = double_1dvec(order);
dmu = 2. / (order - 1);
mu[0] = -1.;
for (int imu = 1; imu < order - 1; imu++) {
mu[imu] = -1. + (imu - 1) * dmu;
}
mu[order - 1] = 1.;
// Get the scattering cross section value by integrating the distribution
// over all mu points and then combining over all outgoing groups
scattxs.resize(groups);
for (int gin = 0; gin < groups; gin++) {
scattxs[gin] = 0.;
for (int i_gout = 0; i_gout < matrix[gin].size(); i_gout++) {
for (int imu = 1; imu < order; imu++) {
scattxs[gin] += 0.5 * dmu * (matrix[gin][i_gout][imu - 1] +
matrix[gin][i_gout][imu]);
}
}
}
// Build the energy transfer matrix from data in the variable matrix
double_2dvec in_energy;
in_energy.resize(groups);
for (int gin = 0; gin < groups; gin++) {
int num_groups = gmax[gin] - gmin[gin] + 1;
in_energy[gin].resize(num_groups);
for (int i_gout = 0; i_gout < num_groups; i_gout++) {
double norm = 0.;
for (int imu = 1; imu < order; imu++) {
norm += 0.5 * dmu * (matrix[gin][i_gout][imu - 1] +
matrix[gin][i_gout][imu]);
}
if (norm != 0.) {
for (auto& n : matrix[gin][i_gout]) n /= norm;
}
}
}
// Initialize the base class attributes
ScattData::generic_init(order, in_gmin, in_gmax, in_energy,
in_mult);
// Calculate f(mu) and integrate it so we can avoid rejection sampling
fmu.resize(groups);
for (int gin = 0; gin < groups; gin++) {
int num_groups = gmax[gin] - gmin[gin] + 1;
fmu[gin].resize(num_groups);
for (int i_gout = 0; i_gout < num_groups; i_gout) {
fmu[gin][i_gout].resize(order);
// The variable matrix contains f(mu); so directly assign it
fmu[gin][i_gout] = matrix[gin][i_gout];
// Ensure positivity
for (auto& val : fmu[gin][i_gout]) {
if (val < 0.) val = 0.;
}
// Now re-normalize for numerical integration issues and to take care of
// the above negative fix-up. Also accrue the CDF
double norm = 0.;
for (int imu = 1; imu < order; imu++) {
norm += 0.5 * dmu * (fmu[gin][i_gout][imu - 1] +
fmu[gin][i_gout][imu]);
// incorporate to the CDF
dist[gin][i_gout][imu] = norm;
}
// now do the normalization
if (norm > 0.) {
for (int imu = 0; imu < order; imu++) {
fmu[gin][i_gout][imu] /= norm;
dist[gin][i_gout][imu] /= norm;
}
}
}
}
}
double ScattDataTabular::calc_f(int gin, int gout, double mu)
{
// TODO: gout >= or gout >?
double f;
if ((gout < gmin[gin]) || (gout >= gmax[gin])) {
f = 0.;
} else {
// Find mu bin
int i_gout = gout - gmin[gin]; //TODO: + 1?
int imu;
if (mu == 1.) {
// use size -2 to have the index one before the end
imu = this->mu.size() - 2;
} else {
imu = std::floor((mu + 1.) / dmu + 1.);
}
double r = (mu - this->mu[imu]) / (this->mu[imu + 1] - this->mu[imu]);
f = (1. - r) * fmu[gin][i_gout][imu] + r * fmu[gin][i_gout][imu + 1];
}
return f;
}
void ScattDataTabular::sample(int gin, int& gout, double& mu, double& wgt)
{
// Sample the outgoing energy using the base-class method
int i_gout;
sample_energy(gin, gout, i_gout);
// Determine the outgoing cosine bin
int NP = this->mu.size();
double xi = prn();
double c_k = dist[gin][i_gout][0];
int k;
for (k = 0; k < NP - 2; k++) {
double c_k1 = dist[gin][i_gout][k + 1];
if (xi < c_k1) break;
c_k = c_k1;
}
// Check to make sure k is <= NP - 1
k = std::min(k, NP - 1);
// Find the pdf values we want
double p0 = fmu[gin][i_gout][k];
double mu0 = this -> mu[k];
double p1 = fmu[gin][i_gout][k + 1];
double mu1 = this -> mu[k + 1];
if (p0 == p1) {
mu = mu0 + (xi - c_k) / p0;
} else {
double frac = (p1 - p0) / (mu1 - mu0);
mu = mu0 + (std::sqrt(std::max(0., p0 * p0 + 2. * frac * (xi - c_k)))
- p0) / frac;
}
if (mu < -1.) {
mu = -1.;
} else if (mu > 1.) {
mu = 1.;
}
// Update the weight to reflect neutron multiplicity
wgt *= mult[gin][i_gout];
}
bool ScattDataTabular::equiv(const ScattDataTabular& that)
{
bool match = false;
if (this->energy.size() == that.energy.size() &&
this->dmu == that.dmu &&
std::equal(this->mu.begin(), this->mu.end(), that.mu.begin())) {
match = true;
}
return match;
}
double_3dvec ScattDataTabular::get_matrix(int max_order)
{
// Get the sizes and initialize the data to 0
int groups = energy.size();
// We ignore the requested order for Histogram and Tabular representations
int order_dim = get_order();
double_3dvec matrix = double_3dvec(groups, double_2dvec(order_dim,
double_1dvec(order_dim, 0.)));
for (int gin = 0; gin < groups; gin++) {
for (int i_gout = 0; i_gout < energy[0].size(); i_gout++) {
int gout = i_gout + gmin[gin];
for (int l = 0; l < order_dim; l++) {
matrix[gin][gout][l] = scattxs[gin] * energy[gin][i_gout] *
fmu[gin][i_gout][l];
}
}
}
return matrix;
}
void convert_legendre_to_tabular(ScattDataLegendre& leg,
ScattDataTabular& tab, int n_mu)
{
// Copy the obvious data
tab.energy = leg.energy;
tab.mult = leg.mult;
tab.gmin = leg.gmin;
tab.gmax = leg.gmax;
// Build mu and dmu
tab.mu = double_1dvec(n_mu);
tab.dmu = 2. / (n_mu - 1);
tab.mu[0] = -1.;
for (int imu = 1; imu < n_mu - 1; imu++) {
tab.mu[imu] = -1. + (imu - 1) * tab.dmu;
}
tab.mu[n_mu - 1] = 1.;
// Calculate f(mu) and integrate it so we can avoid rejection sampling
int groups = tab.energy.size();
tab.fmu.resize(groups);
for (int gin = 0; gin < groups; gin++) {
int num_groups = tab.gmax[gin] - tab.gmin[gin] + 1;
tab.fmu[gin].resize(num_groups);
for (int i_gout = 0; i_gout < num_groups; i_gout) {
tab.fmu[gin][i_gout].resize(n_mu);
for (int imu = 0; imu < n_mu; imu++) {
tab.fmu[gin][i_gout][imu] =
evaluate_legendre_c(leg.dist[gin][i_gout].size() - 1,
leg.dist[gin][i_gout].data(), tab.mu[imu]);
}
// Ensure positivity
for (auto& val : tab.fmu[gin][i_gout]) {
if (val < 0.) val = 0.;
}
// Now re-normalize for numerical integration issues and to take care of
// the above negative fix-up. Also accrue the CDF
double norm = 0.;
for (int imu = 1; imu < n_mu; imu++) {
norm += 0.5 * tab.dmu * (tab.fmu[gin][i_gout][imu - 1] +
tab.fmu[gin][i_gout][imu]);
// incorporate to the CDF
tab.dist[gin][i_gout][imu] = norm;
}
// now do the normalization
if (norm > 0.) {
for (int imu = 0; imu < n_mu; imu++) {
tab.fmu[gin][i_gout][imu] /= norm;
tab.dist[gin][i_gout][imu] /= norm;
}
}
}
}
}
} // namespace openmc

106
src/scattdata.h Normal file
View file

@ -0,0 +1,106 @@
//! \file scattdata.h
//! A collection of multi-group scattering data classes
#ifndef SCATTDATA_H
#define SCATTDATA_H
#include <algorithm>
#include <numeric>
#include <vector>
#include <cmath>
#include "constants.h"
#include "math_functions.h"
#include "random_lcg.h"
#include "error.h"
namespace openmc {
//==============================================================================
// SCATTDATA contains all the data needed to describe the scattering energy and
// angular distribution data
//==============================================================================
// temporary declaations so we can name our friend functions
class ScattDataLegendre;
class ScattDataTabular;
class ScattData {
protected:
double_2dvec energy; // Normalized p0 matrix for sampling Eout
double_2dvec mult; // nu-scatter multiplication (nu-scatt/scatt)
double_3dvec dist; // Angular distribution
int_1dvec gmin; // minimum outgoing group
int_1dvec gmax; // maximum outgoing group
public:
double_1dvec scattxs; // Isotropic Sigma_{s,g_{in}}
virtual double calc_f(int gin, int gout, double mu) = 0;
virtual void sample(int gin, int& gout, double& mu, double& wgt) = 0;
virtual void init(int_1dvec in_gmin, int_1dvec in_gmax,
double_2dvec in_mult, double_3dvec coeffs) = 0;
void sample_energy(int gin, int& gout, int& i_gout);
double get_xs(const char* xstype, int gin, int* gout, double* mu);
void generic_init(int order, int_1dvec in_gmin, int_1dvec in_gmax,
double_2dvec in_energy, double_2dvec in_mult);
virtual void combine(std::vector<ScattData*> those_scatts,
double_1dvec& scalars) = 0;
virtual int get_order() = 0;
virtual double_3dvec get_matrix(int max_order) = 0;
};
class ScattDataLegendre: public ScattData {
protected:
// Maximal value for rejection sampling from a rectangle
double_2dvec max_val;
friend void convert_legendre_to_tabular(ScattDataLegendre& leg,
ScattDataTabular& tab, int n_mu);
public:
void init(int_1dvec in_gmin, int_1dvec in_gmax, double_2dvec in_mult,
double_3dvec coeffs);
void update_max_val();
double calc_f(int gin, int gout, double mu);
void sample(int gin, int& gout, double& mu, double& wgt);
bool equiv(const ScattDataLegendre& that);
void combine(std::vector<ScattData*> those_scatts, double_1dvec& scalars);
int get_order() {return dist[0][0].size() - 1;};
double_3dvec get_matrix(int max_order);
};
class ScattDataHistogram: public ScattData {
protected:
double_1dvec mu;
double dmu;
double_3dvec fmu;
public:
void init(int_1dvec in_gmin, int_1dvec in_gmax, double_2dvec in_mult,
double_3dvec coeffs);
double calc_f(int gin, int gout, double mu);
void sample(int gin, int& gout, double& mu, double& wgt);
void combine(std::vector<ScattData*> those_scatts, double_1dvec& scalars);
bool equiv(const ScattDataHistogram& that);
int get_order() {return dist[0][0].size();};
double_3dvec get_matrix(int max_order);
};
class ScattDataTabular: public ScattData {
protected:
double_1dvec mu;
double dmu;
double_3dvec fmu;
friend void convert_legendre_to_tabular(ScattDataLegendre& leg,
ScattDataTabular& tab, int n_mu);
public:
void init(int_1dvec in_gmin, int_1dvec in_gmax, double_2dvec in_mult,
double_3dvec coeffs);
double calc_f(int gin, int gout, double mu);
void sample(int gin, int& gout, double& mu, double& wgt);
void combine(std::vector<ScattData*> those_scatts, double_1dvec& scalars);
bool equiv(const ScattDataTabular& that);
int get_order() {return dist[0][0].size();};
double_3dvec get_matrix(int max_order);
};
void convert_legendre_to_tabular(ScattDataLegendre& leg,
ScattDataTabular& tab, int n_mu);
} // namespace openmc
#endif // SCATTDATA_H

54
src/string_functions.h Normal file
View file

@ -0,0 +1,54 @@
//! \file string_functions.h
//! A collection of helper routines for C-strings and STL strings
#ifndef STRING_FUNCTIONS_H
#define STRING_FUNCTIONS_H
// for string functions
#include <functional>
#include <cctype>
#include <locale>
#include <string>
namespace openmc {
void strtrim(char* str)
{
int start = 0; // number of leading spaces
char* buffer = str;
while (*str && *str++ == ' ') ++start;
while (*str++); // move to end of string
// backup over trailing spaces
int end = str - buffer - 1;
while (end > 0 && buffer[end - 1] == ' ') --end;
buffer[end] = 0; // remove trailing spaces
// exit if no leading spaces or string is now empty
if (end <= start || start == 0) return;
str = buffer + start;
while ((*buffer++ = *str++)); // remove leading spaces: K&R
}
std::string strtrim(std::string in_str)
{
std::string str = in_str;
// perform the left trim
str.erase(str.begin(), std::find_if(str.begin(), str.end(),
std::not1(std::ptr_fun<int, int>(std::isspace))));
// perform the right trim
str.erase(std::find_if(str.rbegin(), str.rend(),
std::not1(std::ptr_fun<int, int>(std::isspace))).base(),
str.end());
}
void to_lower(std::string& str)
{
for (int i = 0; i < str.size(); i++) str[i] = std::tolower(str[i]);
}
} // namespace openmc
#endif // STRING_FUNCTIONS_H

782
src/xsdata.cpp Normal file
View file

@ -0,0 +1,782 @@
#include "xsdata.h"
namespace openmc {
//==============================================================================
// XsData class methods
//==============================================================================
XsData::XsData(int energy_groups, int num_delayed_groups, bool fissionable,
int scatter_format, int n_pol, int n_azi)
{
// check to make sure scatter format is OK before we allocate
if (scatter_format != ANGLE_HISTOGRAM && scatter_format != ANGLE_TABULAR &&
scatter_format != ANGLE_LEGENDRE) {
fatal_error("Invalid scatter_format!");
}
// allocate all [temperature][phi][theta][in group] quantities
total = double_3dvec(n_pol, double_2dvec(n_azi,
double_1dvec(energy_groups, 0.)));
absorption = double_3dvec(n_pol, double_2dvec(n_azi,
double_1dvec(energy_groups, 0.)));
inverse_velocity = double_3dvec(n_pol,
double_2dvec(n_azi, double_1dvec(energy_groups, 0.)));
if (fissionable) {
fission = double_3dvec(n_pol, double_2dvec(n_azi,
double_1dvec(energy_groups, 0.)));
prompt_nu_fission = double_3dvec(n_pol, double_2dvec(n_azi,
double_1dvec(energy_groups, 0.)));
kappa_fission = double_3dvec(n_pol, double_2dvec(n_azi,
double_1dvec(energy_groups, 0.)));
}
// allocate decay_rate; [temperature][phi][theta][delayed group]
decay_rate = double_3dvec(n_pol, double_2dvec(n_azi,
double_1dvec(num_delayed_groups, 0.)));
if (fissionable) {
// allocate delayed_nu_fission; [temperature][phi][theta][in group][out group]
delayed_nu_fission = double_4dvec(n_pol, double_3dvec(n_azi,
double_2dvec(energy_groups, double_1dvec(energy_groups, 0.))));
// chi_prompt; [temperature][phi][theta][in group][delayed group]
chi_prompt = double_4dvec(n_pol, double_3dvec(n_azi,
double_2dvec(energy_groups, double_1dvec(num_delayed_groups, 0.))));
// chi_delayed; [temperature][phi][theta][in group][out group][delay group]
chi_delayed = double_5dvec(n_pol, double_4dvec(n_azi,
double_3dvec(energy_groups, double_2dvec(energy_groups,
double_1dvec(num_delayed_groups, 0.)))));
}
scatter.resize(n_pol);
for (int p = 0; p < n_pol; p++) {
scatter[p].resize(n_azi);
for (int a = 0; a < n_azi; a++) {
if (scatter_format == ANGLE_HISTOGRAM) {
scatter[p][a] = new ScattDataHistogram;
} else if (scatter_format == ANGLE_TABULAR) {
scatter[p][a] = new ScattDataTabular;
} else if (scatter_format == ANGLE_LEGENDRE) {
scatter[p][a] = new ScattDataLegendre;
}
}
}
}
XsData::~XsData()
{
for (int p = 0; p < scatter.size(); p++) {
for (int a = 0; a < scatter[p].size(); a++) delete scatter[p][a];
scatter[p].clear();
}
scatter.clear();
}
void XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, int scatter_format,
int final_scatter_format, int order_data, int max_order,
int legendre_to_tabular_points)
{
// Reconstruct the dimension information so it doesn't need to be passed
int n_pol = total.size();
int n_azi = total[0].size();
int energy_groups = total[0][0].size();
int delayed_groups = decay_rate[0][0].size();
// Set the fissionable-specific data
if (fissionable) {
_fissionable_from_hdf5(xsdata_grp, n_pol, n_azi, energy_groups,
delayed_groups);
}
// Get the non-fission-specific data
read_nd_vector(xsdata_grp, "decay_rate", decay_rate);
read_nd_vector(xsdata_grp, "absorption", absorption, true);
read_nd_vector(xsdata_grp, "inverse-velocity", inverse_velocity);
// Get scattering data
_scatter_from_hdf5(xsdata_grp, n_pol, n_azi, energy_groups, scatter_format,
final_scatter_format, order_data, max_order,
legendre_to_tabular_points);
// Check absorption to ensure it is not 0 since it is often the
// denominator in tally methods
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
for (int gin = 0; gin < energy_groups; gin++) {
if (absorption[gin][p][a] == 0.) {
absorption[p][a][gin] = 1.e-10;
}
}
}
}
// Get or calculate the total x/s
if (object_exists(xsdata_grp, "total")) {
read_nd_vector(xsdata_grp, "total", total);
} else {
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
for (int gin = 0; gin < energy_groups; gin++) {
total[p][a][gin] = absorption[p][a][gin] +
scatter[p][a]->scattxs[gin];
}
}
}
}
// Check total to ensure it is not 0 since it is often the denominator in
// tally methods
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
for (int gin = 0; gin < energy_groups; gin++) {
if (total[p][a][gin] == 0.) total[p][a][gin] = 1.e-10;
}
}
}
}
void XsData::_fissionable_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi,
int energy_groups, int delayed_groups)
{
double_4dvec temp_beta =
double_4dvec(n_pol, double_3dvec(n_azi,
double_2dvec(energy_groups, double_1dvec(delayed_groups, 0.))));
// Set/get beta
if (object_exists(xsdata_grp, "beta")) {
hid_t xsdata = open_dataset(xsdata_grp, "beta");
int ndims = dataset_ndims(xsdata);
if (ndims == 3) {
// Beta is input as [delayed group]
double_1dvec temp_arr = double_1dvec(n_pol * n_azi * delayed_groups);
read_nd_vector(xsdata_grp, "beta", temp_arr);
// Broadcast to all incoming groups
int temp_idx = 0;
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
for (int dg = 0; dg < delayed_groups; dg++) {
// Set the first group index and copy the rest
temp_beta[p][a][0][dg] = temp_arr[temp_idx++];
for (int gin = 1; gin < energy_groups; gin++) {
temp_beta[p][a][gin] = temp_beta[p][a][0];
}
}
}
}
} else if (ndims == 4) {
// Beta is input as [in group][delayed group]
read_nd_vector(xsdata_grp, "beta", temp_beta);
} else {
fatal_error("beta must be provided as a 1D or 2D array!");
}
}
// If chi is provided, set chi-prompt and chi-delayed
if (object_exists(xsdata_grp, "chi")) {
double_3dvec temp_arr = double_3dvec(n_pol, double_2dvec(n_azi,
double_1dvec(energy_groups)));
read_nd_vector(xsdata_grp, "chi", temp_arr);
int temp_idx = 0;
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
// First set the first group
for (int gout = 0; gout < energy_groups; gout++) {
chi_prompt[p][a][0][gout] = temp_arr[p][a][temp_idx++];
}
// Now normalize this data
double chi_sum = std::accumulate(chi_prompt[p][a][0].begin(),
chi_prompt[p][a][0].end(),
0.);
if (chi_sum <= 0.) {
fatal_error("Encountered chi for a group that is <= 0!");
}
for (int gout = 0; gout < energy_groups; gout++) {
chi_prompt[p][a][0][gout] /= chi_sum;
}
// And extend to the remaining incoming groups
for (int gin = 1; gin < energy_groups; gin++) {
chi_prompt[p][a][gin] = chi_prompt[p][a][0];
}
// Finally set chi-delayed equal to chi-prompt
// Set chi-delayed to chi-prompt
for(int gin = 0; gin < energy_groups; gin++) {
for (int gout = 0; gout < energy_groups; gout++) {
for (int dg = 0; dg < delayed_groups; dg++) {
chi_delayed[p][a][gin][gout][dg] =
chi_prompt[p][a][gin][gout];
}
}
}
}
}
}
// If nu-fission is provided, set prompt- and delayed-nu-fission;
// if nu-fission is a matrix, set chi-prompt and chi-delayed.
if (object_exists(xsdata_grp, "nu-fission")) {
hid_t xsdata = open_dataset(xsdata_grp, "nu-fission");
int ndims = dataset_ndims(xsdata);
if (ndims == 3) {
// nu-fission is a 3-d array
read_nd_vector(xsdata_grp, "nu-fission", prompt_nu_fission);
// set delayed-nu-fission and correct prompt-nu-fission with beta
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
for (int gin = 0; gin < energy_groups; gin++) {
for (int dg = 0; dg < delayed_groups; dg++) {
delayed_nu_fission[p][a][gin][dg] =
temp_beta[p][a][gin][dg] *
prompt_nu_fission[p][a][gin];
}
// Correct the prompt-nu-fission using the delayed neutron fraction
if (delayed_groups > 0) {
double beta_sum = std::accumulate(temp_beta[p][a][gin].begin(),
temp_beta[p][a][gin].end(), 0.);
prompt_nu_fission[p][a][gin] *= (1. - beta_sum);
}
}
}
}
} else if (ndims == 4) {
// nu-fission is a matrix
read_nd_vector(xsdata_grp, "nu_fission", chi_prompt);
// Normalize the chi info so the CDF is 1.
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
for (int gin = 0; gin < energy_groups; gin++) {
double chi_sum = std::accumulate(chi_prompt[p][a][gin].begin(),
chi_prompt[p][a][gin].end(), 0.);
if (chi_sum >= 0.) {
for (int gout = 0; gout < energy_groups; gout++) {
chi_prompt[p][a][gin][gout] /= chi_sum;
}
} else {
fatal_error("Encountered chi for a group that is <= 0!");
}
}
// set chi-delayed to chi-prompt
for (int gin = 0; gin < energy_groups; gin++) {
for (int gout = 0; gout < energy_groups; gout++) {
for (int dg = 0; dg < delayed_groups; dg++) {
chi_delayed[p][a][gin][gout][dg] =
chi_prompt[p][a][gin][gout];
}
}
}
// Set the vector nu-fission from the matrix nu-fission
for (int gin = 0; gin < energy_groups; gin++) {
double sum = std::accumulate(chi_prompt[p][a][gin].begin(),
chi_prompt[p][a][gin].end(), 0.);
prompt_nu_fission[p][a][gin] = sum;
}
// Set the delayed-nu-fission and correct prompt-nu-fission with beta
for (int gin = 0; gin < energy_groups; gin++) {
for (int dg = 0; dg < delayed_groups; dg++) {
delayed_nu_fission[p][a][gin][dg] =
temp_beta[p][a][gin][dg] *
prompt_nu_fission[p][a][gin];
}
// Correct prompt-nu-fission using the delayed neutron fraction
if (delayed_groups > 0) {
double beta_sum = std::accumulate(temp_beta[p][a][gin].begin(),
temp_beta[p][a][gin].end(), 0.);
prompt_nu_fission[p][a][gin] *= (1. - beta_sum);
}
}
}
}
} else {
fatal_error("beta must be provided as a 3D or 4D array!");
}
close_dataset(xsdata);
}
// If chi-prompt is provided, set chi-prompt
if (object_exists(xsdata_grp, "chi-prompt")) {
double_3dvec temp_arr = double_3dvec(n_pol, double_2dvec(n_azi,
double_1dvec(energy_groups)));
read_nd_vector(xsdata_grp, "chi-prompt", temp_arr);
for (int a = 0; a < n_azi; a++) {
for (int p = 0; p < n_pol; p++) {
for (int gin = 0; gin < energy_groups; gin++) {
for (int gout = 0; gout < energy_groups; gout++) {
chi_prompt[p][a][gin][gout] = temp_arr[p][a][gout];
}
// Normalize chi so its CDF goes to 1
double chi_sum = std::accumulate(chi_prompt[p][a][gin].begin(),
chi_prompt[p][a][gin].end(), 0.);
if (chi_sum >= 0.) {
for (int gout = 0; gout < energy_groups; gout++) {
chi_prompt[p][a][gin][gout] /= chi_sum;
}
} else {
fatal_error("Encountered chi-prompt for a group that is <= 0.!");
}
}
}
}
}
// If chi-delayed is provided, set chi-delayed
if (object_exists(xsdata_grp, "chi-delayed")) {
hid_t xsdata = open_dataset(xsdata_grp, "chi-delayed");
int ndims = dataset_ndims(xsdata);
close_dataset(xsdata);
if (ndims == 3) {
// chi-delayed is a [in group] vector
double_3dvec temp_arr = double_3dvec(n_pol, double_2dvec(n_azi,
double_1dvec(energy_groups)));
read_nd_vector(xsdata_grp, "chi-delayed", temp_arr);
for (int a = 0; a < n_azi; a++) {
for (int p = 0; p < n_pol; p++) {
// normalize the chi CDF to 1
double chi_sum = std::accumulate(temp_arr[p][a].begin(),
temp_arr[p][a].end(), 0.);
if (chi_sum <= 0.) {
fatal_error("Encountered chi-delayed for a group that is <= 0!");
}
// set chi-delayed
for (int gin = 0; gin < energy_groups; gin++) {
for (int gout = 0; gout < energy_groups; gout++) {
for (int dg = 0; dg < delayed_groups; dg++) {
chi_delayed[p][a][gin][gout][dg] =
temp_arr[p][a][gout] / chi_sum;
}
}
}
}
}
} else if (ndims == 4) {
// chi_delayed is a matrix
read_nd_vector(xsdata_grp, "chi-delayed", chi_delayed);
// Normalize the chi info so the CDF is 1.
for (int a = 0; a < n_azi; a++) {
for (int p = 0; p < n_pol; p++) {
for (int dg = 0; dg < delayed_groups; dg++) {
for (int gin = 0; gin < energy_groups; gin++) {
double chi_sum = 0.;
for (int gout = 0; gout < energy_groups; gout++) {
chi_sum += chi_delayed[p][a][gin][gout][dg];
}
if (chi_sum > 0.) {
for (int gout = 0; gout < energy_groups; gout++) {
chi_delayed[p][a][gin][gout][dg] /= chi_sum;
}
} else {
fatal_error("Encountered chi-delayed for a group that is <= 0!");
}
}
}
}
}
} else {
fatal_error("chi-delayed must be provided as a 3D or 4D array!");
}
}
// Get prompt-nu-fission, if present
if (object_exists(xsdata_grp, "prompt-nu-fission")) {
hid_t xsdata = open_dataset(xsdata_grp, "prompt-nu-fission");
int ndims = dataset_ndims(xsdata);
close_dataset(xsdata);
if (ndims == 3) {
// prompt-nu-fission is a [in group] vector
read_nd_vector(xsdata_grp, "prompt-nu-fission",
prompt_nu_fission);
} else if (ndims == 4) {
// prompt nu fission is a matrix,
// so set prompt_nu_fiss & chi_prompt
double_4dvec temp_arr = double_4dvec(n_pol, double_3dvec(n_azi,
double_2dvec(energy_groups, double_1dvec(energy_groups))));
read_nd_vector(xsdata_grp, "prompt-nu-fission", temp_arr);
// The prompt_nu_fission vector from the matrix form
for (int a = 0; a < n_azi; a++) {
for (int p = 0; p < n_pol; p++) {
for (int gin = 0; gin < energy_groups; gin++) {
double prompt_sum = std::accumulate(temp_arr[p][a][gin].begin(),
temp_arr[p][a][gin].end(), 0.);
prompt_nu_fission[p][a][gin] = prompt_sum;
}
// The chi_prompt data is just the normalized fission matrix
for (int gin= 0; gin < energy_groups; gin++) {
if (prompt_nu_fission[p][a][gin] > 0.) {
for (int gout = 0; gout < energy_groups; gout++) {
chi_prompt[p][a][gin][gout] =
temp_arr[p][a][gin][gout] /
prompt_nu_fission[p][a][gin];
}
} else {
fatal_error("Encountered chi-prompt for a group that is <= 0!");
}
}
}
}
} else {
fatal_error("prompt-nu-fission must be provided as a 3D or 4D array!");
}
}
// Get delayed-nu-fission, if present
if (object_exists(xsdata_grp, "delayed-nu-fission")) {
hid_t xsdata = open_dataset(xsdata_grp, "delayed-nu-fission");
int ndims = dataset_ndims(xsdata);
if (ndims == 3) {
// delayed-nu-fission is a [in group] vector
if (temp_beta[0][0][0][0] == 0.) {
fatal_error("cannot set delayed-nu-fission with a 1D array if "
"beta is not provided");
}
double_3dvec temp_arr = double_3dvec(n_pol, double_2dvec(n_azi,
double_1dvec(energy_groups)));
read_nd_vector(xsdata_grp, "delayed-nu-fission", temp_arr);
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
for (int gin = 0; gin < energy_groups; gin++) {
for (int dg = 0; dg < delayed_groups; dg++) {
// Set delayed-nu-fission using beta
delayed_nu_fission[p][a][gin][dg] =
temp_beta[p][a][gin][dg] * temp_arr[p][a][gin];
}
}
}
}
} else if (ndims == 4) {
// delayed nu fission is a [pol][azi][energy_group][delayed_group] matrix;
// matrix use this to set delayed-nu-fission separately for each
// delayed group
std::vector<hsize_t> dims(ndims);
get_shape(xsdata, &dims[0]);
if (dims[2] != delayed_groups) {
fatal_error("The delayed-nu-fission matrix was input with a 1st "
"dimension not equal to the number of delayed groups");
}
if (dims[3] != energy_groups) {
fatal_error("The delayed-nu-fission matrix was input with a 2nd "
"dimension not equal to the number of energy groups");
}
if (delayed_groups == energy_groups) {
warning("delayed-nu-fission was input as a dimension-4 matrix "
"with the same number of delayed groups and energy "
"groups. OpenMC assumes the dimensions in the matrix "
"are [delayed_groups][energy_groups]. Currently, "
"delayed-nu-fission cannot be set as a group-by-group "
"matrix");
}
read_nd_vector(xsdata_grp, "delayed-nu-fission",
delayed_nu_fission);
} else if (ndims == 5) {
// This will contain delayed-nu-fision and chi-delayed data
double_5dvec temp_arr = double_5dvec(n_pol, double_4dvec(n_azi,
double_3dvec(energy_groups, double_2dvec(energy_groups,
double_1dvec(delayed_groups)))));
read_nd_vector(xsdata_grp, "delayed-nu-fission", temp_arr);
// Set the 4D delayed-nu-fission matrix and 5D chi-delayed matrix
// from the 5D delayed-nu-fission matrix
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
for (int dg = 0; dg < delayed_groups; dg++) {
for (int gin = 0; gin < energy_groups; gin++) {
double gout_sum = 0.;
for (int gout = 0; gout < energy_groups; gout++) {
gout_sum += temp_arr[p][a][gin][gout][dg];
chi_delayed[p][a][gin][gout][dg] =
temp_arr[p][a][gin][gout][dg];
}
delayed_nu_fission[p][a][gin][dg] = gout_sum;
// Normalize chi-delayed
if (gout_sum > 0.) {
for (int gout = 0; gout < energy_groups; gout++) {
chi_delayed[p][a][gin][gout][dg] /= gout_sum;
}
} else {
fatal_error("Encountered chi-delayed for a group that is <= 0!");
}
}
}
}
}
} else {
fatal_error("prompt-nu-fission must be provided as a 3D, 4D, or 5D "
"array!");
}
close_dataset(xsdata);
}
// Get the fission and kappa_fission data xs
read_nd_vector(xsdata_grp, "fission", fission);
read_nd_vector(xsdata_grp, "kappa-fission", kappa_fission);
}
void XsData::_scatter_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi,
int energy_groups, int scatter_format, int final_scatter_format,
int order_data, int max_order, int legendre_to_tabular_points)
{
if (!object_exists(xsdata_grp, "scatter_data")) {
fatal_error("Must provide scatter_data group!");
}
hid_t scatt_grp = open_group(xsdata_grp, "scatter_data");
// Get the outgoing group boundary indices
int_3dvec gmin = int_3dvec(n_pol, int_2dvec(n_azi,
int_1dvec(energy_groups)));
read_nd_vector(scatt_grp, "g_min", gmin, true);
int_3dvec gmax = int_3dvec(n_pol, int_2dvec(n_azi,
int_1dvec(energy_groups)));
read_nd_vector(scatt_grp, "g_max", gmax, true);
// Make gmin and gmax start from 0 vice 1 as they do in the library
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
for (int gin = 0; gin < energy_groups; gin++) {
gmin[p][a][gin] -= 1;
gmax[p][a][gin] -= 1;
}
}
}
// Now use this info to find the length of a vector to hold the flattened
// data.
int length = 0;
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
for (int gin = 0; gin < energy_groups; gin++) {
length += order_data * (gmax[p][a][gin] - gmin[p][a][gin] + 1);
}
}
}
double_1dvec temp_arr = double_1dvec(length);
read_nd_vector(scatt_grp, "scatter_matrix", temp_arr, true);
// Compare the number of orders given with the max order of the problem;
// strip off the superfluous orders if needed
int order_dim;
if (scatter_format == ANGLE_LEGENDRE) {
order_dim = std::min(order_data - 1, max_order) + 1;
} else {
order_dim = order_data;
}
// convert the flattened temp_arr to a jagged array for passing to
// scatt data
double_5dvec input_scatt =
double_5dvec(n_pol, double_4dvec(n_azi, double_3dvec(energy_groups)));
int temp_idx = 0;
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
for (int gin = 0; gin < energy_groups; gin++) {
input_scatt[p][a][gin].resize(gmax[p][a][gin] - gmin[p][a][gin] + 1);
for (int i_gout = 0; i_gout < input_scatt[p][a][gin].size(); i_gout++) {
input_scatt[p][a][gin][i_gout].resize(order_dim);
for (int l = 0; l < order_dim; l++) {
input_scatt[p][a][gin][i_gout][l] = temp_arr[temp_idx++];
}
// Adjust index for the orders we didnt take
temp_idx += (order_data - order_dim);
}
}
}
}
temp_arr.clear();
// Get multiplication matrix
double_4dvec temp_mult = double_4dvec(n_pol, double_3dvec(n_azi,
double_2dvec(energy_groups)));
if (object_exists(scatt_grp, "multiplicity_matrix")) {
temp_arr.resize(length);
read_nd_vector(scatt_grp, "multiplicity_matrix", temp_arr);
// convert the flat temp_arr to a jagged array for passing to scatt data
int temp_idx = 0;
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
for (int gin = 0; gin < energy_groups; gin++) {
temp_mult[p][a][gin].resize(gmax[p][a][gin] - gmin[p][a][gin] + 1);
for (int i_gout = 0; i_gout < temp_mult[p][a][gin].size(); i_gout++) {
temp_mult[p][a][gin][i_gout] = temp_arr[temp_idx++];
}
}
}
}
} else {
// Use a default: multiplicities are 1.0.
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
for (int gin = 0; gin < energy_groups; gin++) {
temp_mult[p][a][gin].resize(gmax[p][a][gin] - gmin[p][a][gin] + 1);
for (int i_gout = 0; i_gout < temp_mult[p][a][gin].size(); i_gout++) {
temp_mult[p][a][gin][i_gout] = 1.;
}
}
}
}
}
close_group(scatt_grp);
// Finally, convert the Legendre data to tabular, if needed
if (scatter_format == ANGLE_LEGENDRE &&
final_scatter_format == ANGLE_TABULAR) {
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
ScattDataLegendre legendre_scatt;
legendre_scatt.init(gmin[p][a], gmax[p][a], temp_mult[p][a],
input_scatt[p][a]);
// Now create a tabular version of legendre_scatt
convert_legendre_to_tabular(legendre_scatt,
*static_cast<ScattDataTabular*>(scatter[p][a]),
legendre_to_tabular_points);
scatter_format = final_scatter_format;
}
}
} else {
// We are sticking with the current representation
// Initialize the ScattData object with this data
for (int p = 0; p < n_pol; p++) {
for (int a = 0; a < n_azi; a++) {
scatter[p][a]->init(gmin[p][a], gmax[p][a], temp_mult[p][a],
input_scatt[p][a]);
}
}
}
}
void XsData::combine(std::vector<XsData*> those_xs, double_1dvec& scalars)
{
// Combine the non-scattering data
for (int i = 0; i < those_xs.size(); i++) {
XsData* that = those_xs[i];
if (!equiv(*that)) fatal_error("Cannot combine the XsData objects!");
double scalar = scalars[i];
for (int p = 0; p < total.size(); p++) {
for (int a = 0; a < total[p].size(); a++) {
for (int gin = 0; gin < total[p][a].size(); gin++) {
total[p][a][gin] += scalar * that->total[p][a][gin];
absorption[p][a][gin] += scalar * that->absorption[p][a][gin];
inverse_velocity[p][a][gin] +=
scalar * that->inverse_velocity[p][a][gin];
prompt_nu_fission[p][a][gin] +=
scalar * that->prompt_nu_fission[p][a][gin];
kappa_fission[p][a][gin] +=
scalar * that->kappa_fission[p][a][gin];
fission[p][a][gin] +=
scalar * that->fission[p][a][gin];
for (int dg = 0; dg < delayed_nu_fission[p][a][gin].size(); dg++) {
delayed_nu_fission[p][a][gin][dg] +=
scalar * that->delayed_nu_fission[p][a][gin][dg];
}
for (int gout = 0; gout < chi_prompt[p][a][gin].size(); gout++) {
chi_prompt[p][a][gin][gout] +=
scalar * that->chi_prompt[p][a][gin][gout];
for (int dg = 0; dg < chi_delayed[p][a][gin][gout].size(); dg++) {
chi_delayed[p][a][gin][gout][dg] +=
scalar * that->chi_delayed[p][a][gin][gout][dg];
}
}
}
for (int dg = 0; dg < decay_rate[p][a].size(); dg++) {
decay_rate[p][a][dg] += scalar * that->decay_rate[p][a][dg];
}
// Normalize chi
for (int gin = 0; gin < chi_prompt[p][a].size(); gin++) {
double norm = std::accumulate(chi_prompt[p][a][gin].begin(),
chi_prompt[p][a][gin].end(), 0.);
if (norm > 0.) {
for (int gout = 0; gout < chi_prompt[p][a][gin].size(); gout++) {
chi_prompt[p][a][gin][gout] /= norm;
}
}
for (int dg = 0; dg < chi_delayed[p][a][gin][0].size(); dg++) {
norm = 0.;
for (int gout = 0; gout < chi_delayed[p][a][gin].size(); gout++) {
norm += chi_delayed[p][a][gin][gout][dg];
}
if (norm > 0.) {
for (int gout = 0; gout < chi_delayed[p][a][gin].size(); gout++) {
chi_delayed[p][a][gin][gout][dg] /= norm;
}
}
}
}
}
}
}
// Allow the ScattData object to combine itself
for (int p = 0; p < total.size(); p++) {
for (int a = 0; a < total[p].size(); a++) {
// Build vector of the scattering objects to incorporate
std::vector<ScattData*> those_scatts(those_xs.size());
for (int i = 0; i < those_xs.size(); i++) {
those_scatts[i] = those_xs[i]->scatter[p][a];
}
// Now combine these guys
scatter[p][a]->combine(those_scatts, scalars);
}
}
}
bool XsData::equiv(const XsData& that)
{
bool match = false;
// check n_pol (total.size()), n_azi (total[0].size()), and
// groups (total[0][0].size())
// This assumes correct initializatino of the remaining cross sections
if ((total.size() == that.total.size()) &&
(total[0].size() == that.total[0].size()) &&
(total[0][0].size() == that.total[0][0].size())) {
match = true;
}
return match;
}
} //namespace openmc

72
src/xsdata.h Normal file
View file

@ -0,0 +1,72 @@
//! \file xsdata.h
//! A collection of classes for containing the Multi-Group Cross Section data
#ifndef XSDATA_H
#define XSDATA_H
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <memory>
#include <string>
#include <valarray>
#include <vector>
#include "constants.h"
#include "hdf5_interface.h"
#include "math_functions.h"
#include "random_lcg.h"
#include "scattdata.h"
namespace openmc {
//==============================================================================
// XSDATA contains the temperature-independent cross section data for an MGXS
//==============================================================================
class XsData {
private:
void _scatter_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi,
int energy_groups, int scatter_format, int final_scatter_format,
int order_data, int max_order, int legendre_to_tabular_points);
void _fissionable_from_hdf5(hid_t xsdata_grp, int n_pol, int n_azi,
int energy_groups, int delayed_groups);
public:
// The following quantities have the following dimensions:
// [phi][theta][incoming group]
double_3dvec total;
double_3dvec absorption;
double_3dvec prompt_nu_fission;
double_3dvec kappa_fission;
double_3dvec fission;
double_3dvec inverse_velocity;
// decay_rate has the following dimensions:
// [phi][theta][delayed group]
double_3dvec decay_rate;
// delayed_nu_fission has the following dimensions:
// [phi][theta][incoming group][delayed group]
double_4dvec delayed_nu_fission;
// chi_prompt has the following dimensions:
// [phi][theta][incoming group][outgoing group]
double_4dvec chi_prompt;
// chi_delayed has the following dimensions:
// [phi][theta][incoming group][outgoing group][delayed group]
double_5dvec chi_delayed;
// scatter has the following dimensions: [phi][theta]
std::vector<std::vector<ScattData*> > scatter;
XsData() = default;
XsData(int num_groups, int num_delayed_groups, bool fissionable,
int scatter_format, int n_pol, int n_azi);
~XsData();
void from_hdf5(hid_t xsdata_grp, bool fissionable, int scatter_format,
int final_scatter_format, int order_data, int max_order,
int legendre_to_tabular_points);
void combine(std::vector<XsData*> those_xs, double_1dvec& scalars);
bool equiv(const XsData& that);
};
} //namespace openmc
#endif // XSDATA_H