Merge pull request #1419 from jtramm/particle_owned_seeds

Particle owned random number state variables
This commit is contained in:
Paul Romano 2019-12-06 14:49:23 -06:00 committed by GitHub
commit bc42e5ccf7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 567 additions and 481 deletions

View file

@ -1,6 +1,8 @@
#ifndef OPENMC_ANGLE_ENERGY_H
#define OPENMC_ANGLE_ENERGY_H
#include <cstdint>
namespace openmc {
//==============================================================================
@ -12,7 +14,8 @@ namespace openmc {
class AngleEnergy {
public:
virtual void sample(double E_in, double& E_out, double& mu) const = 0;
virtual void sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const = 0;
virtual ~AngleEnergy() = default;
};

View file

@ -21,7 +21,7 @@ namespace openmc {
class Distribution {
public:
virtual ~Distribution() = default;
virtual double sample() const = 0;
virtual double sample(uint64_t* seed) const = 0;
};
//==============================================================================
@ -34,8 +34,9 @@ public:
Discrete(const double* x, const double* p, int n);
//! Sample a value from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled value
double sample() const;
double sample(uint64_t* seed) const;
// Properties
const std::vector<double>& x() const { return x_; }
@ -58,8 +59,9 @@ public:
Uniform(double a, double b) : a_{a}, b_{b} {};
//! Sample a value from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled value
double sample() const;
double sample(uint64_t* seed) const;
private:
double a_; //!< Lower bound of distribution
double b_; //!< Upper bound of distribution
@ -75,8 +77,9 @@ public:
Maxwell(double theta) : theta_{theta} { };
//! Sample a value from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled value
double sample() const;
double sample(uint64_t* seed) const;
private:
double theta_; //!< Factor in exponential [eV]
};
@ -91,8 +94,9 @@ public:
Watt(double a, double b) : a_{a}, b_{b} { };
//! Sample a value from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled value
double sample() const;
double sample(uint64_t* seed) const;
private:
double a_; //!< Factor in exponential [eV]
double b_; //!< Factor in square root [1/eV]
@ -108,8 +112,9 @@ public:
Normal(double mean_value, double std_dev) : mean_value_{mean_value}, std_dev_{std_dev} { };
//! Sample a value from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled value
double sample() const;
double sample(uint64_t* seed) const;
private:
double mean_value_; //!< middle of distribution [eV]
double std_dev_; //!< standard deviation [eV]
@ -126,8 +131,9 @@ public:
Muir(double e0, double m_rat, double kt) : e0_{e0}, m_rat_{m_rat}, kt_{kt} { };
//! Sample a value from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled value
double sample() const;
double sample(uint64_t* seed) const;
private:
// example DT fusion m_rat = 5 (D = 2 + T = 3)
// ion temp = 20000 eV
@ -148,8 +154,9 @@ public:
const double* c=nullptr);
//! Sample a value from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled value
double sample() const;
double sample(uint64_t* seed) const;
// x property
std::vector<double>& x() { return x_; }
@ -178,8 +185,9 @@ public:
Equiprobable(const double* x, int n) : x_{x, x+n} { };
//! Sample a value from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled value
double sample() const;
double sample(uint64_t* seed) const;
private:
std::vector<double> x_; //! Possible outcomes
};

View file

@ -23,8 +23,9 @@ public:
//! Sample an angle given an incident particle energy
//! \param[in] E Particle energy in [eV]
//! \param[inout] seed pseudorandom number seed pointer
//! \return Cosine of the angle in the range [-1,1]
double sample(double E) const;
double sample(double E, uint64_t* seed) const;
//! Determine whether angle distribution is empty
//! \return Whether distribution is empty

View file

@ -22,7 +22,7 @@ namespace openmc {
class EnergyDistribution {
public:
virtual double sample(double E) const = 0;
virtual double sample(double E, uint64_t* seed) const = 0;
virtual ~EnergyDistribution() = default;
};
@ -36,8 +36,9 @@ public:
//! Sample energy distribution
//! \param[in] E Incident particle energy in [eV]
//! \param[inout] seed Pseudorandom number seed pointer
//! \return Sampled energy in [eV]
double sample(double E) const;
double sample(double E, uint64_t* seed) const;
private:
int primary_flag_; //!< Indicator of whether the photon is a primary or
//!< non-primary photon.
@ -55,8 +56,9 @@ public:
//! Sample energy distribution
//! \param[in] E Incident particle energy in [eV]
//! \param[inout] seed Pseudorandom number seed pointer
//! \return Sampled energy in [eV]
double sample(double E) const;
double sample(double E, uint64_t* seed) const;
private:
double threshold_; //!< Energy threshold in lab, (A + 1)/A * |Q|
double mass_ratio_; //!< (A/(A+1))^2
@ -74,8 +76,9 @@ public:
//! Sample energy distribution
//! \param[in] E Incident particle energy in [eV]
//! \param[inout] seed Pseudorandom number seed pointer
//! \return Sampled energy in [eV]
double sample(double E) const;
double sample(double E, uint64_t* seed) const;
private:
//! Outgoing energy for a single incoming energy
struct CTTable {
@ -103,8 +106,9 @@ public:
//! Sample energy distribution
//! \param[in] E Incident particle energy in [eV]
//! \param[inout] seed Pseudorandom number seed pointer
//! \return Sampled energy in [eV]
double sample(double E) const;
double sample(double E, uint64_t* seed) const;
private:
Tabulated1D theta_; //!< Incoming energy dependent parameter
double u_; //!< Restriction energy
@ -121,8 +125,9 @@ public:
//! Sample energy distribution
//! \param[in] E Incident particle energy in [eV]
//! \param[inout] seed Pseudorandom number seed pointer
//! \return Sampled energy in [eV]
double sample(double E) const;
double sample(double E, uint64_t* seed) const;
private:
Tabulated1D theta_; //!< Incoming energy dependent parameter
double u_; //!< Restriction energy
@ -139,8 +144,9 @@ public:
//! Sample energy distribution
//! \param[in] E Incident particle energy in [eV]
//! \param[inout] seed Pseudorandom number seed pointer
//! \return Sampled energy in [eV]
double sample(double E) const;
double sample(double E, uint64_t* seed) const;
private:
Tabulated1D a_; //!< Energy-dependent 'a' parameter
Tabulated1D b_; //!< Energy-dependent 'b' parameter

View file

@ -23,8 +23,9 @@ public:
virtual ~UnitSphereDistribution() = default;
//! Sample a direction from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Direction sampled
virtual Direction sample() const = 0;
virtual Direction sample(uint64_t* seed) const = 0;
Direction u_ref_ {0.0, 0.0, 1.0}; //!< reference direction
};
@ -39,8 +40,9 @@ public:
explicit PolarAzimuthal(pugi::xml_node node);
//! Sample a direction from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Direction sampled
Direction sample() const;
Direction sample(uint64_t* seed) const;
private:
UPtrDist mu_; //!< Distribution of polar angle
UPtrDist phi_; //!< Distribution of azimuthal angle
@ -55,8 +57,9 @@ public:
Isotropic() { };
//! Sample a direction from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled direction
Direction sample() const;
Direction sample(uint64_t* seed) const;
};
//==============================================================================
@ -69,8 +72,9 @@ public:
explicit Monodirectional(pugi::xml_node node) : UnitSphereDistribution{node} { };
//! Sample a direction from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled direction
Direction sample() const;
Direction sample(uint64_t* seed) const;
};
using UPtrAngle = std::unique_ptr<UnitSphereDistribution>;

View file

@ -17,7 +17,7 @@ public:
virtual ~SpatialDistribution() = default;
//! Sample a position from the distribution
virtual Position sample() const = 0;
virtual Position sample(uint64_t* seed) const = 0;
};
//==============================================================================
@ -29,8 +29,9 @@ public:
explicit CartesianIndependent(pugi::xml_node node);
//! Sample a position from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled position
Position sample() const;
Position sample(uint64_t* seed) const;
private:
UPtrDist x_; //!< Distribution of x coordinates
UPtrDist y_; //!< Distribution of y coordinates
@ -46,8 +47,9 @@ public:
explicit SphericalIndependent(pugi::xml_node node);
//! Sample a position from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled position
Position sample() const;
Position sample(uint64_t* seed) const;
private:
UPtrDist r_; //!< Distribution of r coordinates
UPtrDist theta_; //!< Distribution of theta coordinates
@ -64,8 +66,9 @@ public:
explicit SpatialBox(pugi::xml_node node, bool fission=false);
//! Sample a position from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled position
Position sample() const;
Position sample(uint64_t* seed) const;
// Properties
bool only_fissionable() const { return only_fissionable_; }
@ -86,8 +89,9 @@ public:
explicit SpatialPoint(pugi::xml_node node);
//! Sample a position from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled position
Position sample() const;
Position sample(uint64_t* seed) const;
private:
Position r_; //!< Single position at which sites are generated
};

View file

@ -129,11 +129,14 @@ extern "C" void calc_zn_rad(int n, double rho, double zn_rad[]);
//! \param mu The cosine of angle in lab or CM
//! \param phi The azimuthal angle; will randomly chosen angle if a nullptr
//! is passed
//! \param seed A pointer to the pseudorandom seed
//==============================================================================
extern "C" void rotate_angle_c(double uvw[3], double mu, const double* phi);
extern "C" void rotate_angle_c(double uvw[3], double mu, const double* phi,
uint64_t* seed);
Direction rotate_angle(Direction u, double mu, const double* phi);
Direction rotate_angle(Direction u, double mu, const double* phi,
uint64_t* seed);
//==============================================================================
//! Samples an energy from the Maxwell fission distribution based on a direct
@ -144,10 +147,11 @@ Direction rotate_angle(Direction u, double mu, const double* phi);
//! rule C64 in the Monte Carlo Sampler LA-9721-MS.
//!
//! \param T The tabulated function of the incoming energy
//! \param seed A pointer to the pseudorandom seed
//! \return The sampled outgoing energy
//==============================================================================
extern "C" double maxwell_spectrum(double T);
extern "C" double maxwell_spectrum(double T, uint64_t* seed);
//==============================================================================
//! Samples an energy from a Watt energy-dependent fission distribution.
@ -159,10 +163,11 @@ extern "C" double maxwell_spectrum(double T);
//!
//! \param a Watt parameter a
//! \param b Watt parameter b
//! \param seed A pointer to the pseudorandom seed
//! \return The sampled outgoing energy
//==============================================================================
extern "C" double watt_spectrum(double a, double b);
extern "C" double watt_spectrum(double a, double b, uint64_t* seed);
//==============================================================================
//! Samples an energy from the Gaussian energy-dependent fission distribution.
@ -175,10 +180,11 @@ extern "C" double watt_spectrum(double a, double b);
//!
//! @param mean mean of the Gaussian distribution
//! @param std_dev standard deviation of the Gaussian distribution
//! @param seed A pointer to the pseudorandom seed
//! @result The sampled outgoing energy
//==============================================================================
extern "C" double normal_variate(double mean, double std_dev);
extern "C" double normal_variate(double mean, double std_dev, uint64_t* seed);
//==============================================================================
//! Samples an energy from the Muir (Gaussian) energy-dependent distribution.
@ -190,10 +196,12 @@ extern "C" double normal_variate(double mean, double std_dev);
//! @param e0 peak neutron energy [eV]
//! @param m_rat ratio of the fusion reactants to AMU
//! @param kt the ion temperature of the reactants [eV]
//! @param seed A pointer to the pseudorandom seed
//! @result The sampled outgoing energy
//==============================================================================
extern "C" double muir_spectrum(double e0, double m_rat, double kt);
extern "C" double muir_spectrum(double e0, double m_rat, double kt,
uint64_t* seed);
//==============================================================================
//! Doppler broadens the windowed multipole curvefit.

View file

@ -154,8 +154,9 @@ class Mgxs {
//! @param gin Incoming energy group.
//! @param dg Sampled delayed group index.
//! @param gout Sampled outgoing energy group.
//! @param seed Pseudorandom seed pointer
void
sample_fission_energy(int gin, int& dg, int& gout);
sample_fission_energy(int gin, int& dg, int& gout, uint64_t* seed);
//! \brief Samples the outgoing energy and angle from a scatter event.
//!
@ -163,8 +164,9 @@ class Mgxs {
//! @param gout Sampled outgoing energy group.
//! @param mu Sampled cosine of the change-in-angle.
//! @param wgt Weight of the particle to be adjusted.
//! @param seed Pseudorandom seed pointer.
void
sample_scatter(int gin, int& gout, double& mu, double& wgt);
sample_scatter(int gin, int& gout, double& mu, double& wgt, uint64_t* seed);
//! \brief Calculates cross section quantities needed for tracking.
//!

View file

@ -13,6 +13,7 @@
#include "openmc/constants.h"
#include "openmc/position.h"
#include "openmc/random_lcg.h"
namespace openmc {
@ -217,6 +218,10 @@ public:
//! create a particle restart HDF5 file
void write_restart() const;
//! Gets the pointer to the particle's current PRN seed
uint64_t* current_seed() {return seeds_ + stream_;}
const uint64_t* current_seed() const {return seeds_ + stream_;}
//==========================================================================
// Data members
@ -285,6 +290,10 @@ public:
// Track output
bool write_track_ {false};
// Current PRNG state
uint64_t seeds_[N_STREAMS]; // current seeds
int stream_; // current RNG stream
};
} // namespace openmc

View file

@ -45,12 +45,12 @@ public:
void calculate_xs(Particle& p) const;
void compton_scatter(double alpha, bool doppler, double* alpha_out,
double* mu, int* i_shell) const;
double* mu, int* i_shell, uint64_t* seed) const;
double rayleigh_scatter(double alpha) const;
double rayleigh_scatter(double alpha, uint64_t* seed) const;
void pair_production(double alpha, double* E_electron, double* E_positron,
double* mu_electron, double* mu_positron) const;
double* mu_electron, double* mu_positron, uint64_t* seed) const;
void atomic_relaxation(const ElectronSubshell& shell, Particle& p) const;
@ -96,14 +96,15 @@ public:
xt::xtensor<double, 2> dcs_;
private:
void compton_doppler(double alpha, double mu, double* E_out, int* i_shell) const;
void compton_doppler(double alpha, double mu, double* E_out, int* i_shell,
uint64_t* seed) const;
};
//==============================================================================
// Non-member functions
//==============================================================================
std::pair<double, double> klein_nishina(double alpha);
std::pair<double, double> klein_nishina(double alpha, uint64_t* seed);
void free_memory_photon();

View file

@ -44,7 +44,7 @@ void sample_positron_reaction(Particle* p);
//!
//! \param[in] p Particle
//! \return Index in the data::nuclides vector
int sample_nuclide(const Particle* p);
int sample_nuclide(Particle* p);
//! Determine the average total, prompt, and delayed neutrons produced from
//! fission and creates appropriate bank sites.
@ -53,13 +53,13 @@ void create_fission_sites(Particle* p, int i_nuclide, const Reaction* rx,
int sample_element(Particle* p);
Reaction* sample_fission(int i_nuclide, const Particle* p);
Reaction* sample_fission(int i_nuclide, Particle* p);
void sample_photon_product(int i_nuclide, const Particle* p, int* i_rx, int* i_product);
void sample_photon_product(int i_nuclide, Particle* p, int* i_rx, int* i_product);
void absorption(Particle* p, int i_nuclide);
void scatter(Particle*, int i_nuclide);
void scatter(Particle* p, int i_nuclide);
//! Treats the elastic scattering of a neutron with a target.
void elastic_scatter(int i_nuclide, const Reaction& rx, double kT,
@ -72,15 +72,17 @@ void sab_scatter(int i_nuclide, int i_sab, Particle* p);
//! dependence of cross sections in treating resonance elastic scattering such
//! as the DBRC and a new, accelerated scheme are also implemented here.
Direction sample_target_velocity(const Nuclide* nuc, double E, Direction u,
Direction v_neut, double xs_eff, double kT);
Direction v_neut, double xs_eff, double kT, uint64_t* seed);
//! samples a target velocity based on the free gas scattering formulation, used
//! by most Monte Carlo codes, in which cross section is assumed to be constant
//! in energy. Excellent documentation for this method can be found in
//! FRA-TM-123.
Direction sample_cxs_target_velocity(double awr, double E, Direction u, double kT);
Direction sample_cxs_target_velocity(double awr, double E, Direction u, double kT,
uint64_t* seed);
void sample_fission_neutron(int i_nuclide, const Reaction* rx, double E_in, Particle::Bank* site);
void sample_fission_neutron(int i_nuclide, const Reaction* rx, double E_in,
Particle::Bank* site, uint64_t* seed);
//! handles all reactions with a single secondary neutron (other than fission),
//! i.e. level scattering, (n,np), (n,na), etc.

View file

@ -14,6 +14,7 @@
#include "openmc/geometry.h"
#include "openmc/particle.h"
#include "openmc/xml_interface.h"
#include "openmc/random_lcg.h"
namespace openmc {
@ -28,6 +29,9 @@ namespace model {
extern std::vector<Plot> plots; //!< Plot instance container
extern std::unordered_map<int, int> plot_map; //!< map of plot ids to index
extern uint64_t plotter_prn_seeds[N_STREAMS]; // Random number seeds used for plotter
extern int plotter_stream; // Stream index used by the plotter
} // namespace model
//===============================================================================

View file

@ -10,46 +10,66 @@ namespace openmc {
// Module constants.
//==============================================================================
extern "C" const int N_STREAMS;
extern "C" const int STREAM_TRACKING;
extern "C" const int STREAM_TALLIES;
extern "C" const int STREAM_SOURCE;
extern "C" const int STREAM_URR_PTABLE;
extern "C" const int STREAM_VOLUME;
extern "C" const int STREAM_PHOTON;
constexpr int64_t DEFAULT_SEED = 1;
constexpr int N_STREAMS {6};
constexpr int STREAM_TRACKING {0};
constexpr int STREAM_TALLIES {1};
constexpr int STREAM_SOURCE {2};
constexpr int STREAM_URR_PTABLE {3};
constexpr int STREAM_VOLUME {4};
constexpr int STREAM_PHOTON {5};
constexpr int64_t DEFAULT_SEED {1};
//==============================================================================
//! Generate a pseudo-random number using a linear congruential generator.
//! @param seed Pseudorandom number seed pointer
//! @return A random number between 0 and 1
//==============================================================================
extern "C" double prn();
double prn(uint64_t* seed);
//==============================================================================
//! Generate a random number which is 'n' times ahead from the current seed.
//!
//! The result of this function will be the same as the result from calling
//! `prn()` 'n' times.
//! `prn()` 'n' times, though without the side effect of altering the RNG
//! state.
//! @param n The number of RNG seeds to skip ahead by
//! @param seed Pseudorandom number seed
//! @return A random number between 0 and 1
//==============================================================================
extern "C" double future_prn(int64_t n);
double future_prn(int64_t n, uint64_t seed);
//==============================================================================
//! Set the RNG seed to a unique value based on the ID of the particle.
//! Set a RNG seed to a unique value based on a unique particle ID by striding
//! the seed.
//! @param id The particle ID
//! @param offset The offset from the master seed to be used (e.g., for creating
//! different streams)
//! @return The initialized seed value
//==============================================================================
uint64_t init_seed(int64_t id, int offset);
//==============================================================================
//! Set the RNG seeds to unique values based on the ID of the particle. This
//! function initializes the seeds for all RNG streams of the particle via
//! striding.
//! @param seeds Pseudorandom number seed array
//! @param id The particle ID
//==============================================================================
extern "C" void set_particle_seed(int64_t id);
void init_particle_seeds(int64_t id, uint64_t* seeds);
//==============================================================================
//! Advance the random number seed 'n' times from the current seed.
//! Advance the random number seed 'n' times from the current seed. This
//! differs from the future_prn() function in that this function does alter
//! the RNG state.
//! @param seed Pseudorandom number seed pointer
//! @param n The number of RNG seeds to skip ahead by
//==============================================================================
extern "C" void advance_prn_seed(int64_t n);
void advance_prn_seed(int64_t n, uint64_t* seed);
//==============================================================================
//! Advance a random number seed 'n' times.
@ -63,18 +83,6 @@ extern "C" void advance_prn_seed(int64_t n);
uint64_t future_seed(uint64_t n, uint64_t seed);
//==============================================================================
//! Switch the RNG to a different stream of random numbers.
//!
//! If random numbers are needed in routines not used directly for tracking
//! (e.g. physics), this allows the numbers to be generated without affecting
//! reproducibility of the physics.
//! @param n The RNG stream to switch to. Use the constants such as
//! `STREAM_TRACKING` and `STREAM_TALLIES` for this argument.
//==============================================================================
extern "C" void prn_set_stream(int n);
//==============================================================================
// API FUNCTIONS
//==============================================================================

View file

@ -42,7 +42,8 @@ public:
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const;
//! \param[inout] seed Pseudorandom seed pointer
void sample(double E_in, double& E_out, double& mu, uint64_t* seed) const;
Particle::Type particle_; //!< Particle type
EmissionMode emission_mode_; //!< Emission mode

View file

@ -65,8 +65,9 @@ class ScattData {
//! @param gout Sampled outgoing energy group.
//! @param mu Sampled cosine of the change-in-angle.
//! @param wgt Weight of the particle to be adjusted.
//! @param seed Pseudorandom number seed pointer
virtual void
sample(int gin, int& gout, double& mu, double& wgt) = 0;
sample(int gin, int& gout, double& mu, double& wgt, uint64_t* seed) = 0;
//! \brief Initializes the ScattData object from a given scatter and
//! multiplicity matrix.
@ -109,8 +110,9 @@ class ScattData {
//! @param gin Incoming energy group.
//! @param gout Sampled outgoing energy group.
//! @param i_gout Sampled outgoing energy group index.
//! @param seed Pseudorandom number seed pointer
void
sample_energy(int gin, int& gout, int& i_gout);
sample_energy(int gin, int& gout, int& i_gout, uint64_t* seed);
//! \brief Provides a cross section value given certain parameters
//!
@ -161,7 +163,7 @@ class ScattDataLegendre: public ScattData {
calc_f(int gin, int gout, double mu);
void
sample(int gin, int& gout, double& mu, double& wgt);
sample(int gin, int& gout, double& mu, double& wgt, uint64_t* seed);
size_t
get_order() {return dist[0][0].size() - 1;};
@ -197,7 +199,7 @@ class ScattDataHistogram: public ScattData {
calc_f(int gin, int gout, double mu);
void
sample(int gin, int& gout, double& mu, double& wgt);
sample(int gin, int& gout, double& mu, double& wgt, uint64_t* seed);
size_t
get_order() {return dist[0][0].size();};
@ -238,7 +240,7 @@ class ScattDataTabular: public ScattData {
calc_f(int gin, int gout, double mu);
void
sample(int gin, int& gout, double& mu, double& wgt);
sample(int gin, int& gout, double& mu, double& wgt, uint64_t* seed);
size_t
get_order() {return dist[0][0].size();};

View file

@ -38,7 +38,9 @@ public:
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const override;
//! \param[inout] seed Pseudorandom seed pointer
void sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const override;
// energy property
std::vector<double>& energy() { return energy_; }

View file

@ -29,7 +29,9 @@ public:
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const override;
//! \param[inout] seed Pseudorandom seed pointer
void sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const override;
private:
//! Outgoing energy/angle at a single incoming energy
struct KMTable {

View file

@ -24,7 +24,9 @@ public:
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const override;
//! \param[inout] seed Pseudorandom seed pointer
void sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const override;
private:
int n_bodies_; //!< Number of particles distributed
double mass_ratio_; //!< Total mass of particles [neutron mass]

View file

@ -31,7 +31,9 @@ public:
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const override;
//! \param[inout] seed Pseudorandom seed pointer
void sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const override;
private:
const CoherentElasticXS& xs_; //!< Coherent elastic scattering cross section
};
@ -51,7 +53,9 @@ public:
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const override;
//! \param[inout] seed Pseudorandom number seed pointer
void sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const override;
private:
double debye_waller_;
};
@ -72,7 +76,9 @@ public:
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const override;
//! \param[inout] seed Pseudorandom number seed pointer
void sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const override;
private:
const std::vector<double>& energy_; //!< Energies at which cosines are tabulated
xt::xtensor<double, 2> mu_out_; //!< Cosines for each incident energy
@ -94,7 +100,9 @@ public:
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const override;
//! \param[inout] seed Pseudorandom number seed pointer
void sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const override;
private:
const std::vector<double>& energy_; //!< Incident energies
xt::xtensor<double, 2> energy_out_; //!< Outgoing energies for each incident energy
@ -117,7 +125,9 @@ public:
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const override;
//! \param[inout] seed Pseudorandom number seed pointer
void sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const override;
private:
//! Secondary energy/angle distribution
struct DistEnergySab {

View file

@ -29,7 +29,9 @@ public:
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const override;
//! \param[inout] seed Pseudorandom seed pointer
void sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const override;
// Accessors
AngleDistribution& angle() { return angle_; }

View file

@ -38,8 +38,9 @@ public:
explicit SourceDistribution(pugi::xml_node node);
//! Sample from the external source distribution
//! \param[inout] seed Pseudorandom seed pointer
//! \return Sampled site
Particle::Bank sample() const;
Particle::Bank sample(uint64_t* seed) const;
// Properties
double strength() const { return strength_; }
@ -60,8 +61,9 @@ extern "C" void initialize_source();
//! Sample a site from all external source distributions in proportion to their
//! source strength
//! \param[inout] seed Pseudorandom seed pointer
//! \return Sampled source site
Particle::Bank sample_external_source();
Particle::Bank sample_external_source(uint64_t* seed);
//! Fill source bank at end of generation for fixed source simulations
void fill_source_bank_fixedsource();

View file

@ -116,7 +116,8 @@ public:
//! \return Outgoing direction of the ray
virtual Direction reflect(Position r, Direction u) const;
virtual Direction diffuse_reflect(Position r, Direction u) const;
virtual Direction diffuse_reflect(Position r, Direction u,
uint64_t* seed) const;
//! Evaluate the equation describing the surface.
//!

View file

@ -64,8 +64,9 @@ public:
//! \param[in] E_in Incident neutron energy in [eV]
//! \param[out] E_out Outgoing neutron energy in [eV]
//! \param[out] mu Outgoing scattering angle cosine
//! \param[inout] seed Pseudorandom seed pointer
void sample(const NuclideMicroXS& micro_xs, double E_in,
double* E_out, double* mu);
double* E_out, double* mu, uint64_t* seed);
private:
struct Reaction {
// Default constructor
@ -100,8 +101,9 @@ public:
//! \param[out] i_temp corresponding temperature index
//! \param[out] elastic Thermal elastic scattering cross section
//! \param[out] inelastic Thermal inelastic scattering cross section
//! \param[inout] seed Pseudorandom seed pointer
void calculate_xs(double E, double sqrtkT, int* i_temp, double* elastic,
double* inelastic) const;
double* inelastic, uint64_t* seed) const;
//! Determine whether table applies to a particular nuclide
//!

View file

@ -1,10 +1,12 @@
from ctypes import (c_int, c_double, POINTER)
from ctypes import c_int, c_double, POINTER, c_uint64
import numpy as np
from numpy.ctypeslib import ndpointer
from . import _dll
from random import getrandbits
_dll.t_percentile.restype = c_double
_dll.t_percentile.argtypes = [c_double, c_int]
@ -26,19 +28,19 @@ _dll.calc_zn_rad.argtypes = [c_int, c_double, ndpointer(c_double)]
_dll.rotate_angle_c.restype = None
_dll.rotate_angle_c.argtypes = [ndpointer(c_double), c_double,
POINTER(c_double)]
POINTER(c_double), POINTER(c_uint64)]
_dll.maxwell_spectrum.restype = c_double
_dll.maxwell_spectrum.argtypes = [c_double]
_dll.maxwell_spectrum.argtypes = [c_double, POINTER(c_uint64)]
_dll.watt_spectrum.restype = c_double
_dll.watt_spectrum.argtypes = [c_double, c_double]
_dll.watt_spectrum.argtypes = [c_double, c_double, POINTER(c_uint64)]
_dll.broaden_wmp_polynomials.restype = None
_dll.broaden_wmp_polynomials.argtypes = [c_double, c_double, c_int,
ndpointer(c_double)]
_dll.normal_variate.restype = c_double
_dll.normal_variate.argtypes = [c_double, c_double]
_dll.normal_variate.argtypes = [c_double, c_double, POINTER(c_uint64)]
def t_percentile(p, df):
""" Calculate the percentile of the Student's t distribution with a
@ -185,7 +187,7 @@ def calc_zn_rad(n, rho):
return zn_rad
def rotate_angle(uvw0, mu, phi=None):
def rotate_angle(uvw0, mu, phi, prn_seed=None):
""" Rotates direction cosines through a polar angle whose cosine is
mu and through an azimuthal angle sampled uniformly.
@ -195,8 +197,10 @@ def rotate_angle(uvw0, mu, phi=None):
Original direction cosine
mu : float
Polar angle cosine to rotate
phi : float, optional
phi : float
Azimuthal angle; if None, one will be sampled uniformly
prn_seed : int
PRNG seed; if None, one will be generated randomly
Returns
-------
@ -205,18 +209,21 @@ def rotate_angle(uvw0, mu, phi=None):
"""
uvw0_arr = np.array(uvw0, dtype=np.float64)
if prn_seed is None:
prn_seed = getrandbits(63)
uvw0_arr = np.array(uvw0, dtype=np.float64)
if phi is None:
_dll.rotate_angle_c(uvw0_arr, mu, None)
_dll.rotate_angle_c(uvw0_arr, mu, None, c_uint64(prn_seed))
else:
_dll.rotate_angle_c(uvw0_arr, mu, c_double(phi))
_dll.rotate_angle_c(uvw0_arr, mu, c_double(phi), c_uint64(prn_seed))
uvw = uvw0_arr
return uvw
def maxwell_spectrum(T):
def maxwell_spectrum(T, prn_seed=None):
""" Samples an energy from the Maxwell fission distribution based
on a direct sampling scheme.
@ -224,6 +231,8 @@ def maxwell_spectrum(T):
----------
T : float
Spectrum parameter
prn_seed : int
PRNG seed; if None, one will be generated randomly
Returns
-------
@ -231,11 +240,14 @@ def maxwell_spectrum(T):
Sampled outgoing energy
"""
return _dll.maxwell_spectrum(T)
if prn_seed is None:
prn_seed = getrandbits(63)
return _dll.maxwell_spectrum(T, c_uint64(prn_seed))
def watt_spectrum(a, b):
def watt_spectrum(a, b, prn_seed=None):
""" Samples an energy from the Watt energy-dependent fission spectrum.
Parameters
@ -244,6 +256,8 @@ def watt_spectrum(a, b):
Spectrum parameter a
b : float
Spectrum parameter b
prn_seed : int
PRNG seed; if None, one will be generated randomly
Returns
-------
@ -251,11 +265,14 @@ def watt_spectrum(a, b):
Sampled outgoing energy
"""
if prn_seed is None:
prn_seed = getrandbits(63)
return _dll.watt_spectrum(a, b)
return _dll.watt_spectrum(a, b, c_uint64(prn_seed))
def normal_variate(mean_value, std_dev):
def normal_variate(mean_value, std_dev, prn_seed=None):
""" Samples an energy from the Normal distribution.
Parameters
@ -264,6 +281,8 @@ def normal_variate(mean_value, std_dev):
Mean of the Normal distribution
std_dev : float
Standard deviation of the normal distribution
prn_seed : int
PRNG seed; if None, one will be generated randomly
Returns
-------
@ -271,8 +290,11 @@ def normal_variate(mean_value, std_dev):
Sampled outgoing normally distributed value
"""
if prn_seed is None:
prn_seed = getrandbits(63)
return _dll.normal_variate(mean_value, std_dev)
return _dll.normal_variate(mean_value, std_dev, c_uint64(prn_seed))
def broaden_wmp_polynomials(E, dopp, n):

View file

@ -65,7 +65,7 @@ void thick_target_bremsstrahlung(Particle& p, double* E_lost)
double y = std::exp(y_l + (y_r - y_l)*f);
// Sample number of secondary bremsstrahlung photons
int n = y + prn();
int n = y + prn(p.current_seed());
*E_lost = 0.0;
if (n == 0) return;
@ -73,7 +73,7 @@ void thick_target_bremsstrahlung(Particle& p, double* E_lost)
// Sample index of the tabulated PDF in the energy grid, j or j+1
double c_max;
int i_e;
if (prn() <= f || j == 0) {
if (prn(p.current_seed()) <= f || j == 0) {
i_e = j + 1;
// Interpolate the maximum value of the CDF at the incoming particle
@ -94,7 +94,7 @@ void thick_target_bremsstrahlung(Particle& p, double* E_lost)
for (int i = 0; i < n; ++i) {
// Generate a random number r and determine the index i for which
// cdf(i) <= r*cdf,max <= cdf(i+1)
double c = prn()*c_max;
double c = prn(p.current_seed())*c_max;
int i_w = lower_bound_index(&mat->cdf(i_e, 0), &mat->cdf(i_e, 0) + i_e, c);
// Sample the photon energy

View file

@ -35,11 +35,11 @@ Discrete::Discrete(const double* x, const double* p, int n)
normalize();
}
double Discrete::sample() const
double Discrete::sample(uint64_t* seed) const
{
int n = x_.size();
if (n > 1) {
double xi = prn();
double xi = prn(seed);
double c = 0.0;
for (int i = 0; i < n; ++i) {
c += p_[i];
@ -74,9 +74,9 @@ Uniform::Uniform(pugi::xml_node node)
b_ = params.at(1);
}
double Uniform::sample() const
double Uniform::sample(uint64_t* seed) const
{
return a_ + prn()*(b_ - a_);
return a_ + prn(seed)*(b_ - a_);
}
//==============================================================================
@ -88,9 +88,9 @@ Maxwell::Maxwell(pugi::xml_node node)
theta_ = std::stod(get_node_value(node, "parameters"));
}
double Maxwell::sample() const
double Maxwell::sample(uint64_t* seed) const
{
return maxwell_spectrum(theta_);
return maxwell_spectrum(theta_, seed);
}
//==============================================================================
@ -108,15 +108,15 @@ Watt::Watt(pugi::xml_node node)
b_ = params.at(1);
}
double Watt::sample() const
double Watt::sample(uint64_t* seed) const
{
return watt_spectrum(a_, b_);
return watt_spectrum(a_, b_, seed);
}
//==============================================================================
// Normal implementation
//==============================================================================
Normal::Normal(pugi::xml_node node)
Normal::Normal(pugi::xml_node node)
{
auto params = get_node_array<double>(node,"parameters");
if (params.size() != 2)
@ -127,15 +127,15 @@ Normal::Normal(pugi::xml_node node)
std_dev_ = params.at(1);
}
double Normal::sample() const
double Normal::sample(uint64_t* seed) const
{
return normal_variate(mean_value_, std_dev_);
return normal_variate(mean_value_, std_dev_, seed);
}
//==============================================================================
// Muir implementation
//==============================================================================
Muir::Muir(pugi::xml_node node)
Muir::Muir(pugi::xml_node node)
{
auto params = get_node_array<double>(node,"parameters");
if (params.size() != 3)
@ -147,9 +147,9 @@ Muir::Muir(pugi::xml_node node)
kt_ = params.at(2);
}
double Muir::sample() const
double Muir::sample(uint64_t* seed) const
{
return muir_spectrum(e0_, m_rat_, kt_);
return muir_spectrum(e0_, m_rat_, kt_, seed);
}
//==============================================================================
@ -220,10 +220,10 @@ void Tabular::init(const double* x, const double* p, std::size_t n, const double
}
}
double Tabular::sample() const
double Tabular::sample(uint64_t* seed) const
{
// Sample value of CDF
double c = prn();
double c = prn(seed);
// Find first CDF bin which is above the sampled value
double c_i = c_[0];
@ -263,11 +263,11 @@ double Tabular::sample() const
// Equiprobable implementation
//==============================================================================
double Equiprobable::sample() const
double Equiprobable::sample(uint64_t* seed) const
{
std::size_t n = x_.size();
double r = prn();
double r = prn(seed);
int i = std::floor((n - 1)*r);
double xl = x_[i];

View file

@ -62,7 +62,7 @@ AngleDistribution::AngleDistribution(hid_t group)
}
}
double AngleDistribution::sample(double E) const
double AngleDistribution::sample(double E, uint64_t* seed) const
{
// Determine number of incoming energies
auto n = energy_.size();
@ -83,10 +83,10 @@ double AngleDistribution::sample(double E) const
}
// Sample between the ith and (i+1)th bin
if (r > prn()) ++i;
if (r > prn(seed)) ++i;
// Sample i-th distribution
double mu = distribution_[i]->sample();
double mu = distribution_[i]->sample(seed);
// Make sure mu is in range [-1,1] and return
if (std::abs(mu) > 1.0) mu = std::copysign(1.0, mu);

View file

@ -25,7 +25,7 @@ DiscretePhoton::DiscretePhoton(hid_t group)
read_attribute(group, "atomic_weight_ratio", A_);
}
double DiscretePhoton::sample(double E) const
double DiscretePhoton::sample(double E, uint64_t* seed) const
{
if (primary_flag_ == 2) {
return energy_ + A_/(A_+ 1)*E;
@ -44,7 +44,7 @@ LevelInelastic::LevelInelastic(hid_t group)
read_attribute(group, "mass_ratio", mass_ratio_);
}
double LevelInelastic::sample(double E) const
double LevelInelastic::sample(double E, uint64_t* seed) const
{
return mass_ratio_*(E - threshold_);
}
@ -146,7 +146,7 @@ ContinuousTabular::ContinuousTabular(hid_t group)
} // incoming energies
}
double ContinuousTabular::sample(double E) const
double ContinuousTabular::sample(double E, uint64_t* seed) const
{
// Read number of interpolation regions and incoming energies
bool histogram_interp;
@ -177,7 +177,7 @@ double ContinuousTabular::sample(double E) const
if (histogram_interp) {
l = i;
} else {
l = r > prn() ? i + 1 : i;
l = r > prn(seed) ? i + 1 : i;
}
// Interpolation for energy E1 and EK
@ -197,7 +197,7 @@ double ContinuousTabular::sample(double E) const
// Determine outgoing energy bin
n_energy_out = distribution_[l].e_out.size();
n_discrete = distribution_[l].n_discrete;
double r1 = prn();
double r1 = prn(seed);
double c_k = distribution_[l].c[0];
int k = 0;
int end = n_energy_out - 2;
@ -275,14 +275,14 @@ MaxwellEnergy::MaxwellEnergy(hid_t group)
close_dataset(dset);
}
double MaxwellEnergy::sample(double E) const
double MaxwellEnergy::sample(double E, uint64_t* seed) const
{
// Get temperature corresponding to incoming energy
double theta = theta_(E);
while (true) {
// Sample maxwell fission spectrum
double E_out = maxwell_spectrum(theta);
double E_out = maxwell_spectrum(theta, seed);
// Accept energy based on restriction energy
if (E_out <= E - u_) return E_out;
@ -301,7 +301,7 @@ Evaporation::Evaporation(hid_t group)
close_dataset(dset);
}
double Evaporation::sample(double E) const
double Evaporation::sample(double E, uint64_t* seed) const
{
// Get temperature corresponding to incoming energy
double theta = theta_(E);
@ -313,7 +313,7 @@ double Evaporation::sample(double E) const
// density function
double x;
while (true) {
x = -std::log((1.0 - v*prn())*(1.0 - v*prn()));
x = -std::log((1.0 - v*prn(seed))*(1.0 - v*prn(seed)));
if (x <= y) break;
}
@ -338,7 +338,7 @@ WattEnergy::WattEnergy(hid_t group)
close_dataset(dset);
}
double WattEnergy::sample(double E) const
double WattEnergy::sample(double E, uint64_t* seed) const
{
// Determine Watt parameters at incident energy
double a = a_(E);
@ -346,7 +346,7 @@ double WattEnergy::sample(double E) const
while (true) {
// Sample energy-dependent Watt fission spectrum
double E_out = watt_spectrum(a, b);
double E_out = watt_spectrum(a, b, seed);
// Accept energy based on restriction energy
if (E_out <= E - u_) return E_out;

View file

@ -53,31 +53,31 @@ PolarAzimuthal::PolarAzimuthal(pugi::xml_node node)
}
}
Direction PolarAzimuthal::sample() const
Direction PolarAzimuthal::sample(uint64_t* seed) const
{
// Sample cosine of polar angle
double mu = mu_->sample();
double mu = mu_->sample(seed);
if (mu == 1.0) return u_ref_;
// Sample azimuthal angle
double phi = phi_->sample();
double phi = phi_->sample(seed);
// If the reference direction is along the z-axis, rotate the aziumthal angle
// to match spherical coordinate conventions.
// TODO: apply this change directly to rotate_angle
if (u_ref_.x == 0 && u_ref_.y == 0) phi += 0.5*PI;
return rotate_angle(u_ref_, mu, &phi);
return rotate_angle(u_ref_, mu, &phi, seed);
}
//==============================================================================
// Isotropic implementation
//==============================================================================
Direction Isotropic::sample() const
Direction Isotropic::sample(uint64_t* seed) const
{
double phi = 2.0*PI*prn();
double mu = 2.0*prn() - 1.0;
double phi = 2.0*PI*prn(seed);
double mu = 2.0*prn(seed) - 1.0;
return {mu, std::sqrt(1.0 - mu*mu) * std::cos(phi),
std::sqrt(1.0 - mu*mu) * std::sin(phi)};
}
@ -86,7 +86,7 @@ Direction Isotropic::sample() const
// Monodirectional implementation
//==============================================================================
Direction Monodirectional::sample() const
Direction Monodirectional::sample(uint64_t* seed) const
{
return u_ref_;
}

View file

@ -46,9 +46,9 @@ CartesianIndependent::CartesianIndependent(pugi::xml_node node)
}
}
Position CartesianIndependent::sample() const
Position CartesianIndependent::sample(uint64_t* seed) const
{
return {x_->sample(), y_->sample(), z_->sample()};
return {x_->sample(seed), y_->sample(seed), z_->sample(seed)};
}
//==============================================================================
@ -107,11 +107,11 @@ SphericalIndependent::SphericalIndependent(pugi::xml_node node)
}
Position SphericalIndependent::sample() const
Position SphericalIndependent::sample(uint64_t* seed) const
{
double r = r_->sample();
double theta = theta_->sample();
double phi = phi_->sample();
double r = r_->sample(seed);
double theta = theta_->sample(seed);
double phi = phi_->sample(seed);
double x = r*sin(theta)*cos(phi) + origin_.x;
double y = r*sin(theta)*sin(phi) + origin_.y;
double z = r*cos(theta) + origin_.z;
@ -135,9 +135,9 @@ SpatialBox::SpatialBox(pugi::xml_node node, bool fission)
upper_right_ = Position{params[3], params[4], params[5]};
}
Position SpatialBox::sample() const
Position SpatialBox::sample(uint64_t* seed) const
{
Position xi {prn(), prn(), prn()};
Position xi {prn(seed), prn(seed), prn(seed)};
return lower_left_ + xi*(upper_right_ - lower_left_);
}
@ -157,7 +157,7 @@ SpatialPoint::SpatialPoint(pugi::xml_node node)
r_ = Position{params.data()};
}
Position SpatialPoint::sample() const
Position SpatialPoint::sample(uint64_t* seed) const
{
return r_;
}

View file

@ -118,8 +118,9 @@ void synchronize_bank()
// skip ahead in the sequence using the starting index in the 'global'
// fission bank for each processor.
set_particle_seed(simulation::total_gen + overall_generation());
advance_prn_seed(start);
int64_t id = simulation::total_gen + overall_generation();
uint64_t seed = init_seed(id, STREAM_TRACKING);
advance_prn_seed(start, &seed);
// Determine how many fission sites we need to sample from the source bank
// and the probability for selecting a site.
@ -154,7 +155,7 @@ void synchronize_bank()
}
// Randomly sample sites needed
if (prn() < p_sample) {
if (prn(&seed) < p_sample) {
temp_sites[index_temp] = site;
++index_temp;
}

View file

@ -630,22 +630,22 @@ void calc_zn_rad(int n, double rho, double zn_rad[]) {
}
void rotate_angle_c(double uvw[3], double mu, const double* phi) {
Direction u = rotate_angle({uvw}, mu, phi);
void rotate_angle_c(double uvw[3], double mu, const double* phi, uint64_t* seed) {
Direction u = rotate_angle({uvw}, mu, phi, seed);
uvw[0] = u.x;
uvw[1] = u.y;
uvw[2] = u.z;
}
Direction rotate_angle(Direction u, double mu, const double* phi)
Direction rotate_angle(Direction u, double mu, const double* phi, uint64_t* seed)
{
// Sample azimuthal angle in [0,2pi) if none provided
double phi_;
if (phi != nullptr) {
phi_ = (*phi);
} else {
phi_ = 2.0*PI*prn();
phi_ = 2.0*PI*prn(seed);
}
// Precompute factors to save flops
@ -675,11 +675,11 @@ Direction rotate_angle(Direction u, double mu, const double* phi)
}
double maxwell_spectrum(double T) {
double maxwell_spectrum(double T, uint64_t* seed) {
// Set the random numbers
double r1 = prn();
double r2 = prn();
double r3 = prn();
double r1 = prn(seed);
double r2 = prn(seed);
double r3 = prn(seed);
// determine cosine of pi/2*r
double c = std::cos(PI / 2. * r3);
@ -691,33 +691,33 @@ double maxwell_spectrum(double T) {
}
double normal_variate(double mean, double standard_deviation) {
double normal_variate(double mean, double standard_deviation, uint64_t* seed) {
// perhaps there should be a limit to the number of resamples
while ( true ) {
double v1 = 2 * prn() - 1.;
double v2 = 2 * prn() - 1.;
double v1 = 2 * prn(seed) - 1.;
double v2 = 2 * prn(seed) - 1.;
double r = std::pow(v1, 2) + std::pow(v2, 2);
double r2 = std::pow(r, 2);
if (r2 < 1) {
double z = std::sqrt(-2.0 * std::log(r2)/r2);
z *= (prn() <= 0.5) ? v1 : v2;
z *= (prn(seed) <= 0.5) ? v1 : v2;
return mean + standard_deviation*z;
}
}
}
double muir_spectrum(double e0, double m_rat, double kt) {
double muir_spectrum(double e0, double m_rat, double kt, uint64_t* seed) {
// note sigma here is a factor of 2 shy of equation
// 8 in https://permalink.lanl.gov/object/tr?what=info:lanl-repo/lareport/LA-05411-MS
double sigma = std::sqrt(2.*e0*kt/m_rat);
return normal_variate(e0, sigma);
return normal_variate(e0, sigma, seed);
}
double watt_spectrum(double a, double b) {
double w = maxwell_spectrum(a);
double E_out = w + 0.25 * a * a * b + (2. * prn() - 1.) * std::sqrt(a * a * b * w);
double watt_spectrum(double a, double b, uint64_t* seed) {
double w = maxwell_spectrum(a, seed);
double E_out = w + 0.25 * a * a * b + (2. * prn(seed) - 1.) * std::sqrt(a * a * b * w);
return E_out;
}

View file

@ -529,7 +529,7 @@ Mgxs::get_xs(int xstype, int gin, const int* gout, const double* mu,
//==============================================================================
void
Mgxs::sample_fission_energy(int gin, int& dg, int& gout)
Mgxs::sample_fission_energy(int gin, int& dg, int& gout, uint64_t* seed)
{
// This method assumes that the temperature and angle indices are set
#ifdef _OPENMP
@ -544,8 +544,8 @@ Mgxs::sample_fission_energy(int gin, int& dg, int& gout)
double prob_prompt = xs_t->prompt_nu_fission(cache[tid].a, gin);
// sample random numbers
double xi_pd = prn() * nu_fission;
double xi_gout = prn();
double xi_pd = prn(seed) * nu_fission;
double xi_gout = prn(seed);
// Select whether the neutron is prompt or delayed
if (xi_pd <= prob_prompt) {
@ -585,7 +585,7 @@ Mgxs::sample_fission_energy(int gin, int& dg, int& gout)
//==============================================================================
void
Mgxs::sample_scatter(int gin, int& gout, double& mu, double& wgt)
Mgxs::sample_scatter(int gin, int& gout, double& mu, double& wgt, uint64_t* seed)
{
// This method assumes that the temperature and angle indices are set
// Sample the data
@ -594,7 +594,7 @@ Mgxs::sample_scatter(int gin, int& gout, double& mu, double& wgt)
#else
int tid = 0;
#endif
xs[cache[tid].t].scatter[cache[tid].a]->sample(gin, gout, mu, wgt);
xs[cache[tid].t].scatter[cache[tid].a]->sample(gin, gout, mu, wgt, seed);
}
//==============================================================================

View file

@ -581,7 +581,7 @@ void Nuclide::calculate_xs(int i_sab, int i_log_union, double sab_frac, Particle
// Randomly sample between temperature i and i+1
f = (kT - kTs_[i_temp]) / (kTs_[i_temp + 1] - kTs_[i_temp]);
if (f > prn()) ++i_temp;
if (f > prn(p.current_seed())) ++i_temp;
break;
}
@ -720,7 +720,7 @@ void Nuclide::calculate_sab_xs(int i_sab, double sab_frac, Particle& p)
int i_temp;
double elastic;
double inelastic;
data::thermal_scatt[i_sab]->calculate_xs(p.E_, p.sqrtkT_, &i_temp, &elastic, &inelastic);
data::thermal_scatt[i_sab]->calculate_xs(p.E_, p.sqrtkT_, &i_temp, &elastic, &inelastic, p.current_seed());
// Store the S(a,b) cross sections.
micro.thermal = sab_frac * (elastic + inelastic);
@ -756,11 +756,11 @@ void Nuclide::calculate_urr_xs(int i_temp, Particle& p) const
// This guarantees the randomness 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);
p.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);
double r = future_prn(static_cast<int64_t>(i_nuclide_ + 1), *p.current_seed());
p.stream_ = STREAM_TRACKING;
int i_low = 0;
while (urr.prob_(i_energy, URR_CUM_PROB, i_low) <= r) {++i_low;};

View file

@ -157,9 +157,9 @@ Particle::transport()
while (true) {
// Set the random number stream
if (type_ == Particle::Type::neutron) {
prn_set_stream(STREAM_TRACKING);
stream_ = STREAM_TRACKING;
} else {
prn_set_stream(STREAM_PHOTON);
stream_ = STREAM_PHOTON;
}
// Store pre-collision particle properties
@ -228,7 +228,7 @@ Particle::transport()
} else if (macro_xs_.total == 0.0) {
d_collision = INFINITY;
} else {
d_collision = -std::log(prn()) / macro_xs_.total;
d_collision = -std::log(prn(this->current_seed())) / macro_xs_.total;
}
// Select smaller of the two distances
@ -461,7 +461,7 @@ Particle::cross_surface()
Direction u = (surf->bc_ == BC_REFLECT) ?
surf->reflect(this->r(), this->u()) :
surf->diffuse_reflect(this->r(), this->u());
surf->diffuse_reflect(this->r(), this->u(), this->current_seed());
// Make sure new particle direction is normalized
this->u() = u / u.norm();

View file

@ -98,7 +98,7 @@ void run_particle_restart()
throw std::runtime_error{"Unexpected run mode: " +
std::to_string(previous_run_mode)};
}
set_particle_seed(particle_seed);
init_particle_seeds(particle_seed, p.seeds_);
// Transport neutron
p.transport();

View file

@ -290,12 +290,12 @@ PhotonInteraction::PhotonInteraction(hid_t group, int i_element)
}
void PhotonInteraction::compton_scatter(double alpha, bool doppler,
double* alpha_out, double* mu, int* i_shell) const
double* alpha_out, double* mu, int* i_shell, uint64_t* seed) const
{
double form_factor_xmax = 0.0;
while (true) {
// Sample Klein-Nishina distribution for trial energy and angle
std::tie(*alpha_out, *mu) = klein_nishina(alpha);
std::tie(*alpha_out, *mu) = klein_nishina(alpha, seed);
// Note that the parameter used here does not correspond exactly to the
// momentum transfer q in ENDF-102 Eq. (27.2). Rather, this is the
@ -309,10 +309,10 @@ void PhotonInteraction::compton_scatter(double alpha, bool doppler,
}
// Perform rejection on form factor
if (prn() < form_factor_x / form_factor_xmax) {
if (prn(seed) < form_factor_x / form_factor_xmax) {
if (doppler) {
double E_out;
this->compton_doppler(alpha, *mu, &E_out, i_shell);
this->compton_doppler(alpha, *mu, &E_out, i_shell, seed);
*alpha_out = E_out/MASS_ELECTRON_EV;
} else {
*i_shell = -1;
@ -323,14 +323,14 @@ void PhotonInteraction::compton_scatter(double alpha, bool doppler,
}
void PhotonInteraction::compton_doppler(double alpha, double mu,
double* E_out, int* i_shell) const
double* E_out, int* i_shell, uint64_t* seed) const
{
auto n = data::compton_profile_pz.size();
int shell; // index for shell
while (true) {
// Sample electron shell
double rn = prn();
double rn = prn(seed);
double c = 0.0;
for (shell = 0; shell < electron_pdf_.size(); ++shell) {
c += electron_pdf_(shell);
@ -377,7 +377,7 @@ void PhotonInteraction::compton_doppler(double alpha, double mu,
}
// Sample value on bounded cdf
c = prn()*c_max;
c = prn(seed)*c_max;
// Determine pz corresponding to sampled cdf value
auto cdf_shell = xt::view(profile_cdf_, shell, xt::all());
@ -418,7 +418,7 @@ void PhotonInteraction::compton_doppler(double alpha, double mu,
if (E_out1 > 0.0) {
if (E_out2 > 0.0) {
// If both are positive, pick one at random
*E_out = prn() < 0.5 ? E_out1 : E_out2;
*E_out = prn(seed) < 0.5 ? E_out1 : E_out2;
} else {
*E_out = E_out1;
}
@ -496,7 +496,7 @@ void PhotonInteraction::calculate_xs(Particle& p) const
xs.last_E = p.E_;
}
double PhotonInteraction::rayleigh_scatter(double alpha) const
double PhotonInteraction::rayleigh_scatter(double alpha, uint64_t* seed) const
{
double mu;
while (true) {
@ -507,7 +507,7 @@ double PhotonInteraction::rayleigh_scatter(double alpha) const
double F_max = coherent_int_form_factor_(x2_max);
// Sample cumulative distribution
double F = prn()*F_max;
double F = prn(seed)*F_max;
// Determine x^2 corresponding to F
const auto& x {coherent_int_form_factor_.x()};
@ -519,13 +519,14 @@ double PhotonInteraction::rayleigh_scatter(double alpha) const
// Calculate mu
mu = 1.0 - 2.0*x2/x2_max;
if (prn() < 0.5*(1.0 + mu*mu)) break;
if (prn(seed) < 0.5*(1.0 + mu*mu)) break;
}
return mu;
}
void PhotonInteraction::pair_production(double alpha, double* E_electron,
double* E_positron, double* mu_electron, double* mu_positron) const
double* E_positron, double* mu_electron, double* mu_positron,
uint64_t* seed) const
{
constexpr double r[] {
122.81, 73.167, 69.228, 67.301, 64.696, 61.228,
@ -592,12 +593,12 @@ void PhotonInteraction::pair_production(double alpha, double* E_electron,
double u2 = phi2_max;
double e;
while (true) {
double rn = prn();
double rn = prn(seed);
// Sample the index i in (1, 2) using the point probabilities
// p(1) = u_1/(u_1 + u_2) and p(2) = u_2/(u_1 + u_2)
int i;
if (prn() < u1/(u1 + u2)) {
if (prn(seed) < u1/(u1 + u2)) {
i = 1;
// Sample e from pi_1 using the inverse transform method
@ -618,10 +619,10 @@ void PhotonInteraction::pair_production(double alpha, double* E_electron,
t3 = b*b*(4.0 - 4.0*t2 - 3.0*std::log(1.0 + 1.0/(b*b)));
if (i == 1) {
double phi1 = 7.0/3.0 - t1 - 6.0*t2 - t3 + t4;
if (prn() <= phi1/phi1_max) break;
if (prn(seed) <= phi1/phi1_max) break;
} else {
double phi2 = 11.0/6.0 - t1 - 3.0*t2 + 0.5*t3 + t4;
if (prn() <= phi2/phi2_max) break;
if (prn(seed) <= phi2/phi2_max) break;
}
}
@ -634,13 +635,13 @@ void PhotonInteraction::pair_production(double alpha, double* E_electron,
// p(mu) = C/(1 - beta*mu)^2 using the inverse transform method.
double beta = std::sqrt(*E_electron*(*E_electron + 2.0*MASS_ELECTRON_EV))
/ (*E_electron + MASS_ELECTRON_EV) ;
double rn = 2.0*prn() - 1.0;
double rn = 2.0*prn(seed) - 1.0;
*mu_electron = (rn + beta)/(rn*beta + 1.0);
// Sample the scattering angle of the positron
beta = std::sqrt(*E_positron*(*E_positron + 2.0*MASS_ELECTRON_EV))
/ (*E_positron + MASS_ELECTRON_EV);
rn = 2.0*prn() - 1.0;
rn = 2.0*prn(seed) - 1.0;
*mu_positron = (rn + beta)/(rn*beta + 1.0);
}
@ -648,8 +649,8 @@ void PhotonInteraction::atomic_relaxation(const ElectronSubshell& shell, Particl
{
// If no transitions, assume fluorescent photon from captured free electron
if (shell.n_transitions == 0) {
double mu = 2.0*prn() - 1.0;
double phi = 2.0*PI*prn();
double mu = 2.0*prn(p.current_seed()) - 1.0;
double phi = 2.0*PI*prn(p.current_seed());
Direction u;
u.x = mu;
u.y = std::sqrt(1.0 - mu*mu)*std::cos(phi);
@ -660,7 +661,7 @@ void PhotonInteraction::atomic_relaxation(const ElectronSubshell& shell, Particl
}
// Sample transition
double rn = prn();
double rn = prn(p.current_seed());
double c = 0.0;
int i_transition;
for (i_transition = 0; i_transition < shell.n_transitions; ++i_transition) {
@ -673,8 +674,8 @@ void PhotonInteraction::atomic_relaxation(const ElectronSubshell& shell, Particl
int secondary = shell.transition_subshells(i_transition, 1);
// Sample angle isotropically
double mu = 2.0*prn() - 1.0;
double phi = 2.0*PI*prn();
double mu = 2.0*prn(p.current_seed()) - 1.0;
double phi = 2.0*PI*prn(p.current_seed());
Direction u;
u.x = mu;
u.y = std::sqrt(1.0 - mu*mu)*std::cos(phi);
@ -710,7 +711,7 @@ void PhotonInteraction::atomic_relaxation(const ElectronSubshell& shell, Particl
// Non-member functions
//==============================================================================
std::pair<double, double> klein_nishina(double alpha)
std::pair<double, double> klein_nishina(double alpha, uint64_t* seed)
{
double alpha_out, mu;
double beta = 1.0 + 2.0*alpha;
@ -719,19 +720,19 @@ std::pair<double, double> klein_nishina(double alpha)
double t = beta/(beta + 8.0);
double x;
while (true) {
if (prn() < t) {
if (prn(seed) < t) {
// Left branch of flow chart
double r = 2.0*prn();
double r = 2.0*prn(seed);
x = 1.0 + alpha*r;
if (prn() < 4.0/x*(1.0 - 1.0/x)) {
if (prn(seed) < 4.0/x*(1.0 - 1.0/x)) {
mu = 1 - r;
break;
}
} else {
// Right branch of flow chart
x = beta/(1.0 + 2.0*alpha*prn());
x = beta/(1.0 + 2.0*alpha*prn(seed));
mu = 1.0 + (1.0 - x)/alpha;
if (prn() < 0.5*(mu*mu + 1.0/x)) break;
if (prn(seed) < 0.5*(mu*mu + 1.0/x)) break;
}
}
alpha_out = alpha/x;
@ -739,24 +740,24 @@ std::pair<double, double> klein_nishina(double alpha)
} else {
// Koblinger's direct method
double gamma = 1.0 - std::pow(beta, -2);
double s = prn()*(4.0/alpha + 0.5*gamma +
double s = prn(seed)*(4.0/alpha + 0.5*gamma +
(1.0 - (1.0 + beta)/(alpha*alpha))*std::log(beta));
if (s <= 2.0/alpha) {
// For first term, x = 1 + 2ar
// Therefore, a' = a/(1 + 2ar)
alpha_out = alpha/(1.0 + 2.0*alpha*prn());
alpha_out = alpha/(1.0 + 2.0*alpha*prn(seed));
} else if (s <= 4.0/alpha) {
// For third term, x = beta/(1 + 2ar)
// Therefore, a' = a(1 + 2ar)/beta
alpha_out = alpha*(1.0 + 2.0*alpha*prn())/beta;
alpha_out = alpha*(1.0 + 2.0*alpha*prn(seed))/beta;
} else if (s <= 4.0/alpha + 0.5*gamma) {
// For fourth term, x = 1/sqrt(1 - gamma*r)
// Therefore, a' = a*sqrt(1 - gamma*r)
alpha_out = alpha*std::sqrt(1.0 - gamma*prn());
alpha_out = alpha*std::sqrt(1.0 - gamma*prn(seed));
} else {
// For third term, x = beta^r
// Therefore, a' = a/beta^r
alpha_out = alpha/std::pow(beta, prn());
alpha_out = alpha/std::pow(beta, prn(seed));
}
// Calculate cosine of scattering angle based on basic relation

View file

@ -112,9 +112,9 @@ void sample_neutron_reaction(Particle* p)
// Create secondary photons
if (settings::photon_transport) {
prn_set_stream(STREAM_PHOTON);
p->stream_ = STREAM_PHOTON;
sample_secondary_photons(p, i_nuclide);
prn_set_stream(STREAM_TRACKING);
p->stream_ = STREAM_TRACKING;
}
// If survival biasing is being used, the following subroutine adjusts the
@ -133,9 +133,9 @@ void sample_neutron_reaction(Particle* p)
// Advance URR seed stream 'N' times after energy changes
if (p->E_ != p->E_last_) {
prn_set_stream(STREAM_URR_PTABLE);
advance_prn_seed(data::nuclides.size());
prn_set_stream(STREAM_TRACKING);
p->stream_ = STREAM_URR_PTABLE;
advance_prn_seed(data::nuclides.size(), p->current_seed());
p->stream_ = STREAM_TRACKING;
}
// Play russian roulette if survival biasing is turned on
@ -159,7 +159,7 @@ create_fission_sites(Particle* p, int i_nuclide, const Reaction* rx,
// Sample the number of neutrons produced
int nu = static_cast<int>(nu_t);
if (prn() <= (nu_t - nu)) ++nu;
if (prn(p->current_seed()) <= (nu_t - nu)) ++nu;
// Begin banking the source neutrons
// First, if our bank is full then don't continue
@ -181,7 +181,7 @@ create_fission_sites(Particle* p, int i_nuclide, const Reaction* rx,
site.wgt = 1. / weight;
// Sample delayed group and angle/energy for fission reaction
sample_fission_neutron(i_nuclide, rx, p->E_, &site);
sample_fission_neutron(i_nuclide, rx, p->E_, &site, p->current_seed());
// Set the delayed group on the particle as well
p->delayed_group_ = site.delayed_group;
@ -223,13 +223,13 @@ void sample_photon_reaction(Particle* p)
// For tallying purposes, this routine might be called directly. In that
// case, we need to sample a reaction via the cutoff variable
double prob = 0.0;
double cutoff = prn() * micro.total;
double cutoff = prn(p->current_seed()) * micro.total;
// Coherent (Rayleigh) scattering
prob += micro.coherent;
if (prob > cutoff) {
double mu = element.rayleigh_scatter(alpha);
p->u() = rotate_angle(p->u(), mu, nullptr);
double mu = element.rayleigh_scatter(alpha, p->current_seed());
p->u() = rotate_angle(p->u(), mu, nullptr, p->current_seed());
p->event_ = EVENT_SCATTER;
p->event_mt_ = COHERENT;
return;
@ -240,7 +240,7 @@ void sample_photon_reaction(Particle* p)
if (prob > cutoff) {
double alpha_out, mu;
int i_shell;
element.compton_scatter(alpha, true, &alpha_out, &mu, &i_shell);
element.compton_scatter(alpha, true, &alpha_out, &mu, &i_shell, p->current_seed());
// Determine binding energy of shell. The binding energy is 0.0 if
// doppler broadening is not used.
@ -252,13 +252,13 @@ void sample_photon_reaction(Particle* p)
}
// Create Compton electron
double phi = 2.0*PI*prn();
double phi = 2.0*PI*prn(p->current_seed());
double E_electron = (alpha - alpha_out)*MASS_ELECTRON_EV - e_b;
int electron = static_cast<int>(Particle::Type::electron);
if (E_electron >= settings::energy_cutoff[electron]) {
double mu_electron = (alpha - alpha_out*mu)
/ std::sqrt(alpha*alpha + alpha_out*alpha_out - 2.0*alpha*alpha_out*mu);
Direction u = rotate_angle(p->u(), mu_electron, &phi);
Direction u = rotate_angle(p->u(), mu_electron, &phi, p->current_seed());
p->create_secondary(u, E_electron, Particle::Type::electron);
}
@ -272,7 +272,7 @@ void sample_photon_reaction(Particle* p)
phi += PI;
p->E_ = alpha_out*MASS_ELECTRON_EV;
p->u() = rotate_angle(p->u(), mu, &phi);
p->u() = rotate_angle(p->u(), mu, &phi, p->current_seed());
p->event_ = EVENT_SCATTER;
p->event_mt_ = INCOHERENT;
return;
@ -304,8 +304,8 @@ void sample_photon_reaction(Particle* p)
// model in Serpent 2" by Toni Kaltiaisenaho
double mu;
while (true) {
double r = prn();
if (4.0*(1.0 - r)*r >= prn()) {
double r = prn(p->current_seed());
if (4.0*(1.0 - r)*r >= prn(p->current_seed())) {
double rel_vel = std::sqrt(E_electron * (E_electron +
2.0*MASS_ELECTRON_EV)) / (E_electron + MASS_ELECTRON_EV);
mu = (2.0*r + rel_vel - 1.0) / (2.0*rel_vel*r - rel_vel + 1.0);
@ -313,7 +313,7 @@ void sample_photon_reaction(Particle* p)
}
}
double phi = 2.0*PI*prn();
double phi = 2.0*PI*prn(p->current_seed());
Direction u;
u.x = mu;
u.y = std::sqrt(1.0 - mu*mu)*std::cos(phi);
@ -341,14 +341,14 @@ void sample_photon_reaction(Particle* p)
double E_electron, E_positron;
double mu_electron, mu_positron;
element.pair_production(alpha, &E_electron, &E_positron,
&mu_electron, &mu_positron);
&mu_electron, &mu_positron, p->current_seed());
// Create secondary electron
Direction u = rotate_angle(p->u(), mu_electron, nullptr);
Direction u = rotate_angle(p->u(), mu_electron, nullptr, p->current_seed());
p->create_secondary(u, E_electron, Particle::Type::electron);
// Create secondary positron
u = rotate_angle(p->u(), mu_positron, nullptr);
u = rotate_angle(p->u(), mu_positron, nullptr, p->current_seed());
p->create_secondary(u, E_positron, Particle::Type::positron);
p->event_ = EVENT_ABSORB;
@ -382,8 +382,8 @@ void sample_positron_reaction(Particle* p)
}
// Sample angle isotropically
double mu = 2.0*prn() - 1.0;
double phi = 2.0*PI*prn();
double mu = 2.0*prn(p->current_seed()) - 1.0;
double phi = 2.0*PI*prn(p->current_seed());
Direction u;
u.x = mu;
u.y = std::sqrt(1.0 - mu*mu)*std::cos(phi);
@ -398,10 +398,10 @@ void sample_positron_reaction(Particle* p)
p->event_ = EVENT_ABSORB;
}
int sample_nuclide(const Particle* p)
int sample_nuclide(Particle* p)
{
// Sample cumulative distribution function
double cutoff = prn() * p->macro_xs_.total;
double cutoff = prn(p->current_seed()) * p->macro_xs_.total;
// Get pointers to nuclide/density arrays
const auto& mat {model::materials[p->material_]};
@ -426,7 +426,7 @@ int sample_nuclide(const Particle* p)
int sample_element(Particle* p)
{
// Sample cumulative distribution function
double cutoff = prn() * p->macro_xs_.total;
double cutoff = prn(p->current_seed()) * p->macro_xs_.total;
// Get pointers to elements, densities
const auto& mat {model::materials[p->material_]};
@ -455,7 +455,7 @@ int sample_element(Particle* p)
fatal_error("Did not sample any element during collision.");
}
Reaction* sample_fission(int i_nuclide, const Particle* p)
Reaction* sample_fission(int i_nuclide, Particle* p)
{
// Get pointer to nuclide
const auto& nuc {data::nuclides[i_nuclide]};
@ -479,7 +479,7 @@ Reaction* sample_fission(int i_nuclide, const Particle* p)
int i_temp = p->neutron_xs_[i_nuclide].index_temp;
int i_grid = p->neutron_xs_[i_nuclide].index_grid;
double f = p->neutron_xs_[i_nuclide].interp_factor;
double cutoff = prn() * p->neutron_xs_[i_nuclide].fission;
double cutoff = prn(p->current_seed()) * p->neutron_xs_[i_nuclide].fission;
double prob = 0.0;
// Loop through each partial fission reaction type
@ -500,13 +500,13 @@ Reaction* sample_fission(int i_nuclide, const Particle* p)
throw std::runtime_error{"No fission reaction was sampled for " + nuc->name_};
}
void sample_photon_product(int i_nuclide, const Particle* p, int* i_rx, int* i_product)
void sample_photon_product(int i_nuclide, Particle* p, int* i_rx, int* i_product)
{
// Get grid index and interpolation factor and sample photon production cdf
int i_temp = p->neutron_xs_[i_nuclide].index_temp;
int i_grid = p->neutron_xs_[i_nuclide].index_grid;
double f = p->neutron_xs_[i_nuclide].interp_factor;
double cutoff = prn() * p->neutron_xs_[i_nuclide].photon_prod;
double cutoff = prn(p->current_seed()) * p->neutron_xs_[i_nuclide].photon_prod;
double prob = 0.0;
// Loop through each reaction type
@ -554,7 +554,7 @@ void absorption(Particle* p, int i_nuclide)
} else {
// See if disappearance reaction happens
if (p->neutron_xs_[i_nuclide].absorption >
prn() * p->neutron_xs_[i_nuclide].total) {
prn(p->current_seed()) * p->neutron_xs_[i_nuclide].total) {
// Score absorption estimate of keff
if (settings::run_mode == RUN_MODE_EIGENVALUE) {
global_tally_absorption += p->wgt_ * p->neutron_xs_[
@ -582,7 +582,7 @@ void scatter(Particle* p, int i_nuclide)
// For tallying purposes, this routine might be called directly. In that
// case, we need to sample a reaction via the cutoff variable
double cutoff = prn() * (micro.total - micro.absorption);
double cutoff = prn(p->current_seed()) * (micro.total - micro.absorption);
bool sampled = false;
// Calculate elastic cross section if it wasn't precalculated
@ -656,8 +656,8 @@ void scatter(Particle* p, int i_nuclide)
int i_nuc_mat = mat->mat_nuclide_index_[i_nuclide];
if (mat->p0_[i_nuc_mat]) {
// Sample isotropic-in-lab outgoing direction
double mu = 2.0*prn() - 1.0;
double phi = 2.0*PI*prn();
double mu = 2.0*prn(p->current_seed()) - 1.0;
double phi = 2.0*PI*prn(p->current_seed());
// Change direction of particle
p->u().x = mu;
@ -684,7 +684,7 @@ void elastic_scatter(int i_nuclide, const Reaction& rx, double kT,
Direction v_t {};
if (!p->neutron_xs_[i_nuclide].use_ptable) {
v_t = sample_target_velocity(nuc.get(), p->E_, p->u(), v_n,
p->neutron_xs_[i_nuclide].elastic, kT);
p->neutron_xs_[i_nuclide].elastic, kT, p->current_seed());
}
// Velocity of center-of-mass
@ -702,9 +702,9 @@ void elastic_scatter(int i_nuclide, const Reaction& rx, double kT,
auto& d = rx.products_[0].distribution_[0];
auto d_ = dynamic_cast<UncorrelatedAngleEnergy*>(d.get());
if (d_) {
mu_cm = d_->angle().sample(p->E_);
mu_cm = d_->angle().sample(p->E_, p->current_seed());
} else {
mu_cm = 2.0*prn() - 1.0;
mu_cm = 2.0*prn(p->current_seed()) - 1.0;
}
// Determine direction cosines in CM
@ -713,7 +713,7 @@ void elastic_scatter(int i_nuclide, const Reaction& rx, double kT,
// Rotate neutron velocity vector to new angle -- note that the speed of the
// neutron in CM does not change in elastic scattering. However, the speed
// will change when we convert back to LAB
v_n = vel * rotate_angle(u_cm, mu_cm, nullptr);
v_n = vel * rotate_angle(u_cm, mu_cm, nullptr, p->current_seed());
// Transform back to LAB frame
v_n += v_cm;
@ -742,15 +742,15 @@ void sab_scatter(int i_nuclide, int i_sab, Particle* p)
// Sample energy and angle
double E_out;
data::thermal_scatt[i_sab]->data_[i_temp].sample(micro, p->E_, &E_out, &p->mu_);
data::thermal_scatt[i_sab]->data_[i_temp].sample(micro, p->E_, &E_out, &p->mu_, p->current_seed());
// Set energy to outgoing, change direction of particle
p->E_ = E_out;
p->u() = rotate_angle(p->u(), p->mu_, nullptr);
p->u() = rotate_angle(p->u(), p->mu_, nullptr, p->current_seed());
}
Direction sample_target_velocity(const Nuclide* nuc, double E, Direction u,
Direction v_neut, double xs_eff, double kT)
Direction v_neut, double xs_eff, double kT, uint64_t* seed)
{
// check if nuclide is a resonant scatterer
ResScatMethod sampling_method;
@ -782,7 +782,7 @@ Direction sample_target_velocity(const Nuclide* nuc, double E, Direction u,
case ResScatMethod::cxs:
// sample target velocity with the constant cross section (cxs) approx.
return sample_cxs_target_velocity(nuc->awr_, E, u, kT);
return sample_cxs_target_velocity(nuc->awr_, E, u, kT, seed);
case ResScatMethod::dbrc:
case ResScatMethod::rvs: {
@ -816,7 +816,7 @@ Direction sample_target_velocity(const Nuclide* nuc, double E, Direction u,
if (i_E_up == i_E_low) {
// Handle degenerate case -- if the upper/lower bounds occur for the same
// index, then using cxs is probably a good approximation
return sample_cxs_target_velocity(nuc->awr_, E, u, kT);
return sample_cxs_target_velocity(nuc->awr_, E, u, kT, seed);
}
if (sampling_method == ResScatMethod::dbrc) {
@ -840,7 +840,7 @@ Direction sample_target_velocity(const Nuclide* nuc, double E, Direction u,
Direction v_target;
while (true) {
// sample target velocity with the constant cross section (cxs) approx.
v_target = sample_cxs_target_velocity(nuc->awr_, E, u, kT);
v_target = sample_cxs_target_velocity(nuc->awr_, E, u, kT, seed);
Direction v_rel = v_neut - v_target;
E_rel = v_rel.dot(v_rel);
if (E_rel < E_up) break;
@ -849,7 +849,7 @@ Direction sample_target_velocity(const Nuclide* nuc, double E, Direction u,
// perform Doppler broadening rejection correction (dbrc)
double xs_0K = nuc->elastic_xs_0K(E_rel);
double R = xs_0K / xs_max;
if (prn() < R) return v_target;
if (prn(seed) < R) return v_target;
}
} else if (sampling_method == ResScatMethod::rvs) {
@ -869,10 +869,10 @@ Direction sample_target_velocity(const Nuclide* nuc, double E, Direction u,
while (true) {
// directly sample Maxwellian
double E_t = -kT * std::log(prn());
double E_t = -kT * std::log(prn(seed));
// sample a relative energy using the xs cdf
double cdf_rel = cdf_low + prn()*(cdf_up - cdf_low);
double cdf_rel = cdf_low + prn(seed)*(cdf_up - cdf_low);
int i_E_rel = lower_bound_index(&nuc->xs_cdf_[i_E_low-1],
&nuc->xs_cdf_[i_E_up+1], cdf_rel);
double E_rel = nuc->energy_0K_[i_E_low + i_E_rel];
@ -890,7 +890,7 @@ Direction sample_target_velocity(const Nuclide* nuc, double E, Direction u,
if (std::abs(mu) < 1.0) {
// set and accept target velocity
E_t /= nuc->awr_;
return std::sqrt(E_t) * rotate_angle(u, mu, nullptr);
return std::sqrt(E_t) * rotate_angle(u, mu, nullptr, seed);
}
}
}
@ -901,7 +901,7 @@ Direction sample_target_velocity(const Nuclide* nuc, double E, Direction u,
}
Direction
sample_cxs_target_velocity(double awr, double E, Direction u, double kT)
sample_cxs_target_velocity(double awr, double E, Direction u, double kT, uint64_t* seed)
{
double beta_vn = std::sqrt(awr * E / kT);
double alpha = 1.0/(1.0 + std::sqrt(PI)*beta_vn/2.0);
@ -910,10 +910,10 @@ sample_cxs_target_velocity(double awr, double E, Direction u, double kT)
double mu;
while (true) {
// Sample two random numbers
double r1 = prn();
double r2 = prn();
double r1 = prn(seed);
double r2 = prn(seed);
if (prn() < alpha) {
if (prn(seed) < alpha) {
// With probability alpha, we sample the distribution p(y) =
// y*e^(-y). This can be done with sampling scheme C45 frmo the Monte
// Carlo sampler
@ -925,7 +925,7 @@ sample_cxs_target_velocity(double awr, double E, Direction u, double kT)
// e^(-y^2). This can be done with sampling scheme C61 from the Monte
// Carlo sampler
double c = std::cos(PI/2.0 * prn());
double c = std::cos(PI/2.0 * prn(seed));
beta_vt_sq = -std::log(r1) - std::log(r2)*c*c;
}
@ -933,14 +933,14 @@ sample_cxs_target_velocity(double awr, double E, Direction u, double kT)
double beta_vt = std::sqrt(beta_vt_sq);
// Sample cosine of angle between neutron and target velocity
mu = 2.0*prn() - 1.0;
mu = 2.0*prn(seed) - 1.0;
// Determine rejection probability
double accept_prob = std::sqrt(beta_vn*beta_vn + beta_vt_sq -
2*beta_vn*beta_vt*mu) / (beta_vn + beta_vt);
// Perform rejection sampling on vt and mu
if (prn() < accept_prob) break;
if (prn(seed) < accept_prob) break;
}
// Determine speed of target nucleus
@ -948,19 +948,19 @@ sample_cxs_target_velocity(double awr, double E, Direction u, double kT)
// Determine velocity vector of target nucleus based on neutron's velocity
// and the sampled angle between them
return vt * rotate_angle(u, mu, nullptr);
return vt * rotate_angle(u, mu, nullptr, seed);
}
void sample_fission_neutron(int i_nuclide, const Reaction* rx, double E_in, Particle::Bank* site)
void sample_fission_neutron(int i_nuclide, const Reaction* rx, double E_in, Particle::Bank* site, uint64_t* seed)
{
// Sample cosine of angle -- fission neutrons are always emitted
// isotropically. Sometimes in ACE data, fission reactions actually have
// an angular distribution listed, but for those that do, it's simply just
// a uniform distribution in mu
double mu = 2.0 * prn() - 1.0;
double mu = 2.0 * prn(seed) - 1.0;
// Sample azimuthal angle uniformly in [0,2*pi)
double phi = 2.0*PI*prn();
double phi = 2.0*PI*prn(seed);
site->u.x = mu;
site->u.y = std::sqrt(1.0 - mu*mu) * std::cos(phi);
site->u.z = std::sqrt(1.0 - mu*mu) * std::sin(phi);
@ -971,12 +971,12 @@ void sample_fission_neutron(int i_nuclide, const Reaction* rx, double E_in, Part
double nu_d = nuc->nu(E_in, Nuclide::EmissionMode::delayed);
double beta = nu_d / nu_t;
if (prn() < beta) {
if (prn(seed) < beta) {
// ====================================================================
// DELAYED NEUTRON SAMPLED
// sampled delayed precursor group
double xi = prn()*nu_d;
double xi = prn(seed)*nu_d;
double prob = 0.0;
int group;
for (group = 1; group < nuc->n_precursor_; ++group) {
@ -1000,7 +1000,7 @@ void sample_fission_neutron(int i_nuclide, const Reaction* rx, double E_in, Part
while (true) {
// sample from energy/angle distribution -- note that mu has already been
// sampled above and doesn't need to be resampled
rx->products_[group].sample(E_in, site->E, mu);
rx->products_[group].sample(E_in, site->E, mu, seed);
// resample if energy is greater than maximum neutron energy
constexpr int neutron = static_cast<int>(Particle::Type::neutron);
@ -1025,7 +1025,7 @@ void sample_fission_neutron(int i_nuclide, const Reaction* rx, double E_in, Part
// sample from prompt neutron energy distribution
int n_sample = 0;
while (true) {
rx->products_[0].sample(E_in, site->E, mu);
rx->products_[0].sample(E_in, site->E, mu, seed);
// resample if energy is greater than maximum neutron energy
constexpr int neutron = static_cast<int>(Particle::Type::neutron);
@ -1050,7 +1050,7 @@ void inelastic_scatter(const Nuclide* nuc, const Reaction* rx, Particle* p)
// sample outgoing energy and scattering cosine
double E;
double mu;
rx->products_[0].sample(E_in, E, mu);
rx->products_[0].sample(E_in, E, mu, p->current_seed());
// if scattering system is in center-of-mass, transfer cosine of scattering
// angle and outgoing energy from CM to LAB
@ -1076,7 +1076,7 @@ void inelastic_scatter(const Nuclide* nuc, const Reaction* rx, Particle* p)
p->mu_ = mu;
// change direction of particle
p->u() = rotate_angle(p->u(), mu, nullptr);
p->u() = rotate_angle(p->u(), mu, nullptr, p->current_seed());
// evaluate yield
double yield = (*rx->products_[0].yield_)(E_in);
@ -1097,7 +1097,7 @@ void sample_secondary_photons(Particle* p, int i_nuclide)
double y_t = p->wgt_ * p->neutron_xs_[i_nuclide].photon_prod /
p->neutron_xs_[i_nuclide].total;
int y = static_cast<int>(y_t);
if (prn() <= y_t - y) ++y;
if (prn(p->current_seed()) <= y_t - y) ++y;
// Sample each secondary photon
for (int i = 0; i < y; ++i) {
@ -1110,10 +1110,10 @@ void sample_secondary_photons(Particle* p, int i_nuclide)
auto& rx = data::nuclides[i_nuclide]->reactions_[i_rx];
double E;
double mu;
rx->products_[i_product].sample(p->E_, E, mu);
rx->products_[i_product].sample(p->E_, E, mu, p->current_seed());
// Sample the new direction
Direction u = rotate_angle(p->u(), mu, nullptr);
Direction u = rotate_angle(p->u(), mu, nullptr, p->current_seed());
// Create the secondary photon
p->create_secondary(u, E, Particle::Type::photon);

View file

@ -12,7 +12,7 @@ namespace openmc {
void russian_roulette(Particle* p)
{
if (p->wgt_ < settings::weight_cutoff) {
if (prn() < p->wgt_ / settings::weight_survive) {
if (prn(p->current_seed()) < p->wgt_ / settings::weight_survive) {
p->wgt_ = settings::weight_survive;
p->wgt_last_ = p->wgt_;
} else {

View file

@ -78,10 +78,10 @@ void
scatter(Particle* p)
{
data::mg.macro_xs_[p->material_].sample_scatter(p->g_last_, p->g_, p->mu_,
p->wgt_);
p->wgt_, p->current_seed());
// Rotate the angle
p->u() = rotate_angle(p->u(), p->mu_, nullptr);
p->u() = rotate_angle(p->u(), p->mu_, nullptr, p->current_seed());
// Update energy value for downstream compatability (in tallying)
p->E_ = data::mg.energy_bin_avg_[p->g_];
@ -103,7 +103,7 @@ create_fission_sites(Particle* p, std::vector<Particle::Bank>& bank)
// Sample the number of neutrons produced
int nu = static_cast<int>(nu_t);
if (prn() <= (nu_t - int(nu_t))) {
if (prn(p->current_seed()) <= (nu_t - int(nu_t))) {
nu++;
}
@ -128,10 +128,10 @@ create_fission_sites(Particle* p, std::vector<Particle::Bank>& bank)
// Sample the cosine of the angle, assuming fission neutrons are emitted
// isotropically
double mu = 2.*prn() - 1.;
double mu = 2.*prn(p->current_seed()) - 1.;
// Sample the azimuthal angle uniformly in [0, 2.pi)
double phi = 2. * PI * prn();
double phi = 2. * PI * prn(p->current_seed() );
site.u.x = mu;
site.u.y = std::sqrt(1. - mu * mu) * std::cos(phi);
site.u.z = std::sqrt(1. - mu * mu) * std::sin(phi);
@ -139,7 +139,8 @@ create_fission_sites(Particle* p, std::vector<Particle::Bank>& bank)
// Sample secondary energy distribution for the fission reaction
int dg;
int gout;
data::mg.macro_xs_[p->material_].sample_fission_energy(p->g_, dg, gout);
data::mg.macro_xs_[p->material_].sample_fission_energy(p->g_, dg, gout,
p->current_seed());
// Store the energy and delayed groups on the fission bank
site.E = gout;
// We add 1 to the delayed_group bc in MG, -1 is prompt, but in the rest
@ -179,7 +180,7 @@ absorption(Particle* p)
global_tally_absorption += p->wgt_absorb_ * p->macro_xs_.nu_fission /
p->macro_xs_.absorption;
} else {
if (p->macro_xs_.absorption > prn() * p->macro_xs_.total) {
if (p->macro_xs_.absorption > prn(p->current_seed()) * p->macro_xs_.total) {
#pragma omp atomic
global_tally_absorption += p->wgt_ * p->macro_xs_.nu_fission /
p->macro_xs_.absorption;

View file

@ -80,6 +80,7 @@ namespace model {
std::vector<Plot> plots;
std::unordered_map<int, int> plot_map;
uint64_t plotter_seed = 1;
} // namespace model
@ -958,8 +959,10 @@ voxel_finalize(hid_t dspace, hid_t dset, hid_t memspace)
H5Sclose(memspace);
}
RGBColor random_color() {
return {int(prn()*255), int(prn()*255), int(prn()*255)};
RGBColor random_color(void) {
return {int(prn(&model::plotter_seed)*255),
int(prn(&model::plotter_seed)*255),
int(prn(&model::plotter_seed)*255)};
}
extern "C" int openmc_id_map(const void* plot, int32_t* data_out)

View file

@ -5,18 +5,8 @@
namespace openmc {
// Constants
extern "C" const int N_STREAMS {6};
extern "C" const int STREAM_TRACKING {0};
extern "C" const int STREAM_TALLIES {1};
extern "C" const int STREAM_SOURCE {2};
extern "C" const int STREAM_URR_PTABLE {3};
extern "C" const int STREAM_VOLUME {4};
extern "C" const int STREAM_PHOTON {5};
// Starting seed
int64_t seed {1};
int64_t master_seed {1};
// LCG parameters
constexpr uint64_t prn_mult {2806196910506780709LL}; // multiplication
@ -28,47 +18,47 @@ constexpr uint64_t prn_stride {152917LL}; // stride between
// particles
constexpr double prn_norm {1.0 / prn_mod}; // 2^-63
// Current PRNG state
uint64_t prn_seed[N_STREAMS]; // current seed
int stream; // current RNG stream
#pragma omp threadprivate(prn_seed, stream)
//==============================================================================
// PRN
//==============================================================================
extern "C" double
prn()
double prn(uint64_t* seed)
{
// This algorithm uses bit-masking to find the next integer(8) value to be
// used to calculate the random number.
prn_seed[stream] = (prn_mult*prn_seed[stream] + prn_add) & prn_mask;
*seed = (prn_mult * (*seed) + prn_add) & prn_mask;
// Once the integer is calculated, we just need to divide by 2**m,
// represented here as multiplying by a pre-calculated factor
return prn_seed[stream] * prn_norm;
return (*seed) * prn_norm;
}
//==============================================================================
// FUTURE_PRN
//==============================================================================
extern "C" double
future_prn(int64_t n)
double future_prn(int64_t n, uint64_t seed)
{
return future_seed(static_cast<uint64_t>(n), prn_seed[stream]) * prn_norm;
return future_seed(static_cast<uint64_t>(n), seed) * prn_norm;
}
//==============================================================================
// SET_PARTICLE_SEED
// INIT_SEED
//==============================================================================
extern "C" void
set_particle_seed(int64_t id)
uint64_t init_seed(int64_t id, int offset)
{
return future_seed(static_cast<uint64_t>(id) * prn_stride, master_seed + offset);
}
//==============================================================================
// INIT_PARTICLE_SEEDS
//==============================================================================
void init_particle_seeds(int64_t id, uint64_t* seeds)
{
for (int i = 0; i < N_STREAMS; i++) {
prn_seed[i] = future_seed(static_cast<uint64_t>(id) * prn_stride, seed + i);
seeds[i] = future_seed(static_cast<uint64_t>(id) * prn_stride, master_seed + i);
}
}
@ -76,18 +66,16 @@ set_particle_seed(int64_t id)
// ADVANCE_PRN_SEED
//==============================================================================
extern "C" void
advance_prn_seed(int64_t n)
void advance_prn_seed(int64_t n, uint64_t* seed)
{
prn_seed[stream] = future_seed(static_cast<uint64_t>(n), prn_seed[stream]);
*seed = future_seed(static_cast<uint64_t>(n), *seed);
}
//==============================================================================
// FUTURE_SEED
//==============================================================================
uint64_t
future_seed(uint64_t n, uint64_t seed)
uint64_t future_seed(uint64_t n, uint64_t seed)
{
// Make sure nskip is less than 2^M.
n &= prn_mask;
@ -121,33 +109,15 @@ future_seed(uint64_t n, uint64_t seed)
return (g_new * seed + c_new) & prn_mask;
}
//==============================================================================
// PRN_SET_STREAM
//==============================================================================
extern "C" void
prn_set_stream(int i)
{
stream = i; // Shift by one to move from Fortran to C indexing.
}
//==============================================================================
// API FUNCTIONS
//==============================================================================
extern "C" int64_t openmc_get_seed() {return seed;}
extern "C" int64_t openmc_get_seed() {return master_seed;}
extern "C" void
openmc_set_seed(int64_t new_seed)
extern "C" void openmc_set_seed(int64_t new_seed)
{
seed = new_seed;
#pragma omp parallel
{
for (int i = 0; i < N_STREAMS; i++) {
prn_seed[i] = seed + i;
}
prn_set_stream(STREAM_TRACKING);
}
master_seed = new_seed;
}
} // namespace openmc

View file

@ -76,25 +76,26 @@ ReactionProduct::ReactionProduct(hid_t group)
}
}
void ReactionProduct::sample(double E_in, double& E_out, double& mu) const
void ReactionProduct::sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const
{
auto n = applicability_.size();
if (n > 1) {
double prob = 0.0;
double c = prn();
double c = prn(seed);
for (int i = 0; i < n; ++i) {
// Determine probability that i-th energy distribution is sampled
prob += applicability_[i](E_in);
// If i-th distribution is sampled, sample energy from the distribution
if (c <= prob) {
distribution_[i]->sample(E_in, E_out, mu);
distribution_[i]->sample(E_in, E_out, mu, seed);
break;
}
}
} else {
// If only one distribution is present, go ahead and sample it
distribution_[0]->sample(E_in, E_out, mu);
distribution_[0]->sample(E_in, E_out, mu, seed);
}
}

View file

@ -167,10 +167,10 @@ ScattData::base_combine(size_t max_order,
//==============================================================================
void
ScattData::sample_energy(int gin, int& gout, int& i_gout)
ScattData::sample_energy(int gin, int& gout, int& i_gout, uint64_t* seed)
{
// Sample the outgoing group
double xi = prn();
double xi = prn(seed);
double prob = 0.;
i_gout = 0;
for (gout = gmin[gin]; gout < gmax[gin]; ++gout) {
@ -347,21 +347,22 @@ ScattDataLegendre::calc_f(int gin, int gout, double mu)
//==============================================================================
void
ScattDataLegendre::sample(int gin, int& gout, double& mu, double& wgt)
ScattDataLegendre::sample(int gin, int& gout, double& mu, double& wgt,
uint64_t* seed)
{
// Sample the outgoing energy using the base-class method
int i_gout;
sample_energy(gin, gout, i_gout);
sample_energy(gin, gout, i_gout, seed);
// 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;
for (samples = 0; samples < MAX_SAMPLE; ++samples) {
mu = 2. * prn() - 1.;
mu = 2. * prn(seed) - 1.;
double f = calc_f(gin, gout, mu);
if (f > 0.) {
double u = prn() * M;
double u = prn(seed) * M;
if (u <= f) break;
}
}
@ -535,14 +536,15 @@ ScattDataHistogram::calc_f(int gin, int gout, double mu)
//==============================================================================
void
ScattDataHistogram::sample(int gin, int& gout, double& mu, double& wgt)
ScattDataHistogram::sample(int gin, int& gout, double& mu, double& wgt,
uint64_t* seed)
{
// Sample the outgoing energy using the base-class method
int i_gout;
sample_energy(gin, gout, i_gout);
sample_energy(gin, gout, i_gout, seed);
// Determine the outgoing cosine bin
double xi = prn();
double xi = prn(seed);
int imu;
if (xi < dist[gin][i_gout][0]) {
@ -554,7 +556,7 @@ ScattDataHistogram::sample(int gin, int& gout, double& mu, double& wgt)
}
// Randomly select mu within the imu bin
mu = prn() * dmu + this->mu[imu];
mu = prn(seed) * dmu + this->mu[imu];
if (mu < -1.) {
mu = -1.;
@ -738,15 +740,16 @@ ScattDataTabular::calc_f(int gin, int gout, double mu)
//==============================================================================
void
ScattDataTabular::sample(int gin, int& gout, double& mu, double& wgt)
ScattDataTabular::sample(int gin, int& gout, double& mu, double& wgt,
uint64_t* seed)
{
// Sample the outgoing energy using the base-class method
int i_gout;
sample_energy(gin, gout, i_gout);
sample_energy(gin, gout, i_gout, seed);
// Determine the outgoing cosine bin
int NP = this->mu.shape()[0];
double xi = prn();
double xi = prn(seed);
double c_k = dist[gin][i_gout][0];
int k;

View file

@ -152,14 +152,15 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group)
} // incoming energies
}
void CorrelatedAngleEnergy::sample(double E_in, double& E_out, double& mu) const
void CorrelatedAngleEnergy::sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const
{
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// Before the secondary distribution refactor, an isotropic polar cosine was
// always sampled but then overwritten with the polar cosine sampled from the
// correlated distribution. To preserve the random number stream, we keep
// this dummy sampling here but can remove it later (will change answers)
mu = 2.0*prn() - 1.0;
mu = 2.0*prn(seed) - 1.0;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// Find energy bin and calculate interpolation factor -- if the energy is
@ -179,7 +180,7 @@ void CorrelatedAngleEnergy::sample(double E_in, double& E_out, double& mu) const
}
// Sample between the ith and [i+1]th bin
int l = r > prn() ? i + 1 : i;
int l = r > prn(seed) ? i + 1 : i;
// Interpolation for energy E1 and EK
int n_energy_out = distribution_[i].e_out.size();
@ -198,7 +199,7 @@ void CorrelatedAngleEnergy::sample(double E_in, double& E_out, double& mu) const
// Determine outgoing energy bin
n_energy_out = distribution_[l].e_out.size();
n_discrete = distribution_[l].n_discrete;
double r1 = prn();
double r1 = prn(seed);
double c_k = distribution_[l].c[0];
int k = 0;
int end = n_energy_out - 2;
@ -259,9 +260,9 @@ void CorrelatedAngleEnergy::sample(double E_in, double& E_out, double& mu) const
// Find correlated angular distribution for closest outgoing energy bin
if (r1 - c_k < c_k1 - r1) {
mu = distribution_[l].angle[k]->sample();
mu = distribution_[l].angle[k]->sample(seed);
} else {
mu = distribution_[l].angle[k + 1]->sample();
mu = distribution_[l].angle[k + 1]->sample(seed);
}
}

View file

@ -113,14 +113,14 @@ KalbachMann::KalbachMann(hid_t group)
} // incoming energies
}
void KalbachMann::sample(double E_in, double& E_out, double& mu) const
void KalbachMann::sample(double E_in, double& E_out, double& mu, uint64_t* seed) const
{
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// Before the secondary distribution refactor, an isotropic polar cosine was
// always sampled but then overwritten with the polar cosine sampled from the
// correlated distribution. To preserve the random number stream, we keep
// this dummy sampling here but can remove it later (will change answers)
mu = 2.0*prn() - 1.0;
mu = 2.0*prn(seed) - 1.0;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// Find energy bin and calculate interpolation factor -- if the energy is
@ -140,7 +140,7 @@ void KalbachMann::sample(double E_in, double& E_out, double& mu) const
}
// Sample between the ith and [i+1]th bin
int l = r > prn() ? i + 1 : i;
int l = r > prn(seed) ? i + 1 : i;
// Interpolation for energy E1 and EK
int n_energy_out = distribution_[i].e_out.size();
@ -159,7 +159,7 @@ void KalbachMann::sample(double E_in, double& E_out, double& mu) const
// Determine outgoing energy bin
n_energy_out = distribution_[l].e_out.size();
n_discrete = distribution_[l].n_discrete;
double r1 = prn();
double r1 = prn(seed);
double c_k = distribution_[l].c[0];
int k = 0;
int end = n_energy_out - 2;
@ -229,11 +229,11 @@ void KalbachMann::sample(double E_in, double& E_out, double& mu) const
}
// Sampled correlated angle from Kalbach-Mann parameters
if (prn() > km_r) {
double T = (2.0*prn() - 1.0) * std::sinh(km_a);
if (prn(seed) > km_r) {
double T = (2.0*prn(seed) - 1.0) * std::sinh(km_a);
mu = std::log(T + std::sqrt(T*T + 1.0))/km_a;
} else {
double r1 = prn();
double r1 = prn(seed);
mu = std::log(r1*std::exp(km_a) + (1.0 - r1)*std::exp(-km_a))/km_a;
}
}

View file

@ -21,38 +21,39 @@ NBodyPhaseSpace::NBodyPhaseSpace(hid_t group)
read_attribute(group, "q_value", Q_);
}
void NBodyPhaseSpace::sample(double E_in, double& E_out, double& mu) const
void NBodyPhaseSpace::sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const
{
// By definition, the distribution of the angle is isotropic for an N-body
// phase space distribution
mu = 2.0*prn() - 1.0;
mu = 2.0*prn(seed) - 1.0;
// Determine E_max parameter
double Ap = mass_ratio_;
double E_max = (Ap - 1.0)/Ap * (A_/(A_ + 1.0)*E_in + Q_);
// x is essentially a Maxwellian distribution
double x = maxwell_spectrum(1.0);
double x = maxwell_spectrum(1.0, seed);
double y;
double r1, r2, r3, r4, r5, r6;
switch (n_bodies_) {
case 3:
y = maxwell_spectrum(1.0);
y = maxwell_spectrum(1.0, seed);
break;
case 4:
r1 = prn();
r2 = prn();
r3 = prn();
r1 = prn(seed);
r2 = prn(seed);
r3 = prn(seed);
y = -std::log(r1*r2*r3);
break;
case 5:
r1 = prn();
r2 = prn();
r3 = prn();
r4 = prn();
r5 = prn();
r6 = prn();
r1 = prn(seed);
r2 = prn(seed);
r3 = prn(seed);
r4 = prn(seed);
r5 = prn(seed);
r6 = prn(seed);
y = -std::log(r1*r2*r3*r4) - std::log(r5) * std::pow(std::cos(PI/2.0*r6), 2);
break;
default:

View file

@ -32,7 +32,8 @@ CoherentElasticAE::CoherentElasticAE(const CoherentElasticXS& xs)
{ }
void
CoherentElasticAE::sample(double E_in, double& E_out, double& mu) const
CoherentElasticAE::sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const
{
// Get index and interpolation factor for elastic grid
int i;
@ -42,7 +43,7 @@ CoherentElasticAE::sample(double E_in, double& E_out, double& mu) const
// Sample a Bragg edge between 1 and i
const auto& factors = xs_.factors();
double prob = prn() * factors[i+1];
double prob = prn(seed) * factors[i+1];
int k = 0;
if (prob >= factors.front()) {
k = lower_bound_index(factors.begin(), factors.begin() + (i+1), prob);
@ -65,11 +66,12 @@ IncoherentElasticAE::IncoherentElasticAE(hid_t group)
}
void
IncoherentElasticAE::sample(double E_in, double& E_out, double& mu) const
IncoherentElasticAE::sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const
{
// Sample angle by inverting the distribution in ENDF-102, Eq. 7.4
double c = 2 * E_in * debye_waller_;
mu = std::log(1.0 + prn()*(std::exp(2.0*c) - 1))/c - 1.0;
mu = std::log(1.0 + prn(seed)*(std::exp(2.0*c) - 1))/c - 1.0;
// Energy doesn't change in elastic scattering (ENDF-102, Eq. 7.4)
E_out = E_in;
@ -87,7 +89,8 @@ IncoherentElasticAEDiscrete::IncoherentElasticAEDiscrete(hid_t group,
}
void
IncoherentElasticAEDiscrete::sample(double E_in, double& E_out, double& mu) const
IncoherentElasticAEDiscrete::sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const
{
// Get index and interpolation factor for elastic grid
int i;
@ -99,7 +102,7 @@ IncoherentElasticAEDiscrete::sample(double E_in, double& E_out, double& mu) cons
// Sample outgoing cosine bin
int n_mu = mu_out_.shape()[1];
int k = prn() * n_mu;
int k = prn(seed) * n_mu;
// Rather than use the sampled discrete mu directly, it is smeared over
// a bin of width 0.5*min(mu[k] - mu[k-1], mu[k+1] - mu[k]) centered on the
@ -122,7 +125,7 @@ IncoherentElasticAEDiscrete::sample(double E_in, double& E_out, double& mu) cons
mu_out_(i, k+1) + f*(mu_out_(i+1, k+1) - mu_out_(i, k+1));
// Smear cosine
mu += std::min(mu - mu_left, mu_right - mu)*(prn() - 0.5);
mu += std::min(mu - mu_left, mu_right - mu)*(prn(seed) - 0.5);
// Energy doesn't change in elastic scattering
E_out = E_in;
@ -142,7 +145,8 @@ IncoherentInelasticAEDiscrete::IncoherentInelasticAEDiscrete(hid_t group,
}
void
IncoherentInelasticAEDiscrete::sample(double E_in, double& E_out, double& mu) const
IncoherentInelasticAEDiscrete::sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const
{
// Get index and interpolation factor for inelastic grid
int i;
@ -160,10 +164,10 @@ IncoherentInelasticAEDiscrete::sample(double E_in, double& E_out, double& mu) co
int n = energy_out_.shape()[1];
if (!skewed_) {
// All bins equally likely
j = prn() * n;
j = prn(seed) * n;
} else {
// Distribution skewed away from edge points
double r = prn() * (n - 3);
double r = prn(seed) * (n - 3);
if (r > 1.0) {
// equally likely N-4 middle bins
j = r + 1;
@ -191,7 +195,7 @@ IncoherentInelasticAEDiscrete::sample(double E_in, double& E_out, double& mu) co
// Sample outgoing cosine bin
int m = mu_out_.shape()[2];
int k = prn() * m;
int k = prn(seed) * m;
// Determine outgoing cosine corresponding to E_in[i] and E_in[i+1]
double mu_ijk = mu_out_(i, j, k);
@ -245,7 +249,8 @@ IncoherentInelasticAE::IncoherentInelasticAE(hid_t group)
}
void
IncoherentInelasticAE::sample(double E_in, double& E_out, double& mu) const
IncoherentInelasticAE::sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const
{
// Get index and interpolation factor for inelastic grid
int i;
@ -258,7 +263,7 @@ IncoherentInelasticAE::sample(double E_in, double& E_out, double& mu) const
// Determine outgoing energy bin
// (First reset n_energy_out to the right value)
auto n = distribution_[l].n_e_out;
double r1 = prn();
double r1 = prn(seed);
double c_j = distribution_[l].e_out_cdf[0];
double c_j1;
std::size_t j;
@ -298,7 +303,7 @@ IncoherentInelasticAE::sample(double E_in, double& E_out, double& mu) const
// Sample outgoing cosine bin
int n_mu = distribution_[l].mu.shape()[1];
std::size_t k = prn() * n_mu;
std::size_t k = prn(seed) * n_mu;
// Rather than use the sampled discrete mu directly, it is smeared over
// a bin of width 0.5*min(mu[k] - mu[k-1], mu[k+1] - mu[k]) centered on the
@ -323,7 +328,7 @@ IncoherentInelasticAE::sample(double E_in, double& E_out, double& mu) const
mu_right = mu_l(j, k+1) + f*(mu_l(j+1, k+1) - mu_l(j, k+1));
// Smear cosine
mu += std::min(mu - mu_left, mu_right - mu)*(prn() - 0.5);
mu += std::min(mu - mu_left, mu_right - mu)*(prn(seed) - 0.5);
}
} // namespace openmc

View file

@ -52,7 +52,8 @@ UncorrelatedAngleEnergy::UncorrelatedAngleEnergy(hid_t group)
}
void
UncorrelatedAngleEnergy::sample(double E_in, double& E_out, double& mu) const
UncorrelatedAngleEnergy::sample(double E_in, double& E_out, double& mu,
uint64_t* seed) const
{
// Sample cosine of scattering angle
if (fission_) {
@ -61,14 +62,14 @@ UncorrelatedAngleEnergy::sample(double E_in, double& E_out, double& mu) const
mu = 1.0;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
} else if (!angle_.empty()) {
mu = angle_.sample(E_in);
mu = angle_.sample(E_in, seed);
} else {
// no angle distribution given => assume isotropic for all energies
mu = 2.0*prn() - 1.0;
mu = 2.0*prn(seed) - 1.0;
}
// Sample outgoing energy
E_out = energy_->sample(E_in);
E_out = energy_->sample(E_in, seed);
}
} // namespace openmc

View file

@ -476,7 +476,7 @@ void initialize_history(Particle* p, int64_t index_source)
// set random number seed
int64_t particle_seed = (simulation::total_gen + overall_generation() - 1)
* settings::n_particles + p->id_;
set_particle_seed(particle_seed);
init_particle_seeds(particle_seed, p->seeds_);
// set particle trace
simulation::trace = false;

View file

@ -142,7 +142,7 @@ SourceDistribution::SourceDistribution(pugi::xml_node node)
}
Particle::Bank SourceDistribution::sample() const
Particle::Bank SourceDistribution::sample(uint64_t* seed) const
{
Particle::Bank site;
@ -158,7 +158,7 @@ Particle::Bank SourceDistribution::sample() const
site.particle = particle_;
// Sample spatial distribution
site.r = space_->sample();
site.r = space_->sample(seed);
double xyz[] {site.r.x, site.r.y, site.r.z};
// Now search to see if location exists in geometry
@ -200,7 +200,7 @@ Particle::Bank SourceDistribution::sample() const
++n_accept;
// Sample angle
site.u = angle_->sample();
site.u = angle_->sample(seed);
// Check for monoenergetic source above maximum particle energy
auto p = static_cast<int>(particle_);
@ -218,7 +218,7 @@ Particle::Bank SourceDistribution::sample() const
while (true) {
// Sample energy spectrum
site.E = energy_->sample();
site.E = energy_->sample(seed);
// Resample if energy falls outside minimum or maximum particle energy
if (site.E < data::energy_max[p] && site.E > data::energy_min[p]) break;
@ -270,10 +270,10 @@ void initialize_source()
// initialize random number seed
int64_t id = simulation::total_gen*settings::n_particles +
simulation::work_index[mpi::rank] + i + 1;
set_particle_seed(id);
uint64_t seed = init_seed(id, STREAM_SOURCE);
// sample external source distribution
simulation::source_bank[i] = sample_external_source();
simulation::source_bank[i] = sample_external_source(&seed);
}
}
@ -287,11 +287,8 @@ void initialize_source()
}
}
Particle::Bank sample_external_source()
Particle::Bank sample_external_source(uint64_t* seed)
{
// Set the random number generator to the source stream.
prn_set_stream(STREAM_SOURCE);
// Determine total source strength
double total_strength = 0.0;
for (auto& s : model::external_sources)
@ -300,7 +297,7 @@ Particle::Bank sample_external_source()
// Sample from among multiple source distributions
int i = 0;
if (model::external_sources.size() > 1) {
double xi = prn()*total_strength;
double xi = prn(seed)*total_strength;
double c = 0.0;
for (; i < model::external_sources.size(); ++i) {
c += model::external_sources[i].strength();
@ -309,7 +306,7 @@ Particle::Bank sample_external_source()
}
// Sample source site from i-th source distribution
Particle::Bank site {model::external_sources[i].sample()};
Particle::Bank site {model::external_sources[i].sample(seed)};
// If running in MG, convert site.E to group
if (!settings::run_CE) {
@ -318,9 +315,6 @@ Particle::Bank sample_external_source()
site.E = data::mg.num_energy_groups_ - site.E - 1.;
}
// Set the random number generator back to the tracking stream.
prn_set_stream(STREAM_TRACKING);
return site;
}
@ -336,10 +330,10 @@ void fill_source_bank_fixedsource()
// initialize random number seed
int64_t id = (simulation::total_gen + overall_generation()) *
settings::n_particles + simulation::work_index[mpi::rank] + i + 1;
set_particle_seed(id);
uint64_t seed = init_seed(id, STREAM_SOURCE);
// sample external source distribution
simulation::source_bank[i] = sample_external_source();
simulation::source_bank[i] = sample_external_source(&seed);
}
}
}

View file

@ -197,24 +197,24 @@ Surface::reflect(Position r, Direction u) const
}
Direction
Surface::diffuse_reflect(Position r, Direction u) const
Surface::diffuse_reflect(Position r, Direction u, uint64_t* seed) const
{
// Diffuse reflect direction according to the normal.
// cosine distribution
// cosine distribution
Direction n = this->normal(r);
n /= n.norm();
const double projection = n.dot(u);
// sample from inverse function, u=sqrt(rand) since p(u)=2u, so F(u)=u^2
const double mu = (projection>=0.0) ?
-std::sqrt(prn()) : std::sqrt(prn());
// sample azimuthal distribution uniformly
u = rotate_angle(n, mu, nullptr);
// normalize the direction
return u/u.norm();
// sample from inverse function, u=sqrt(rand) since p(u)=2u, so F(u)=u^2
const double mu = (projection>=0.0) ?
-std::sqrt(prn(seed)) : std::sqrt(prn(seed));
// sample azimuthal distribution uniformly
u = rotate_angle(n, mu, nullptr, seed);
// normalize the direction
return u/u.norm();
}
CSGSurface::CSGSurface() : Surface{} {};

View file

@ -150,7 +150,8 @@ ThermalScattering::ThermalScattering(hid_t group, const std::vector<double>& tem
void
ThermalScattering::calculate_xs(double E, double sqrtkT, int* i_temp,
double* elastic, double* inelastic) const
double* elastic, double* inelastic,
uint64_t* seed) const
{
// Determine temperature for S(a,b) table
double kT = sqrtkT*sqrtkT;
@ -172,7 +173,7 @@ ThermalScattering::calculate_xs(double E, double sqrtkT, int* i_temp,
// Randomly sample between temperature i and i+1
double f = (kT - kTs_[i]) / (kTs_[i+1] - kTs_[i]);
if (f > prn()) ++i;
if (f > prn(seed)) ++i;
}
// Set temperature index
@ -265,13 +266,13 @@ ThermalData::calculate_xs(double E, double* elastic, double* inelastic) const
void
ThermalData::sample(const NuclideMicroXS& micro_xs, double E,
double* E_out, double* mu)
double* E_out, double* mu, uint64_t* seed)
{
// Determine whether inelastic or elastic scattering will occur
if (prn() < micro_xs.thermal_elastic / micro_xs.thermal) {
elastic_.distribution->sample(E, *E_out, *mu);
if (prn(seed) < micro_xs.thermal_elastic / micro_xs.thermal) {
elastic_.distribution->sample(E, *E_out, *mu, seed);
} else {
inelastic_.distribution->sample(E, *E_out, *mu);
inelastic_.distribution->sample(E, *E_out, *mu, seed);
}
// Because of floating-point roundoff, it may be possible for mu to be

View file

@ -125,15 +125,14 @@ std::vector<VolumeCalculation::Result> VolumeCalculation::execute() const
std::vector<std::vector<int>> hits(n);
Particle p;
prn_set_stream(STREAM_VOLUME);
// Sample locations and count hits
#pragma omp for
for (size_t i = i_start; i < i_end; i++) {
set_particle_seed(iterations * n_samples_ + i);
int64_t id = iterations * n_samples_ + i;
uint64_t seed = init_seed(id, STREAM_VOLUME);
p.n_coord_ = 1;
Position xi {prn(), prn(), prn()};
Position xi {prn(&seed), prn(&seed), prn(&seed)};
p.r() = lower_left_ + xi*(upper_right_ - lower_left_);
p.u() = {0.5, 0.5, 0.5};
@ -203,7 +202,6 @@ std::vector<VolumeCalculation::Result> VolumeCalculation::execute() const
}
}
}
prn_set_stream(STREAM_TRACKING);
} // omp parallel
// Reduce hits onto master process

View file

@ -174,53 +174,50 @@ def test_rotate_angle():
# Now to test phi is None
mu = 0.9
settings = openmc.lib.settings
settings.seed = 1
phi = None
prn_seed = 1
# When seed = 1, phi will be sampled as 1.9116495709698769
# The resultant reference is from hand-calculations given the above
ref_uvw = [0.9, 0.410813051297112, 0.1457142302040]
test_uvw = openmc.lib.math.rotate_angle(uvw0, mu)
test_uvw = openmc.lib.math.rotate_angle(uvw0, mu, phi, prn_seed)
assert np.allclose(ref_uvw, test_uvw)
def test_maxwell_spectrum():
settings = openmc.lib.settings
settings.seed = 1
prn_seed = 1
T = 0.5
ref_val = 0.6129982175261098
test_val = openmc.lib.math.maxwell_spectrum(T)
test_val = openmc.lib.math.maxwell_spectrum(T, prn_seed)
assert ref_val == test_val
def test_watt_spectrum():
settings = openmc.lib.settings
settings.seed = 1
prn_seed = 1
a = 0.5
b = 0.75
ref_val = 0.6247242713640233
test_val = openmc.lib.math.watt_spectrum(a, b)
test_val = openmc.lib.math.watt_spectrum(a, b, prn_seed)
assert ref_val == test_val
def test_normal_dist():
settings = openmc.lib.settings
settings.seed = 1
prn_seed = 1
a = 14.08
b = 0.0
ref_val = 14.08
test_val = openmc.lib.math.normal_variate(a, b)
test_val = openmc.lib.math.normal_variate(a, b, prn_seed)
assert ref_val == pytest.approx(test_val)
settings.seed = 1
prn_seed = 1
a = 14.08
b = 1.0
ref_val = 16.436645416691427
test_val = openmc.lib.math.normal_variate(a, b)
test_val = openmc.lib.math.normal_variate(a, b, prn_seed)
assert ref_val == pytest.approx(test_val)