Got URR working, now just need a few simplifications of the Fortran code

This commit is contained in:
Adam G Nelson 2018-12-05 21:02:59 -05:00
parent 12540cbc5b
commit 5eab6dbed0
7 changed files with 262 additions and 234 deletions

View file

@ -257,12 +257,12 @@ constexpr int LIBRARY_PHOTON {3};
constexpr int LIBRARY_MULTIGROUP {4};
// Probability table parameters
constexpr int URR_CUM_PROB {1};
constexpr int URR_TOTAL {2};
constexpr int URR_ELASTIC {3};
constexpr int URR_FISSION {4};
constexpr int URR_N_GAMMA {5};
constexpr int URR_HEATING {6};
constexpr int URR_CUM_PROB {0};
constexpr int URR_TOTAL {1};
constexpr int URR_ELASTIC {2};
constexpr int URR_FISSION {3};
constexpr int URR_N_GAMMA {4};
constexpr int URR_HEATING {5};
// Maximum number of partial fission reactions
constexpr int PARTIAL_FISSION_MAX {4};
@ -424,7 +424,7 @@ constexpr int F90_NONE {0}; //TODO: replace usage of this with C_NONE
// Interpolation rules
enum class Interpolation {
histogram, lin_lin, lin_log, log_lin, log_log
histogram = 1, lin_lin = 2, lin_log = 3, log_lin = 4, log_log = 5
};
// Run modes

View file

@ -24,63 +24,6 @@ namespace openmc {
constexpr double CACHE_INVALID {-1.0};
//===============================================================================
// Data for a nuclide
//===============================================================================
class Nuclide {
public:
// Types, aliases
using EmissionMode = ReactionProduct::EmissionMode;
struct EnergyGrid {
std::vector<int> grid_index;
std::vector<double> energy;
};
// Constructors
Nuclide(hid_t group, const double* temperature, int n);
// Methods
double nu(double E, EmissionMode mode, int group=0) const;
void calculate_elastic_xs(int i_nuclide) const;
//! Determines the microscopic 0K elastic cross section at a trial relative
//! energy used in resonance scattering
double elastic_xs_0K(double E) const;
// Data members
std::string name_; //! Name of nuclide, e.g. "U235"
int Z_; //! Atomic number
int A_; //! Mass number
int metastable_; //! Metastable state
double awr_; //! Atomic weight ratio
std::vector<double> kTs_; //! temperatures in eV (k*T)
std::vector<EnergyGrid> grid_; //! Energy grid at each temperature
bool fissionable_ {false}; //! Whether nuclide is fissionable
bool has_partial_fission_ {false}; //! has partial fission reactions?
std::vector<Reaction*> fission_rx_; //! Fission reactions
int n_precursor_ {0}; //! Number of delayed neutron precursors
std::unique_ptr<Function1D> total_nu_; //! Total neutron yield
// Resonance scattering information
bool resonant_ {false};
std::vector<double> energy_0K_;
std::vector<double> elastic_0K_;
std::vector<double> xs_cdf_;
// Unresolved resonance range information
bool urr_present_ {false};
int urr_inelastic_ {C_NONE};
std::vector<UrrData> urr_data_;
std::vector<std::unique_ptr<Reaction>> reactions_; //! Reactions
std::vector<int> index_inelastic_scatter_;
private:
void create_derived();
};
//===============================================================================
//! Cached microscopic cross sections for a particular nuclide at the current
//! energy
@ -138,6 +81,68 @@ struct MaterialMacroXS {
double pair_production; //!< macroscopic pair production xs
};
//===============================================================================
// Data for a nuclide
//===============================================================================
class Nuclide {
public:
// Types, aliases
using EmissionMode = ReactionProduct::EmissionMode;
struct EnergyGrid {
std::vector<int> grid_index;
std::vector<double> energy;
};
// Constructors
Nuclide(hid_t group, const double* temperature, int n, int i_nuclide);
// Methods
double nu(double E, EmissionMode mode, int group=0) const;
void calculate_elastic_xs() const;
//! Determines the microscopic 0K elastic cross section at a trial relative
//! energy used in resonance scattering
double elastic_xs_0K(double E) const;
//! \brief Determines cross sections in the unresolved resonance range
//! from probability tables.
void calculate_urr_xs(const int i_temp, const double E);
// Data members
std::string name_; //! Name of nuclide, e.g. "U235"
int Z_; //! Atomic number
int A_; //! Mass number
int metastable_; //! Metastable state
double awr_; //! Atomic weight ratio
std::vector<double> kTs_; //! temperatures in eV (k*T)
std::vector<EnergyGrid> grid_; //! Energy grid at each temperature
int i_nuclide_; //! Index in the nuclides array
bool fissionable_ {false}; //! Whether nuclide is fissionable
bool has_partial_fission_ {false}; //! has partial fission reactions?
std::vector<Reaction*> fission_rx_; //! Fission reactions
int n_precursor_ {0}; //! Number of delayed neutron precursors
std::unique_ptr<Function1D> total_nu_; //! Total neutron yield
// Resonance scattering information
bool resonant_ {false};
std::vector<double> energy_0K_;
std::vector<double> elastic_0K_;
std::vector<double> xs_cdf_;
// Unresolved resonance range information
bool urr_present_ {false};
int urr_inelastic_ {C_NONE};
std::vector<UrrData> urr_data_;
std::vector<std::unique_ptr<Reaction>> reactions_; //! Reactions
std::vector<int> index_inelastic_scatter_;
private:
void create_derived();
};
//==============================================================================
// Global variables
//==============================================================================
@ -170,6 +175,8 @@ extern "C" void set_micro_xs();
extern "C" bool nuclide_wmp_present(int i_nuclide);
extern "C" double nuclide_wmp_emin(int i_nuclide);
extern "C" double nuclide_wmp_emax(int i_nuclide);
extern "C" void nuclide_calculate_urr_xs(const int i_nuclide,
const int i_temp, const double E);
} // namespace openmc

View file

@ -5,6 +5,7 @@
#include "xtensor/xtensor.hpp"
#include "openmc/constants.h"
#include "openmc/hdf5_interface.h"
namespace openmc {
@ -15,16 +16,14 @@ namespace openmc {
class UrrData{
public:
long unsigned int n_energy_; // # of incident energies
long unsigned int n_prob_; // # of probabilities
int interp_; // interpolation type (2=lin-lin, 5=log-log)
Interpolation interp_; // interpolation type
int inelastic_flag_; // inelastic competition flag
int absorption_flag_; // other absorption flag
bool multiply_smooth_; // multiply by smooth cross section?
xt::xtensor<double, 1> energy_; // incident energies
xt::xtensor<double, 3> prob_; // Actual probability tables
//! Load the URR data from the provided HDF5 group
//! \brief Load the URR data from the provided HDF5 group
void
from_hdf5(hid_t group_id);
};

View file

@ -5,6 +5,7 @@
#include "openmc/error.h"
#include "openmc/hdf5_interface.h"
#include "openmc/message_passing.h"
#include "openmc/random_lcg.h"
#include "openmc/search.h"
#include "openmc/settings.h"
#include "openmc/string_utils.h"
@ -33,7 +34,7 @@ MaterialMacroXS material_xs;
// Nuclide implementation
//==============================================================================
Nuclide::Nuclide(hid_t group, const double* temperature, int n)
Nuclide::Nuclide(hid_t group, const double* temperature, int n, int i_nuclide)
{
// Get name of nuclide from group, removing leading '/'
name_ = object_name(group).substr(1);
@ -42,6 +43,7 @@ Nuclide::Nuclide(hid_t group, const double* temperature, int n)
read_attribute(group, "A", A_);
read_attribute(group, "metastable", metastable_);
read_attribute(group, "atomic_weight_ratio", awr_);
i_nuclide_ = i_nuclide;
// Determine temperatures available
hid_t kT_group = open_group(group, "kTs");
@ -365,10 +367,10 @@ double Nuclide::nu(double E, EmissionMode mode, int group) const
}
}
void Nuclide::calculate_elastic_xs(int i_nuclide) const
void Nuclide::calculate_elastic_xs() const
{
// Get temperature index, grid index, and interpolation factor
auto& micro = simulation::micro_xs[i_nuclide];
auto& micro = simulation::micro_xs[i_nuclide_];
int i_temp = micro.index_temp - 1;
int i_grid = micro.index_grid - 1;
double f = micro.interp_factor;
@ -402,6 +404,142 @@ double Nuclide::elastic_xs_0K(double E) const
return (1.0 - f)*elastic_0K_[i_grid] + f*elastic_0K_[i_grid + 1];
}
void Nuclide::calculate_urr_xs(const int i_temp, const double E)
{
auto& micro = simulation::micro_xs[i_nuclide_];
micro.use_ptable = true;
// Create a shorthand for the URR data
UrrData* urr = &(urr_data_[i_temp]);
// Determine the energy table
int i_energy = 0;
while (true) {
if (E < urr->energy_(i_energy + 1)) {break;}
i_energy++;
}
// Sample the probability table using the cumulative distribution
// Random nmbers for the xs calculation are sampled from a separate stream.
// This guarantees the rnadomness and, at the same time, makes sure we
// reuse random numbers for the same nuclide at different temperatures,
// therefore preserving correlation of temperature in probability tables.
prn_set_stream(STREAM_URR_PTABLE);
//TODO: to maintain the same random number stream as the Fortran code this
//replaces, the seed is set with i_nuclide_ + 1 instead of i_nuclide_
double r = future_prn(static_cast<int64_t>(i_nuclide_ + 1));
prn_set_stream(STREAM_TRACKING);
int i_low = 0;
while (true) {
if (urr->prob_(i_energy, URR_CUM_PROB, i_low) > r) {break;}
i_low++;
}
int i_up = 0;
while (true) {
if (urr->prob_(i_energy + 1, URR_CUM_PROB, i_up) > r) {break;}
i_up++;
}
// Determine elastic, fission, and capture cross sections from the
// probability table
double elastic = 0.;
double fission = 0.;
double capture = 0.;
double f;
if (urr->interp_ == Interpolation::lin_lin) {
// Determine the interpolation factor on the table
f = (E - urr->energy_(i_energy)) /
(urr->energy_(i_energy + 1) - urr->energy_(i_energy));
elastic = (1. - f) * urr->prob_(i_energy, URR_ELASTIC, i_low) +
f * urr->prob_(i_energy + 1, URR_ELASTIC, i_up);
fission = (1. - f) * urr->prob_(i_energy, URR_FISSION, i_low) +
f * urr->prob_(i_energy + 1, URR_FISSION, i_up);
capture = (1. - f) * urr->prob_(i_energy, URR_N_GAMMA, i_low) +
f * urr->prob_(i_energy + 1, URR_N_GAMMA, i_up);
} else if (urr->interp_ == Interpolation::log_log) {
// Determine interpolation factor on the table
f = std::log(E / urr->energy_(i_energy)) /
std::log(urr->energy_(i_energy + 1) / urr->energy_(i_energy));
// Calculate the elastic cross section/factor
if ((urr->prob_(i_energy, URR_ELASTIC, i_low) > 0.) &&
(urr->prob_(i_energy + 1, URR_ELASTIC, i_up) > 0.)) {
elastic =
std::exp((1. - f) *
std::log(urr->prob_(i_energy, URR_ELASTIC, i_low)) +
f * std::log(urr->prob_(i_energy + 1, URR_ELASTIC, i_up)));
} else {
elastic = 0.;
}
// Calculate the fission cross section/factor
if ((urr->prob_(i_energy, URR_FISSION, i_low) > 0.) &&
(urr->prob_(i_energy + 1, URR_FISSION, i_up) > 0.)) {
fission =
std::exp((1. - f) *
std::log(urr->prob_(i_energy, URR_FISSION, i_low)) +
f * std::log(urr->prob_(i_energy + 1, URR_FISSION, i_up)));
} else {
fission = 0.;
}
// Calculate the capture cross section/factor
if ((urr->prob_(i_energy, URR_N_GAMMA, i_low) > 0.) &&
(urr->prob_(i_energy + 1, URR_N_GAMMA, i_up) > 0.)) {
capture =
std::exp((1. - f) *
std::log(urr->prob_(i_energy, URR_N_GAMMA, i_low)) +
f * std::log(urr->prob_(i_energy + 1, URR_N_GAMMA, i_up)));
} else {
capture = 0.;
}
}
// Determine the treatment of inelastic scattering
double inelastic = 0.;
if (urr->inelastic_flag_ != C_NONE) {
// get interpolation factor
f = micro.interp_factor;
// Determine inelastic scattering cross section
Reaction* rx = reactions_[urr_inelastic_].get();
int xs_index = micro.index_grid - rx->xs_[i_temp].threshold;
if (xs_index >= 0) {
inelastic = (1. - f) * rx->xs_[i_temp].value[xs_index] +
f * rx->xs_[i_temp].value[xs_index + 1];
}
}
// Multiply by smooth cross-section if needed
if (urr->multiply_smooth_) {
calculate_elastic_xs();
elastic *= micro.elastic;
capture *= (micro.absorption - micro.fission);
fission *= micro.fission;
}
// Check for negative values
if (elastic < 0.) {elastic = 0.;}
if (fission < 0.) {fission = 0.;}
if (capture < 0.) {capture = 0.;}
// Set elastic, absorption, fission, and total x/s. Note that the total x/s
// is calculated as a sum of partials instead of the table-provided value
micro.elastic = elastic;
micro.absorption = capture + fission;
micro.fission = fission;
micro.total = elastic + inelastic + capture + fission;
// Determine nu-fission cross-section
if (fissionable_) {
micro.nu_fission = nu(E, EmissionMode::total) * micro.fission;
}
}
//==============================================================================
// Fortran compatibility functions
//==============================================================================
@ -415,7 +553,8 @@ set_particle_energy_bounds(int particle, double E_min, double E_max)
extern "C" Nuclide* nuclide_from_hdf5_c(hid_t group, const double* temperature, int n)
{
data::nuclides.push_back(std::make_unique<Nuclide>(group, temperature, n));
data::nuclides.push_back(std::make_unique<Nuclide>(group, temperature, n,
data::nuclides.size()));
return data::nuclides.back().get();
}
@ -436,4 +575,10 @@ void set_micro_xs()
}
}
extern "C" void
nuclide_calculate_urr_xs(const int i_nuclide, const int i_temp, const double E)
{
data::nuclides[i_nuclide - 1]->calculate_urr_xs(i_temp - 1, E);
}
} // namespace openmc

View file

@ -198,6 +198,14 @@ module nuclide_header
character(kind=C_CHAR), intent(in) :: name(*)
type(C_PTR) :: path
end function
subroutine nuclide_calculate_urr_xs_c(i_nuclide, i_temp, E) &
bind(C, name='nuclide_calculate_urr_xs')
import C_INT, C_DOUBLE
integer(C_INT), value, intent(in) :: i_nuclide
integer(C_INT), value, intent(in) :: i_temp
real(C_DOUBLE), value, intent(in) :: E
end subroutine nuclide_calculate_urr_xs_c
end interface
contains
@ -980,7 +988,7 @@ contains
if (urr_ptables_on .and. this % urr_present .and. .not. use_mp) then
if (E > this % urr_data(i_temp) % energy(1) .and. E < this % &
urr_data(i_temp) % energy(this % urr_data(i_temp) % n_energy)) then
call calculate_urr_xs(this, i_temp, E, micro_xs)
call nuclide_calculate_urr_xs_c(this % i_nuclide, i_temp, E)
end if
end if
@ -1246,152 +1254,6 @@ contains
end if
end subroutine multipole_deriv_eval
!===============================================================================
! CALCULATE_URR_XS determines cross sections in the unresolved resonance range
! from probability tables
!===============================================================================
subroutine calculate_urr_xs(this, i_temp, E, micro_xs)
class(Nuclide), intent(in) :: this ! Nuclide object
integer, intent(in) :: i_temp ! temperature index
real(8), intent(in) :: E ! energy
type(NuclideMicroXS), intent(inout) :: micro_xs ! Cross section cache
integer :: i_energy ! index for energy
integer :: i_low ! band index at lower bounding energy
integer :: i_up ! band index at upper bounding energy
integer :: threshold ! threshold energy index
real(8) :: f ! interpolation factor
real(8) :: r ! pseudo-random number
real(8) :: elastic ! elastic cross section
real(8) :: capture ! (n,gamma) cross section
real(8) :: fission ! fission cross section
real(8) :: inelastic ! inelastic cross section
micro_xs % use_ptable = .true.
associate (urr => this % urr_data(i_temp))
! determine energy table
i_energy = 1
do
if (E < urr % energy(i_energy + 1)) exit
i_energy = i_energy + 1
end do
! determine interpolation factor on table
f = (E - urr % energy(i_energy)) / &
(urr % energy(i_energy + 1) - urr % energy(i_energy))
! sample probability table using the cumulative distribution
! Random numbers for xs calculation are sampled from a separated stream.
! This guarantees the randomness and, at the same time, makes sure we reuse
! random number for the same nuclide at different temperatures, therefore
! preserving correlation of temperature in probability tables.
call prn_set_stream(STREAM_URR_PTABLE)
r = future_prn(int(this % i_nuclide, 8))
call prn_set_stream(STREAM_TRACKING)
i_low = 1
do
if (urr % prob(i_energy, URR_CUM_PROB, i_low) > r) exit
i_low = i_low + 1
end do
i_up = 1
do
if (urr % prob(i_energy + 1, URR_CUM_PROB, i_up) > r) exit
i_up = i_up + 1
end do
! determine elastic, fission, and capture cross sections from probability
! table
if (urr % interp == LINEAR_LINEAR) then
elastic = (ONE - f) * urr % prob(i_energy, URR_ELASTIC, i_low) + &
f * urr % prob(i_energy + 1, URR_ELASTIC, i_up)
fission = (ONE - f) * urr % prob(i_energy, URR_FISSION, i_low) + &
f * urr % prob(i_energy + 1, URR_FISSION, i_up)
capture = (ONE - f) * urr % prob(i_energy, URR_N_GAMMA, i_low) + &
f * urr % prob(i_energy + 1, URR_N_GAMMA, i_up)
elseif (urr % interp == LOG_LOG) then
! Get logarithmic interpolation factor
f = log(E / urr % energy(i_energy)) / &
log(urr % energy(i_energy + 1) / urr % energy(i_energy))
! Calculate elastic cross section/factor
elastic = ZERO
if (urr % prob(i_energy, URR_ELASTIC, i_low) > ZERO .and. &
urr % prob(i_energy + 1, URR_ELASTIC, i_up) > ZERO) then
elastic = exp((ONE - f) * log(urr % prob(i_energy, URR_ELASTIC, &
i_low)) + f * log(urr % prob(i_energy + 1, URR_ELASTIC, &
i_up)))
end if
! Calculate fission cross section/factor
fission = ZERO
if (urr % prob(i_energy, URR_FISSION, i_low) > ZERO .and. &
urr % prob(i_energy + 1, URR_FISSION, i_up) > ZERO) then
fission = exp((ONE - f) * log(urr % prob(i_energy, URR_FISSION, &
i_low)) + f * log(urr % prob(i_energy + 1, URR_FISSION, &
i_up)))
end if
! Calculate capture cross section/factor
capture = ZERO
if (urr % prob(i_energy, URR_N_GAMMA, i_low) > ZERO .and. &
urr % prob(i_energy + 1, URR_N_GAMMA, i_up) > ZERO) then
capture = exp((ONE - f) * log(urr % prob(i_energy, URR_N_GAMMA, &
i_low)) + f * log(urr % prob(i_energy + 1, URR_N_GAMMA, &
i_up)))
end if
end if
! Determine treatment of inelastic scattering
inelastic = ZERO
if (urr % inelastic_flag > 0) then
! Get index on energy grid and interpolation factor
i_energy = micro_xs % index_grid
f = micro_xs % interp_factor
! Determine inelastic scattering cross section
associate (rx => this % reactions(this % urr_inelastic))
threshold = rx % xs_threshold(i_temp)
if (i_energy >= threshold) then
inelastic = (ONE - f) * rx % xs(i_temp, i_energy - threshold + 1) + &
f * rx % xs(i_temp, i_energy - threshold + 2)
end if
end associate
end if
! Multiply by smooth cross-section if needed
if (urr % multiply_smooth) then
call this % calculate_elastic_xs(micro_xs)
elastic = elastic * micro_xs % elastic
capture = capture * (micro_xs % absorption - micro_xs % fission)
fission = fission * micro_xs % fission
end if
! Check for negative values
if (elastic < ZERO) elastic = ZERO
if (fission < ZERO) fission = ZERO
if (capture < ZERO) capture = ZERO
! Set elastic, absorption, fission, and total cross sections. Note that the
! total cross section is calculated as sum of partials rather than using the
! table-provided value
micro_xs % elastic = elastic
micro_xs % absorption = capture + fission
micro_xs % fission = fission
micro_xs % total = elastic + inelastic + capture + fission
! Determine nu-fission cross section
if (this % fissionable) then
micro_xs % nu_fission = this % nu(E, EMISSION_TOTAL) * &
micro_xs % fission
end if
end associate
end subroutine calculate_urr_xs
!===============================================================================
! CHECK_DATA_VERSION checks for the right version of nuclear data within HDF5
! files

View file

@ -627,7 +627,7 @@ void scatter(Particle* p, int i_nuclide, int i_nuc_mat)
// Calculate elastic cross section if it wasn't precalculated
if (micro.elastic == CACHE_INVALID) {
nuc->calculate_elastic_xs(i_nuclide);
nuc->calculate_elastic_xs();
}
double prob = micro.elastic - micro.thermal;

View file

@ -1,25 +1,45 @@
#include "openmc/urr.h"
#include <iostream>
namespace openmc {
void
UrrData::from_hdf5(hid_t group_id)
{
// Read interpolation and other flags
read_attribute(group_id, "interpolation", interp_);
int interp_temp;
read_attribute(group_id, "interpolation", interp_temp);
switch (interp_temp) {
case static_cast<int>(Interpolation::histogram):
interp_ = Interpolation::histogram;
break;
case static_cast<int>(Interpolation::lin_lin):
interp_ = Interpolation::lin_lin;
break;
case static_cast<int>(Interpolation::lin_log):
interp_ = Interpolation::lin_log;
break;
case static_cast<int>(Interpolation::log_lin):
interp_ = Interpolation::log_lin;
break;
case static_cast<int>(Interpolation::log_log):
interp_ = Interpolation::log_log;
}
read_attribute(group_id, "inelastic", inelastic_flag_);
read_attribute(group_id, "absorption", absorption_flag_);
int i;
read_attribute(group_id, "multiply_smooth", i);
multiply_smooth_ = (i == 1);
int temp_multiply_smooth;
read_attribute(group_id, "multiply_smooth", temp_multiply_smooth);
multiply_smooth_ = (temp_multiply_smooth == 1);
// read the enrgies at which tables exist
hid_t dset = open_dataset(group_id, "energy");
hsize_t dims[1];
get_shape(dset, dims);
close_dataset(dset);
n_energy_ = static_cast<int>(dims[0]);
energy_ = xt::xtensor<double, 1>({n_energy_}, 0.);
// n_energy_ = static_cast<int>(dims[0]);
energy_ = xt::xtensor<double, 1>({dims[0]}, 0.);
read_dataset_as_shape(group_id, "energy", energy_);
// Read URR tables
@ -27,14 +47,9 @@ UrrData::from_hdf5(hid_t group_id)
hsize_t dims3[3];
get_shape(dset, dims3);
close_dataset(dset);
n_prob_ = static_cast<int>(dims3[0]);
xt::xarray<double> temp({n_energy_, 6, n_prob_});
read_dataset(group_id, "table", temp);
prob_ = xt::xtensor<double, 3>({n_energy_, 6, n_prob_}, 0.);
prob_ = temp;
//TODO: swap 1st and last indices?
xt::xarray<double> temp_arr({dims3[0], dims3[1], dims3[2]}, 0.);
read_dataset(group_id, "table", temp_arr);
prob_ = temp_arr;
}
}