From 06ae298be3400c8173212ee46f016a67d68caf77 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 27 Aug 2025 18:29:33 +0300 Subject: [PATCH 01/88] Implement get_pdf functionality --- include/openmc/distribution.h | 17 +- include/openmc/distribution_angle.h | 3 +- include/openmc/particle_data.h | 5 + include/openmc/reaction_product.h | 8 +- include/openmc/secondary_correlated.h | 5 + include/openmc/secondary_kalbach.h | 5 + include/openmc/secondary_nbody.h | 5 + include/openmc/secondary_thermal.h | 6 + include/openmc/secondary_uncorrelated.h | 5 + include/openmc/thermal.h | 2 + src/distribution.cpp | 77 ++++ src/distribution_angle.cpp | 33 ++ src/physics.cpp | 14 +- src/reaction_product.cpp | 70 ++++ src/secondary_correlated.cpp | 119 ++++++ src/secondary_kalbach.cpp | 142 +++++++ src/secondary_nbody.cpp | 64 ++++ src/secondary_thermal.cpp | 480 ++++++++++++++++++++++++ src/secondary_uncorrelated.cpp | 28 ++ src/thermal.cpp | 54 ++- 20 files changed, 1128 insertions(+), 14 deletions(-) diff --git a/include/openmc/distribution.h b/include/openmc/distribution.h index 854cf7d7719..4424f0e7b9f 100644 --- a/include/openmc/distribution.h +++ b/include/openmc/distribution.h @@ -23,7 +23,7 @@ class Distribution { public: virtual ~Distribution() = default; virtual double sample(uint64_t* seed) const = 0; - + virtual double get_pdf(double x) const = 0; //! Return integral of distribution //! \return Integral of distribution virtual double integral() const { return 1.0; }; @@ -84,6 +84,10 @@ class Discrete : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; + //! Calculate the probability density function (PDF) at a given value + //! \param x The value at which to evaluate the PDF + //! \return The value of the PDF at the given point + double get_pdf(double x) const; double integral() const override { return di_.integral(); }; @@ -111,6 +115,7 @@ class Uniform : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; + double get_pdf(double x) const; double a() const { return a_; } double b() const { return b_; } @@ -135,6 +140,7 @@ class PowerLaw : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; + double get_pdf(double x) const; double a() const { return std::pow(offset_, ninv_); } double b() const { return std::pow(offset_ + span_, ninv_); } @@ -160,6 +166,7 @@ class Maxwell : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; + double get_pdf(double x) const; double theta() const { return theta_; } @@ -180,6 +187,7 @@ class Watt : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; + double get_pdf(double x) const; double a() const { return a_; } double b() const { return b_; } @@ -204,6 +212,7 @@ class Normal : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; + double get_pdf(double x) const; double mean_value() const { return mean_value_; } double std_dev() const { return std_dev_; } @@ -227,8 +236,10 @@ class Tabular : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; + double get_pdf(double x) const; - // properties + // double get_pdf_value(double x,uint64_t* seed) const; + // properties vector& x() { return x_; } const vector& x() const { return x_; } const vector& p() const { return p_; } @@ -263,6 +274,7 @@ class Equiprobable : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; + double get_pdf(double x) const; const vector& x() const { return x_; } @@ -282,6 +294,7 @@ class Mixture : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; + double get_pdf(double x) const; double integral() const override { return integral_; } diff --git a/include/openmc/distribution_angle.h b/include/openmc/distribution_angle.h index efd4e58425b..b0547ac6678 100644 --- a/include/openmc/distribution_angle.h +++ b/include/openmc/distribution_angle.h @@ -25,10 +25,11 @@ class AngleDistribution { //! \param[inout] seed pseudorandom number seed pointer //! \return Cosine of the angle in the range [-1,1] double sample(double E, uint64_t* seed) const; - + double get_pdf(double E, double mu, uint64_t* seed) const; //! Determine whether angle distribution is empty //! \return Whether distribution is empty bool empty() const { return energy_.empty(); } + double get_energy(int num) { return energy_[num]; } private: vector energy_; diff --git a/include/openmc/particle_data.h b/include/openmc/particle_data.h index ec393845f81..d93eaf6cf43 100644 --- a/include/openmc/particle_data.h +++ b/include/openmc/particle_data.h @@ -488,7 +488,9 @@ class ParticleData : public GeometryState { int event_nuclide_; int event_mt_; int delayed_group_ {0}; + int event_index_mt_; int parent_nuclide_ {-1}; + // Direction v_t_; future use for thermal velocity int n_bank_ {0}; double bank_second_E_ {0.0}; @@ -622,7 +624,10 @@ class ParticleData : public GeometryState { const int& event_nuclide() const { return event_nuclide_; } int& event_mt() { return event_mt_; } // MT number of collision const int& event_mt() const { return event_mt_; } + int& event_index_mt() { return event_index_mt_; } int& delayed_group() { return delayed_group_; } // delayed group + // Position& v_t() { return v_t_; } // future use for thermal velocity + // const Position& v_t() const { return v_t_; } const int& parent_nuclide() const { return parent_nuclide_; } int& parent_nuclide() { return parent_nuclide_; } // Parent nuclide diff --git a/include/openmc/reaction_product.h b/include/openmc/reaction_product.h index 4fbbc1b626a..3d2262c7100 100644 --- a/include/openmc/reaction_product.h +++ b/include/openmc/reaction_product.h @@ -11,8 +11,9 @@ #include "openmc/endf.h" #include "openmc/memory.h" // for unique_ptr #include "openmc/particle.h" +#include "openmc/tallies/filter.h" +#include "openmc/tallies/tally.h" #include "openmc/vector.h" // for vector - namespace openmc { //============================================================================== @@ -48,7 +49,10 @@ class ReactionProduct { //! \param[out] mu Outgoing cosine with respect to current direction //! \param[inout] seed Pseudorandom seed pointer void sample(double E_in, double& E_out, double& mu, uint64_t* seed) const; - + void get_pdf(int i_tally, double E_in, double& E_out, uint64_t* seed, + Particle& p, std::vector& mu_cm, std::vector& Js, + std::vector& ghost_particles, + std::vector& pdfs_lab) const; ParticleType particle_; //!< Particle type EmissionMode emission_mode_; //!< Emission mode double decay_rate_; //!< Decay rate (for delayed neutron precursors) in [1/s] diff --git a/include/openmc/secondary_correlated.h b/include/openmc/secondary_correlated.h index 6905c38e369..991f8d61138 100644 --- a/include/openmc/secondary_correlated.h +++ b/include/openmc/secondary_correlated.h @@ -10,6 +10,7 @@ #include "openmc/angle_energy.h" #include "openmc/distribution.h" #include "openmc/endf.h" +#include "openmc/particle.h" #include "openmc/vector.h" namespace openmc { @@ -40,6 +41,10 @@ class CorrelatedAngleEnergy : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + void get_pdf(double det_pos[4], double E_in, double& E_out, uint64_t* seed, + Particle& p, std::vector& mu_cm, std::vector& Js, + std::vector& ghost_particles, + std::vector& pdfs_lab) const; // energy property vector& energy() { return energy_; } diff --git a/include/openmc/secondary_kalbach.h b/include/openmc/secondary_kalbach.h index 83806d35248..ff5f13bd7dc 100644 --- a/include/openmc/secondary_kalbach.h +++ b/include/openmc/secondary_kalbach.h @@ -10,6 +10,7 @@ #include "openmc/angle_energy.h" #include "openmc/constants.h" #include "openmc/endf.h" +#include "openmc/particle.h" #include "openmc/vector.h" namespace openmc { @@ -31,6 +32,10 @@ class KalbachMann : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + void get_pdf(double det_pos[4], double E_in, double& E_out, uint64_t* seed, + Particle& p, std::vector& mu_cm, std::vector& Js, + std::vector& ghost_particles, + std::vector& pdfs_lab) const; private: //! Outgoing energy/angle at a single incoming energy diff --git a/include/openmc/secondary_nbody.h b/include/openmc/secondary_nbody.h index efb4fd75ba1..8395c68290d 100644 --- a/include/openmc/secondary_nbody.h +++ b/include/openmc/secondary_nbody.h @@ -7,6 +7,7 @@ #include "hdf5.h" #include "openmc/angle_energy.h" +#include "openmc/particle.h" namespace openmc { @@ -27,6 +28,10 @@ class NBodyPhaseSpace : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + void get_pdf(double det_pos[4], double E_in, double& E_out, uint64_t* seed, + Particle& p, std::vector& mu_cm, std::vector& Js, + std::vector& ghost_particles, + std::vector& pdfs_lab) const; private: int n_bodies_; //!< Number of particles distributed diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index 5b18902afbb..7fb758ec570 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -32,6 +32,7 @@ class CoherentElasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + double get_pdf(double E_in, double& E_out, double& mu, uint64_t* seed) const; private: const CoherentElasticXS& xs_; //!< Coherent elastic scattering cross section @@ -55,6 +56,7 @@ class IncoherentElasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + double get_pdf(double E_in, double& E_out, double& mu, uint64_t* seed) const; private: double debye_waller_; @@ -80,6 +82,7 @@ class IncoherentElasticAEDiscrete : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + double get_pdf(double E_in, double& E_out, double& mu, uint64_t* seed) const; private: const vector& energy_; //!< Energies at which cosines are tabulated @@ -106,6 +109,8 @@ class IncoherentInelasticAEDiscrete : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + double get_pdf( + double E_in, double& E_out, double& mu, uint64_t* seed, int l = -1) const; private: const vector& energy_; //!< Incident energies @@ -134,6 +139,7 @@ class IncoherentInelasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + double get_pdf(double E_in, double& E_out, double& mu, uint64_t* seed) const; private: //! Secondary energy/angle distribution diff --git a/include/openmc/secondary_uncorrelated.h b/include/openmc/secondary_uncorrelated.h index 3afa3d9ceb7..30d9c4b10df 100644 --- a/include/openmc/secondary_uncorrelated.h +++ b/include/openmc/secondary_uncorrelated.h @@ -10,6 +10,7 @@ #include "openmc/distribution_angle.h" #include "openmc/distribution_energy.h" #include "openmc/memory.h" +#include "openmc/particle.h" #include "openmc/vector.h" namespace openmc { @@ -31,6 +32,10 @@ class UncorrelatedAngleEnergy : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + void get_pdf(double det_pos[4], double E_in, double& E_out, uint64_t* seed, + Particle& p, std::vector& mu_cm, std::vector& Js, + std::vector& ghost_particles, + std::vector& pdfs_lab) const; // Accessors AngleDistribution& angle() { return angle_; } diff --git a/include/openmc/thermal.h b/include/openmc/thermal.h index de0767d0af0..d6291c30e5e 100644 --- a/include/openmc/thermal.h +++ b/include/openmc/thermal.h @@ -52,6 +52,8 @@ class ThermalData { //! \param[inout] seed Pseudorandom seed pointer void sample(const NuclideMicroXS& micro_xs, double E_in, double* E_out, double* mu, uint64_t* seed); + double get_pdf(const NuclideMicroXS& micro_xs, double E, double& E_out, + double mu, uint64_t* seed); private: struct Reaction { diff --git a/src/distribution.cpp b/src/distribution.cpp index ca00cbde4eb..1f0eb29c88b 100644 --- a/src/distribution.cpp +++ b/src/distribution.cpp @@ -12,6 +12,7 @@ #include "openmc/math_functions.h" #include "openmc/random_dist.h" #include "openmc/random_lcg.h" +#include "openmc/search.h" #include "openmc/xml_interface.h" namespace openmc { @@ -134,6 +135,12 @@ double Discrete::sample(uint64_t* seed) const { return x_[di_.sample(seed)]; } +// Not implemented +double Discrete::get_pdf(double x) const +{ + + return -1; +} //============================================================================== // Uniform implementation @@ -155,6 +162,13 @@ double Uniform::sample(uint64_t* seed) const { return a_ + prn(seed) * (b_ - a_); } +double Uniform::get_pdf(double x) const +{ + if (x <= b_ && x >= a_) + return 1 / (b_ - a_); + else + return 0; +} //============================================================================== // PowerLaw implementation @@ -181,6 +195,10 @@ double PowerLaw::sample(uint64_t* seed) const { return std::pow(offset_ + prn(seed) * span_, ninv_); } +double PowerLaw::get_pdf(double x) const +{ + return x / span_ / ninv_; +} //============================================================================== // Maxwell implementation @@ -195,6 +213,11 @@ double Maxwell::sample(uint64_t* seed) const { return maxwell_spectrum(theta_, seed); } +// Not implemented +double Maxwell::get_pdf(double x) const +{ + return -1; +} //============================================================================== // Watt implementation @@ -215,6 +238,11 @@ double Watt::sample(uint64_t* seed) const { return watt_spectrum(a_, b_, seed); } +// Not implemented +double Watt::get_pdf(double x) const +{ + return -1; +} //============================================================================== // Normal implementation @@ -235,6 +263,12 @@ double Normal::sample(uint64_t* seed) const { return normal_variate(mean_value_, std_dev_, seed); } +double Normal::get_pdf(double x) const +{ + double exponent = -0.5 * std::pow((x - mean_value_) / std_dev_, 2); + double coefficient = 1 / (std_dev_ * std::sqrt(2 * PI)); + return coefficient * std::exp(exponent); +} //============================================================================== // Tabular implementation @@ -356,6 +390,41 @@ double Tabular::sample(uint64_t* seed) const } } +double Tabular::get_pdf(double x) const +{ + // get PDF value at x + + int i; + std::size_t n = x_.size(); + if (x < x_[0]) { + return 0; + } else if (x > x_[n - 1]) { + return 0; + } else { + i = lower_bound_index(x_.begin(), x_.end(), x); + } + + // Determine bounding PDF values + double x_i = x_[i]; + double p_i = p_[i]; + + if (interp_ == Interpolation::histogram) { + // Histogram interpolation + return p_i; + } else { + // Linear-linear interpolation + double x_i1 = x_[i + 1]; + double p_i1 = p_[i + 1]; + + double m = (p_i1 - p_i) / (x_i1 - x_i); + if (m == 0.0) { + return p_i; + } else { + return p_i + (x - x_i) * m; + } + } +} + //============================================================================== // Equiprobable implementation //============================================================================== @@ -371,6 +440,10 @@ double Equiprobable::sample(uint64_t* seed) const double xr = x_[i + i]; return xl + ((n - 1) * r - i) * (xr - xl); } +double Equiprobable::get_pdf(double x) const +{ + return -1; +} //============================================================================== // Mixture implementation @@ -420,6 +493,10 @@ double Mixture::sample(uint64_t* seed) const // Sample the chosen distribution return it->second->sample(seed); } +double Mixture::get_pdf(double x) const +{ + return -1; +} //============================================================================== // Helper function diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index 5e92b794a72..5c35ff9497a 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -95,4 +95,37 @@ double AngleDistribution::sample(double E, uint64_t* seed) const return mu; } +double AngleDistribution::get_pdf(double E, double mu, uint64_t* seed) const +{ + // Determine number of incoming energies + auto n = energy_.size(); + + // Find energy bin and calculate interpolation factor -- if the energy is + // outside the range of the tabulated energies, choose the first or last bins + int i; + double r; + if (E < energy_[0]) { + i = 0; + r = 0.0; + } else if (E > energy_[n - 1]) { + i = n - 2; + r = 1.0; + } else { + i = lower_bound_index(energy_.begin(), energy_.end(), E); + r = (E - energy_[i]) / (energy_[i + 1] - energy_[i]); + } + + // Sample between the ith and (i+1)th bin + if (r > prn(seed)) + ++i; + + // Sample i-th distribution + double pdf = distribution_[i]->get_pdf(mu); + + // Make sure pdf > 0 and return + if (std::abs(pdf) < 0) + fmt::print("pdf = {} <1", pdf); + return pdf; +} + } // namespace openmc diff --git a/src/physics.cpp b/src/physics.cpp index 3c06e543de1..1b8201cead3 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -1,4 +1,5 @@ #include "openmc/physics.h" +#include #include "openmc/bank.h" #include "openmc/bremsstrahlung.h" @@ -216,7 +217,7 @@ void create_fission_sites(Particle& p, int i_nuclide, const Reaction& rx) // Sample delayed group and angle/energy for fission reaction sample_fission_neutron(i_nuclide, rx, &site, p); - + p.event_index_mt() = -999; // Store fission site in bank if (use_fission_bank) { int64_t idx = simulation::fission_bank.thread_safe_append(site); @@ -665,7 +666,7 @@ void scatter(Particle& p, int i_nuclide) { // copy incoming direction Direction u_old {p.u()}; - + p.event_index_mt() = 0; // Get pointer to nuclide and grid index/interpolation factor const auto& nuc {data::nuclides[i_nuclide]}; const auto& micro {p.neutron_xs(i_nuclide)}; @@ -702,7 +703,7 @@ void scatter(Particle& p, int i_nuclide) // S(A,B) SCATTERING sab_scatter(i_nuclide, micro.index_sab, p); - + p.event_index_mt() = -1234; // to distinguish from elastic p.event_mt() = ELASTIC; sampled = true; } @@ -724,6 +725,7 @@ void scatter(Particle& p, int i_nuclide) const auto& rx {nuc->reactions_[i]}; inelastic_scatter(*nuc, *rx, p); p.event_mt() = rx->mt_; + p.event_index_mt() = i; } // Set event component @@ -758,8 +760,9 @@ void elastic_scatter(int i_nuclide, const Reaction& rx, double kT, Particle& p) v_t = sample_target_velocity(*nuc, p.E(), p.u(), v_n, p.neutron_xs(i_nuclide).elastic, kT, p.current_seed()); } - - // Velocity of center-of-mass + // change units in the future + // p.v_t() = C_LIGHT * std::sqrt(2 / p.getMass()) * v_t; + // Velocity of center-of-mass Direction v_cm = (v_n + awr * v_t) / (awr + 1.0); // Transform to CM frame @@ -1104,6 +1107,7 @@ void sample_fission_neutron( void inelastic_scatter(const Nuclide& nuc, const Reaction& rx, Particle& p) { + // p.v_t() = {0, 0, 0}; // intialize target velocity to zero // copy energy of neutron double E_in = p.E(); diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index 3ba2c0cfb05..e30e040f91a 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -13,7 +13,9 @@ #include "openmc/secondary_correlated.h" #include "openmc/secondary_kalbach.h" #include "openmc/secondary_nbody.h" +#include "openmc/secondary_thermal.h" #include "openmc/secondary_uncorrelated.h" +#include "openmc/tallies/tally_scoring.h" namespace openmc { @@ -128,5 +130,73 @@ void ReactionProduct::sample( distribution_[0]->sample(E_in, E_out, mu, seed); } } +void ReactionProduct::get_pdf(int i_tally, double E_in, double& E_out, + uint64_t* seed, Particle& p, std::vector& mu_cm, + std::vector& Js, std::vector& ghost_particles, + std::vector& pdfs_lab) const +{ + /* function to get detector position from user - Future implementation + double det_pos[4]; + get_det_pos(det_pos, i_tally); + */ + double det_pos[4] = {0.0, 0.0, 0.0, 1.0}; // Placeholder for detector position + + int distribution_index; + auto n = applicability_.size(); + if (n > 1) { + double prob = 0.0; + 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, seed); + distribution_index = i; + break; + } + } + } else { + // If only one distribution is present, go ahead and sample it + // distribution_[0]->sample(E_in, E_out, mu, seed); + distribution_index = 0; + } + // now extract pdf + + AngleEnergy* angleEnergyPtr = distribution_[distribution_index].get(); + + if (CorrelatedAngleEnergy* correlatedAE = + dynamic_cast(angleEnergyPtr)) { + + (*correlatedAE) + .get_pdf( + det_pos, E_in, E_out, seed, p, mu_cm, Js, ghost_particles, pdfs_lab); + // Handle CorrelatedAngleEnergy + } else if (KalbachMann* kalbachMann = + dynamic_cast(angleEnergyPtr)) { + + (*kalbachMann) + .get_pdf( + det_pos, E_in, E_out, seed, p, mu_cm, Js, ghost_particles, pdfs_lab); + + // Handle KalbachMann + } else if (NBodyPhaseSpace* nBodyPS = + dynamic_cast(angleEnergyPtr)) { + + (*nBodyPS).get_pdf( + det_pos, E_in, E_out, seed, p, mu_cm, Js, ghost_particles, pdfs_lab); + // Handle NBodyPhaseSpace + } else if (UncorrelatedAngleEnergy* uncorrelatedAE = + dynamic_cast(angleEnergyPtr)) { + + (*uncorrelatedAE) + .get_pdf( + det_pos, E_in, E_out, seed, p, mu_cm, Js, ghost_particles, pdfs_lab); + // Handle UncorrelatedAngleEnergy + } else { + std::cout << "Unknown derived type." << std::endl; + } +} } // namespace openmc diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index 0e4891dd3e7..aaad6cd9515 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -10,8 +10,11 @@ #include "openmc/endf.h" #include "openmc/hdf5_interface.h" +#include "openmc/nuclide.h" +#include "openmc/particle.h" #include "openmc/random_lcg.h" #include "openmc/search.h" +#include "openmc/tallies/tally_scoring.h" namespace openmc { @@ -263,4 +266,120 @@ void CorrelatedAngleEnergy::sample( } } +void CorrelatedAngleEnergy::get_pdf(double det_pos[4], double E_in, + double& E_out, uint64_t* seed, Particle& p, std::vector& mu_cm, + std::vector& Js, std::vector& ghost_particles, + std::vector& pdfs_lab) const +{ + // Find energy bin and calculate interpolation factor -- if the energy is + // outside the range of the tabulated energies, choose the first or last bins + auto n_energy_in = energy_.size(); + int i; + double r; + if (E_in < energy_[0]) { + i = 0; + r = 0.0; + } else if (E_in > energy_[n_energy_in - 1]) { + i = n_energy_in - 2; + r = 1.0; + } else { + i = lower_bound_index(energy_.begin(), energy_.end(), E_in); + r = (E_in - energy_[i]) / (energy_[i + 1] - energy_[i]); + } + + // Sample between the ith and [i+1]th bin + int l = r > prn(seed) ? i + 1 : i; + + // Interpolation for energy E1 and EK + int n_energy_out = distribution_[i].e_out.size(); + int n_discrete = distribution_[i].n_discrete; + double E_i_1 = distribution_[i].e_out[n_discrete]; + double E_i_K = distribution_[i].e_out[n_energy_out - 1]; + + n_energy_out = distribution_[i + 1].e_out.size(); + n_discrete = distribution_[i + 1].n_discrete; + double E_i1_1 = distribution_[i + 1].e_out[n_discrete]; + double E_i1_K = distribution_[i + 1].e_out[n_energy_out - 1]; + + double E_1 = E_i_1 + r * (E_i1_1 - E_i_1); + double E_K = E_i_K + r * (E_i1_K - E_i_K); + + // Determine outgoing energy bin + n_energy_out = distribution_[l].e_out.size(); + n_discrete = distribution_[l].n_discrete; + double r1 = prn(seed); + double c_k = distribution_[l].c[0]; + int k = 0; + int end = n_energy_out - 2; + + // Discrete portion + for (int j = 0; j < n_discrete; ++j) { + k = j; + c_k = distribution_[l].c[k]; + if (r1 < c_k) { + end = j; + break; + } + } + + // Continuous portion + double c_k1; + for (int j = n_discrete; j < end; ++j) { + k = j; + c_k1 = distribution_[l].c[k + 1]; + if (r1 < c_k1) + break; + k = j + 1; + c_k = c_k1; + } + + double E_l_k = distribution_[l].e_out[k]; + double p_l_k = distribution_[l].p[k]; + if (distribution_[l].interpolation == Interpolation::histogram) { + // Histogram interpolation + if (p_l_k > 0.0 && k >= n_discrete) { + E_out = E_l_k + (r1 - c_k) / p_l_k; + } else { + E_out = E_l_k; + } + + } else if (distribution_[l].interpolation == Interpolation::lin_lin) { + // Linear-linear interpolation + double E_l_k1 = distribution_[l].e_out[k + 1]; + double p_l_k1 = distribution_[l].p[k + 1]; + + double frac = (p_l_k1 - p_l_k) / (E_l_k1 - E_l_k); + if (frac == 0.0) { + E_out = E_l_k + (r1 - c_k) / p_l_k; + } else { + E_out = + E_l_k + + (std::sqrt(std::max(0.0, p_l_k * p_l_k + 2.0 * frac * (r1 - c_k))) - + p_l_k) / + frac; + } + } + + // Now interpolate between incident energy bins i and i + 1 + if (k >= n_discrete) { + if (l == i) { + E_out = E_1 + (E_out - E_i_1) * (E_K - E_1) / (E_i_K - E_i_1); + } else { + E_out = E_1 + (E_out - E_i1_1) * (E_K - E_1) / (E_i1_K - E_i1_1); + } + } + + const auto& nuc {data::nuclides[p.event_nuclide()]}; + const auto& rx {nuc->reactions_[p.event_index_mt()]}; + if (rx->scatter_in_cm_) + + { + /* transform pdf cm to lab frame + */ + } + + if (!rx->scatter_in_cm_) { + } +} + } // namespace openmc diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index 4a7878343c6..99f215601cb 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -9,9 +9,12 @@ #include "xtensor/xview.hpp" #include "openmc/hdf5_interface.h" +#include "openmc/nuclide.h" +#include "openmc/particle.h" #include "openmc/random_dist.h" #include "openmc/random_lcg.h" #include "openmc/search.h" +#include "openmc/tallies/tally_scoring.h" #include "openmc/vector.h" namespace openmc { @@ -237,5 +240,144 @@ void KalbachMann::sample( mu = std::log(r1 * std::exp(km_a) + (1.0 - r1) * std::exp(-km_a)) / km_a; } } +void KalbachMann::get_pdf(double det_pos[4], double E_in, double& E_out, + uint64_t* seed, Particle& p, std::vector& mu_cm, + std::vector& Js, std::vector& ghost_particles, + std::vector& pdfs_lab) const +{ + // Find energy bin and calculate interpolation factor -- if the energy is + // outside the range of the tabulated energies, choose the first or last bins + const auto& nuc {data::nuclides[p.event_nuclide()]}; + + // double E_out; + auto n_energy_in = energy_.size(); + int i; + double r; + if (E_in < energy_[0]) { + i = 0; + r = 0.0; + } else if (E_in > energy_[n_energy_in - 1]) { + i = n_energy_in - 2; + r = 1.0; + } else { + i = lower_bound_index(energy_.begin(), energy_.end(), E_in); + r = (E_in - energy_[i]) / (energy_[i + 1] - energy_[i]); + } + + // Sample between the ith and [i+1]th bin + int l = r > prn(seed) ? i + 1 : i; + + // Interpolation for energy E1 and EK + int n_energy_out = distribution_[i].e_out.size(); + int n_discrete = distribution_[i].n_discrete; + double E_i_1 = distribution_[i].e_out[n_discrete]; + double E_i_K = distribution_[i].e_out[n_energy_out - 1]; + + n_energy_out = distribution_[i + 1].e_out.size(); + n_discrete = distribution_[i + 1].n_discrete; + double E_i1_1 = distribution_[i + 1].e_out[n_discrete]; + double E_i1_K = distribution_[i + 1].e_out[n_energy_out - 1]; + + double E_1 = E_i_1 + r * (E_i1_1 - E_i_1); + double E_K = E_i_K + r * (E_i1_K - E_i_K); + + // Determine outgoing energy bin + n_energy_out = distribution_[l].e_out.size(); + n_discrete = distribution_[l].n_discrete; + double r1 = prn(seed); + double c_k = distribution_[l].c[0]; + int k = 0; + int end = n_energy_out - 2; + + // Discrete portion + for (int j = 0; j < n_discrete; ++j) { + k = j; + c_k = distribution_[l].c[k]; + if (r1 < c_k) { + end = j; + break; + } + } + + // Continuous portion + double c_k1; + for (int j = n_discrete; j < end; ++j) { + k = j; + c_k1 = distribution_[l].c[k + 1]; + if (r1 < c_k1) + break; + k = j + 1; + c_k = c_k1; + } + + double E_l_k = distribution_[l].e_out[k]; + double p_l_k = distribution_[l].p[k]; + double km_r, km_a; + if (distribution_[l].interpolation == Interpolation::histogram) { + // Histogram interpolation + if (p_l_k > 0.0 && k >= n_discrete) { + E_out = E_l_k + (r1 - c_k) / p_l_k; + } else { + E_out = E_l_k; + } + // Determine Kalbach-Mann parameters + km_r = distribution_[l].r[k]; + km_a = distribution_[l].a[k]; + + } else { + // Linear-linear interpolation + double E_l_k1 = distribution_[l].e_out[k + 1]; + double p_l_k1 = distribution_[l].p[k + 1]; + + double frac = (p_l_k1 - p_l_k) / (E_l_k1 - E_l_k); + if (frac == 0.0) { + E_out = E_l_k + (r1 - c_k) / p_l_k; + } else { + E_out = + E_l_k + + (std::sqrt(std::max(0.0, p_l_k * p_l_k + 2.0 * frac * (r1 - c_k))) - + p_l_k) / + frac; + } + // Linear-linear interpolation + // Determine Kalbach-Mann parameters + km_r = distribution_[l].r[k] + + (E_out - E_l_k) / (E_l_k1 - E_l_k) * + (distribution_[l].r[k + 1] - distribution_[l].r[k]); + km_a = distribution_[l].a[k] + + (E_out - E_l_k) / (E_l_k1 - E_l_k) * + (distribution_[l].a[k + 1] - distribution_[l].a[k]); + } + + // Now interpolate between incident energy bins i and i + 1 + if (k >= n_discrete) { + if (l == i) { + E_out = E_1 + (E_out - E_i_1) * (E_K - E_1) / (E_i_K - E_i_1); + } else { + E_out = E_1 + (E_out - E_i1_1) * (E_K - E_1) / (E_i1_K - E_i1_1); + } + } + /* function to transform pdf CM to lab frame - Future implementation + get_pdf_to_point_elastic(det_pos, p, mu_cm, Js, ghost_particles, E_out / + 1e6); for (std::size_t i = 0; i < mu_cm.size(); ++i) { + // Assuming Js.size() is the same as mu_cm.size() + double mu_c = mu_cm[i]; + double derivative = Js[i]; + double pdf_cm = km_a / (2 * std::sinh(km_a)) * + (std::cosh(km_a * mu_c) + + km_r * std::sinh(km_a * mu_c)); // center of mass + pdfs_lab.push_back(pdf_cm / std::abs(derivative)); + } + */ + + // https://docs.openmc.org/en/v0.8.0/methods/physics.html#equation-KM-pdf-angle + // double pdf_mu = km_a / (2 * std::sinh(km_a)) * (std::cosh(km_a * mymu) + + // km_r * std::sinh(km_a * mymu)); // center of mass return pdf_mu; + + const auto& rx {nuc->reactions_[p.event_index_mt()]}; + if (!rx->scatter_in_cm_) { + fatal_error("didn't implement lab"); + } +} } // namespace openmc diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index da0bb81c471..b08a662d7bb 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -5,8 +5,10 @@ #include "openmc/constants.h" #include "openmc/hdf5_interface.h" #include "openmc/math_functions.h" +#include "openmc/nuclide.h" #include "openmc/random_dist.h" #include "openmc/random_lcg.h" +#include "openmc/tallies/tally_scoring.h" namespace openmc { @@ -67,4 +69,66 @@ void NBodyPhaseSpace::sample( E_out = E_max * v; } +void NBodyPhaseSpace::get_pdf(double det_pos[4], double E_in, double& E_out, + uint64_t* seed, Particle& p, std::vector& mu_cm, + std::vector& Js, std::vector& ghost_particles, + std::vector& pdfs_lab) const +{ + // By definition, the distribution of the angle is isotropic for an N-body + // phase space distribution + + // 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, seed); + + double y; + double r1, r2, r3, r4, r5, r6; + switch (n_bodies_) { + case 3: + y = maxwell_spectrum(1.0, seed); + break; + case 4: + r1 = prn(seed); + r2 = prn(seed); + r3 = prn(seed); + y = -std::log(r1 * r2 * r3); + break; + case 5: + 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: + throw std::runtime_error {"N-body phase space with >5 bodies."}; + } + + // Now determine v and E_out + double v = x / (x + y); + E_out = E_max * v; + /* function to transform pdf CM to lab frame - Future implementation + get_pdf_to_point_elastic(det_pos, p, mu_cm, Js, ghost_particles, E_out / + 1e6); for (std::size_t i = 0; i < mu_cm.size(); ++i) { + // Assuming Js.size() is the same as mu_cm.size() + double mu_c = mu_cm[i]; + double derivative = Js[i]; + double pdf_cm = 0.5; + pdfs_lab.push_back(pdf_cm / std::abs(derivative)); + } + */ + + const auto& nuc {data::nuclides[p.event_nuclide()]}; + const auto& rx {nuc->reactions_[p.event_index_mt()]}; + if (!rx->scatter_in_cm_) { + fatal_error("didn't implement lab"); + } +} + } // namespace openmc diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 8b9e8737c66..6c790d5a611 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -25,6 +25,299 @@ void get_energy_index( } } +// Structure to hold information about nearest neighbors and their counts +struct Neighbors { + double a_0, a_1, b_0, b_1; + int a_0_index, b_0_index; + int leftCount, rightCount; +}; + +Neighbors findNearestNeighbors( + const std::vector& sortedVector, double mu_0) +{ + Neighbors result; + result.leftCount = 0; + result.rightCount = 0; + result.a_0_index = -1; // Initialize with invalid index + result.b_0_index = -1; // Initialize with invalid index + + // Find the iterator pointing to the first element that is not less than mu_0 + auto it = std::lower_bound(sortedVector.begin(), sortedVector.end(), mu_0); + + // Process neighbors to the left + if (it != sortedVector.begin()) { + --it; + result.a_0 = *it; + result.a_0_index = std::distance(sortedVector.begin(), it); + ++result.leftCount; + + // Process the second left neighbor, if available + if (it != sortedVector.begin()) { + --it; + result.a_1 = *it; + ++result.leftCount; + } else { + result.a_1 = + result + .a_0; // If no second left neighbor, set it to the same as the first + } + } else { + result.a_0 = result.a_1 = mu_0; // If no left neighbors, set both to mu_0 + } + + // Find the iterator pointing to the first element that is greater than mu_0 + it = std::upper_bound(sortedVector.begin(), sortedVector.end(), mu_0); + + // Process neighbors to the right + if (it != sortedVector.end()) { + result.b_0 = *it; + result.b_0_index = std::distance(sortedVector.begin(), it); + ++result.rightCount; + + // Process the second right neighbor, if available + if (++it != sortedVector.end()) { + result.b_1 = *it; + ++result.rightCount; + } else { + result.b_1 = + result + .b_0; // If no second right neighbor, set it to the same as the first + } + } else { + result.b_0 = result.b_1 = mu_0; // If no right neighbors, set both to mu_0 + } + + // Return the result + return result; +} + +int checkNeighborCase(Neighbors neighbors) +{ + + if (neighbors.leftCount == 2 && neighbors.rightCount == 2) + return 1; + if (neighbors.leftCount == 0 && neighbors.rightCount == 2) + return 2; + if (neighbors.leftCount == 1 && neighbors.rightCount == 2) + return 3; + if (neighbors.leftCount == 2 && neighbors.rightCount == 1) + return 4; + if (neighbors.leftCount == 2 && neighbors.rightCount == 0) + return 5; + if (neighbors.leftCount == 1 && neighbors.rightCount == 1) + return 6; + if (neighbors.leftCount == 0 && neighbors.rightCount == 1) + return 7; + if (neighbors.leftCount == 1 && neighbors.rightCount == 0) + return 8; + + return -1; +} + +double get_pdf_discrete(const std::vector& sortedVector, double mu_0, + double nu, const std::vector& cdfVector = std::vector {-1}) +{ + // Use findNearestNeighbors to get information about nearest neighbors + // Make sure mu is in range [-1,1] and return + if (std::abs(mu_0) > 1.0) + mu_0 = std::copysign(1.0, mu_0); + Neighbors neighbors = findNearestNeighbors(sortedVector, mu_0); + int mycase = checkNeighborCase(neighbors); + double pdf; + double a_0 = neighbors.a_0; + double a_1 = neighbors.a_1; + double b_0 = neighbors.b_0; + double b_1 = neighbors.b_1; + int a_0_index = neighbors.a_0_index; + int b_0_index = neighbors.b_0_index; + int mu_index; + double prob; + switch (mycase) { + case 1: { + // Calculate Delta_a and Delta_b + double delta_a = 0.5 * std::min(b_0 - a_0, a_0 - a_1); + double delta_b = 0.5 * std::min(b_1 - b_0, b_0 - a_0); + + // Check conditions and calculate pdf + if (mu_0 >= a_0 && mu_0 < a_0 + delta_a) { + pdf = 1.0 / (2.0 * delta_a); + mu_index = a_0_index; + } else if (mu_0 > b_0 - delta_b && mu_0 <= b_0) { + pdf = 1.0 / (2.0 * delta_b); + mu_index = b_0_index; + } else if (mu_0 > a_0 + delta_a && mu_0 <= b_0 - delta_b) { + pdf = 0.0; + mu_index = 0; + } else { + pdf = -1; // + mu_index = -1; + } + break; + } + case 2: { + // Calculate Delta_b for Case 2 + double delta_b_case2 = 0.5 * std::min(b_1 - b_0, b_0 - (-1)); + // Check conditions and calculate pdf + if (mu_0 > b_0 - delta_b_case2 && mu_0 <= b_0) { + pdf = 1.0 / (2.0 * delta_b_case2); + mu_index = b_0_index; + } else if (mu_0 >= -1 && mu_0 <= b_0 - delta_b_case2) { + pdf = 0.0; + mu_index = 0; + } else { + pdf = -1; // + mu_index = -1; + } + break; + } + case 3: { + + // Calculate Delta_a and Delta_b for Case 3 + double delta_a_case3 = 0.5 * std::min(b_0 - a_0, a_0 - (-1)); + double delta_b_case3 = 0.5 * std::min(b_1 - b_0, b_0 - a_0); + + // Check conditions and calculate pdf + if (mu_0 >= a_0 && mu_0 < a_0 + delta_a_case3) { + pdf = 1.0 / (2.0 * delta_a_case3); + mu_index = a_0_index; + } else if (mu_0 > b_0 - delta_b_case3 && mu_0 <= b_0) { + pdf = 1.0 / (2.0 * delta_b_case3); + mu_index = b_0_index; + } else if (mu_0 > a_0 + delta_a_case3 && mu_0 <= b_0 - delta_b_case3) { + pdf = 0.0; + mu_index = 0; + } else { + pdf = -1; // + mu_index = -1; + } + break; + } + case 4: { + // Calculate Delta_a and Delta_b for Case 4 + double delta_a_case4 = 0.5 * std::min(b_0 - a_0, a_0 - a_1); + double delta_b_case4 = 0.5 * std::min(1 - b_0, b_0 - a_0); + + // Check conditions and calculate pdf + if (mu_0 >= a_0 && mu_0 < a_0 + delta_a_case4) { + pdf = 1.0 / (2.0 * delta_a_case4); + mu_index = a_0_index; + } else if (mu_0 > b_0 - delta_b_case4 && mu_0 <= b_0) { + pdf = 1.0 / (2.0 * delta_b_case4); + mu_index = b_0_index; + } else if (mu_0 > a_0 + delta_a_case4 && mu_0 <= b_0 - delta_b_case4) { + pdf = 0.0; + mu_index = 0; + } else { + pdf = -1; // + mu_index = -1; + } + + break; + } + case 5: { + // Calculate Delta_a for Case 5 + double delta_a_case5 = 0.5 * std::min(1 - a_0, a_0 - a_1); + + // Check conditions and calculate pdf + if (mu_0 >= a_0 && mu_0 < a_0 + delta_a_case5) { + pdf = 1.0 / (2.0 * delta_a_case5); + mu_index = a_0_index; + } else if (mu_0 > a_0 + delta_a_case5 && mu_0 <= 1) { + pdf = 0.0; + mu_index = 0; + } else { + pdf = -1; // + mu_index = -1; + } + + break; + } + case 6: { // mu has exactly one left neighbor and exactly one right neighbor + + // Calculate Delta_a and Delta_b for Case 6 + double delta_a_case6 = 0.5 * std::min(b_0 - a_0, a_0 - (-1)); + double delta_b_case6 = 0.5 * std::min(1 - b_0, b_0 - a_0); + + // Check conditions and calculate pdf + if (mu_0 >= a_0 && mu_0 < a_0 + delta_a_case6) { + pdf = 1.0 / (2.0 * delta_a_case6); + mu_index = a_0_index; + } else if (mu_0 > b_0 - delta_b_case6 && mu_0 <= b_0) { + pdf = 1.0 / (2.0 * delta_b_case6); + mu_index = b_0_index; + } else if (mu_0 > a_0 + delta_a_case6 && mu_0 <= b_0 - delta_b_case6) { + pdf = 0.0; + mu_index = 0; + } else { + pdf = -1; // + mu_index = -1; + } + + break; + } + case 7: { // mu has no left neighbor and exactly one right neighbor + // Calculate Delta_a and Delta_b for Case 7 + double delta_case7 = 0.5 * std::min(1 - b_0, b_0 - (-1)); + + // Check conditions and calculate pdf + if (mu_0 >= b_0 - delta_case7 && mu_0 <= b_0) { + pdf = 1.0 / (2.0 * delta_case7); + mu_index = b_0_index; + } else if (mu_0 >= (-1) && mu_0 <= b_0 - delta_case7) { + pdf = 0.0; + mu_index = 0; + } else { + pdf = -1; // + mu_index = b_0_index; + } + + break; + } + + case 8: { + // Calculate Delta_a and Delta_b for Case 8 + double delta_case8 = 0.5 * std::min(1 - a_0, a_0 - (-1)); + + // Check conditions and calculate pdf + if (mu_0 >= a_0 && mu_0 <= a_0 + delta_case8) { + pdf = 1.0 / (2.0 * delta_case8); + mu_index = a_0_index; + } else if (mu_0 <= 1 && mu_0 >= a_0 + delta_case8) { + pdf = 0.0; + mu_index = 0; + } else { + + pdf = -1; // + mu_index = -1; + } + + break; + } + default: { + + pdf = -1; + } + } + if (cdfVector[0] == -1) + // equiprobable distribution + prob = 1 / nu; + else { + // calculate probability for each mu + std::vector pVector; + if (!cdfVector.empty()) { + double norm = cdfVector.back(); + + for (size_t i = cdfVector.size() - 1; i > 0; --i) { + pVector.push_back((cdfVector[i] - cdfVector[i - 1]) / norm); + } + pVector.push_back(cdfVector[0] / norm); + } + prob = pVector[mu_index]; + } + + return pdf * prob; +} + //============================================================================== // CoherentElasticAE implementation //============================================================================== @@ -55,6 +348,36 @@ void CoherentElasticAE::sample( mu = 1.0 - 2.0 * energies[k] / E_in; } +double CoherentElasticAE::get_pdf( + double E_in, double& E_out, double& mu, uint64_t* seed) const +{ + // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7-1) + + double pdf; + E_out = E_in; + const auto& energies {xs_.bragg_edges()}; + const auto& factors = xs_.factors(); + + if (E_in < energies.front() || E_in > energies.back()) { + return 0; + } + + const int i = lower_bound_index(energies.begin(), energies.end(), E_in); + std::vector energies_cut(energies.begin(), energies.begin() + i + 1); + std::vector factors_cut(factors.begin(), factors.begin() + i + 1); + + std::vector mu_vector_rev; + std::transform(energies_cut.begin(), energies_cut.end(), + std::back_inserter(mu_vector_rev), + [E_in](double Ei) { return 1 - 2 * Ei / E_in; }); + std::vector mu_vector(mu_vector_rev.rbegin(), mu_vector_rev.rend()); + // find mu index in mu_vector + const int k_i = lower_bound_index(mu_vector.begin(), mu_vector.end(), mu); + const int k_f = k_i + 1; + + return get_pdf_discrete(mu_vector, mu, 1, factors_cut); +} + //============================================================================== // IncoherentElasticAE implementation //============================================================================== @@ -74,6 +397,19 @@ void IncoherentElasticAE::sample( // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7.4) E_out = E_in; } +double IncoherentElasticAE::get_pdf( + 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_; + E_out = E_in; + + double A = c / (1 - std::exp(-2.0 * c)); // normalization factor + double pdf = A * std::exp(-c * (1 - mu)); + return pdf; + + // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7.4) +} //============================================================================== // IncoherentElasticAEDiscrete implementation @@ -129,6 +465,27 @@ void IncoherentElasticAEDiscrete::sample( E_out = E_in; } +double IncoherentElasticAEDiscrete::get_pdf( + double E_in, double& E_out, double& mu, uint64_t* seed) const +{ + // Get index and interpolation factor for elastic grid + int i; + double f; + get_energy_index(energy_, E_in, i, f); + // Energy doesn't change in elastic scattering + E_out = E_in; + int n_mu = mu_out_.shape()[1]; + + std::vector mu_vector; + + for (int k = 0; k < n_mu; ++k) { + double mu_k = mu_out_(i, k) + f * (mu_out_(i + 1, k) - mu_out_(i, k)); + mu_vector.push_back(mu_k); + } + + return get_pdf_discrete(mu_vector, mu, n_mu); +} + //============================================================================== // IncoherentInelasticAEDiscrete implementation //============================================================================== @@ -201,6 +558,59 @@ void IncoherentInelasticAEDiscrete::sample( // Cosine of angle between incoming and outgoing neutron mu = (1 - f) * mu_ijk + f * mu_i1jk; } +double IncoherentInelasticAEDiscrete::get_pdf( + double E_in, double& E_out, double& mu, uint64_t* seed, int l) const +{ + // Get index and interpolation factor for inelastic grid + int i; + double f; + get_energy_index(energy_, E_in, i, f); + int j; + int n = energy_out_.shape()[1]; + if (!skewed_) { + // All bins equally likely + j = prn(seed) * n; + } else { + // Distribution skewed away from edge points + double r = prn(seed) * (n - 3); + if (r > 1.0) { + // equally likely N-4 middle bins + j = r + 1; + } else if (r > 0.6) { + // second to last bin has relative probability of 0.4 + j = n - 2; + } else if (r > 0.5) { + // last bin has relative probability of 0.1 + j = n - 1; + } else if (r > 0.1) { + // second bin has relative probability of 0.4 + j = 1; + } else { + // first bin has relative probability of 0.1 + j = 0; + } + } + if (l != -1) { + j = l; + } // take j as param + // Determine outgoing energy corresponding to E_in[i] and E_in[i+1] + double E_ij = energy_out_(i, j); + double E_i1j = energy_out_(i + 1, j); + + // Outgoing energy + E_out = (1 - f) * E_ij + f * E_i1j; + int m = mu_out_.shape()[2]; + std::vector mu_vector; + + for (int k = 0; k < m; ++k) { + double mu_ijk = mu_out_(i, j, k); + double mu_i1jk = mu_out_(i + 1, j, k); + double mu_k = (1 - f) * mu_ijk + f * mu_i1jk; + mu_vector.push_back(mu_k); + } + + return get_pdf_discrete(mu_vector, mu, m); +} //============================================================================== // IncoherentInelasticAE implementation @@ -331,6 +741,76 @@ void IncoherentInelasticAE::sample( mu += std::min(mu - mu_left, mu_right - mu) * (prn(seed) - 0.5); } +double IncoherentInelasticAE::get_pdf( + double E_in, double& E_out, double& mu, uint64_t* seed) const +{ + // Get index and interpolation factor for inelastic grid + int i; + double f; + get_energy_index(energy_, E_in, i, f); + + // Pick closer energy based on interpolation factor + int l = f > 0.5 ? i + 1 : i; + + // Determine outgoing energy bin + // (First reset n_energy_out to the right value) + auto n = distribution_[l].n_e_out; + double r1 = prn(seed); + double c_j = distribution_[l].e_out_cdf[0]; + double c_j1; + std::size_t j; + for (j = 0; j < n - 1; ++j) { + c_j1 = distribution_[l].e_out_cdf[j + 1]; + if (r1 < c_j1) + break; + c_j = c_j1; + } + + // check to make sure j is <= n_energy_out - 2 + j = std::min(j, n - 2); + + // Get the data to interpolate between + double E_l_j = distribution_[l].e_out[j]; + double p_l_j = distribution_[l].e_out_pdf[j]; + + // Next part assumes linear-linear interpolation in standard + double E_l_j1 = distribution_[l].e_out[j + 1]; + double p_l_j1 = distribution_[l].e_out_pdf[j + 1]; + + // Find secondary energy (variable E) + double frac = (p_l_j1 - p_l_j) / (E_l_j1 - E_l_j); + if (frac == 0.0) { + E_out = E_l_j + (r1 - c_j) / p_l_j; + } else { + E_out = E_l_j + + (std::sqrt(std::max(0.0, p_l_j * p_l_j + 2.0 * frac * (r1 - c_j))) - + p_l_j) / + frac; + } + + // Adjustment of outgoing energy + double E_l = energy_[l]; + if (E_out < 0.5 * E_l) { + E_out *= 2.0 * E_in / E_l - 1.0; + } else { + E_out += E_in - E_l; + } + + // Sample outgoing cosine bin + int n_mu = distribution_[l].mu.shape()[1]; + const auto& mu_l = distribution_[l].mu; + f = (r1 - c_j) / (c_j1 - c_j); + + std::vector mu_vector; + + for (int k = 0; k < n_mu; ++k) { + double mu_k = mu_l(j, k) + f * (mu_l(j + 1, k) - mu_l(j, k)); + mu_vector.push_back(mu_k); + } + + return get_pdf_discrete(mu_vector, mu, n_mu); +} + //============================================================================== // MixedElasticAE implementation //============================================================================== diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index 5cbb76fb9b9..540fa6d6293 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -6,7 +6,10 @@ #include "openmc/error.h" #include "openmc/hdf5_interface.h" +#include "openmc/nuclide.h" +#include "openmc/particle.h" #include "openmc/random_dist.h" +#include "openmc/tallies/tally_scoring.h" namespace openmc { @@ -65,4 +68,29 @@ void UncorrelatedAngleEnergy::sample( E_out = energy_->sample(E_in, seed); } +void UncorrelatedAngleEnergy::get_pdf(double det_pos[4], double E_in, + double& E_out, uint64_t* seed, Particle& p, std::vector& mu_cm, + std::vector& Js, std::vector& ghost_particles, + std::vector& pdfs_lab) const +{ + bool COM = false; + const auto& nuc {data::nuclides[p.event_nuclide()]}; + if (p.event_index_mt() != -999) { + const auto& rx {nuc->reactions_[p.event_index_mt()]}; + COM = rx->scatter_in_cm_; + } + + // Sample outgoing energy + E_out = energy_->sample(E_in, seed); + + if (COM) { + + /* transform pdf cm to lab frame + */ + } + + if (!COM) { + } +} + } // namespace openmc diff --git a/src/thermal.cpp b/src/thermal.cpp index cbe0983ed65..a4206f31e41 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -1,15 +1,15 @@ #include "openmc/thermal.h" - -#include // for sort, move, min, max, find -#include // for round, sqrt, abs - #include "xtensor/xarray.hpp" #include "xtensor/xbuilder.hpp" #include "xtensor/xmath.hpp" #include "xtensor/xsort.hpp" #include "xtensor/xtensor.hpp" #include "xtensor/xview.hpp" +#include // for sort, move, min, max, find +#include // for round, sqrt, abs #include +#include +#include #include "openmc/constants.h" #include "openmc/endf.h" @@ -313,6 +313,52 @@ void ThermalData::sample(const NuclideMicroXS& micro_xs, double E, *mu = std::copysign(1.0, *mu); } +double ThermalData::get_pdf(const NuclideMicroXS& micro_xs, double E, + double& E_out, double mu, uint64_t* seed) +{ + double pdf = -1; + AngleEnergy* angleEnergyPtr; + // Determine whether inelastic or elastic scattering occured + if (prn(seed) < micro_xs.thermal_elastic / micro_xs.thermal) { + // elastic_.distribution->get_pdf(E, *E_out, *mu, seed); + angleEnergyPtr = elastic_.distribution.get(); + } else { + // inelastic_.distribution->get_pdf(E, *E_out, *mu, seed); + angleEnergyPtr = inelastic_.distribution.get(); + } + + if (CoherentElasticAE* coherentElasticAE = + dynamic_cast(angleEnergyPtr)) { + pdf = (*coherentElasticAE).get_pdf(E, E_out, mu, seed); + + bool creat_pdf_file = false; + + // Handle CoherentElasticAE + } else if (IncoherentElasticAE* incoherentElasticAE = + dynamic_cast(angleEnergyPtr)) { + // Handle IncoherentElasticAE + } else if (IncoherentElasticAEDiscrete* incoherentElasticAEDiscrete = + dynamic_cast(angleEnergyPtr)) { + // Handle IncoherentElasticAEDiscrete + } else if (IncoherentInelasticAEDiscrete* incoherentInelasticAEDiscrete = + dynamic_cast(angleEnergyPtr)) { + pdf = (*incoherentInelasticAEDiscrete).get_pdf(E, E_out, mu, seed, -1); + bool creat_pdf_file = false; + // Handle IncoherentInelasticAEDiscrete + } else if (IncoherentInelasticAE* incoherentInelasticAE = + dynamic_cast(angleEnergyPtr)) { + + // Handle IncoherentInelasticAE + } else if (MixedElasticAE* mixedElasticAE = + dynamic_cast(angleEnergyPtr)) { + // Handle MixedElasticAE + } else { + std::cout << "Unknown derived type." << std::endl; + } + + return pdf; +} + void free_memory_thermal() { data::thermal_scatt.clear(); From 78d841ea57766c81ff1e1a1f9864415ace303108 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Thu, 28 Aug 2025 08:32:21 +0300 Subject: [PATCH 02/88] Update include/openmc/distribution.h Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- include/openmc/distribution.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/openmc/distribution.h b/include/openmc/distribution.h index 4424f0e7b9f..160b0b86916 100644 --- a/include/openmc/distribution.h +++ b/include/openmc/distribution.h @@ -238,7 +238,6 @@ class Tabular : public Distribution { double sample(uint64_t* seed) const override; double get_pdf(double x) const; - // double get_pdf_value(double x,uint64_t* seed) const; // properties vector& x() { return x_; } const vector& x() const { return x_; } From 33a27a9ca6749119f5dea83f25ffac45d1e0c3d4 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Thu, 28 Aug 2025 08:32:31 +0300 Subject: [PATCH 03/88] Update include/openmc/particle_data.h Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- include/openmc/particle_data.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/openmc/particle_data.h b/include/openmc/particle_data.h index d93eaf6cf43..1b63444e436 100644 --- a/include/openmc/particle_data.h +++ b/include/openmc/particle_data.h @@ -626,8 +626,6 @@ class ParticleData : public GeometryState { const int& event_mt() const { return event_mt_; } int& event_index_mt() { return event_index_mt_; } int& delayed_group() { return delayed_group_; } // delayed group - // Position& v_t() { return v_t_; } // future use for thermal velocity - // const Position& v_t() const { return v_t_; } const int& parent_nuclide() const { return parent_nuclide_; } int& parent_nuclide() { return parent_nuclide_; } // Parent nuclide From d96887521a823dbb81d09da0f7abef8a017204f5 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Thu, 28 Aug 2025 08:32:49 +0300 Subject: [PATCH 04/88] Update include/openmc/particle_data.h Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- include/openmc/particle_data.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/openmc/particle_data.h b/include/openmc/particle_data.h index 1b63444e436..cb06424cab9 100644 --- a/include/openmc/particle_data.h +++ b/include/openmc/particle_data.h @@ -490,7 +490,6 @@ class ParticleData : public GeometryState { int delayed_group_ {0}; int event_index_mt_; int parent_nuclide_ {-1}; - // Direction v_t_; future use for thermal velocity int n_bank_ {0}; double bank_second_E_ {0.0}; From 3b896c03191d740bb203ab28172dd31805a8c958 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Thu, 28 Aug 2025 08:32:59 +0300 Subject: [PATCH 05/88] Update src/physics.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/physics.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/physics.cpp b/src/physics.cpp index 1b8201cead3..b1d622deca0 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -760,8 +760,6 @@ void elastic_scatter(int i_nuclide, const Reaction& rx, double kT, Particle& p) v_t = sample_target_velocity(*nuc, p.E(), p.u(), v_n, p.neutron_xs(i_nuclide).elastic, kT, p.current_seed()); } - // change units in the future - // p.v_t() = C_LIGHT * std::sqrt(2 / p.getMass()) * v_t; // Velocity of center-of-mass Direction v_cm = (v_n + awr * v_t) / (awr + 1.0); From c034bffca6f2a1be96965dbb226c11c7886fbaa8 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Thu, 28 Aug 2025 08:33:42 +0300 Subject: [PATCH 06/88] Update src/physics.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/physics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/physics.cpp b/src/physics.cpp index b1d622deca0..2fe5728bc59 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -760,7 +760,7 @@ void elastic_scatter(int i_nuclide, const Reaction& rx, double kT, Particle& p) v_t = sample_target_velocity(*nuc, p.E(), p.u(), v_n, p.neutron_xs(i_nuclide).elastic, kT, p.current_seed()); } - // Velocity of center-of-mass + // Velocity of center-of-mass Direction v_cm = (v_n + awr * v_t) / (awr + 1.0); // Transform to CM frame From a5d2f4c8946207617b9a3f20d4c8e8d89cfbaee6 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Thu, 28 Aug 2025 08:34:02 +0300 Subject: [PATCH 07/88] Update src/physics.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/physics.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/physics.cpp b/src/physics.cpp index 2fe5728bc59..4db412bb7e3 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -1105,7 +1105,6 @@ void sample_fission_neutron( void inelastic_scatter(const Nuclide& nuc, const Reaction& rx, Particle& p) { - // p.v_t() = {0, 0, 0}; // intialize target velocity to zero // copy energy of neutron double E_in = p.E(); From ca5b44e6fcdc3fe53db79fe0f655de0c8575a5a4 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 31 Aug 2025 14:50:46 +0300 Subject: [PATCH 08/88] Cleaning some code --- include/openmc/particle_data.h | 2 -- src/physics.cpp | 4 ---- src/secondary_correlated.cpp | 19 ++++++++----------- src/secondary_kalbach.cpp | 4 ---- src/secondary_nbody.cpp | 6 +----- src/secondary_uncorrelated.cpp | 14 -------------- 6 files changed, 9 insertions(+), 40 deletions(-) diff --git a/include/openmc/particle_data.h b/include/openmc/particle_data.h index cb06424cab9..ec393845f81 100644 --- a/include/openmc/particle_data.h +++ b/include/openmc/particle_data.h @@ -488,7 +488,6 @@ class ParticleData : public GeometryState { int event_nuclide_; int event_mt_; int delayed_group_ {0}; - int event_index_mt_; int parent_nuclide_ {-1}; int n_bank_ {0}; @@ -623,7 +622,6 @@ class ParticleData : public GeometryState { const int& event_nuclide() const { return event_nuclide_; } int& event_mt() { return event_mt_; } // MT number of collision const int& event_mt() const { return event_mt_; } - int& event_index_mt() { return event_index_mt_; } int& delayed_group() { return delayed_group_; } // delayed group const int& parent_nuclide() const { return parent_nuclide_; } int& parent_nuclide() { return parent_nuclide_; } // Parent nuclide diff --git a/src/physics.cpp b/src/physics.cpp index 4db412bb7e3..bbc8ed85edc 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -217,7 +217,6 @@ void create_fission_sites(Particle& p, int i_nuclide, const Reaction& rx) // Sample delayed group and angle/energy for fission reaction sample_fission_neutron(i_nuclide, rx, &site, p); - p.event_index_mt() = -999; // Store fission site in bank if (use_fission_bank) { int64_t idx = simulation::fission_bank.thread_safe_append(site); @@ -666,7 +665,6 @@ void scatter(Particle& p, int i_nuclide) { // copy incoming direction Direction u_old {p.u()}; - p.event_index_mt() = 0; // Get pointer to nuclide and grid index/interpolation factor const auto& nuc {data::nuclides[i_nuclide]}; const auto& micro {p.neutron_xs(i_nuclide)}; @@ -703,7 +701,6 @@ void scatter(Particle& p, int i_nuclide) // S(A,B) SCATTERING sab_scatter(i_nuclide, micro.index_sab, p); - p.event_index_mt() = -1234; // to distinguish from elastic p.event_mt() = ELASTIC; sampled = true; } @@ -725,7 +722,6 @@ void scatter(Particle& p, int i_nuclide) const auto& rx {nuc->reactions_[i]}; inelastic_scatter(*nuc, *rx, p); p.event_mt() = rx->mt_; - p.event_index_mt() = i; } // Set event component diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index aaad6cd9515..3aa9004fa23 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -247,6 +247,10 @@ void CorrelatedAngleEnergy::sample( frac; } } + else { + throw std::runtime_error{ + "Unsupported interpolation type in CorrelatedAngleEnergy::get_pdf"}; +} // Now interpolate between incident energy bins i and i + 1 if (k >= n_discrete) { @@ -359,6 +363,10 @@ void CorrelatedAngleEnergy::get_pdf(double det_pos[4], double E_in, frac; } } + else { + throw std::runtime_error{ + "Unsupported interpolation type in CorrelatedAngleEnergy::sample"}; +} // Now interpolate between incident energy bins i and i + 1 if (k >= n_discrete) { @@ -369,17 +377,6 @@ void CorrelatedAngleEnergy::get_pdf(double det_pos[4], double E_in, } } - const auto& nuc {data::nuclides[p.event_nuclide()]}; - const auto& rx {nuc->reactions_[p.event_index_mt()]}; - if (rx->scatter_in_cm_) - - { - /* transform pdf cm to lab frame - */ - } - - if (!rx->scatter_in_cm_) { - } } } // namespace openmc diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index 99f215601cb..5bee263acd0 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -374,10 +374,6 @@ void KalbachMann::get_pdf(double det_pos[4], double E_in, double& E_out, // double pdf_mu = km_a / (2 * std::sinh(km_a)) * (std::cosh(km_a * mymu) + // km_r * std::sinh(km_a * mymu)); // center of mass return pdf_mu; - const auto& rx {nuc->reactions_[p.event_index_mt()]}; - if (!rx->scatter_in_cm_) { - fatal_error("didn't implement lab"); - } } } // namespace openmc diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index b08a662d7bb..c937920d52d 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -124,11 +124,7 @@ void NBodyPhaseSpace::get_pdf(double det_pos[4], double E_in, double& E_out, } */ - const auto& nuc {data::nuclides[p.event_nuclide()]}; - const auto& rx {nuc->reactions_[p.event_index_mt()]}; - if (!rx->scatter_in_cm_) { - fatal_error("didn't implement lab"); - } + } } // namespace openmc diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index 540fa6d6293..263649f08fa 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -74,23 +74,9 @@ void UncorrelatedAngleEnergy::get_pdf(double det_pos[4], double E_in, std::vector& pdfs_lab) const { bool COM = false; - const auto& nuc {data::nuclides[p.event_nuclide()]}; - if (p.event_index_mt() != -999) { - const auto& rx {nuc->reactions_[p.event_index_mt()]}; - COM = rx->scatter_in_cm_; - } // Sample outgoing energy E_out = energy_->sample(E_in, seed); - - if (COM) { - - /* transform pdf cm to lab frame - */ - } - - if (!COM) { - } } } // namespace openmc From eed8b1cd318005f6aa3bcdf1c6f8718be34cb8c3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 31 Aug 2025 15:15:26 +0300 Subject: [PATCH 09/88] Resolving conversations --- include/openmc/angle_energy.h | 6 +++++- src/distribution.cpp | 12 +++++++++++- src/secondary_correlated.cpp | 15 ++++++--------- src/secondary_kalbach.cpp | 17 ----------------- src/secondary_nbody.cpp | 2 -- src/thermal.cpp | 36 ++--------------------------------- 6 files changed, 24 insertions(+), 64 deletions(-) diff --git a/include/openmc/angle_energy.h b/include/openmc/angle_energy.h index ac931b1b533..0a8b4c38849 100644 --- a/include/openmc/angle_energy.h +++ b/include/openmc/angle_energy.h @@ -1,7 +1,7 @@ #ifndef OPENMC_ANGLE_ENERGY_H #define OPENMC_ANGLE_ENERGY_H - #include +#include namespace openmc { @@ -16,6 +16,10 @@ class AngleEnergy { public: virtual void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const = 0; + virtual double get_pdf(double, double&, double, uint64_t*) const + { + throw std::runtime_error("get_pdf not available for this AngleEnergy type"); + } virtual ~AngleEnergy() = default; }; diff --git a/src/distribution.cpp b/src/distribution.cpp index 1f0eb29c88b..3066dfe854f 100644 --- a/src/distribution.cpp +++ b/src/distribution.cpp @@ -197,7 +197,17 @@ double PowerLaw::sample(uint64_t* seed) const } double PowerLaw::get_pdf(double x) const { - return x / span_ / ninv_; + // Recover the lower/upper bounds from offset_ and span_ + double a = std::pow(offset_, ninv_); // since offset_ = a^(n+1) + double b = std::pow(offset_ + span_, ninv_); // since offset_+span_ = b^(n+1) + + if (x < a || x > b) { + return 0.0; // outside support + } + + double n = 1.0 / ninv_ - 1.0; // since ninv_ = 1/(n+1) + + return (n + 1.0) * std::pow(x, n) / (std::pow(b, n + 1) - std::pow(a, n + 1)); } //============================================================================== diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index 3aa9004fa23..1862efd9b80 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -246,11 +246,10 @@ void CorrelatedAngleEnergy::sample( p_l_k) / frac; } + } else { + throw std::runtime_error { + "Unsupported interpolation type in CorrelatedAngleEnergy::get_pdf"}; } - else { - throw std::runtime_error{ - "Unsupported interpolation type in CorrelatedAngleEnergy::get_pdf"}; -} // Now interpolate between incident energy bins i and i + 1 if (k >= n_discrete) { @@ -362,11 +361,10 @@ void CorrelatedAngleEnergy::get_pdf(double det_pos[4], double E_in, p_l_k) / frac; } + } else { + throw std::runtime_error { + "Unsupported interpolation type in CorrelatedAngleEnergy::sample"}; } - else { - throw std::runtime_error{ - "Unsupported interpolation type in CorrelatedAngleEnergy::sample"}; -} // Now interpolate between incident energy bins i and i + 1 if (k >= n_discrete) { @@ -376,7 +374,6 @@ void CorrelatedAngleEnergy::get_pdf(double det_pos[4], double E_in, E_out = E_1 + (E_out - E_i1_1) * (E_K - E_1) / (E_i1_K - E_i1_1); } } - } } // namespace openmc diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index 5bee263acd0..f742f957593 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -357,23 +357,6 @@ void KalbachMann::get_pdf(double det_pos[4], double E_in, double& E_out, E_out = E_1 + (E_out - E_i1_1) * (E_K - E_1) / (E_i1_K - E_i1_1); } } - /* function to transform pdf CM to lab frame - Future implementation - get_pdf_to_point_elastic(det_pos, p, mu_cm, Js, ghost_particles, E_out / - 1e6); for (std::size_t i = 0; i < mu_cm.size(); ++i) { - // Assuming Js.size() is the same as mu_cm.size() - double mu_c = mu_cm[i]; - double derivative = Js[i]; - double pdf_cm = km_a / (2 * std::sinh(km_a)) * - (std::cosh(km_a * mu_c) + - km_r * std::sinh(km_a * mu_c)); // center of mass - pdfs_lab.push_back(pdf_cm / std::abs(derivative)); - } - */ - - // https://docs.openmc.org/en/v0.8.0/methods/physics.html#equation-KM-pdf-angle - // double pdf_mu = km_a / (2 * std::sinh(km_a)) * (std::cosh(km_a * mymu) + - // km_r * std::sinh(km_a * mymu)); // center of mass return pdf_mu; - } } // namespace openmc diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index c937920d52d..4bc52900622 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -123,8 +123,6 @@ void NBodyPhaseSpace::get_pdf(double det_pos[4], double E_in, double& E_out, pdfs_lab.push_back(pdf_cm / std::abs(derivative)); } */ - - } } // namespace openmc diff --git a/src/thermal.cpp b/src/thermal.cpp index a4206f31e41..949a6c2de69 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -316,47 +316,15 @@ void ThermalData::sample(const NuclideMicroXS& micro_xs, double E, double ThermalData::get_pdf(const NuclideMicroXS& micro_xs, double E, double& E_out, double mu, uint64_t* seed) { - double pdf = -1; AngleEnergy* angleEnergyPtr; - // Determine whether inelastic or elastic scattering occured + if (prn(seed) < micro_xs.thermal_elastic / micro_xs.thermal) { - // elastic_.distribution->get_pdf(E, *E_out, *mu, seed); angleEnergyPtr = elastic_.distribution.get(); } else { - // inelastic_.distribution->get_pdf(E, *E_out, *mu, seed); angleEnergyPtr = inelastic_.distribution.get(); } - if (CoherentElasticAE* coherentElasticAE = - dynamic_cast(angleEnergyPtr)) { - pdf = (*coherentElasticAE).get_pdf(E, E_out, mu, seed); - - bool creat_pdf_file = false; - - // Handle CoherentElasticAE - } else if (IncoherentElasticAE* incoherentElasticAE = - dynamic_cast(angleEnergyPtr)) { - // Handle IncoherentElasticAE - } else if (IncoherentElasticAEDiscrete* incoherentElasticAEDiscrete = - dynamic_cast(angleEnergyPtr)) { - // Handle IncoherentElasticAEDiscrete - } else if (IncoherentInelasticAEDiscrete* incoherentInelasticAEDiscrete = - dynamic_cast(angleEnergyPtr)) { - pdf = (*incoherentInelasticAEDiscrete).get_pdf(E, E_out, mu, seed, -1); - bool creat_pdf_file = false; - // Handle IncoherentInelasticAEDiscrete - } else if (IncoherentInelasticAE* incoherentInelasticAE = - dynamic_cast(angleEnergyPtr)) { - - // Handle IncoherentInelasticAE - } else if (MixedElasticAE* mixedElasticAE = - dynamic_cast(angleEnergyPtr)) { - // Handle MixedElasticAE - } else { - std::cout << "Unknown derived type." << std::endl; - } - - return pdf; + return angleEnergyPtr->get_pdf(E, E_out, mu, seed); } void free_memory_thermal() From f2ecdd4ad370ff3efb5a57ce44e70ab0212ceb72 Mon Sep 17 00:00:00 2001 From: GuySten Date: Sun, 31 Aug 2025 15:29:29 +0300 Subject: [PATCH 10/88] cosmetic fixes --- include/openmc/distribution.h | 4 +++- include/openmc/distribution_angle.h | 1 + include/openmc/reaction_product.h | 1 + src/distribution.cpp | 9 ++++++++- src/physics.cpp | 5 ++++- src/reaction_product.cpp | 1 + src/secondary_kalbach.cpp | 1 + src/secondary_thermal.cpp | 1 + src/thermal.cpp | 10 ++++++---- 9 files changed, 26 insertions(+), 7 deletions(-) diff --git a/include/openmc/distribution.h b/include/openmc/distribution.h index 160b0b86916..bab4acb16f2 100644 --- a/include/openmc/distribution.h +++ b/include/openmc/distribution.h @@ -24,6 +24,7 @@ class Distribution { virtual ~Distribution() = default; virtual double sample(uint64_t* seed) const = 0; virtual double get_pdf(double x) const = 0; + //! Return integral of distribution //! \return Integral of distribution virtual double integral() const { return 1.0; }; @@ -84,6 +85,7 @@ class Discrete : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; + //! Calculate the probability density function (PDF) at a given value //! \param x The value at which to evaluate the PDF //! \return The value of the PDF at the given point @@ -238,7 +240,7 @@ class Tabular : public Distribution { double sample(uint64_t* seed) const override; double get_pdf(double x) const; - // properties + // properties vector& x() { return x_; } const vector& x() const { return x_; } const vector& p() const { return p_; } diff --git a/include/openmc/distribution_angle.h b/include/openmc/distribution_angle.h index b0547ac6678..81194c2b10a 100644 --- a/include/openmc/distribution_angle.h +++ b/include/openmc/distribution_angle.h @@ -26,6 +26,7 @@ class AngleDistribution { //! \return Cosine of the angle in the range [-1,1] double sample(double E, uint64_t* seed) const; double get_pdf(double E, double mu, uint64_t* seed) const; + //! Determine whether angle distribution is empty //! \return Whether distribution is empty bool empty() const { return energy_.empty(); } diff --git a/include/openmc/reaction_product.h b/include/openmc/reaction_product.h index 3d2262c7100..d4b475d7280 100644 --- a/include/openmc/reaction_product.h +++ b/include/openmc/reaction_product.h @@ -14,6 +14,7 @@ #include "openmc/tallies/filter.h" #include "openmc/tallies/tally.h" #include "openmc/vector.h" // for vector + namespace openmc { //============================================================================== diff --git a/src/distribution.cpp b/src/distribution.cpp index 3066dfe854f..8bdfc7706ca 100644 --- a/src/distribution.cpp +++ b/src/distribution.cpp @@ -135,10 +135,10 @@ double Discrete::sample(uint64_t* seed) const { return x_[di_.sample(seed)]; } + // Not implemented double Discrete::get_pdf(double x) const { - return -1; } @@ -162,6 +162,7 @@ double Uniform::sample(uint64_t* seed) const { return a_ + prn(seed) * (b_ - a_); } + double Uniform::get_pdf(double x) const { if (x <= b_ && x >= a_) @@ -195,6 +196,7 @@ double PowerLaw::sample(uint64_t* seed) const { return std::pow(offset_ + prn(seed) * span_, ninv_); } + double PowerLaw::get_pdf(double x) const { // Recover the lower/upper bounds from offset_ and span_ @@ -223,6 +225,7 @@ double Maxwell::sample(uint64_t* seed) const { return maxwell_spectrum(theta_, seed); } + // Not implemented double Maxwell::get_pdf(double x) const { @@ -248,6 +251,7 @@ double Watt::sample(uint64_t* seed) const { return watt_spectrum(a_, b_, seed); } + // Not implemented double Watt::get_pdf(double x) const { @@ -273,6 +277,7 @@ double Normal::sample(uint64_t* seed) const { return normal_variate(mean_value_, std_dev_, seed); } + double Normal::get_pdf(double x) const { double exponent = -0.5 * std::pow((x - mean_value_) / std_dev_, 2); @@ -450,6 +455,7 @@ double Equiprobable::sample(uint64_t* seed) const double xr = x_[i + i]; return xl + ((n - 1) * r - i) * (xr - xl); } + double Equiprobable::get_pdf(double x) const { return -1; @@ -503,6 +509,7 @@ double Mixture::sample(uint64_t* seed) const // Sample the chosen distribution return it->second->sample(seed); } + double Mixture::get_pdf(double x) const { return -1; diff --git a/src/physics.cpp b/src/physics.cpp index bbc8ed85edc..3c06e543de1 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -1,5 +1,4 @@ #include "openmc/physics.h" -#include #include "openmc/bank.h" #include "openmc/bremsstrahlung.h" @@ -217,6 +216,7 @@ void create_fission_sites(Particle& p, int i_nuclide, const Reaction& rx) // Sample delayed group and angle/energy for fission reaction sample_fission_neutron(i_nuclide, rx, &site, p); + // Store fission site in bank if (use_fission_bank) { int64_t idx = simulation::fission_bank.thread_safe_append(site); @@ -665,6 +665,7 @@ void scatter(Particle& p, int i_nuclide) { // copy incoming direction Direction u_old {p.u()}; + // Get pointer to nuclide and grid index/interpolation factor const auto& nuc {data::nuclides[i_nuclide]}; const auto& micro {p.neutron_xs(i_nuclide)}; @@ -701,6 +702,7 @@ void scatter(Particle& p, int i_nuclide) // S(A,B) SCATTERING sab_scatter(i_nuclide, micro.index_sab, p); + p.event_mt() = ELASTIC; sampled = true; } @@ -756,6 +758,7 @@ void elastic_scatter(int i_nuclide, const Reaction& rx, double kT, Particle& p) v_t = sample_target_velocity(*nuc, p.E(), p.u(), v_n, p.neutron_xs(i_nuclide).elastic, kT, p.current_seed()); } + // Velocity of center-of-mass Direction v_cm = (v_n + awr * v_t) / (awr + 1.0); diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index e30e040f91a..345e0755043 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -130,6 +130,7 @@ void ReactionProduct::sample( distribution_[0]->sample(E_in, E_out, mu, seed); } } + void ReactionProduct::get_pdf(int i_tally, double E_in, double& E_out, uint64_t* seed, Particle& p, std::vector& mu_cm, std::vector& Js, std::vector& ghost_particles, diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index f742f957593..db40ed72ef2 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -240,6 +240,7 @@ void KalbachMann::sample( mu = std::log(r1 * std::exp(km_a) + (1.0 - r1) * std::exp(-km_a)) / km_a; } } + void KalbachMann::get_pdf(double det_pos[4], double E_in, double& E_out, uint64_t* seed, Particle& p, std::vector& mu_cm, std::vector& Js, std::vector& ghost_particles, diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 6c790d5a611..e9ad1a83ee1 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -558,6 +558,7 @@ void IncoherentInelasticAEDiscrete::sample( // Cosine of angle between incoming and outgoing neutron mu = (1 - f) * mu_ijk + f * mu_i1jk; } + double IncoherentInelasticAEDiscrete::get_pdf( double E_in, double& E_out, double& mu, uint64_t* seed, int l) const { diff --git a/src/thermal.cpp b/src/thermal.cpp index 949a6c2de69..0e2cfbdd9c5 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -1,15 +1,17 @@ #include "openmc/thermal.h" + +#include // for sort, move, min, max, find +#include // for round, sqrt, abs +#include +#include + #include "xtensor/xarray.hpp" #include "xtensor/xbuilder.hpp" #include "xtensor/xmath.hpp" #include "xtensor/xsort.hpp" #include "xtensor/xtensor.hpp" #include "xtensor/xview.hpp" -#include // for sort, move, min, max, find -#include // for round, sqrt, abs #include -#include -#include #include "openmc/constants.h" #include "openmc/endf.h" From c3b4d8206779420a75dd154a7e507adff8008330 Mon Sep 17 00:00:00 2001 From: GuySten Date: Sun, 31 Aug 2025 17:44:28 +0300 Subject: [PATCH 11/88] simplified code significantly --- src/secondary_thermal.cpp | 340 ++++++-------------------------------- 1 file changed, 48 insertions(+), 292 deletions(-) diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index e9ad1a83ee1..47047c6c2bf 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -3,6 +3,7 @@ #include "openmc/hdf5_interface.h" #include "openmc/random_lcg.h" #include "openmc/search.h" +#include "openmc/vector.h" #include "xtensor/xview.hpp" @@ -25,297 +26,52 @@ void get_energy_index( } } -// Structure to hold information about nearest neighbors and their counts -struct Neighbors { - double a_0, a_1, b_0, b_1; - int a_0_index, b_0_index; - int leftCount, rightCount; -}; - -Neighbors findNearestNeighbors( - const std::vector& sortedVector, double mu_0) +double get_pdf_discrete( + const vector& mu, const vector& w, double mu_0) { - Neighbors result; - result.leftCount = 0; - result.rightCount = 0; - result.a_0_index = -1; // Initialize with invalid index - result.b_0_index = -1; // Initialize with invalid index - - // Find the iterator pointing to the first element that is not less than mu_0 - auto it = std::lower_bound(sortedVector.begin(), sortedVector.end(), mu_0); - - // Process neighbors to the left - if (it != sortedVector.begin()) { - --it; - result.a_0 = *it; - result.a_0_index = std::distance(sortedVector.begin(), it); - ++result.leftCount; - - // Process the second left neighbor, if available - if (it != sortedVector.begin()) { - --it; - result.a_1 = *it; - ++result.leftCount; - } else { - result.a_1 = - result - .a_0; // If no second left neighbor, set it to the same as the first - } + // Make sure mu is in range [-1,1] + if (std::abs(mu_0) > 1.0) + mu_0 = std::copysign(1.0, mu_0); + double a0; + double a1; + double b0; + double b1; + int32_t ai = -1; + int32_t bi = -1; + if (mu_0 > mu[0]) { + ai = lower_bound_index(mu.begin(), mu.end(), mu_0); + a0 = mu[ai]; + a1 = (ai > 1) ? mu[ai - 1] : -1.0; } else { - result.a_0 = result.a_1 = mu_0; // If no left neighbors, set both to mu_0 + a0 = -1.0; + a1 = -1.0; } - - // Find the iterator pointing to the first element that is greater than mu_0 - it = std::upper_bound(sortedVector.begin(), sortedVector.end(), mu_0); - - // Process neighbors to the right - if (it != sortedVector.end()) { - result.b_0 = *it; - result.b_0_index = std::distance(sortedVector.begin(), it); - ++result.rightCount; - - // Process the second right neighbor, if available - if (++it != sortedVector.end()) { - result.b_1 = *it; - ++result.rightCount; - } else { - result.b_1 = - result - .b_0; // If no second right neighbor, set it to the same as the first - } + if (mu_0 < mu[mu.size() - 1]) { + bi = upper_bound_index(mu.begin(), mu.end(), mu_0); + b0 = mu[bi]; + b1 = (bi < mu.size() - 1) ? mu[bi + 1] : 1.0; } else { - result.b_0 = result.b_1 = mu_0; // If no right neighbors, set both to mu_0 + b0 = 1.0; + b1 = 1.0; } - // Return the result - return result; -} + // Calculate Delta_a and Delta_b + double delta_a = 0.5 * std::min(b_0 - a_0, a_0 - a_1); + double delta_b = 0.5 * std::min(b_1 - b_0, b_0 - a_0); -int checkNeighborCase(Neighbors neighbors) -{ - - if (neighbors.leftCount == 2 && neighbors.rightCount == 2) - return 1; - if (neighbors.leftCount == 0 && neighbors.rightCount == 2) - return 2; - if (neighbors.leftCount == 1 && neighbors.rightCount == 2) - return 3; - if (neighbors.leftCount == 2 && neighbors.rightCount == 1) - return 4; - if (neighbors.leftCount == 2 && neighbors.rightCount == 0) - return 5; - if (neighbors.leftCount == 1 && neighbors.rightCount == 1) - return 6; - if (neighbors.leftCount == 0 && neighbors.rightCount == 1) - return 7; - if (neighbors.leftCount == 1 && neighbors.rightCount == 0) - return 8; - - return -1; + if (mu_0 < a_0 + delta_a) + return w[ai] / (2.0 * delta_a); + else if (mu_0 + delta_b < b_0) + return w[bi] / (2.0 * delta_b); + else + return 0.0; } -double get_pdf_discrete(const std::vector& sortedVector, double mu_0, - double nu, const std::vector& cdfVector = std::vector {-1}) +double get_pdf_discrete(const vector& mu, double mu_0) { - // Use findNearestNeighbors to get information about nearest neighbors - // Make sure mu is in range [-1,1] and return - if (std::abs(mu_0) > 1.0) - mu_0 = std::copysign(1.0, mu_0); - Neighbors neighbors = findNearestNeighbors(sortedVector, mu_0); - int mycase = checkNeighborCase(neighbors); - double pdf; - double a_0 = neighbors.a_0; - double a_1 = neighbors.a_1; - double b_0 = neighbors.b_0; - double b_1 = neighbors.b_1; - int a_0_index = neighbors.a_0_index; - int b_0_index = neighbors.b_0_index; - int mu_index; - double prob; - switch (mycase) { - case 1: { - // Calculate Delta_a and Delta_b - double delta_a = 0.5 * std::min(b_0 - a_0, a_0 - a_1); - double delta_b = 0.5 * std::min(b_1 - b_0, b_0 - a_0); - - // Check conditions and calculate pdf - if (mu_0 >= a_0 && mu_0 < a_0 + delta_a) { - pdf = 1.0 / (2.0 * delta_a); - mu_index = a_0_index; - } else if (mu_0 > b_0 - delta_b && mu_0 <= b_0) { - pdf = 1.0 / (2.0 * delta_b); - mu_index = b_0_index; - } else if (mu_0 > a_0 + delta_a && mu_0 <= b_0 - delta_b) { - pdf = 0.0; - mu_index = 0; - } else { - pdf = -1; // - mu_index = -1; - } - break; - } - case 2: { - // Calculate Delta_b for Case 2 - double delta_b_case2 = 0.5 * std::min(b_1 - b_0, b_0 - (-1)); - // Check conditions and calculate pdf - if (mu_0 > b_0 - delta_b_case2 && mu_0 <= b_0) { - pdf = 1.0 / (2.0 * delta_b_case2); - mu_index = b_0_index; - } else if (mu_0 >= -1 && mu_0 <= b_0 - delta_b_case2) { - pdf = 0.0; - mu_index = 0; - } else { - pdf = -1; // - mu_index = -1; - } - break; - } - case 3: { - - // Calculate Delta_a and Delta_b for Case 3 - double delta_a_case3 = 0.5 * std::min(b_0 - a_0, a_0 - (-1)); - double delta_b_case3 = 0.5 * std::min(b_1 - b_0, b_0 - a_0); - - // Check conditions and calculate pdf - if (mu_0 >= a_0 && mu_0 < a_0 + delta_a_case3) { - pdf = 1.0 / (2.0 * delta_a_case3); - mu_index = a_0_index; - } else if (mu_0 > b_0 - delta_b_case3 && mu_0 <= b_0) { - pdf = 1.0 / (2.0 * delta_b_case3); - mu_index = b_0_index; - } else if (mu_0 > a_0 + delta_a_case3 && mu_0 <= b_0 - delta_b_case3) { - pdf = 0.0; - mu_index = 0; - } else { - pdf = -1; // - mu_index = -1; - } - break; - } - case 4: { - // Calculate Delta_a and Delta_b for Case 4 - double delta_a_case4 = 0.5 * std::min(b_0 - a_0, a_0 - a_1); - double delta_b_case4 = 0.5 * std::min(1 - b_0, b_0 - a_0); - - // Check conditions and calculate pdf - if (mu_0 >= a_0 && mu_0 < a_0 + delta_a_case4) { - pdf = 1.0 / (2.0 * delta_a_case4); - mu_index = a_0_index; - } else if (mu_0 > b_0 - delta_b_case4 && mu_0 <= b_0) { - pdf = 1.0 / (2.0 * delta_b_case4); - mu_index = b_0_index; - } else if (mu_0 > a_0 + delta_a_case4 && mu_0 <= b_0 - delta_b_case4) { - pdf = 0.0; - mu_index = 0; - } else { - pdf = -1; // - mu_index = -1; - } - - break; - } - case 5: { - // Calculate Delta_a for Case 5 - double delta_a_case5 = 0.5 * std::min(1 - a_0, a_0 - a_1); - - // Check conditions and calculate pdf - if (mu_0 >= a_0 && mu_0 < a_0 + delta_a_case5) { - pdf = 1.0 / (2.0 * delta_a_case5); - mu_index = a_0_index; - } else if (mu_0 > a_0 + delta_a_case5 && mu_0 <= 1) { - pdf = 0.0; - mu_index = 0; - } else { - pdf = -1; // - mu_index = -1; - } - - break; - } - case 6: { // mu has exactly one left neighbor and exactly one right neighbor - - // Calculate Delta_a and Delta_b for Case 6 - double delta_a_case6 = 0.5 * std::min(b_0 - a_0, a_0 - (-1)); - double delta_b_case6 = 0.5 * std::min(1 - b_0, b_0 - a_0); - - // Check conditions and calculate pdf - if (mu_0 >= a_0 && mu_0 < a_0 + delta_a_case6) { - pdf = 1.0 / (2.0 * delta_a_case6); - mu_index = a_0_index; - } else if (mu_0 > b_0 - delta_b_case6 && mu_0 <= b_0) { - pdf = 1.0 / (2.0 * delta_b_case6); - mu_index = b_0_index; - } else if (mu_0 > a_0 + delta_a_case6 && mu_0 <= b_0 - delta_b_case6) { - pdf = 0.0; - mu_index = 0; - } else { - pdf = -1; // - mu_index = -1; - } - - break; - } - case 7: { // mu has no left neighbor and exactly one right neighbor - // Calculate Delta_a and Delta_b for Case 7 - double delta_case7 = 0.5 * std::min(1 - b_0, b_0 - (-1)); - - // Check conditions and calculate pdf - if (mu_0 >= b_0 - delta_case7 && mu_0 <= b_0) { - pdf = 1.0 / (2.0 * delta_case7); - mu_index = b_0_index; - } else if (mu_0 >= (-1) && mu_0 <= b_0 - delta_case7) { - pdf = 0.0; - mu_index = 0; - } else { - pdf = -1; // - mu_index = b_0_index; - } - - break; - } - - case 8: { - // Calculate Delta_a and Delta_b for Case 8 - double delta_case8 = 0.5 * std::min(1 - a_0, a_0 - (-1)); - - // Check conditions and calculate pdf - if (mu_0 >= a_0 && mu_0 <= a_0 + delta_case8) { - pdf = 1.0 / (2.0 * delta_case8); - mu_index = a_0_index; - } else if (mu_0 <= 1 && mu_0 >= a_0 + delta_case8) { - pdf = 0.0; - mu_index = 0; - } else { - - pdf = -1; // - mu_index = -1; - } - - break; - } - default: { - - pdf = -1; - } - } - if (cdfVector[0] == -1) - // equiprobable distribution - prob = 1 / nu; - else { - // calculate probability for each mu - std::vector pVector; - if (!cdfVector.empty()) { - double norm = cdfVector.back(); - - for (size_t i = cdfVector.size() - 1; i > 0; --i) { - pVector.push_back((cdfVector[i] - cdfVector[i - 1]) / norm); - } - pVector.push_back(cdfVector[0] / norm); - } - prob = pVector[mu_index]; - } - - return pdf * prob; + vector w(mu.size(), 1.0 / mu.size()); + return get_pdf_discrete( + const vector& mu, const vector& w, double mu_0); } //============================================================================== @@ -363,19 +119,19 @@ double CoherentElasticAE::get_pdf( } const int i = lower_bound_index(energies.begin(), energies.end(), E_in); - std::vector energies_cut(energies.begin(), energies.begin() + i + 1); - std::vector factors_cut(factors.begin(), factors.begin() + i + 1); + vector energies_cut(energies.begin(), energies.begin() + i + 1); + vector factors_cut(factors.begin(), factors.begin() + i + 1); - std::vector mu_vector_rev; + vector mu_vector_rev; std::transform(energies_cut.begin(), energies_cut.end(), std::back_inserter(mu_vector_rev), [E_in](double Ei) { return 1 - 2 * Ei / E_in; }); - std::vector mu_vector(mu_vector_rev.rbegin(), mu_vector_rev.rend()); - // find mu index in mu_vector - const int k_i = lower_bound_index(mu_vector.begin(), mu_vector.end(), mu); - const int k_f = k_i + 1; + vector mu_vector(mu_vector_rev.rbegin(), mu_vector_rev.rend()); + + auto weights = xt::diff(factors_cut); + weights /= xt::sum(weights); - return get_pdf_discrete(mu_vector, mu, 1, factors_cut); + return get_pdf_discrete(mu_vector, weights, mu); } //============================================================================== @@ -483,7 +239,7 @@ double IncoherentElasticAEDiscrete::get_pdf( mu_vector.push_back(mu_k); } - return get_pdf_discrete(mu_vector, mu, n_mu); + return get_pdf_discrete(mu_vector, mu); } //============================================================================== @@ -610,7 +366,7 @@ double IncoherentInelasticAEDiscrete::get_pdf( mu_vector.push_back(mu_k); } - return get_pdf_discrete(mu_vector, mu, m); + return get_pdf_discrete(mu_vector, mu); } //============================================================================== @@ -809,7 +565,7 @@ double IncoherentInelasticAE::get_pdf( mu_vector.push_back(mu_k); } - return get_pdf_discrete(mu_vector, mu, n_mu); + return get_pdf_discrete(mu_vector, mu); } //============================================================================== From 78a33cc13ce1c16c1150924e43f898258930a6b9 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Sun, 31 Aug 2025 18:06:11 +0300 Subject: [PATCH 12/88] Update src/distribution_angle.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/distribution_angle.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index 5c35ff9497a..de1ff9cd299 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -122,9 +122,6 @@ double AngleDistribution::get_pdf(double E, double mu, uint64_t* seed) const // Sample i-th distribution double pdf = distribution_[i]->get_pdf(mu); - // Make sure pdf > 0 and return - if (std::abs(pdf) < 0) - fmt::print("pdf = {} <1", pdf); return pdf; } From 6d70d39b1926eee167581cbd1a366ebb8d6cdf2a Mon Sep 17 00:00:00 2001 From: GuySten Date: Sun, 31 Aug 2025 22:37:34 +0300 Subject: [PATCH 13/88] ran clang format --- src/thermal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thermal.cpp b/src/thermal.cpp index 0e2cfbdd9c5..a97583caef9 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -2,8 +2,8 @@ #include // for sort, move, min, max, find #include // for round, sqrt, abs -#include #include +#include #include "xtensor/xarray.hpp" #include "xtensor/xbuilder.hpp" From acfd4020673a6a32a228773769412f516b8a83f3 Mon Sep 17 00:00:00 2001 From: GuySten Date: Sun, 31 Aug 2025 22:51:56 +0300 Subject: [PATCH 14/88] fixes --- src/secondary_thermal.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 47047c6c2bf..1d3721611f5 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -56,12 +56,12 @@ double get_pdf_discrete( } // Calculate Delta_a and Delta_b - double delta_a = 0.5 * std::min(b_0 - a_0, a_0 - a_1); - double delta_b = 0.5 * std::min(b_1 - b_0, b_0 - a_0); + double delta_a = 0.5 * std::min(b0 - a0, a0 - a1); + double delta_b = 0.5 * std::min(b1 - b0, b0 - a0); - if (mu_0 < a_0 + delta_a) + if (mu_0 < a0 + delta_a) return w[ai] / (2.0 * delta_a); - else if (mu_0 + delta_b < b_0) + else if (mu_0 + delta_b < b0) return w[bi] / (2.0 * delta_b); else return 0.0; @@ -70,8 +70,7 @@ double get_pdf_discrete( double get_pdf_discrete(const vector& mu, double mu_0) { vector w(mu.size(), 1.0 / mu.size()); - return get_pdf_discrete( - const vector& mu, const vector& w, double mu_0); + return get_pdf_discrete(mu, w, mu_0); } //============================================================================== @@ -128,10 +127,13 @@ double CoherentElasticAE::get_pdf( [E_in](double Ei) { return 1 - 2 * Ei / E_in; }); vector mu_vector(mu_vector_rev.rbegin(), mu_vector_rev.rend()); - auto weights = xt::diff(factors_cut); + auto f = xt::adapt(factors_cut, { + factors_cut.size(), + }); + auto weights = xt::diff(f); weights /= xt::sum(weights); - - return get_pdf_discrete(mu_vector, weights, mu); + vector w(weights.begin(), weights.end()); + return get_pdf_discrete(mu_vector, w, mu); } //============================================================================== From 98ca32e81b3e8cf4fe45ba7870b4de807d749426 Mon Sep 17 00:00:00 2001 From: GuySten Date: Sun, 31 Aug 2025 22:54:48 +0300 Subject: [PATCH 15/88] cosmetics --- include/openmc/angle_energy.h | 1 + include/openmc/reaction_product.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/openmc/angle_energy.h b/include/openmc/angle_energy.h index 0a8b4c38849..767da4fae7c 100644 --- a/include/openmc/angle_energy.h +++ b/include/openmc/angle_energy.h @@ -1,5 +1,6 @@ #ifndef OPENMC_ANGLE_ENERGY_H #define OPENMC_ANGLE_ENERGY_H + #include #include diff --git a/include/openmc/reaction_product.h b/include/openmc/reaction_product.h index d4b475d7280..d459d2effdf 100644 --- a/include/openmc/reaction_product.h +++ b/include/openmc/reaction_product.h @@ -50,10 +50,12 @@ class ReactionProduct { //! \param[out] mu Outgoing cosine with respect to current direction //! \param[inout] seed Pseudorandom seed pointer void sample(double E_in, double& E_out, double& mu, uint64_t* seed) const; + void get_pdf(int i_tally, double E_in, double& E_out, uint64_t* seed, Particle& p, std::vector& mu_cm, std::vector& Js, std::vector& ghost_particles, std::vector& pdfs_lab) const; + ParticleType particle_; //!< Particle type EmissionMode emission_mode_; //!< Emission mode double decay_rate_; //!< Decay rate (for delayed neutron precursors) in [1/s] From 26d2a56394b5eb8ee62fe32b666d8f502e919d35 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 1 Sep 2025 19:07:08 +0000 Subject: [PATCH 16/88] using openmc's fatal_error --- include/openmc/angle_energy.h | 3 ++- src/secondary_correlated.cpp | 12 ++++++------ src/secondary_nbody.cpp | 5 +++-- vendor/gsl-lite | 1 + 4 files changed, 12 insertions(+), 9 deletions(-) create mode 160000 vendor/gsl-lite diff --git a/include/openmc/angle_energy.h b/include/openmc/angle_energy.h index 767da4fae7c..0d029fb9166 100644 --- a/include/openmc/angle_energy.h +++ b/include/openmc/angle_energy.h @@ -3,6 +3,7 @@ #include #include +#include "openmc/error.h" namespace openmc { @@ -19,7 +20,7 @@ class AngleEnergy { double E_in, double& E_out, double& mu, uint64_t* seed) const = 0; virtual double get_pdf(double, double&, double, uint64_t*) const { - throw std::runtime_error("get_pdf not available for this AngleEnergy type"); + fatal_error("get_pdf not available for this AngleEnergy type"); } virtual ~AngleEnergy() = default; }; diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index 1862efd9b80..f5e9c08979c 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -247,9 +247,9 @@ void CorrelatedAngleEnergy::sample( frac; } } else { - throw std::runtime_error { - "Unsupported interpolation type in CorrelatedAngleEnergy::get_pdf"}; - } + fatal_error("Unsupported interpolation type in CorrelatedAngleEnergy::get_pdf"); +} + // Now interpolate between incident energy bins i and i + 1 if (k >= n_discrete) { @@ -362,9 +362,9 @@ void CorrelatedAngleEnergy::get_pdf(double det_pos[4], double E_in, frac; } } else { - throw std::runtime_error { - "Unsupported interpolation type in CorrelatedAngleEnergy::sample"}; - } + fatal_error("Unsupported interpolation type in CorrelatedAngleEnergy::sample"); +} + // Now interpolate between incident energy bins i and i + 1 if (k >= n_discrete) { diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index 4bc52900622..4ff554243d6 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -61,8 +61,9 @@ void NBodyPhaseSpace::sample( std::log(r5) * std::pow(std::cos(PI / 2.0 * r6), 2); break; default: - throw std::runtime_error {"N-body phase space with >5 bodies."}; - } + fatal_error("N-body phase space with >5 bodies."); +} + // Now determine v and E_out double v = x / (x + y); diff --git a/vendor/gsl-lite b/vendor/gsl-lite new file mode 160000 index 00000000000..913e86d49c6 --- /dev/null +++ b/vendor/gsl-lite @@ -0,0 +1 @@ +Subproject commit 913e86d49c6a1acca980f4e325378f9dc393493a From 7abe69cb58bca70a06825b33b05b57cdafb49705 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 1 Sep 2025 19:13:33 +0000 Subject: [PATCH 17/88] Use accessors in powerlaw --- src/distribution.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/distribution.cpp b/src/distribution.cpp index 8bdfc7706ca..e4c5fcb888e 100644 --- a/src/distribution.cpp +++ b/src/distribution.cpp @@ -199,19 +199,20 @@ double PowerLaw::sample(uint64_t* seed) const double PowerLaw::get_pdf(double x) const { - // Recover the lower/upper bounds from offset_ and span_ - double a = std::pow(offset_, ninv_); // since offset_ = a^(n+1) - double b = std::pow(offset_ + span_, ninv_); // since offset_+span_ = b^(n+1) + // Use accessors + double a_val = this->a(); + double b_val = this->b(); + double n_val = this->n(); - if (x < a || x > b) { + if (x < a_val || x > b_val) { return 0.0; // outside support } - double n = 1.0 / ninv_ - 1.0; // since ninv_ = 1/(n+1) - - return (n + 1.0) * std::pow(x, n) / (std::pow(b, n + 1) - std::pow(a, n + 1)); + return (n_val + 1.0) * std::pow(x, n_val) / + (std::pow(b_val, n_val + 1.0) - std::pow(a_val, n_val + 1.0)); } + //============================================================================== // Maxwell implementation //============================================================================== From d183cce71e45f1287e50c341f94568c78f60cced Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 1 Sep 2025 19:17:02 +0000 Subject: [PATCH 18/88] clausing error for different distributions --- src/distribution.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/distribution.cpp b/src/distribution.cpp index e4c5fcb888e..8428618adf8 100644 --- a/src/distribution.cpp +++ b/src/distribution.cpp @@ -424,21 +424,24 @@ double Tabular::get_pdf(double x) const double x_i = x_[i]; double p_i = p_[i]; - if (interp_ == Interpolation::histogram) { + switch (interp_) { + case Interpolation::histogram: // Histogram interpolation return p_i; - } else { + + case Interpolation::lin_lin: { // Linear-linear interpolation double x_i1 = x_[i + 1]; double p_i1 = p_[i + 1]; double m = (p_i1 - p_i) / (x_i1 - x_i); - if (m == 0.0) { - return p_i; - } else { - return p_i + (x - x_i) * m; - } + return (m == 0.0) ? p_i : p_i + (x - x_i) * m; } + + default: + fatal_error("Unsupported interpolation type in PDF evaluation."); +} + } //============================================================================== From 37b41c4f0ee594eaee0ff2bc1d6f693d466d09d6 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 1 Sep 2025 19:26:54 +0000 Subject: [PATCH 19/88] fixing function signature --- include/openmc/reaction_product.h | 6 ++--- src/reaction_product.cpp | 41 ++----------------------------- 2 files changed, 4 insertions(+), 43 deletions(-) diff --git a/include/openmc/reaction_product.h b/include/openmc/reaction_product.h index d459d2effdf..05fba4b73cc 100644 --- a/include/openmc/reaction_product.h +++ b/include/openmc/reaction_product.h @@ -51,10 +51,8 @@ class ReactionProduct { //! \param[inout] seed Pseudorandom seed pointer void sample(double E_in, double& E_out, double& mu, uint64_t* seed) const; - void get_pdf(int i_tally, double E_in, double& E_out, uint64_t* seed, - Particle& p, std::vector& mu_cm, std::vector& Js, - std::vector& ghost_particles, - std::vector& pdfs_lab) const; + void get_pdf(int i_tally, double E_in, double& E_out, uint64_t* seed + , double mu_cm) const; ParticleType particle_; //!< Particle type EmissionMode emission_mode_; //!< Emission mode diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index 345e0755043..1516af17b4c 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -132,15 +132,9 @@ void ReactionProduct::sample( } void ReactionProduct::get_pdf(int i_tally, double E_in, double& E_out, - uint64_t* seed, Particle& p, std::vector& mu_cm, - std::vector& Js, std::vector& ghost_particles, - std::vector& pdfs_lab) const + uint64_t* seed, double mu_cm) const { - /* function to get detector position from user - Future implementation - double det_pos[4]; - get_det_pos(det_pos, i_tally); - */ - double det_pos[4] = {0.0, 0.0, 0.0, 1.0}; // Placeholder for detector position + int distribution_index; auto n = applicability_.size(); @@ -167,37 +161,6 @@ void ReactionProduct::get_pdf(int i_tally, double E_in, double& E_out, AngleEnergy* angleEnergyPtr = distribution_[distribution_index].get(); - if (CorrelatedAngleEnergy* correlatedAE = - dynamic_cast(angleEnergyPtr)) { - - (*correlatedAE) - .get_pdf( - det_pos, E_in, E_out, seed, p, mu_cm, Js, ghost_particles, pdfs_lab); - // Handle CorrelatedAngleEnergy - } else if (KalbachMann* kalbachMann = - dynamic_cast(angleEnergyPtr)) { - - (*kalbachMann) - .get_pdf( - det_pos, E_in, E_out, seed, p, mu_cm, Js, ghost_particles, pdfs_lab); - - // Handle KalbachMann - } else if (NBodyPhaseSpace* nBodyPS = - dynamic_cast(angleEnergyPtr)) { - - (*nBodyPS).get_pdf( - det_pos, E_in, E_out, seed, p, mu_cm, Js, ghost_particles, pdfs_lab); - // Handle NBodyPhaseSpace - } else if (UncorrelatedAngleEnergy* uncorrelatedAE = - dynamic_cast(angleEnergyPtr)) { - - (*uncorrelatedAE) - .get_pdf( - det_pos, E_in, E_out, seed, p, mu_cm, Js, ghost_particles, pdfs_lab); - // Handle UncorrelatedAngleEnergy - } else { - std::cout << "Unknown derived type." << std::endl; - } } } // namespace openmc From 007dcf544dbf70df171add7cc3adc3335875998c Mon Sep 17 00:00:00 2001 From: Eliezer214 <110336440+Eliezer214@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:55:21 +0300 Subject: [PATCH 20/88] Update include/openmc/angle_energy.h Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- include/openmc/angle_energy.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/openmc/angle_energy.h b/include/openmc/angle_energy.h index 0d029fb9166..fd1b723130d 100644 --- a/include/openmc/angle_energy.h +++ b/include/openmc/angle_energy.h @@ -2,7 +2,6 @@ #define OPENMC_ANGLE_ENERGY_H #include -#include #include "openmc/error.h" namespace openmc { From ebebb7a939fb64bebba82764e11a9eb6b82003cf Mon Sep 17 00:00:00 2001 From: Eliezer214 <110336440+Eliezer214@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:55:32 +0300 Subject: [PATCH 21/88] Update include/openmc/distribution_angle.h Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- include/openmc/distribution_angle.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/openmc/distribution_angle.h b/include/openmc/distribution_angle.h index 81194c2b10a..4a9bc1aa4fa 100644 --- a/include/openmc/distribution_angle.h +++ b/include/openmc/distribution_angle.h @@ -30,7 +30,6 @@ class AngleDistribution { //! Determine whether angle distribution is empty //! \return Whether distribution is empty bool empty() const { return energy_.empty(); } - double get_energy(int num) { return energy_[num]; } private: vector energy_; From 0239c1f1f01bc074ac5f40a2e86b7cac6f662fb1 Mon Sep 17 00:00:00 2001 From: Eliezer214 <110336440+Eliezer214@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:04:17 +0300 Subject: [PATCH 22/88] Update include/openmc/secondary_thermal.h Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- include/openmc/secondary_thermal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index 7fb758ec570..ed3e4f822a2 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -32,7 +32,7 @@ class CoherentElasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf(double E_in, double& E_out, double& mu, uint64_t* seed) const; + double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; private: const CoherentElasticXS& xs_; //!< Coherent elastic scattering cross section From 225f837a5dfab18d8c92a9fbeb1b94c0d3f92897 Mon Sep 17 00:00:00 2001 From: Eliezer214 <110336440+Eliezer214@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:04:40 +0300 Subject: [PATCH 23/88] Update include/openmc/angle_energy.h Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- include/openmc/angle_energy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/angle_energy.h b/include/openmc/angle_energy.h index fd1b723130d..b15e8fcb3ba 100644 --- a/include/openmc/angle_energy.h +++ b/include/openmc/angle_energy.h @@ -17,7 +17,7 @@ class AngleEnergy { public: virtual void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const = 0; - virtual double get_pdf(double, double&, double, uint64_t*) const + virtual double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const { fatal_error("get_pdf not available for this AngleEnergy type"); } From fa22e14409d1c9c4f338c2f047b7265a41e38a6d Mon Sep 17 00:00:00 2001 From: Eliezer214 <110336440+Eliezer214@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:05:08 +0300 Subject: [PATCH 24/88] Update include/openmc/reaction_product.h Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- include/openmc/reaction_product.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/openmc/reaction_product.h b/include/openmc/reaction_product.h index 05fba4b73cc..913341ce43b 100644 --- a/include/openmc/reaction_product.h +++ b/include/openmc/reaction_product.h @@ -51,8 +51,7 @@ class ReactionProduct { //! \param[inout] seed Pseudorandom seed pointer void sample(double E_in, double& E_out, double& mu, uint64_t* seed) const; - void get_pdf(int i_tally, double E_in, double& E_out, uint64_t* seed - , double mu_cm) const; + double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; ParticleType particle_; //!< Particle type EmissionMode emission_mode_; //!< Emission mode From a0678c9b41c59d4e8bcf28bedb72b1b96d00fd78 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:08:11 +0300 Subject: [PATCH 25/88] Update src/reaction_product.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/reaction_product.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index 1516af17b4c..46900259830 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -131,8 +131,8 @@ void ReactionProduct::sample( } } -void ReactionProduct::get_pdf(int i_tally, double E_in, double& E_out, - uint64_t* seed, double mu_cm) const +double ReactionProduct::get_pdf(double E_in, double mu, double& E_out, + uint64_t* seed) const { From e590d82cf0789bbab41c312bd7db143583c7a795 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:08:50 +0300 Subject: [PATCH 26/88] Update src/thermal.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/thermal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thermal.cpp b/src/thermal.cpp index a97583caef9..c240312d0de 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -316,7 +316,7 @@ void ThermalData::sample(const NuclideMicroXS& micro_xs, double E, } double ThermalData::get_pdf(const NuclideMicroXS& micro_xs, double E, - double& E_out, double mu, uint64_t* seed) + double mu, double& E_out, uint64_t* seed) { AngleEnergy* angleEnergyPtr; From 1b1134ee022672402c25de981c1f155be712eeca Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:09:47 +0300 Subject: [PATCH 27/88] Update include/openmc/secondary_thermal.h Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- include/openmc/secondary_thermal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index ed3e4f822a2..e8a874b36c3 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -56,7 +56,7 @@ class IncoherentElasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf(double E_in, double& E_out, double& mu, uint64_t* seed) const; + double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; private: double debye_waller_; From 5c348f14471692e6110bd38bb2cf31f74d27f99e Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:10:05 +0300 Subject: [PATCH 28/88] Update src/secondary_uncorrelated.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/secondary_uncorrelated.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index 263649f08fa..e522e08c260 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -9,7 +9,6 @@ #include "openmc/nuclide.h" #include "openmc/particle.h" #include "openmc/random_dist.h" -#include "openmc/tallies/tally_scoring.h" namespace openmc { From 203a19148e5b8da58ad39ba08663b5b7c353a1ba Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:10:12 +0300 Subject: [PATCH 29/88] Update src/secondary_thermal.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/secondary_thermal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 1d3721611f5..7d29c0fcb84 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -501,7 +501,7 @@ void IncoherentInelasticAE::sample( } double IncoherentInelasticAE::get_pdf( - double E_in, double& E_out, double& mu, uint64_t* seed) const + double E_in, double mu, double& E_out, uint64_t* seed) const { // Get index and interpolation factor for inelastic grid int i; From c4d8aa63dcb38dbb90a01013ffc95f0882b231e9 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:10:31 +0300 Subject: [PATCH 30/88] Update include/openmc/secondary_thermal.h Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- include/openmc/secondary_thermal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index e8a874b36c3..44517382948 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -82,7 +82,7 @@ class IncoherentElasticAEDiscrete : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf(double E_in, double& E_out, double& mu, uint64_t* seed) const; + double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; private: const vector& energy_; //!< Energies at which cosines are tabulated From e9b6860d7bbc9413a3685347c51268271e8e64f4 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:11:21 +0300 Subject: [PATCH 31/88] Update src/reaction_product.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/reaction_product.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index 46900259830..2c2a7d5c2f5 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -15,7 +15,6 @@ #include "openmc/secondary_nbody.h" #include "openmc/secondary_thermal.h" #include "openmc/secondary_uncorrelated.h" -#include "openmc/tallies/tally_scoring.h" namespace openmc { From ee1f2c5ffb28679c9fe20b7477055373e256da99 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:16:59 +0300 Subject: [PATCH 32/88] Apply suggestion from @GuySten Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- include/openmc/thermal.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/openmc/thermal.h b/include/openmc/thermal.h index d6291c30e5e..e4aac00822a 100644 --- a/include/openmc/thermal.h +++ b/include/openmc/thermal.h @@ -52,8 +52,7 @@ class ThermalData { //! \param[inout] seed Pseudorandom seed pointer void sample(const NuclideMicroXS& micro_xs, double E_in, double* E_out, double* mu, uint64_t* seed); - double get_pdf(const NuclideMicroXS& micro_xs, double E, double& E_out, - double mu, uint64_t* seed); + double get_pdf(const NuclideMicroXS& micro_xs, double E_in, double mu, double& E_out, uint64_t* seed); private: struct Reaction { From e07e312941b1953cb16272f7544ae3c7fdff484f Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:17:34 +0300 Subject: [PATCH 33/88] Apply suggestion from @GuySten Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- include/openmc/secondary_thermal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index 44517382948..05db3accb58 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -139,7 +139,7 @@ class IncoherentInelasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf(double E_in, double& E_out, double& mu, uint64_t* seed) const; + double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; private: //! Secondary energy/angle distribution From 1ab0da155bdc86056ff5e86a59a536f7975799a1 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Tue, 2 Sep 2025 17:33:57 +0300 Subject: [PATCH 34/88] Update src/thermal.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/thermal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thermal.cpp b/src/thermal.cpp index c240312d0de..9693bdf0fc5 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -315,7 +315,7 @@ void ThermalData::sample(const NuclideMicroXS& micro_xs, double E, *mu = std::copysign(1.0, *mu); } -double ThermalData::get_pdf(const NuclideMicroXS& micro_xs, double E, +double ThermalData::get_pdf(const NuclideMicroXS& micro_xs, double E_in, double mu, double& E_out, uint64_t* seed) { AngleEnergy* angleEnergyPtr; From c98566ad960125202aeea45b8c3bed91cd3f1dd2 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Tue, 2 Sep 2025 17:34:11 +0300 Subject: [PATCH 35/88] Update src/thermal.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/thermal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thermal.cpp b/src/thermal.cpp index 9693bdf0fc5..f91006708f3 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -326,7 +326,7 @@ double ThermalData::get_pdf(const NuclideMicroXS& micro_xs, double E_in, angleEnergyPtr = inelastic_.distribution.get(); } - return angleEnergyPtr->get_pdf(E, E_out, mu, seed); + return angleEnergyPtr->get_pdf(E_in, mu, E_out, seed); } void free_memory_thermal() From de3fc79c53316c5bedc22abca081f68f024957d1 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Tue, 2 Sep 2025 17:34:22 +0300 Subject: [PATCH 36/88] Update include/openmc/reaction_product.h Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- include/openmc/reaction_product.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/openmc/reaction_product.h b/include/openmc/reaction_product.h index 913341ce43b..93950ca1a49 100644 --- a/include/openmc/reaction_product.h +++ b/include/openmc/reaction_product.h @@ -11,8 +11,6 @@ #include "openmc/endf.h" #include "openmc/memory.h" // for unique_ptr #include "openmc/particle.h" -#include "openmc/tallies/filter.h" -#include "openmc/tallies/tally.h" #include "openmc/vector.h" // for vector namespace openmc { From 978bf746fd24386855b1d7daf7a2ed1b5cec68a1 Mon Sep 17 00:00:00 2001 From: GuySten Date: Tue, 2 Sep 2025 17:51:38 +0300 Subject: [PATCH 37/88] remove gsl-lite --- vendor/gsl-lite | 1 - 1 file changed, 1 deletion(-) delete mode 160000 vendor/gsl-lite diff --git a/vendor/gsl-lite b/vendor/gsl-lite deleted file mode 160000 index 913e86d49c6..00000000000 --- a/vendor/gsl-lite +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 913e86d49c6a1acca980f4e325378f9dc393493a From eb8389d37af2bd8268502f176819cf7a226408f5 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 Sep 2025 14:49:51 +0000 Subject: [PATCH 38/88] few fixes to signature --- include/openmc/secondary_uncorrelated.h | 5 +---- src/secondary_thermal.cpp | 7 +++---- src/secondary_uncorrelated.cpp | 7 +------ 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/include/openmc/secondary_uncorrelated.h b/include/openmc/secondary_uncorrelated.h index 30d9c4b10df..7c7e9205c4c 100644 --- a/include/openmc/secondary_uncorrelated.h +++ b/include/openmc/secondary_uncorrelated.h @@ -32,10 +32,7 @@ class UncorrelatedAngleEnergy : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - void get_pdf(double det_pos[4], double E_in, double& E_out, uint64_t* seed, - Particle& p, std::vector& mu_cm, std::vector& Js, - std::vector& ghost_particles, - std::vector& pdfs_lab) const; + double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; // Accessors AngleDistribution& angle() { return angle_; } diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 7d29c0fcb84..941ad3d25b7 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -104,7 +104,7 @@ void CoherentElasticAE::sample( } double CoherentElasticAE::get_pdf( - double E_in, double& E_out, double& mu, uint64_t* seed) const + double E_in, double mu, double& E_out, uint64_t* seed) const { // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7-1) @@ -156,7 +156,7 @@ void IncoherentElasticAE::sample( E_out = E_in; } double IncoherentElasticAE::get_pdf( - double E_in, double& E_out, double& mu, uint64_t* seed) const + double E_in, double mu, double& E_out, uint64_t* seed) const { // Sample angle by inverting the distribution in ENDF-102, Eq. 7.4 double c = 2 * E_in * debye_waller_; @@ -223,8 +223,7 @@ void IncoherentElasticAEDiscrete::sample( E_out = E_in; } -double IncoherentElasticAEDiscrete::get_pdf( - double E_in, double& E_out, double& mu, uint64_t* seed) const +double IncoherentElasticAEDiscrete::get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const { // Get index and interpolation factor for elastic grid int i; diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index e522e08c260..59a00b1f942 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -67,13 +67,8 @@ void UncorrelatedAngleEnergy::sample( E_out = energy_->sample(E_in, seed); } -void UncorrelatedAngleEnergy::get_pdf(double det_pos[4], double E_in, - double& E_out, uint64_t* seed, Particle& p, std::vector& mu_cm, - std::vector& Js, std::vector& ghost_particles, - std::vector& pdfs_lab) const +double UncorrelatedAngleEnergy::get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const { - bool COM = false; - // Sample outgoing energy E_out = energy_->sample(E_in, seed); } From 220f0993cc506a058499efa7cbc337b0295efd72 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 Sep 2025 14:57:20 +0000 Subject: [PATCH 39/88] more signature fixes --- include/openmc/secondary_correlated.h | 5 +---- src/secondary_correlated.cpp | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/include/openmc/secondary_correlated.h b/include/openmc/secondary_correlated.h index 991f8d61138..6cb6e7f2086 100644 --- a/include/openmc/secondary_correlated.h +++ b/include/openmc/secondary_correlated.h @@ -41,10 +41,7 @@ class CorrelatedAngleEnergy : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - void get_pdf(double det_pos[4], double E_in, double& E_out, uint64_t* seed, - Particle& p, std::vector& mu_cm, std::vector& Js, - std::vector& ghost_particles, - std::vector& pdfs_lab) const; + double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; // energy property vector& energy() { return energy_; } diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index f5e9c08979c..dd3e2250ac9 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -269,10 +269,7 @@ void CorrelatedAngleEnergy::sample( } } -void CorrelatedAngleEnergy::get_pdf(double det_pos[4], double E_in, - double& E_out, uint64_t* seed, Particle& p, std::vector& mu_cm, - std::vector& Js, std::vector& ghost_particles, - std::vector& pdfs_lab) const +double CorrelatedAngleEnergy::get_pdf(double E_in,double mu, double& E_out, uint64_t* seed) const { // Find energy bin and calculate interpolation factor -- if the energy is // outside the range of the tabulated energies, choose the first or last bins From d9a2b53b1b8ea0748e7ba76b31e448bbd70a0e8b Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 Sep 2025 15:01:32 +0000 Subject: [PATCH 40/88] more signature fixes --- include/openmc/secondary_correlated.h | 1 - include/openmc/secondary_kalbach.h | 5 +---- include/openmc/secondary_nbody.h | 5 +---- src/secondary_kalbach.cpp | 6 +----- src/secondary_nbody.cpp | 5 +---- 5 files changed, 4 insertions(+), 18 deletions(-) diff --git a/include/openmc/secondary_correlated.h b/include/openmc/secondary_correlated.h index 6cb6e7f2086..3c36684720f 100644 --- a/include/openmc/secondary_correlated.h +++ b/include/openmc/secondary_correlated.h @@ -10,7 +10,6 @@ #include "openmc/angle_energy.h" #include "openmc/distribution.h" #include "openmc/endf.h" -#include "openmc/particle.h" #include "openmc/vector.h" namespace openmc { diff --git a/include/openmc/secondary_kalbach.h b/include/openmc/secondary_kalbach.h index ff5f13bd7dc..a70c04d063a 100644 --- a/include/openmc/secondary_kalbach.h +++ b/include/openmc/secondary_kalbach.h @@ -32,10 +32,7 @@ class KalbachMann : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - void get_pdf(double det_pos[4], double E_in, double& E_out, uint64_t* seed, - Particle& p, std::vector& mu_cm, std::vector& Js, - std::vector& ghost_particles, - std::vector& pdfs_lab) const; + double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; private: //! Outgoing energy/angle at a single incoming energy diff --git a/include/openmc/secondary_nbody.h b/include/openmc/secondary_nbody.h index 8395c68290d..b1397c624d8 100644 --- a/include/openmc/secondary_nbody.h +++ b/include/openmc/secondary_nbody.h @@ -28,10 +28,7 @@ class NBodyPhaseSpace : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - void get_pdf(double det_pos[4], double E_in, double& E_out, uint64_t* seed, - Particle& p, std::vector& mu_cm, std::vector& Js, - std::vector& ghost_particles, - std::vector& pdfs_lab) const; + double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; private: int n_bodies_; //!< Number of particles distributed diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index db40ed72ef2..c77d8422022 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -241,14 +241,10 @@ void KalbachMann::sample( } } -void KalbachMann::get_pdf(double det_pos[4], double E_in, double& E_out, - uint64_t* seed, Particle& p, std::vector& mu_cm, - std::vector& Js, std::vector& ghost_particles, - std::vector& pdfs_lab) const +double KalbachMann::get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const { // Find energy bin and calculate interpolation factor -- if the energy is // outside the range of the tabulated energies, choose the first or last bins - const auto& nuc {data::nuclides[p.event_nuclide()]}; // double E_out; auto n_energy_in = energy_.size(); diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index 4ff554243d6..797b09079ca 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -70,10 +70,7 @@ void NBodyPhaseSpace::sample( E_out = E_max * v; } -void NBodyPhaseSpace::get_pdf(double det_pos[4], double E_in, double& E_out, - uint64_t* seed, Particle& p, std::vector& mu_cm, - std::vector& Js, std::vector& ghost_particles, - std::vector& pdfs_lab) const +double NBodyPhaseSpace::get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const { // By definition, the distribution of the angle is isotropic for an N-body // phase space distribution From 8271e15d8fb6fdb9486ed54fdbac474ecb7d4f06 Mon Sep 17 00:00:00 2001 From: GuySten Date: Tue, 2 Sep 2025 18:09:49 +0300 Subject: [PATCH 41/88] ran clang format --- include/openmc/angle_energy.h | 6 ++++-- include/openmc/thermal.h | 3 ++- src/distribution.cpp | 4 +--- src/reaction_product.cpp | 6 ++---- src/secondary_correlated.cpp | 15 ++++++++------- src/secondary_kalbach.cpp | 3 ++- src/secondary_nbody.cpp | 8 ++++---- src/secondary_thermal.cpp | 3 ++- src/secondary_uncorrelated.cpp | 3 ++- 9 files changed, 27 insertions(+), 24 deletions(-) diff --git a/include/openmc/angle_energy.h b/include/openmc/angle_energy.h index b15e8fcb3ba..f7263a3334f 100644 --- a/include/openmc/angle_energy.h +++ b/include/openmc/angle_energy.h @@ -2,6 +2,7 @@ #define OPENMC_ANGLE_ENERGY_H #include + #include "openmc/error.h" namespace openmc { @@ -17,9 +18,10 @@ class AngleEnergy { public: virtual void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const = 0; - virtual double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const + virtual double get_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { - fatal_error("get_pdf not available for this AngleEnergy type"); + fatal_error("get_pdf not available for this AngleEnergy type"); } virtual ~AngleEnergy() = default; }; diff --git a/include/openmc/thermal.h b/include/openmc/thermal.h index e4aac00822a..f46264dba00 100644 --- a/include/openmc/thermal.h +++ b/include/openmc/thermal.h @@ -52,7 +52,8 @@ class ThermalData { //! \param[inout] seed Pseudorandom seed pointer void sample(const NuclideMicroXS& micro_xs, double E_in, double* E_out, double* mu, uint64_t* seed); - double get_pdf(const NuclideMicroXS& micro_xs, double E_in, double mu, double& E_out, uint64_t* seed); + double get_pdf(const NuclideMicroXS& micro_xs, double E_in, double mu, + double& E_out, uint64_t* seed); private: struct Reaction { diff --git a/src/distribution.cpp b/src/distribution.cpp index 8428618adf8..022796c077e 100644 --- a/src/distribution.cpp +++ b/src/distribution.cpp @@ -212,7 +212,6 @@ double PowerLaw::get_pdf(double x) const (std::pow(b_val, n_val + 1.0) - std::pow(a_val, n_val + 1.0)); } - //============================================================================== // Maxwell implementation //============================================================================== @@ -440,8 +439,7 @@ double Tabular::get_pdf(double x) const default: fatal_error("Unsupported interpolation type in PDF evaluation."); -} - + } } //============================================================================== diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index 2c2a7d5c2f5..ae9f65bde11 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -130,11 +130,10 @@ void ReactionProduct::sample( } } -double ReactionProduct::get_pdf(double E_in, double mu, double& E_out, - uint64_t* seed) const +double ReactionProduct::get_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { - int distribution_index; auto n = applicability_.size(); if (n > 1) { @@ -159,7 +158,6 @@ double ReactionProduct::get_pdf(double E_in, double mu, double& E_out, // now extract pdf AngleEnergy* angleEnergyPtr = distribution_[distribution_index].get(); - } } // namespace openmc diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index dd3e2250ac9..0dacb723af3 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -247,9 +247,9 @@ void CorrelatedAngleEnergy::sample( frac; } } else { - fatal_error("Unsupported interpolation type in CorrelatedAngleEnergy::get_pdf"); -} - + fatal_error( + "Unsupported interpolation type in CorrelatedAngleEnergy::get_pdf"); + } // Now interpolate between incident energy bins i and i + 1 if (k >= n_discrete) { @@ -269,7 +269,8 @@ void CorrelatedAngleEnergy::sample( } } -double CorrelatedAngleEnergy::get_pdf(double E_in,double mu, double& E_out, uint64_t* seed) const +double CorrelatedAngleEnergy::get_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { // Find energy bin and calculate interpolation factor -- if the energy is // outside the range of the tabulated energies, choose the first or last bins @@ -359,9 +360,9 @@ double CorrelatedAngleEnergy::get_pdf(double E_in,double mu, double& E_out, uint frac; } } else { - fatal_error("Unsupported interpolation type in CorrelatedAngleEnergy::sample"); -} - + fatal_error( + "Unsupported interpolation type in CorrelatedAngleEnergy::sample"); + } // Now interpolate between incident energy bins i and i + 1 if (k >= n_discrete) { diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index c77d8422022..18e933d4f39 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -241,7 +241,8 @@ void KalbachMann::sample( } } -double KalbachMann::get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const +double KalbachMann::get_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { // Find energy bin and calculate interpolation factor -- if the energy is // outside the range of the tabulated energies, choose the first or last bins diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index 797b09079ca..a09456e7413 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -61,16 +61,16 @@ void NBodyPhaseSpace::sample( std::log(r5) * std::pow(std::cos(PI / 2.0 * r6), 2); break; default: - fatal_error("N-body phase space with >5 bodies."); -} - + fatal_error("N-body phase space with >5 bodies."); + } // Now determine v and E_out double v = x / (x + y); E_out = E_max * v; } -double NBodyPhaseSpace::get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const +double NBodyPhaseSpace::get_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { // By definition, the distribution of the angle is isotropic for an N-body // phase space distribution diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 941ad3d25b7..6342effa40d 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -223,7 +223,8 @@ void IncoherentElasticAEDiscrete::sample( E_out = E_in; } -double IncoherentElasticAEDiscrete::get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const +double IncoherentElasticAEDiscrete::get_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { // Get index and interpolation factor for elastic grid int i; diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index 59a00b1f942..3ebebd803a2 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -67,7 +67,8 @@ void UncorrelatedAngleEnergy::sample( E_out = energy_->sample(E_in, seed); } -double UncorrelatedAngleEnergy::get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const +double UncorrelatedAngleEnergy::get_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { // Sample outgoing energy E_out = energy_->sample(E_in, seed); From d0d84944dcd3b1ce6dd80b7a3c520e6b96d61a89 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 Sep 2025 15:19:23 +0000 Subject: [PATCH 42/88] Restore PDF calculation block in secondary_kalbach.cpp after accidental deletion --- src/secondary_kalbach.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index 18e933d4f39..1b8bc9ce13d 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -240,9 +240,7 @@ void KalbachMann::sample( mu = std::log(r1 * std::exp(km_a) + (1.0 - r1) * std::exp(-km_a)) / km_a; } } - -double KalbachMann::get_pdf( - double E_in, double mu, double& E_out, uint64_t* seed) const +double KalbachMann::get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const { // Find energy bin and calculate interpolation factor -- if the energy is // outside the range of the tabulated energies, choose the first or last bins @@ -355,6 +353,17 @@ double KalbachMann::get_pdf( E_out = E_1 + (E_out - E_i1_1) * (E_K - E_1) / (E_i1_K - E_i1_1); } } + + +double pdf_cm = km_a / (2 * std::sinh(km_a)) * + (std::cosh(km_a * mu) + + km_r * std::sinh(km_a * mu)); // center of mass + +return pdf_cm; + // https://docs.openmc.org/en/v0.8.0/methods/physics.html#equation-KM-pdf-angle + // double pdf_mu = km_a / (2 * std::sinh(km_a)) * (std::cosh(km_a * mymu) + + // km_r * std::sinh(km_a * mymu)); // center of mass return pdf_mu; + } } // namespace openmc From f2418d23ce012def8f9f2dcc95086a92a91ca84b Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 Sep 2025 15:48:00 +0000 Subject: [PATCH 43/88] cleaning --- include/openmc/secondary_kalbach.h | 1 - include/openmc/secondary_nbody.h | 1 - vendor/gsl-lite | 1 + 3 files changed, 1 insertion(+), 2 deletions(-) create mode 160000 vendor/gsl-lite diff --git a/include/openmc/secondary_kalbach.h b/include/openmc/secondary_kalbach.h index a70c04d063a..517bcd2833a 100644 --- a/include/openmc/secondary_kalbach.h +++ b/include/openmc/secondary_kalbach.h @@ -10,7 +10,6 @@ #include "openmc/angle_energy.h" #include "openmc/constants.h" #include "openmc/endf.h" -#include "openmc/particle.h" #include "openmc/vector.h" namespace openmc { diff --git a/include/openmc/secondary_nbody.h b/include/openmc/secondary_nbody.h index b1397c624d8..49b20c64bbd 100644 --- a/include/openmc/secondary_nbody.h +++ b/include/openmc/secondary_nbody.h @@ -7,7 +7,6 @@ #include "hdf5.h" #include "openmc/angle_energy.h" -#include "openmc/particle.h" namespace openmc { diff --git a/vendor/gsl-lite b/vendor/gsl-lite new file mode 160000 index 00000000000..913e86d49c6 --- /dev/null +++ b/vendor/gsl-lite @@ -0,0 +1 @@ +Subproject commit 913e86d49c6a1acca980f4e325378f9dc393493a From 9a5d70156e65b3f34530c5854dca5bc71f29fe8e Mon Sep 17 00:00:00 2001 From: GuySten Date: Tue, 2 Sep 2025 17:51:38 +0300 Subject: [PATCH 44/88] remove gsl-lite --- vendor/gsl-lite | 1 - 1 file changed, 1 deletion(-) delete mode 160000 vendor/gsl-lite diff --git a/vendor/gsl-lite b/vendor/gsl-lite deleted file mode 160000 index 913e86d49c6..00000000000 --- a/vendor/gsl-lite +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 913e86d49c6a1acca980f4e325378f9dc393493a From 3f91b0d64a619e5c9facdde3f3cebd70454338e4 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 Sep 2025 15:56:01 +0000 Subject: [PATCH 45/88] clean --- include/openmc/secondary_thermal.h | 2 +- src/secondary_thermal.cpp | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index 05db3accb58..8cb5e257b43 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -110,7 +110,7 @@ class IncoherentInelasticAEDiscrete : public AngleEnergy { void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; double get_pdf( - double E_in, double& E_out, double& mu, uint64_t* seed, int l = -1) const; + double E_in, double& E_out, double& mu, uint64_t* seed) const; private: const vector& energy_; //!< Incident energies diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 6342effa40d..6994fdffca0 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -318,7 +318,7 @@ void IncoherentInelasticAEDiscrete::sample( } double IncoherentInelasticAEDiscrete::get_pdf( - double E_in, double& E_out, double& mu, uint64_t* seed, int l) const + double E_in, double& E_out, double& mu, uint64_t* seed) const { // Get index and interpolation factor for inelastic grid int i; @@ -349,9 +349,7 @@ double IncoherentInelasticAEDiscrete::get_pdf( j = 0; } } - if (l != -1) { - j = l; - } // take j as param + // Determine outgoing energy corresponding to E_in[i] and E_in[i+1] double E_ij = energy_out_(i, j); double E_i1j = energy_out_(i + 1, j); From e1d069c1ad5ba565640c9d85347abd5a81211a9c Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Tue, 2 Sep 2025 18:58:49 +0300 Subject: [PATCH 46/88] Update src/secondary_correlated.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/secondary_correlated.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index 0dacb723af3..f32e4667b0d 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -14,7 +14,6 @@ #include "openmc/particle.h" #include "openmc/random_lcg.h" #include "openmc/search.h" -#include "openmc/tallies/tally_scoring.h" namespace openmc { From f9e4693ee47dff57718c1b5ff01c7843bfe919d4 Mon Sep 17 00:00:00 2001 From: GuySten <62616591+GuySten@users.noreply.github.com> Date: Tue, 2 Sep 2025 19:21:02 +0300 Subject: [PATCH 47/88] Update include/openmc/secondary_thermal.h --- include/openmc/secondary_thermal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index 8cb5e257b43..afce9c17e9e 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -110,7 +110,7 @@ class IncoherentInelasticAEDiscrete : public AngleEnergy { void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; double get_pdf( - double E_in, double& E_out, double& mu, uint64_t* seed) const; + double E_in, double mu, double& E_out, uint64_t* seed) const; private: const vector& energy_; //!< Incident energies From b45dcfd56e82257f5cd6eb5f4c6236e6b9c54578 Mon Sep 17 00:00:00 2001 From: GuySten Date: Tue, 2 Sep 2025 19:21:39 +0300 Subject: [PATCH 48/88] ran clang format --- include/openmc/secondary_thermal.h | 3 +-- src/secondary_kalbach.cpp | 13 ++++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index afce9c17e9e..0eff575d730 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -109,8 +109,7 @@ class IncoherentInelasticAEDiscrete : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf( - double E_in, double mu, double& E_out, uint64_t* seed) const; + double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; private: const vector& energy_; //!< Incident energies diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index 1b8bc9ce13d..69b4b91e028 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -240,7 +240,8 @@ void KalbachMann::sample( mu = std::log(r1 * std::exp(km_a) + (1.0 - r1) * std::exp(-km_a)) / km_a; } } -double KalbachMann::get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const +double KalbachMann::get_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { // Find energy bin and calculate interpolation factor -- if the energy is // outside the range of the tabulated energies, choose the first or last bins @@ -354,16 +355,14 @@ double KalbachMann::get_pdf(double E_in, double mu, double& E_out, uint64_t* see } } - -double pdf_cm = km_a / (2 * std::sinh(km_a)) * - (std::cosh(km_a * mu) + - km_r * std::sinh(km_a * mu)); // center of mass + double pdf_cm = + km_a / (2 * std::sinh(km_a)) * + (std::cosh(km_a * mu) + km_r * std::sinh(km_a * mu)); // center of mass -return pdf_cm; + return pdf_cm; // https://docs.openmc.org/en/v0.8.0/methods/physics.html#equation-KM-pdf-angle // double pdf_mu = km_a / (2 * std::sinh(km_a)) * (std::cosh(km_a * mymu) + // km_r * std::sinh(km_a * mymu)); // center of mass return pdf_mu; - } } // namespace openmc From a85365c0153e933a0e30b799e40d6b016639e8a3 Mon Sep 17 00:00:00 2001 From: GuySten <62616591+GuySten@users.noreply.github.com> Date: Tue, 2 Sep 2025 19:23:32 +0300 Subject: [PATCH 49/88] Update src/secondary_kalbach.cpp --- src/secondary_kalbach.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index 69b4b91e028..db248781cd7 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -14,7 +14,6 @@ #include "openmc/random_dist.h" #include "openmc/random_lcg.h" #include "openmc/search.h" -#include "openmc/tallies/tally_scoring.h" #include "openmc/vector.h" namespace openmc { From d78201388a9771b7d5b81f873d7588f68fdb2b35 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 Sep 2025 16:19:50 +0000 Subject: [PATCH 50/88] returning pdf assuming data in lab --- src/secondary_correlated.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index f32e4667b0d..cc6642ab4c0 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -245,9 +245,6 @@ void CorrelatedAngleEnergy::sample( p_l_k) / frac; } - } else { - fatal_error( - "Unsupported interpolation type in CorrelatedAngleEnergy::get_pdf"); } // Now interpolate between incident energy bins i and i + 1 @@ -268,8 +265,7 @@ void CorrelatedAngleEnergy::sample( } } -double CorrelatedAngleEnergy::get_pdf( - double E_in, double mu, double& E_out, uint64_t* seed) const +double CorrelatedAngleEnergy::get_pdf(double E_in, double& E_out, double& mu, uint64_t* seed) const { // Find energy bin and calculate interpolation factor -- if the energy is // outside the range of the tabulated energies, choose the first or last bins @@ -358,9 +354,6 @@ double CorrelatedAngleEnergy::get_pdf( p_l_k) / frac; } - } else { - fatal_error( - "Unsupported interpolation type in CorrelatedAngleEnergy::sample"); } // Now interpolate between incident energy bins i and i + 1 @@ -371,6 +364,21 @@ double CorrelatedAngleEnergy::get_pdf( E_out = E_1 + (E_out - E_i1_1) * (E_K - E_1) / (E_i1_K - E_i1_1); } } + + double pdf_mu_lab; // assuming the data in lab frame! + if (r1 - c_k < c_k1 - r1 || + distribution_[l].interpolation == Interpolation::histogram) { + pdf_mu_lab = + distribution_[l].angle[k]->get_pdf(mu); + } else { + pdf_mu_lab = + distribution_[l].angle[k + 1]->get_pdf(mu); + } + + return pdf_mu_lab; + + + } } // namespace openmc From c72bb025c05cb62e877b26597d3bc1d06dd44bc1 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 Sep 2025 16:27:29 +0000 Subject: [PATCH 51/88] cleaning --- src/secondary_correlated.cpp | 2 +- src/secondary_thermal.cpp | 2 +- vendor/gsl-lite | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 160000 vendor/gsl-lite diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index cc6642ab4c0..06bb7f7f417 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -265,7 +265,7 @@ void CorrelatedAngleEnergy::sample( } } -double CorrelatedAngleEnergy::get_pdf(double E_in, double& E_out, double& mu, uint64_t* seed) const +double CorrelatedAngleEnergy::get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const { // Find energy bin and calculate interpolation factor -- if the energy is // outside the range of the tabulated energies, choose the first or last bins diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 6994fdffca0..526db8b1b84 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -318,7 +318,7 @@ void IncoherentInelasticAEDiscrete::sample( } double IncoherentInelasticAEDiscrete::get_pdf( - double E_in, double& E_out, double& mu, uint64_t* seed) const + double E_in, double mu, double& E_out, uint64_t* seed) const { // Get index and interpolation factor for inelastic grid int i; diff --git a/vendor/gsl-lite b/vendor/gsl-lite new file mode 160000 index 00000000000..913e86d49c6 --- /dev/null +++ b/vendor/gsl-lite @@ -0,0 +1 @@ +Subproject commit 913e86d49c6a1acca980f4e325378f9dc393493a From 3cb6821e1a049c95a8273a0e4c275a77a160a217 Mon Sep 17 00:00:00 2001 From: GuySten Date: Tue, 2 Sep 2025 17:51:38 +0300 Subject: [PATCH 52/88] remove gsl-lite --- vendor/gsl-lite | 1 - 1 file changed, 1 deletion(-) delete mode 160000 vendor/gsl-lite diff --git a/vendor/gsl-lite b/vendor/gsl-lite deleted file mode 160000 index 913e86d49c6..00000000000 --- a/vendor/gsl-lite +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 913e86d49c6a1acca980f4e325378f9dc393493a From 47e96a6e8fc0d125d827d310f77f771b56090081 Mon Sep 17 00:00:00 2001 From: GuySten <62616591+GuySten@users.noreply.github.com> Date: Tue, 2 Sep 2025 19:48:06 +0300 Subject: [PATCH 53/88] Apply suggestions from code review --- include/openmc/secondary_uncorrelated.h | 1 - src/secondary_correlated.cpp | 2 -- src/secondary_kalbach.cpp | 2 -- src/secondary_nbody.cpp | 1 - src/secondary_uncorrelated.cpp | 2 -- src/thermal.cpp | 2 -- 6 files changed, 10 deletions(-) diff --git a/include/openmc/secondary_uncorrelated.h b/include/openmc/secondary_uncorrelated.h index 7c7e9205c4c..b2b9a6c1388 100644 --- a/include/openmc/secondary_uncorrelated.h +++ b/include/openmc/secondary_uncorrelated.h @@ -10,7 +10,6 @@ #include "openmc/distribution_angle.h" #include "openmc/distribution_energy.h" #include "openmc/memory.h" -#include "openmc/particle.h" #include "openmc/vector.h" namespace openmc { diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index 06bb7f7f417..972d1620331 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -10,8 +10,6 @@ #include "openmc/endf.h" #include "openmc/hdf5_interface.h" -#include "openmc/nuclide.h" -#include "openmc/particle.h" #include "openmc/random_lcg.h" #include "openmc/search.h" diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index db248781cd7..6968f406708 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -9,8 +9,6 @@ #include "xtensor/xview.hpp" #include "openmc/hdf5_interface.h" -#include "openmc/nuclide.h" -#include "openmc/particle.h" #include "openmc/random_dist.h" #include "openmc/random_lcg.h" #include "openmc/search.h" diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index a09456e7413..77299e97d0a 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -8,7 +8,6 @@ #include "openmc/nuclide.h" #include "openmc/random_dist.h" #include "openmc/random_lcg.h" -#include "openmc/tallies/tally_scoring.h" namespace openmc { diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index 3ebebd803a2..a7c00028716 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -6,8 +6,6 @@ #include "openmc/error.h" #include "openmc/hdf5_interface.h" -#include "openmc/nuclide.h" -#include "openmc/particle.h" #include "openmc/random_dist.h" namespace openmc { diff --git a/src/thermal.cpp b/src/thermal.cpp index f91006708f3..eda5884f331 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -2,8 +2,6 @@ #include // for sort, move, min, max, find #include // for round, sqrt, abs -#include -#include #include "xtensor/xarray.hpp" #include "xtensor/xbuilder.hpp" From f97f4ad12343e103b1e2b0b1a2688870376ac2eb Mon Sep 17 00:00:00 2001 From: GuySten <62616591+GuySten@users.noreply.github.com> Date: Tue, 2 Sep 2025 19:49:27 +0300 Subject: [PATCH 54/88] Update secondary_nbody.cpp --- src/secondary_nbody.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index 77299e97d0a..849f2f7ae2d 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -5,7 +5,6 @@ #include "openmc/constants.h" #include "openmc/hdf5_interface.h" #include "openmc/math_functions.h" -#include "openmc/nuclide.h" #include "openmc/random_dist.h" #include "openmc/random_lcg.h" From 1e4b7635337eac699aebc530e0fd23fbad3ae117 Mon Sep 17 00:00:00 2001 From: GuySten Date: Tue, 2 Sep 2025 21:13:21 +0300 Subject: [PATCH 55/88] ran clang format --- src/secondary_correlated.cpp | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index 972d1620331..8b53c401665 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -263,7 +263,8 @@ void CorrelatedAngleEnergy::sample( } } -double CorrelatedAngleEnergy::get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const +double CorrelatedAngleEnergy::get_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { // Find energy bin and calculate interpolation factor -- if the energy is // outside the range of the tabulated energies, choose the first or last bins @@ -363,20 +364,15 @@ double CorrelatedAngleEnergy::get_pdf(double E_in, double mu, double& E_out, uin } } - double pdf_mu_lab; // assuming the data in lab frame! - if (r1 - c_k < c_k1 - r1 || - distribution_[l].interpolation == Interpolation::histogram) { - pdf_mu_lab = - distribution_[l].angle[k]->get_pdf(mu); - } else { - pdf_mu_lab = - distribution_[l].angle[k + 1]->get_pdf(mu); - } - - return pdf_mu_lab; - + double pdf_mu_lab; // assuming the data in lab frame! + if (r1 - c_k < c_k1 - r1 || + distribution_[l].interpolation == Interpolation::histogram) { + pdf_mu_lab = distribution_[l].angle[k]->get_pdf(mu); + } else { + pdf_mu_lab = distribution_[l].angle[k + 1]->get_pdf(mu); + } - + return pdf_mu_lab; } } // namespace openmc From 3bc46989efa10ca132b5c5cf350a61a1c19735e3 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Wed, 3 Sep 2025 22:03:44 +0300 Subject: [PATCH 56/88] Update src/secondary_kalbach.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/secondary_kalbach.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index 6968f406708..8d966c4d247 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -352,14 +352,12 @@ double KalbachMann::get_pdf( } } - double pdf_cm = + // https://docs.openmc.org/en/latest/methods/neutron_physics.html#equation-KM-pdf-angle + double pdf = km_a / (2 * std::sinh(km_a)) * - (std::cosh(km_a * mu) + km_r * std::sinh(km_a * mu)); // center of mass + (std::cosh(km_a * mu) + km_r * std::sinh(km_a * mu)); - return pdf_cm; - // https://docs.openmc.org/en/v0.8.0/methods/physics.html#equation-KM-pdf-angle - // double pdf_mu = km_a / (2 * std::sinh(km_a)) * (std::cosh(km_a * mymu) + - // km_r * std::sinh(km_a * mymu)); // center of mass return pdf_mu; + return pdf; } } // namespace openmc From 4e85033d349485295db2b6f0f0445217d170aca0 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 Sep 2025 19:14:34 +0000 Subject: [PATCH 57/88] returning pdf --- src/reaction_product.cpp | 1 + vendor/gsl-lite | 1 + 2 files changed, 2 insertions(+) create mode 160000 vendor/gsl-lite diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index ae9f65bde11..6e008fa2ff7 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -158,6 +158,7 @@ double ReactionProduct::get_pdf( // now extract pdf AngleEnergy* angleEnergyPtr = distribution_[distribution_index].get(); + return angleEnergyPtr->get_pdf(E_in,mu , E_out , seed); } } // namespace openmc diff --git a/vendor/gsl-lite b/vendor/gsl-lite new file mode 160000 index 00000000000..913e86d49c6 --- /dev/null +++ b/vendor/gsl-lite @@ -0,0 +1 @@ +Subproject commit 913e86d49c6a1acca980f4e325378f9dc393493a From 6727a033f047aed9a3c153cc461f558602bc5d89 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Wed, 3 Sep 2025 22:17:18 +0300 Subject: [PATCH 58/88] Update src/secondary_nbody.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/secondary_nbody.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index 849f2f7ae2d..9d7b6213757 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -109,16 +109,6 @@ double NBodyPhaseSpace::get_pdf( // Now determine v and E_out double v = x / (x + y); E_out = E_max * v; - /* function to transform pdf CM to lab frame - Future implementation - get_pdf_to_point_elastic(det_pos, p, mu_cm, Js, ghost_particles, E_out / - 1e6); for (std::size_t i = 0; i < mu_cm.size(); ++i) { - // Assuming Js.size() is the same as mu_cm.size() - double mu_c = mu_cm[i]; - double derivative = Js[i]; - double pdf_cm = 0.5; - pdfs_lab.push_back(pdf_cm / std::abs(derivative)); - } - */ } } // namespace openmc From c5a737b26226820c5134a759145bcf0fd5ee1ed8 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 Sep 2025 19:21:24 +0000 Subject: [PATCH 59/88] isotropic and no lab yet --- src/secondary_nbody.cpp | 40 +--------------------------------------- 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index 9d7b6213757..32a828efaa7 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -70,45 +70,7 @@ void NBodyPhaseSpace::sample( double NBodyPhaseSpace::get_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { - // By definition, the distribution of the angle is isotropic for an N-body - // phase space distribution - - // 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, seed); - - double y; - double r1, r2, r3, r4, r5, r6; - switch (n_bodies_) { - case 3: - y = maxwell_spectrum(1.0, seed); - break; - case 4: - r1 = prn(seed); - r2 = prn(seed); - r3 = prn(seed); - y = -std::log(r1 * r2 * r3); - break; - case 5: - 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: - throw std::runtime_error {"N-body phase space with >5 bodies."}; - } - - // Now determine v and E_out - double v = x / (x + y); - E_out = E_max * v; + return 0.5; } } // namespace openmc From 186ad34e0b9e3eae603ea52b13f97837d4d9ad18 Mon Sep 17 00:00:00 2001 From: itay-space <126396074+itay-space@users.noreply.github.com> Date: Wed, 3 Sep 2025 22:40:02 +0300 Subject: [PATCH 60/88] Update src/distribution_angle.cpp Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> --- src/distribution_angle.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index de1ff9cd299..d308534793d 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -95,7 +95,7 @@ double AngleDistribution::sample(double E, uint64_t* seed) const return mu; } -double AngleDistribution::get_pdf(double E, double mu, uint64_t* seed) const +double AngleDistribution::get_pdf(double E, double mu) const { // Determine number of incoming energies auto n = energy_.size(); @@ -115,13 +115,11 @@ double AngleDistribution::get_pdf(double E, double mu, uint64_t* seed) const r = (E - energy_[i]) / (energy_[i + 1] - energy_[i]); } - // Sample between the ith and (i+1)th bin - if (r > prn(seed)) - ++i; - - // Sample i-th distribution - double pdf = distribution_[i]->get_pdf(mu); - + double pdf = 0.0; + if (r>0.0) + pdf += r * distribution_[i+1]->get_pdf(mu); + if (r<1.0) + pdf += (1.0-r) * distribution_[i]->get_pdf(mu); return pdf; } From 108fa5242ab82f95a3b6842570201aea3ff095b3 Mon Sep 17 00:00:00 2001 From: GuySten Date: Tue, 2 Sep 2025 17:51:38 +0300 Subject: [PATCH 61/88] remove gsl-lite --- vendor/gsl-lite | 1 - 1 file changed, 1 deletion(-) delete mode 160000 vendor/gsl-lite diff --git a/vendor/gsl-lite b/vendor/gsl-lite deleted file mode 160000 index 913e86d49c6..00000000000 --- a/vendor/gsl-lite +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 913e86d49c6a1acca980f4e325378f9dc393493a From 0f4002284302fbb8dffa9e37bb9bf53c41b8e726 Mon Sep 17 00:00:00 2001 From: GuySten Date: Wed, 3 Sep 2025 22:45:35 +0300 Subject: [PATCH 62/88] ran clang format --- src/distribution_angle.cpp | 8 ++++---- src/reaction_product.cpp | 2 +- src/secondary_kalbach.cpp | 5 ++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index d308534793d..bb28ee3fe7e 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -116,10 +116,10 @@ double AngleDistribution::get_pdf(double E, double mu) const } double pdf = 0.0; - if (r>0.0) - pdf += r * distribution_[i+1]->get_pdf(mu); - if (r<1.0) - pdf += (1.0-r) * distribution_[i]->get_pdf(mu); + if (r > 0.0) + pdf += r * distribution_[i + 1]->get_pdf(mu); + if (r < 1.0) + pdf += (1.0 - r) * distribution_[i]->get_pdf(mu); return pdf; } diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index 6e008fa2ff7..fe5da49b62d 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -158,7 +158,7 @@ double ReactionProduct::get_pdf( // now extract pdf AngleEnergy* angleEnergyPtr = distribution_[distribution_index].get(); - return angleEnergyPtr->get_pdf(E_in,mu , E_out , seed); + return angleEnergyPtr->get_pdf(E_in, mu, E_out, seed); } } // namespace openmc diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index 8d966c4d247..c38305886dc 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -353,9 +353,8 @@ double KalbachMann::get_pdf( } // https://docs.openmc.org/en/latest/methods/neutron_physics.html#equation-KM-pdf-angle - double pdf = - km_a / (2 * std::sinh(km_a)) * - (std::cosh(km_a * mu) + km_r * std::sinh(km_a * mu)); + double pdf = km_a / (2 * std::sinh(km_a)) * + (std::cosh(km_a * mu) + km_r * std::sinh(km_a * mu)); return pdf; } From 4bf8f234c528dd75d07e6c219dd14b62e89cb270 Mon Sep 17 00:00:00 2001 From: GuySten <62616591+GuySten@users.noreply.github.com> Date: Wed, 3 Sep 2025 22:46:23 +0300 Subject: [PATCH 63/88] Update include/openmc/distribution_angle.h --- include/openmc/distribution_angle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/distribution_angle.h b/include/openmc/distribution_angle.h index 4a9bc1aa4fa..4beb39b09a5 100644 --- a/include/openmc/distribution_angle.h +++ b/include/openmc/distribution_angle.h @@ -25,7 +25,7 @@ class AngleDistribution { //! \param[inout] seed pseudorandom number seed pointer //! \return Cosine of the angle in the range [-1,1] double sample(double E, uint64_t* seed) const; - double get_pdf(double E, double mu, uint64_t* seed) const; + double get_pdf(double E, double mu) const; //! Determine whether angle distribution is empty //! \return Whether distribution is empty From ca7cd8501b1ffdfb04b20fb6f98f2622be8ff016 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 Sep 2025 19:45:33 +0000 Subject: [PATCH 64/88] returning pdf in uncorrelated --- src/secondary_uncorrelated.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index a7c00028716..342d3a75575 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -70,6 +70,14 @@ double UncorrelatedAngleEnergy::get_pdf( { // Sample outgoing energy E_out = energy_->sample(E_in, seed); + double pdf_cm; + if (!angle_.empty()) { + pdf_cm = angle_.get_pdf(E_in, mu, seed); + } else { + // no angle distribution given => assume isotropic for all energies + pdf_cm = 0.5; + } +return pdf_cm; } } // namespace openmc From d7b16210ccc5451cfa0cac00c20b3452a771efc7 Mon Sep 17 00:00:00 2001 From: GuySten Date: Wed, 3 Sep 2025 22:56:25 +0300 Subject: [PATCH 65/88] fix access to get_pdf --- src/secondary_uncorrelated.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index 342d3a75575..a91eea02eb7 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -72,7 +72,7 @@ double UncorrelatedAngleEnergy::get_pdf( E_out = energy_->sample(E_in, seed); double pdf_cm; if (!angle_.empty()) { - pdf_cm = angle_.get_pdf(E_in, mu, seed); + pdf_cm = angle_.get_pdf(E_in, mu); } else { // no angle distribution given => assume isotropic for all energies pdf_cm = 0.5; From 7cb99e8187518ec59d473fb63204b4a85ba9b819 Mon Sep 17 00:00:00 2001 From: GuySten Date: Wed, 3 Sep 2025 22:56:38 +0300 Subject: [PATCH 66/88] ran clang format --- src/secondary_uncorrelated.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index a91eea02eb7..5bdf7d9b32a 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -71,13 +71,13 @@ double UncorrelatedAngleEnergy::get_pdf( // Sample outgoing energy E_out = energy_->sample(E_in, seed); double pdf_cm; - if (!angle_.empty()) { - pdf_cm = angle_.get_pdf(E_in, mu); - } else { - // no angle distribution given => assume isotropic for all energies - pdf_cm = 0.5; - } -return pdf_cm; + if (!angle_.empty()) { + pdf_cm = angle_.get_pdf(E_in, mu); + } else { + // no angle distribution given => assume isotropic for all energies + pdf_cm = 0.5; + } + return pdf_cm; } } // namespace openmc From e2de1c4073e972152b7180110564448334b08459 Mon Sep 17 00:00:00 2001 From: GuySten Date: Wed, 3 Sep 2025 23:09:41 +0300 Subject: [PATCH 67/88] cosmetics --- include/openmc/distribution.h | 15 +++++---------- src/distribution.cpp | 28 ---------------------------- src/reaction_product.cpp | 2 -- src/secondary_correlated.cpp | 8 ++++---- src/secondary_uncorrelated.cpp | 8 ++++---- 5 files changed, 13 insertions(+), 48 deletions(-) diff --git a/include/openmc/distribution.h b/include/openmc/distribution.h index bab4acb16f2..3967dd1fe40 100644 --- a/include/openmc/distribution.h +++ b/include/openmc/distribution.h @@ -9,6 +9,7 @@ #include "pugixml.hpp" #include "openmc/constants.h" +#include "openmc/error.h" #include "openmc/memory.h" // for unique_ptr #include "openmc/span.h" #include "openmc/vector.h" // for vector @@ -23,7 +24,10 @@ class Distribution { public: virtual ~Distribution() = default; virtual double sample(uint64_t* seed) const = 0; - virtual double get_pdf(double x) const = 0; + virtual double get_pdf(double x) const + { + fatal_error("get_pdf not available for this Distribution type"); + } //! Return integral of distribution //! \return Integral of distribution @@ -86,11 +90,6 @@ class Discrete : public Distribution { //! \return Sampled value double sample(uint64_t* seed) const override; - //! Calculate the probability density function (PDF) at a given value - //! \param x The value at which to evaluate the PDF - //! \return The value of the PDF at the given point - double get_pdf(double x) const; - double integral() const override { return di_.integral(); }; // Properties @@ -168,7 +167,6 @@ class Maxwell : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; - double get_pdf(double x) const; double theta() const { return theta_; } @@ -189,7 +187,6 @@ class Watt : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; - double get_pdf(double x) const; double a() const { return a_; } double b() const { return b_; } @@ -275,7 +272,6 @@ class Equiprobable : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; - double get_pdf(double x) const; const vector& x() const { return x_; } @@ -295,7 +291,6 @@ class Mixture : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; - double get_pdf(double x) const; double integral() const override { return integral_; } diff --git a/src/distribution.cpp b/src/distribution.cpp index 022796c077e..2401527d6b2 100644 --- a/src/distribution.cpp +++ b/src/distribution.cpp @@ -136,12 +136,6 @@ double Discrete::sample(uint64_t* seed) const return x_[di_.sample(seed)]; } -// Not implemented -double Discrete::get_pdf(double x) const -{ - return -1; -} - //============================================================================== // Uniform implementation //============================================================================== @@ -226,12 +220,6 @@ double Maxwell::sample(uint64_t* seed) const return maxwell_spectrum(theta_, seed); } -// Not implemented -double Maxwell::get_pdf(double x) const -{ - return -1; -} - //============================================================================== // Watt implementation //============================================================================== @@ -252,12 +240,6 @@ double Watt::sample(uint64_t* seed) const return watt_spectrum(a_, b_, seed); } -// Not implemented -double Watt::get_pdf(double x) const -{ - return -1; -} - //============================================================================== // Normal implementation //============================================================================== @@ -458,11 +440,6 @@ double Equiprobable::sample(uint64_t* seed) const return xl + ((n - 1) * r - i) * (xr - xl); } -double Equiprobable::get_pdf(double x) const -{ - return -1; -} - //============================================================================== // Mixture implementation //============================================================================== @@ -512,11 +489,6 @@ double Mixture::sample(uint64_t* seed) const return it->second->sample(seed); } -double Mixture::get_pdf(double x) const -{ - return -1; -} - //============================================================================== // Helper function //============================================================================== diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index fe5da49b62d..fe9fa0e6099 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -151,8 +151,6 @@ double ReactionProduct::get_pdf( } } } else { - // If only one distribution is present, go ahead and sample it - // distribution_[0]->sample(E_in, E_out, mu, seed); distribution_index = 0; } // now extract pdf diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index 8b53c401665..7213c132046 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -364,15 +364,15 @@ double CorrelatedAngleEnergy::get_pdf( } } - double pdf_mu_lab; // assuming the data in lab frame! + double pdf; // assuming the data in lab frame! if (r1 - c_k < c_k1 - r1 || distribution_[l].interpolation == Interpolation::histogram) { - pdf_mu_lab = distribution_[l].angle[k]->get_pdf(mu); + pdf = distribution_[l].angle[k]->get_pdf(mu); } else { - pdf_mu_lab = distribution_[l].angle[k + 1]->get_pdf(mu); + pdf = distribution_[l].angle[k + 1]->get_pdf(mu); } - return pdf_mu_lab; + return pdf; } } // namespace openmc diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index 5bdf7d9b32a..26975fbd68d 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -70,14 +70,14 @@ double UncorrelatedAngleEnergy::get_pdf( { // Sample outgoing energy E_out = energy_->sample(E_in, seed); - double pdf_cm; + double pdf; if (!angle_.empty()) { - pdf_cm = angle_.get_pdf(E_in, mu); + pdf = angle_.get_pdf(E_in, mu); } else { // no angle distribution given => assume isotropic for all energies - pdf_cm = 0.5; + pdf = 0.5; } - return pdf_cm; + return pdf; } } // namespace openmc From d5ea1c0f5a57d5703ac991395875f4f853a52347 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 4 Sep 2025 17:13:07 +0300 Subject: [PATCH 68/88] WIP: trying new approach until reaction_product.cpp --- include/openmc/angle_energy.h | 9 ++------- include/openmc/distribution.h | 12 ++++++------ include/openmc/distribution_angle.h | 2 +- include/openmc/reaction_product.h | 4 +++- include/openmc/secondary_correlated.h | 5 ++++- include/openmc/secondary_kalbach.h | 3 ++- include/openmc/secondary_nbody.h | 3 ++- include/openmc/secondary_thermal.h | 3 ++- include/openmc/secondary_uncorrelated.h | 3 ++- include/openmc/thermal.h | 4 ++-- src/distribution.cpp | 8 ++++---- src/distribution_angle.cpp | 2 +- 12 files changed, 31 insertions(+), 27 deletions(-) diff --git a/include/openmc/angle_energy.h b/include/openmc/angle_energy.h index f7263a3334f..04e8d3d3f11 100644 --- a/include/openmc/angle_energy.h +++ b/include/openmc/angle_energy.h @@ -3,8 +3,6 @@ #include -#include "openmc/error.h" - namespace openmc { //============================================================================== @@ -18,11 +16,8 @@ class AngleEnergy { public: virtual void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const = 0; - virtual double get_pdf( - double E_in, double mu, double& E_out, uint64_t* seed) const - { - fatal_error("get_pdf not available for this AngleEnergy type"); - } + virtual double angular_pdf(double E_in, double mu) const = 0; + virtual double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const = 0; virtual ~AngleEnergy() = default; }; diff --git a/include/openmc/distribution.h b/include/openmc/distribution.h index 3967dd1fe40..92db01c98cc 100644 --- a/include/openmc/distribution.h +++ b/include/openmc/distribution.h @@ -24,9 +24,9 @@ class Distribution { public: virtual ~Distribution() = default; virtual double sample(uint64_t* seed) const = 0; - virtual double get_pdf(double x) const + virtual double pdf(double x) const { - fatal_error("get_pdf not available for this Distribution type"); + fatal_error("pdf not available for this Distribution type"); } //! Return integral of distribution @@ -116,7 +116,7 @@ class Uniform : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; - double get_pdf(double x) const; + double pdf(double x) const override; double a() const { return a_; } double b() const { return b_; } @@ -141,7 +141,7 @@ class PowerLaw : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; - double get_pdf(double x) const; + double pdf(double x) const override; double a() const { return std::pow(offset_, ninv_); } double b() const { return std::pow(offset_ + span_, ninv_); } @@ -211,7 +211,7 @@ class Normal : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; - double get_pdf(double x) const; + double pdf(double x) const override; double mean_value() const { return mean_value_; } double std_dev() const { return std_dev_; } @@ -235,7 +235,7 @@ class Tabular : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; - double get_pdf(double x) const; + double pdf(double x) const override; // properties vector& x() { return x_; } diff --git a/include/openmc/distribution_angle.h b/include/openmc/distribution_angle.h index 4beb39b09a5..e7fdda75079 100644 --- a/include/openmc/distribution_angle.h +++ b/include/openmc/distribution_angle.h @@ -25,7 +25,7 @@ class AngleDistribution { //! \param[inout] seed pseudorandom number seed pointer //! \return Cosine of the angle in the range [-1,1] double sample(double E, uint64_t* seed) const; - double get_pdf(double E, double mu) const; + double pdf(double E, double mu) const; //! Determine whether angle distribution is empty //! \return Whether distribution is empty diff --git a/include/openmc/reaction_product.h b/include/openmc/reaction_product.h index 93950ca1a49..9d75682defa 100644 --- a/include/openmc/reaction_product.h +++ b/include/openmc/reaction_product.h @@ -49,7 +49,9 @@ class ReactionProduct { //! \param[inout] seed Pseudorandom seed pointer void sample(double E_in, double& E_out, double& mu, uint64_t* seed) const; - double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; + double angular_pdf(double E_in, double mu) const; + + double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const; ParticleType particle_; //!< Particle type EmissionMode emission_mode_; //!< Emission mode diff --git a/include/openmc/secondary_correlated.h b/include/openmc/secondary_correlated.h index 3c36684720f..4d1462a8965 100644 --- a/include/openmc/secondary_correlated.h +++ b/include/openmc/secondary_correlated.h @@ -40,7 +40,10 @@ class CorrelatedAngleEnergy : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; + + double angular_pdf(double E_in, double mu) const override; + double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; + // energy property vector& energy() { return energy_; } diff --git a/include/openmc/secondary_kalbach.h b/include/openmc/secondary_kalbach.h index 517bcd2833a..7a41b1fe005 100644 --- a/include/openmc/secondary_kalbach.h +++ b/include/openmc/secondary_kalbach.h @@ -31,7 +31,8 @@ class KalbachMann : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; + double angular_pdf(double E_in, double mu) const override; + double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; private: //! Outgoing energy/angle at a single incoming energy diff --git a/include/openmc/secondary_nbody.h b/include/openmc/secondary_nbody.h index 49b20c64bbd..80511f66eae 100644 --- a/include/openmc/secondary_nbody.h +++ b/include/openmc/secondary_nbody.h @@ -27,7 +27,8 @@ class NBodyPhaseSpace : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; + double angular_pdf(double E_in, double mu) const override; + double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; private: int n_bodies_; //!< Number of particles distributed diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index 0eff575d730..1f2e62bae2a 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -32,7 +32,8 @@ class CoherentElasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; + double angular_pdf(double E_in, double mu) const override; + double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; private: const CoherentElasticXS& xs_; //!< Coherent elastic scattering cross section diff --git a/include/openmc/secondary_uncorrelated.h b/include/openmc/secondary_uncorrelated.h index b2b9a6c1388..d1a436700d5 100644 --- a/include/openmc/secondary_uncorrelated.h +++ b/include/openmc/secondary_uncorrelated.h @@ -31,7 +31,8 @@ class UncorrelatedAngleEnergy : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; + double angular_pdf(double E_in, double mu) const override; + double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; // Accessors AngleDistribution& angle() { return angle_; } diff --git a/include/openmc/thermal.h b/include/openmc/thermal.h index f46264dba00..e7d770eba53 100644 --- a/include/openmc/thermal.h +++ b/include/openmc/thermal.h @@ -52,8 +52,8 @@ class ThermalData { //! \param[inout] seed Pseudorandom seed pointer void sample(const NuclideMicroXS& micro_xs, double E_in, double* E_out, double* mu, uint64_t* seed); - double get_pdf(const NuclideMicroXS& micro_xs, double E_in, double mu, - double& E_out, uint64_t* seed); + double angular_pdf(const NuclideMicroXS& micro_xs, double E_in, double mu) const; + double conditional_sample_energy(const NuclideMicroXS& micro_xs, double E_in, double mu, uint64_t* seed) const; private: struct Reaction { diff --git a/src/distribution.cpp b/src/distribution.cpp index 2401527d6b2..bf936f28b3b 100644 --- a/src/distribution.cpp +++ b/src/distribution.cpp @@ -157,7 +157,7 @@ double Uniform::sample(uint64_t* seed) const return a_ + prn(seed) * (b_ - a_); } -double Uniform::get_pdf(double x) const +double Uniform::pdf(double x) const { if (x <= b_ && x >= a_) return 1 / (b_ - a_); @@ -191,7 +191,7 @@ double PowerLaw::sample(uint64_t* seed) const return std::pow(offset_ + prn(seed) * span_, ninv_); } -double PowerLaw::get_pdf(double x) const +double PowerLaw::pdf(double x) const { // Use accessors double a_val = this->a(); @@ -260,7 +260,7 @@ double Normal::sample(uint64_t* seed) const return normal_variate(mean_value_, std_dev_, seed); } -double Normal::get_pdf(double x) const +double Normal::pdf(double x) const { double exponent = -0.5 * std::pow((x - mean_value_) / std_dev_, 2); double coefficient = 1 / (std_dev_ * std::sqrt(2 * PI)); @@ -387,7 +387,7 @@ double Tabular::sample(uint64_t* seed) const } } -double Tabular::get_pdf(double x) const +double Tabular::pdf(double x) const { // get PDF value at x diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index bb28ee3fe7e..42d211f78b2 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -95,7 +95,7 @@ double AngleDistribution::sample(double E, uint64_t* seed) const return mu; } -double AngleDistribution::get_pdf(double E, double mu) const +double AngleDistribution::pdf(double E, double mu) const { // Determine number of incoming energies auto n = energy_.size(); From dc87a7fb52734d4ca0a1dc71f8dc58226f691ac0 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 4 Sep 2025 17:55:17 +0300 Subject: [PATCH 69/88] fix additional signatures --- include/openmc/secondary_thermal.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index 1f2e62bae2a..d21fa532c33 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -57,8 +57,9 @@ class IncoherentElasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; - + double angular_pdf(double E_in, double mu) const override; + double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; + private: double debye_waller_; }; @@ -83,7 +84,8 @@ class IncoherentElasticAEDiscrete : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; + double angular_pdf(double E_in, double mu) const override; + double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; private: const vector& energy_; //!< Energies at which cosines are tabulated @@ -110,7 +112,8 @@ class IncoherentInelasticAEDiscrete : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; + double angular_pdf(double E_in, double mu) const override; + double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; private: const vector& energy_; //!< Incident energies @@ -139,7 +142,8 @@ class IncoherentInelasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double get_pdf(double E_in, double mu, double& E_out, uint64_t* seed) const; + double angular_pdf(double E_in, double mu) const override; + double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; private: //! Secondary energy/angle distribution @@ -175,6 +179,8 @@ class MixedElasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + double angular_pdf(double E_in, double mu) const override; + double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; private: CoherentElasticAE coherent_dist_; //!< Coherent distribution From ad868232a005833cee1ae91a59c60b3d490ee584 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 4 Sep 2025 19:04:07 +0300 Subject: [PATCH 70/88] wip --- src/distribution_angle.cpp | 4 ++-- src/secondary_nbody.cpp | 20 +++++++++++------- src/secondary_thermal.cpp | 43 ++++++++++++++++++++++---------------- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index 42d211f78b2..05e02e5a9b1 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -117,9 +117,9 @@ double AngleDistribution::pdf(double E, double mu) const double pdf = 0.0; if (r > 0.0) - pdf += r * distribution_[i + 1]->get_pdf(mu); + pdf += r * distribution_[i + 1]->pdf(mu); if (r < 1.0) - pdf += (1.0 - r) * distribution_[i]->get_pdf(mu); + pdf += (1.0 - r) * distribution_[i]->pdf(mu); return pdf; } diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index 32a828efaa7..beb35234af8 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -29,6 +29,16 @@ void NBodyPhaseSpace::sample( // phase space distribution mu = uniform_distribution(-1., 1., seed); + E_out = conditional_sample_energy(E_in, mu, seed); +} + +double NBodyPhaseSpace::angular_pdf(double E_in, double mu) const +{ + return 0.5; +} + +double NBodyPhaseSpace::conditional_sample_energy(double E_in, double mu, uint64_t* seed) const +{ // Determine E_max parameter double Ap = mass_ratio_; double E_max = (Ap - 1.0) / Ap * (A_ / (A_ + 1.0) * E_in + Q_); @@ -56,7 +66,7 @@ void NBodyPhaseSpace::sample( 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); + std::log(r5) * std::pow(std::cos(PI / 2.0 * r6), 2); break; default: fatal_error("N-body phase space with >5 bodies."); @@ -64,13 +74,7 @@ void NBodyPhaseSpace::sample( // Now determine v and E_out double v = x / (x + y); - E_out = E_max * v; -} - -double NBodyPhaseSpace::get_pdf( - double E_in, double mu, double& E_out, uint64_t* seed) const -{ - return 0.5; + return E_max * v; } } // namespace openmc diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 526db8b1b84..36d7beb5437 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -103,13 +103,14 @@ void CoherentElasticAE::sample( mu = 1.0 - 2.0 * energies[k] / E_in; } -double CoherentElasticAE::get_pdf( - double E_in, double mu, double& E_out, uint64_t* seed) const +double CoherentElasticAE::conditional_sample_energy(double E_in, double mu, uint64_t* seed) const { - // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7-1) + return E_in; +} +double CoherentElasticAE::angular_pdf(double E_in, double mu) const +{ double pdf; - E_out = E_in; const auto& energies {xs_.bragg_edges()}; const auto& factors = xs_.factors(); @@ -155,18 +156,18 @@ void IncoherentElasticAE::sample( // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7.4) E_out = E_in; } -double IncoherentElasticAE::get_pdf( - double E_in, double mu, double& E_out, uint64_t* seed) const + +double IncoherentElasticAE::conditional_sample_energy(double E_in, double mu, uint64_t* seed) const +{ + return E_in; +} + +double IncoherentElasticAE::angular_pdf(double E_in, double mu) const { // Sample angle by inverting the distribution in ENDF-102, Eq. 7.4 double c = 2 * E_in * debye_waller_; - E_out = E_in; - double A = c / (1 - std::exp(-2.0 * c)); // normalization factor - double pdf = A * std::exp(-c * (1 - mu)); - return pdf; - - // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7.4) + return A * std::exp(-c * (1 - mu)); } //============================================================================== @@ -223,15 +224,17 @@ void IncoherentElasticAEDiscrete::sample( E_out = E_in; } -double IncoherentElasticAEDiscrete::get_pdf( - double E_in, double mu, double& E_out, uint64_t* seed) const +double IncoherentElasticAEDiscrete::conditional_sample_energy(double E_in, double mu, uint64_t* seed) const +{ + return E_in; +} + +double IncoherentElasticAEDiscrete::angular_pdf(double E_in, double mu) const { // Get index and interpolation factor for elastic grid int i; double f; get_energy_index(energy_, E_in, i, f); - // Energy doesn't change in elastic scattering - E_out = E_in; int n_mu = mu_out_.shape()[1]; std::vector mu_vector; @@ -317,8 +320,12 @@ void IncoherentInelasticAEDiscrete::sample( mu = (1 - f) * mu_ijk + f * mu_i1jk; } -double IncoherentInelasticAEDiscrete::get_pdf( - double E_in, double mu, double& E_out, uint64_t* seed) const +double IncoherentInelasticAEDiscrete::conditional_sample_energy(double E_in, double mu, uint64_t* seed) const +{ + return E_in; +} + +double IncoherentInelasticAEDiscrete::angular_pdf(double E_in, double mu) const { // Get index and interpolation factor for inelastic grid int i; From 4500491a1cd29351eaf09daba7ee73f041b3d010 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 4 Sep 2025 22:26:56 +0300 Subject: [PATCH 71/88] reverted most of the changes in the new approach --- include/openmc/angle_energy.h | 9 +++-- include/openmc/reaction_product.h | 5 ++- include/openmc/secondary_correlated.h | 6 ++-- include/openmc/secondary_kalbach.h | 4 +-- include/openmc/secondary_nbody.h | 4 +-- include/openmc/secondary_thermal.h | 26 +++++++------- include/openmc/secondary_uncorrelated.h | 4 +-- include/openmc/thermal.h | 4 +-- src/reaction_product.cpp | 5 ++- src/secondary_correlated.cpp | 6 ++-- src/secondary_kalbach.cpp | 2 +- src/secondary_nbody.cpp | 48 ++++++++++++++++++++----- src/secondary_thermal.cpp | 45 ++++++++++------------- src/secondary_uncorrelated.cpp | 4 +-- src/thermal.cpp | 6 ++-- 15 files changed, 102 insertions(+), 76 deletions(-) diff --git a/include/openmc/angle_energy.h b/include/openmc/angle_energy.h index 04e8d3d3f11..97977803b3b 100644 --- a/include/openmc/angle_energy.h +++ b/include/openmc/angle_energy.h @@ -3,6 +3,8 @@ #include +#include "openmc/error.h" + namespace openmc { //============================================================================== @@ -16,8 +18,11 @@ class AngleEnergy { public: virtual void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const = 0; - virtual double angular_pdf(double E_in, double mu) const = 0; - virtual double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const = 0; + virtual double sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const + { + fatal_error("get_pdf not available for this AngleEnergy type"); + } virtual ~AngleEnergy() = default; }; diff --git a/include/openmc/reaction_product.h b/include/openmc/reaction_product.h index 9d75682defa..32e82524f51 100644 --- a/include/openmc/reaction_product.h +++ b/include/openmc/reaction_product.h @@ -49,9 +49,8 @@ class ReactionProduct { //! \param[inout] seed Pseudorandom seed pointer void sample(double E_in, double& E_out, double& mu, uint64_t* seed) const; - double angular_pdf(double E_in, double mu) const; - - double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const; + double sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const; ParticleType particle_; //!< Particle type EmissionMode emission_mode_; //!< Emission mode diff --git a/include/openmc/secondary_correlated.h b/include/openmc/secondary_correlated.h index 4d1462a8965..a4435171860 100644 --- a/include/openmc/secondary_correlated.h +++ b/include/openmc/secondary_correlated.h @@ -40,10 +40,8 @@ class CorrelatedAngleEnergy : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - - double angular_pdf(double E_in, double mu) const override; - double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; - + double sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const override; // energy property vector& energy() { return energy_; } diff --git a/include/openmc/secondary_kalbach.h b/include/openmc/secondary_kalbach.h index 7a41b1fe005..1c3e80c36ac 100644 --- a/include/openmc/secondary_kalbach.h +++ b/include/openmc/secondary_kalbach.h @@ -31,8 +31,8 @@ class KalbachMann : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double angular_pdf(double E_in, double mu) const override; - double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; + double sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const override; private: //! Outgoing energy/angle at a single incoming energy diff --git a/include/openmc/secondary_nbody.h b/include/openmc/secondary_nbody.h index 80511f66eae..82789bfa585 100644 --- a/include/openmc/secondary_nbody.h +++ b/include/openmc/secondary_nbody.h @@ -27,8 +27,8 @@ class NBodyPhaseSpace : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double angular_pdf(double E_in, double mu) const override; - double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; + double sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const override; private: int n_bodies_; //!< Number of particles distributed diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index d21fa532c33..d5b7f0190b6 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -32,8 +32,8 @@ class CoherentElasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double angular_pdf(double E_in, double mu) const override; - double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; + double sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const override; private: const CoherentElasticXS& xs_; //!< Coherent elastic scattering cross section @@ -57,9 +57,9 @@ class IncoherentElasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double angular_pdf(double E_in, double mu) const override; - double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; - + double sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const override; + private: double debye_waller_; }; @@ -84,8 +84,8 @@ class IncoherentElasticAEDiscrete : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double angular_pdf(double E_in, double mu) const override; - double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; + double sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const override; private: const vector& energy_; //!< Energies at which cosines are tabulated @@ -112,8 +112,8 @@ class IncoherentInelasticAEDiscrete : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double angular_pdf(double E_in, double mu) const override; - double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; + double sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const override; private: const vector& energy_; //!< Incident energies @@ -142,8 +142,8 @@ class IncoherentInelasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double angular_pdf(double E_in, double mu) const override; - double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; + double sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const override; private: //! Secondary energy/angle distribution @@ -179,8 +179,8 @@ class MixedElasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double angular_pdf(double E_in, double mu) const override; - double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; + double sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const override; private: CoherentElasticAE coherent_dist_; //!< Coherent distribution diff --git a/include/openmc/secondary_uncorrelated.h b/include/openmc/secondary_uncorrelated.h index d1a436700d5..d971231b277 100644 --- a/include/openmc/secondary_uncorrelated.h +++ b/include/openmc/secondary_uncorrelated.h @@ -31,8 +31,8 @@ class UncorrelatedAngleEnergy : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; - double angular_pdf(double E_in, double mu) const override; - double conditional_sample_energy(double E_in, double mu, uint64_t* seed) const override; + double sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const override; // Accessors AngleDistribution& angle() { return angle_; } diff --git a/include/openmc/thermal.h b/include/openmc/thermal.h index e7d770eba53..c520d4903e9 100644 --- a/include/openmc/thermal.h +++ b/include/openmc/thermal.h @@ -52,8 +52,8 @@ class ThermalData { //! \param[inout] seed Pseudorandom seed pointer void sample(const NuclideMicroXS& micro_xs, double E_in, double* E_out, double* mu, uint64_t* seed); - double angular_pdf(const NuclideMicroXS& micro_xs, double E_in, double mu) const; - double conditional_sample_energy(const NuclideMicroXS& micro_xs, double E_in, double mu, uint64_t* seed) const; + double sample_energy_and_pdf(const NuclideMicroXS& micro_xs, double E_in, + double mu, uint64_t* seed) const; private: struct Reaction { diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index fe9fa0e6099..36b92e0b913 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -13,7 +13,6 @@ #include "openmc/secondary_correlated.h" #include "openmc/secondary_kalbach.h" #include "openmc/secondary_nbody.h" -#include "openmc/secondary_thermal.h" #include "openmc/secondary_uncorrelated.h" namespace openmc { @@ -130,7 +129,7 @@ void ReactionProduct::sample( } } -double ReactionProduct::get_pdf( +double ReactionProduct::sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { @@ -156,7 +155,7 @@ double ReactionProduct::get_pdf( // now extract pdf AngleEnergy* angleEnergyPtr = distribution_[distribution_index].get(); - return angleEnergyPtr->get_pdf(E_in, mu, E_out, seed); + return angleEnergyPtr->sample_energy_and_pdf(E_in, mu, E_out, seed); } } // namespace openmc diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index 7213c132046..d7993894c3e 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -263,7 +263,7 @@ void CorrelatedAngleEnergy::sample( } } -double CorrelatedAngleEnergy::get_pdf( +double CorrelatedAngleEnergy::sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { // Find energy bin and calculate interpolation factor -- if the energy is @@ -367,9 +367,9 @@ double CorrelatedAngleEnergy::get_pdf( double pdf; // assuming the data in lab frame! if (r1 - c_k < c_k1 - r1 || distribution_[l].interpolation == Interpolation::histogram) { - pdf = distribution_[l].angle[k]->get_pdf(mu); + pdf = distribution_[l].angle[k]->pdf(mu); } else { - pdf = distribution_[l].angle[k + 1]->get_pdf(mu); + pdf = distribution_[l].angle[k + 1]->pdf(mu); } return pdf; diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index c38305886dc..b8ebe29b8ca 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -237,7 +237,7 @@ void KalbachMann::sample( mu = std::log(r1 * std::exp(km_a) + (1.0 - r1) * std::exp(-km_a)) / km_a; } } -double KalbachMann::get_pdf( +double KalbachMann::sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { // Find energy bin and calculate interpolation factor -- if the energy is diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index beb35234af8..bc170f1d8b8 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -29,15 +29,46 @@ void NBodyPhaseSpace::sample( // phase space distribution mu = uniform_distribution(-1., 1., seed); - E_out = conditional_sample_energy(E_in, mu, seed); -} + // Determine E_max parameter + double Ap = mass_ratio_; + double E_max = (Ap - 1.0) / Ap * (A_ / (A_ + 1.0) * E_in + Q_); -double NBodyPhaseSpace::angular_pdf(double E_in, double mu) const -{ - return 0.5; + // x is essentially a Maxwellian distribution + 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, seed); + break; + case 4: + r1 = prn(seed); + r2 = prn(seed); + r3 = prn(seed); + y = -std::log(r1 * r2 * r3); + break; + case 5: + 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: + fatal_error("N-body phase space with >5 bodies."); + } + + // Now determine v and E_out + double v = x / (x + y); + E_out = E_max * v; } -double NBodyPhaseSpace::conditional_sample_energy(double E_in, double mu, uint64_t* seed) const +double NBodyPhaseSpace::sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { // Determine E_max parameter double Ap = mass_ratio_; @@ -66,7 +97,7 @@ double NBodyPhaseSpace::conditional_sample_energy(double E_in, double mu, uint64 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); + std::log(r5) * std::pow(std::cos(PI / 2.0 * r6), 2); break; default: fatal_error("N-body phase space with >5 bodies."); @@ -74,7 +105,8 @@ double NBodyPhaseSpace::conditional_sample_energy(double E_in, double mu, uint64 // Now determine v and E_out double v = x / (x + y); - return E_max * v; + E_out = E_max * v; + return 0.5; } } // namespace openmc diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 36d7beb5437..ac7457c0636 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -103,14 +103,13 @@ void CoherentElasticAE::sample( mu = 1.0 - 2.0 * energies[k] / E_in; } -double CoherentElasticAE::conditional_sample_energy(double E_in, double mu, uint64_t* seed) const +double CoherentElasticAE::sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { - return E_in; -} + // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7-1) -double CoherentElasticAE::angular_pdf(double E_in, double mu) const -{ double pdf; + E_out = E_in; const auto& energies {xs_.bragg_edges()}; const auto& factors = xs_.factors(); @@ -156,18 +155,18 @@ void IncoherentElasticAE::sample( // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7.4) E_out = E_in; } - -double IncoherentElasticAE::conditional_sample_energy(double E_in, double mu, uint64_t* seed) const -{ - return E_in; -} - -double IncoherentElasticAE::angular_pdf(double E_in, double mu) const +double IncoherentElasticAE::sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { // Sample angle by inverting the distribution in ENDF-102, Eq. 7.4 double c = 2 * E_in * debye_waller_; + E_out = E_in; + double A = c / (1 - std::exp(-2.0 * c)); // normalization factor - return A * std::exp(-c * (1 - mu)); + double pdf = A * std::exp(-c * (1 - mu)); + return pdf; + + // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7.4) } //============================================================================== @@ -224,17 +223,15 @@ void IncoherentElasticAEDiscrete::sample( E_out = E_in; } -double IncoherentElasticAEDiscrete::conditional_sample_energy(double E_in, double mu, uint64_t* seed) const -{ - return E_in; -} - -double IncoherentElasticAEDiscrete::angular_pdf(double E_in, double mu) const +double IncoherentElasticAEDiscrete::sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { // Get index and interpolation factor for elastic grid int i; double f; get_energy_index(energy_, E_in, i, f); + // Energy doesn't change in elastic scattering + E_out = E_in; int n_mu = mu_out_.shape()[1]; std::vector mu_vector; @@ -320,12 +317,8 @@ void IncoherentInelasticAEDiscrete::sample( mu = (1 - f) * mu_ijk + f * mu_i1jk; } -double IncoherentInelasticAEDiscrete::conditional_sample_energy(double E_in, double mu, uint64_t* seed) const -{ - return E_in; -} - -double IncoherentInelasticAEDiscrete::angular_pdf(double E_in, double mu) const +double IncoherentInelasticAEDiscrete::sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const { // Get index and interpolation factor for inelastic grid int i; @@ -505,7 +498,7 @@ void IncoherentInelasticAE::sample( mu += std::min(mu - mu_left, mu_right - mu) * (prn(seed) - 0.5); } -double IncoherentInelasticAE::get_pdf( +double IncoherentInelasticAE::sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { // Get index and interpolation factor for inelastic grid diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index 26975fbd68d..75ab605f4da 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -65,14 +65,14 @@ void UncorrelatedAngleEnergy::sample( E_out = energy_->sample(E_in, seed); } -double UncorrelatedAngleEnergy::get_pdf( +double UncorrelatedAngleEnergy::pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { // Sample outgoing energy E_out = energy_->sample(E_in, seed); double pdf; if (!angle_.empty()) { - pdf = angle_.get_pdf(E_in, mu); + pdf = angle_.pdf(E_in, mu); } else { // no angle distribution given => assume isotropic for all energies pdf = 0.5; diff --git a/src/thermal.cpp b/src/thermal.cpp index eda5884f331..46e0b3af3ea 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -313,8 +313,8 @@ void ThermalData::sample(const NuclideMicroXS& micro_xs, double E, *mu = std::copysign(1.0, *mu); } -double ThermalData::get_pdf(const NuclideMicroXS& micro_xs, double E_in, - double mu, double& E_out, uint64_t* seed) +double ThermalData::sample_energy_and_pdf(const NuclideMicroXS& micro_xs, + double E_in, double mu, double& E_out, uint64_t* seed) { AngleEnergy* angleEnergyPtr; @@ -324,7 +324,7 @@ double ThermalData::get_pdf(const NuclideMicroXS& micro_xs, double E_in, angleEnergyPtr = inelastic_.distribution.get(); } - return angleEnergyPtr->get_pdf(E_in, mu, E_out, seed); + return angleEnergyPtr->sample_energy_and_pdf(E_in, mu, E_out, seed); } void free_memory_thermal() From 7a1b5cceb405cd512c1fe305ea57c0b67a28e181 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 4 Sep 2025 22:41:33 +0300 Subject: [PATCH 72/88] bugfix --- src/secondary_uncorrelated.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index 75ab605f4da..cc2db0ae312 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -65,7 +65,7 @@ void UncorrelatedAngleEnergy::sample( E_out = energy_->sample(E_in, seed); } -double UncorrelatedAngleEnergy::pdf( +double UncorrelatedAngleEnergy::sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { // Sample outgoing energy From ae2a0fbdac86004e5e95fea2727e2ca184261a8e Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 4 Sep 2025 22:46:46 +0300 Subject: [PATCH 73/88] bugfix --- include/openmc/thermal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/thermal.h b/include/openmc/thermal.h index c520d4903e9..708d7264bc2 100644 --- a/include/openmc/thermal.h +++ b/include/openmc/thermal.h @@ -53,7 +53,7 @@ class ThermalData { void sample(const NuclideMicroXS& micro_xs, double E_in, double* E_out, double* mu, uint64_t* seed); double sample_energy_and_pdf(const NuclideMicroXS& micro_xs, double E_in, - double mu, uint64_t* seed) const; + double mu, double& E_out, uint64_t* seed) const; private: struct Reaction { From f3aa6dce687d2e110283c6d980085fe82e487109 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 4 Sep 2025 22:49:14 +0300 Subject: [PATCH 74/88] bugfix --- src/thermal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thermal.cpp b/src/thermal.cpp index 46e0b3af3ea..8791db5c745 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -314,7 +314,7 @@ void ThermalData::sample(const NuclideMicroXS& micro_xs, double E, } double ThermalData::sample_energy_and_pdf(const NuclideMicroXS& micro_xs, - double E_in, double mu, double& E_out, uint64_t* seed) + double E_in, double mu, double& E_out, uint64_t* seed) const { AngleEnergy* angleEnergyPtr; From 52feef7d854ec3bc5486de1c6e18c2056e98f7fa Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 4 Sep 2025 22:55:17 +0300 Subject: [PATCH 75/88] another fix for mixed elastic --- src/secondary_thermal.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index ac7457c0636..0c26e4d2d82 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -604,4 +604,18 @@ void MixedElasticAE::sample( } } +double MixedElasticAE::sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const +{ + // Evaluate coherent and incoherent elastic cross sections + double xs_coh = coherent_xs_(E_in); + double xs_incoh = incoherent_xs_(E_in); + + if (prn(seed) * (xs_coh + xs_incoh) < xs_coh) { + return coherent_dist_.sample_energy_and_pdf(E_in, mu, E_out, seed); + } else { + return incoherent_dist_->sample_energy_and_pdf(E_in, mu, E_out, seed); + } +} + } // namespace openmc From 12d537242a3b9244aee184d27c2684f41bf72d69 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 4 Sep 2025 22:57:46 +0300 Subject: [PATCH 76/88] guarantee every AngleEnergy has sample_energy_and_pdf --- include/openmc/angle_energy.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/include/openmc/angle_energy.h b/include/openmc/angle_energy.h index 97977803b3b..35e5b68b64b 100644 --- a/include/openmc/angle_energy.h +++ b/include/openmc/angle_energy.h @@ -3,8 +3,6 @@ #include -#include "openmc/error.h" - namespace openmc { //============================================================================== @@ -19,10 +17,7 @@ class AngleEnergy { virtual void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const = 0; virtual double sample_energy_and_pdf( - double E_in, double mu, double& E_out, uint64_t* seed) const - { - fatal_error("get_pdf not available for this AngleEnergy type"); - } + double E_in, double mu, double& E_out, uint64_t* seed) const = 0; virtual ~AngleEnergy() = default; }; From ff83fc4b9457a46c939471f039802f390542edab Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 4 Sep 2025 23:05:53 +0300 Subject: [PATCH 77/88] fix for decay photons --- include/openmc/chain.h | 2 ++ src/chain.cpp | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/include/openmc/chain.h b/include/openmc/chain.h index a3bc6f3a364..c2a45f75adc 100644 --- a/include/openmc/chain.h +++ b/include/openmc/chain.h @@ -70,6 +70,8 @@ class DecayPhotonAngleEnergy : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + double sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const override; private: const Distribution* photon_energy_; diff --git a/src/chain.cpp b/src/chain.cpp index e279d1f5916..17efdc69d3b 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -74,6 +74,13 @@ void DecayPhotonAngleEnergy::sample( mu = Uniform(-1., 1.).sample(seed); } +double DecayPhotonAngleEnergy::sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const +{ + E_out = photon_energy_->sample(seed); + return 0.5; +} + //============================================================================== // Global variables //============================================================================== From f859882cfecab316f7449eaf8b5aee215ddcf1eb Mon Sep 17 00:00:00 2001 From: GuySten Date: Sat, 6 Sep 2025 23:58:29 +0300 Subject: [PATCH 78/88] replace pdf->evaluate --- include/openmc/distribution.h | 12 ++++++------ include/openmc/distribution_angle.h | 2 +- src/distribution.cpp | 8 ++++---- src/distribution_angle.cpp | 6 +++--- src/secondary_correlated.cpp | 4 ++-- src/secondary_uncorrelated.cpp | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/include/openmc/distribution.h b/include/openmc/distribution.h index 92db01c98cc..9f082edcdf0 100644 --- a/include/openmc/distribution.h +++ b/include/openmc/distribution.h @@ -24,9 +24,9 @@ class Distribution { public: virtual ~Distribution() = default; virtual double sample(uint64_t* seed) const = 0; - virtual double pdf(double x) const + virtual double evaluate(double x) const { - fatal_error("pdf not available for this Distribution type"); + fatal_error("evaluate not available for this Distribution type"); } //! Return integral of distribution @@ -116,7 +116,7 @@ class Uniform : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; - double pdf(double x) const override; + double evaluate(double x) const override; double a() const { return a_; } double b() const { return b_; } @@ -141,7 +141,7 @@ class PowerLaw : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; - double pdf(double x) const override; + double evaluate(double x) const override; double a() const { return std::pow(offset_, ninv_); } double b() const { return std::pow(offset_ + span_, ninv_); } @@ -211,7 +211,7 @@ class Normal : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; - double pdf(double x) const override; + double evaluate(double x) const override; double mean_value() const { return mean_value_; } double std_dev() const { return std_dev_; } @@ -235,7 +235,7 @@ class Tabular : public Distribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled value double sample(uint64_t* seed) const override; - double pdf(double x) const override; + double evaluate(double x) const override; // properties vector& x() { return x_; } diff --git a/include/openmc/distribution_angle.h b/include/openmc/distribution_angle.h index e7fdda75079..a7e021085f0 100644 --- a/include/openmc/distribution_angle.h +++ b/include/openmc/distribution_angle.h @@ -25,7 +25,7 @@ class AngleDistribution { //! \param[inout] seed pseudorandom number seed pointer //! \return Cosine of the angle in the range [-1,1] double sample(double E, uint64_t* seed) const; - double pdf(double E, double mu) const; + double evaluate(double E, double mu) const; //! Determine whether angle distribution is empty //! \return Whether distribution is empty diff --git a/src/distribution.cpp b/src/distribution.cpp index bf936f28b3b..4e09a00b879 100644 --- a/src/distribution.cpp +++ b/src/distribution.cpp @@ -157,7 +157,7 @@ double Uniform::sample(uint64_t* seed) const return a_ + prn(seed) * (b_ - a_); } -double Uniform::pdf(double x) const +double Uniform::evaluate(double x) const { if (x <= b_ && x >= a_) return 1 / (b_ - a_); @@ -191,7 +191,7 @@ double PowerLaw::sample(uint64_t* seed) const return std::pow(offset_ + prn(seed) * span_, ninv_); } -double PowerLaw::pdf(double x) const +double PowerLaw::evaluate(double x) const { // Use accessors double a_val = this->a(); @@ -260,7 +260,7 @@ double Normal::sample(uint64_t* seed) const return normal_variate(mean_value_, std_dev_, seed); } -double Normal::pdf(double x) const +double Normal::evaluate(double x) const { double exponent = -0.5 * std::pow((x - mean_value_) / std_dev_, 2); double coefficient = 1 / (std_dev_ * std::sqrt(2 * PI)); @@ -387,7 +387,7 @@ double Tabular::sample(uint64_t* seed) const } } -double Tabular::pdf(double x) const +double Tabular::evaluate(double x) const { // get PDF value at x diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index 05e02e5a9b1..7540db0a673 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -95,7 +95,7 @@ double AngleDistribution::sample(double E, uint64_t* seed) const return mu; } -double AngleDistribution::pdf(double E, double mu) const +double AngleDistribution::evaluate(double E, double mu) const { // Determine number of incoming energies auto n = energy_.size(); @@ -117,9 +117,9 @@ double AngleDistribution::pdf(double E, double mu) const double pdf = 0.0; if (r > 0.0) - pdf += r * distribution_[i + 1]->pdf(mu); + pdf += r * distribution_[i + 1]->evaluate(mu); if (r < 1.0) - pdf += (1.0 - r) * distribution_[i]->pdf(mu); + pdf += (1.0 - r) * distribution_[i]->evaluate(mu); return pdf; } diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index d7993894c3e..1e648bce003 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -367,9 +367,9 @@ double CorrelatedAngleEnergy::sample_energy_and_pdf( double pdf; // assuming the data in lab frame! if (r1 - c_k < c_k1 - r1 || distribution_[l].interpolation == Interpolation::histogram) { - pdf = distribution_[l].angle[k]->pdf(mu); + pdf = distribution_[l].angle[k]->evaluate(mu); } else { - pdf = distribution_[l].angle[k + 1]->pdf(mu); + pdf = distribution_[l].angle[k + 1]->evaluate(mu); } return pdf; diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index cc2db0ae312..27adf5a172e 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -72,7 +72,7 @@ double UncorrelatedAngleEnergy::sample_energy_and_pdf( E_out = energy_->sample(E_in, seed); double pdf; if (!angle_.empty()) { - pdf = angle_.pdf(E_in, mu); + pdf = angle_.evaluate(E_in, mu); } else { // no angle distribution given => assume isotropic for all energies pdf = 0.5; From cc7c3a5ecf92b31188f01da4dce9ffcdb6aeb411 Mon Sep 17 00:00:00 2001 From: GuySten Date: Mon, 3 Nov 2025 18:12:05 +0200 Subject: [PATCH 79/88] added evaluate for SphericalUnitDistribution --- include/openmc/distribution_multi.h | 6 ++++++ src/distribution_multi.cpp | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/openmc/distribution_multi.h b/include/openmc/distribution_multi.h index 75126593f7d..965c832a330 100644 --- a/include/openmc/distribution_multi.h +++ b/include/openmc/distribution_multi.h @@ -28,6 +28,10 @@ class UnitSphereDistribution { //! \param seed Pseudorandom number seed pointer //! \return Direction sampled virtual Direction sample(uint64_t* seed) const = 0; + virtual double evaluate(Direction u) const + { + fatal_error("evaluate not available for this UnitSphereDistribution type"); + } Direction u_ref_ {0.0, 0.0, 1.0}; //!< reference direction }; @@ -45,6 +49,7 @@ class PolarAzimuthal : public UnitSphereDistribution { //! \param seed Pseudorandom number seed pointer //! \return Direction sampled Direction sample(uint64_t* seed) const override; + double evaluate(Direction u) const override; // Observing pointers Distribution* mu() const { return mu_.get(); } @@ -71,6 +76,7 @@ class Isotropic : public UnitSphereDistribution { //! \param seed Pseudorandom number seed pointer //! \return Sampled direction Direction sample(uint64_t* seed) const override; + double evaluate(Direction u) const override; }; //============================================================================== diff --git a/src/distribution_multi.cpp b/src/distribution_multi.cpp index cdb33adc2ad..edd2dfe9163 100644 --- a/src/distribution_multi.cpp +++ b/src/distribution_multi.cpp @@ -44,6 +44,7 @@ UnitSphereDistribution::UnitSphereDistribution(pugi::xml_node node) fatal_error("Angular distribution reference direction must have " "three parameters specified."); u_ref_ = Direction(u_ref.data()); + u_ref_ /= u_ref_.norm(); } } @@ -65,6 +66,7 @@ PolarAzimuthal::PolarAzimuthal(pugi::xml_node node) fatal_error("Angular distribution reference v direction must have " "three parameters specified."); v_ref_ = Direction(v_ref.data()); + v_ref_ /= v_ref_.norm(); } w_ref_ = u_ref_.cross(v_ref_); if (check_for_node(node, "mu")) { @@ -99,6 +101,13 @@ Direction PolarAzimuthal::sample(uint64_t* seed) const return mu * u_ref_ + f * std::cos(phi) * v_ref_ + f * std::sin(phi) * w_ref_; } +double PolarAzimuthal::evaluate(Direction u) const +{ + double mu = u.dot(u_ref_); + double phi = std::acos(u.dot(v_ref_) / std::sqrt(1 - mu * mu)); + return mu_->evaluate(mu) * phi_->evaluate(phi); +} + //============================================================================== // Isotropic implementation //============================================================================== @@ -116,6 +125,11 @@ Direction Isotropic::sample(uint64_t* seed) const return isotropic_direction(seed); } +double Isotropic::evaluate(Direction u) const +{ + return 1.0 / (4.0 * PI); +} + //============================================================================== // Monodirectional implementation //============================================================================== From 481901eb4d4ec0cedd60ae38fb5ec709476c4ce0 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 4 Dec 2025 00:06:42 +0200 Subject: [PATCH 80/88] simplify PR significantly --- include/openmc/math_functions.h | 11 ++ include/openmc/reaction_product.h | 2 + include/openmc/secondary_correlated.h | 1 + include/openmc/secondary_kalbach.h | 2 + include/openmc/secondary_nbody.h | 1 + include/openmc/secondary_thermal.h | 4 + include/openmc/thermal.h | 4 +- src/distribution_angle.cpp | 35 +---- src/math_functions.cpp | 15 +++ src/reaction_product.cpp | 38 ++---- src/secondary_correlated.cpp | 139 ++------------------ src/secondary_kalbach.cpp | 145 +++------------------ src/secondary_nbody.cpp | 56 ++------ src/secondary_thermal.cpp | 179 +++++++------------------- src/secondary_uncorrelated.cpp | 6 +- src/thermal.cpp | 24 ++-- 16 files changed, 158 insertions(+), 504 deletions(-) diff --git a/include/openmc/math_functions.h b/include/openmc/math_functions.h index c30ef75585a..0d960c33db7 100644 --- a/include/openmc/math_functions.h +++ b/include/openmc/math_functions.h @@ -9,6 +9,7 @@ #include #include "openmc/position.h" +#include "openmc/search.h" namespace openmc { @@ -200,5 +201,15 @@ std::complex faddeeva(std::complex z); //! \return Derivative of Faddeeva function evaluated at z std::complex w_derivative(std::complex z, int order); +//! Helper function to get index and interpolation function on an incident +//! energy grid +//! +//! \param energies energy grid +//! \param E incident energy +//! \param i grid index +//! \param f interpolation factor +void get_energy_index( + const vector& energies, double E, int& i, double& f); + } // namespace openmc #endif // OPENMC_MATH_FUNCTIONS_H diff --git a/include/openmc/reaction_product.h b/include/openmc/reaction_product.h index 32e82524f51..98e5910db9b 100644 --- a/include/openmc/reaction_product.h +++ b/include/openmc/reaction_product.h @@ -49,6 +49,8 @@ class ReactionProduct { //! \param[inout] seed Pseudorandom seed pointer void sample(double E_in, double& E_out, double& mu, uint64_t* seed) const; + AngleEnergy& sample_dist(double E_in, uint64_t* seed) const; + double sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const; diff --git a/include/openmc/secondary_correlated.h b/include/openmc/secondary_correlated.h index a4435171860..e7c933bb614 100644 --- a/include/openmc/secondary_correlated.h +++ b/include/openmc/secondary_correlated.h @@ -40,6 +40,7 @@ class CorrelatedAngleEnergy : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + Distribution& sample_dist(double E_in, double& E_out, uint64_t* seed) const; double sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const override; diff --git a/include/openmc/secondary_kalbach.h b/include/openmc/secondary_kalbach.h index 1c3e80c36ac..0168ee13ced 100644 --- a/include/openmc/secondary_kalbach.h +++ b/include/openmc/secondary_kalbach.h @@ -31,6 +31,8 @@ class KalbachMann : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + void sample_params(double E_in, double& E_out, double& km_a, double& km_r, + uint64_t* seed) const; double sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const override; diff --git a/include/openmc/secondary_nbody.h b/include/openmc/secondary_nbody.h index 82789bfa585..dad896cd740 100644 --- a/include/openmc/secondary_nbody.h +++ b/include/openmc/secondary_nbody.h @@ -27,6 +27,7 @@ class NBodyPhaseSpace : public AngleEnergy { //! \param[inout] seed Pseudorandom seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + double sample_energy(double E_in, uint64_t* seed) const; double sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const override; diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index d5b7f0190b6..ff6d7c09d65 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -112,6 +112,7 @@ class IncoherentInelasticAEDiscrete : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + void sample_params(double E_in, double& E_out, int& j, uint64_t* seed) const; double sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const override; @@ -142,6 +143,8 @@ class IncoherentInelasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + void sample_params(double E_in, double& E_out, double& f, int& l, int& j, + uint64_t* seed) const; double sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const override; @@ -179,6 +182,7 @@ class MixedElasticAE : public AngleEnergy { //! \param[inout] seed Pseudorandom number seed pointer void sample( double E_in, double& E_out, double& mu, uint64_t* seed) const override; + const AngleEnergy& sample_dist(double E_in, uint64_t* seed) const; double sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const override; diff --git a/include/openmc/thermal.h b/include/openmc/thermal.h index 708d7264bc2..73c343ad6a7 100644 --- a/include/openmc/thermal.h +++ b/include/openmc/thermal.h @@ -51,7 +51,9 @@ class ThermalData { //! \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, uint64_t* seed); + double* mu, uint64_t* seed) const; + AngleEnergy& sample_dist( + const NuclideMicroXS& micro_xs, double E, uint64_t* seed) const; double sample_energy_and_pdf(const NuclideMicroXS& micro_xs, double E_in, double mu, double& E_out, uint64_t* seed) const; diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index 7540db0a673..f2773006176 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -7,6 +7,7 @@ #include "openmc/endf.h" #include "openmc/hdf5_interface.h" +#include "openmc/math_functions.h" #include "openmc/random_lcg.h" #include "openmc/search.h" #include "openmc/vector.h" // for vector @@ -64,23 +65,10 @@ AngleDistribution::AngleDistribution(hid_t group) double AngleDistribution::sample(double E, uint64_t* seed) const { - // Determine number of incoming energies - auto n = energy_.size(); - - // Find energy bin and calculate interpolation factor -- if the energy is - // outside the range of the tabulated energies, choose the first or last bins + // Find energy bin and calculate interpolation factor int i; double r; - if (E < energy_[0]) { - i = 0; - r = 0.0; - } else if (E > energy_[n - 1]) { - i = n - 2; - r = 1.0; - } else { - i = lower_bound_index(energy_.begin(), energy_.end(), E); - r = (E - energy_[i]) / (energy_[i + 1] - energy_[i]); - } + get_energy_index(energy_, E, i, r); // Sample between the ith and (i+1)th bin if (r > prn(seed)) @@ -97,23 +85,10 @@ double AngleDistribution::sample(double E, uint64_t* seed) const double AngleDistribution::evaluate(double E, double mu) const { - // Determine number of incoming energies - auto n = energy_.size(); - - // Find energy bin and calculate interpolation factor -- if the energy is - // outside the range of the tabulated energies, choose the first or last bins + // Find energy bin and calculate interpolation factor int i; double r; - if (E < energy_[0]) { - i = 0; - r = 0.0; - } else if (E > energy_[n - 1]) { - i = n - 2; - r = 1.0; - } else { - i = lower_bound_index(energy_.begin(), energy_.end(), E); - r = (E - energy_[i]) / (energy_[i + 1] - energy_[i]); - } + get_energy_index(energy_, E, i, r); double pdf = 0.0; if (r > 0.0) diff --git a/src/math_functions.cpp b/src/math_functions.cpp index 5469b56c87c..9473f928b2c 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -919,4 +919,19 @@ std::complex w_derivative(std::complex z, int order) } } +// Helper function to get index and interpolation function on an incident energy +// grid +void get_energy_index( + const vector& energies, double E, int& i, double& f) +{ + // Get index and interpolation factor for linear-linear energy grid + i = 0; + f = 0.0; + if (E >= energies.front()) { + i = lower_bound_index(energies.begin(), energies.end(), E); + if (i + 1 < energies.size()) + f = (E - energies[i]) / (energies[i + 1] - energies[i]); + } +} + } // namespace openmc diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index 36b92e0b913..398f72e7967 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -106,8 +106,7 @@ ReactionProduct::ReactionProduct(const ChainNuclide::Product& product) make_unique(chain_nuc->photon_energy())); } -void ReactionProduct::sample( - double E_in, double& E_out, double& mu, uint64_t* seed) const +AngleEnergy& ReactionProduct::sample_dist(double E_in, uint64_t* seed) const { auto n = applicability_.size(); if (n > 1) { @@ -119,43 +118,26 @@ void ReactionProduct::sample( // If i-th distribution is sampled, sample energy from the distribution if (c <= prob) { - distribution_[i]->sample(E_in, E_out, mu, seed); + return *distribution_[i]; break; } } } else { // If only one distribution is present, go ahead and sample it - distribution_[0]->sample(E_in, E_out, mu, seed); + return *distribution_[0]; } } +void ReactionProduct::sample( + double E_in, double& E_out, double& mu, uint64_t* seed) const +{ + sample_dist(E_in, seed).sample(E_in, E_out, mu, seed); +} + double ReactionProduct::sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { - - int distribution_index; - auto n = applicability_.size(); - if (n > 1) { - double prob = 0.0; - 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, seed); - distribution_index = i; - break; - } - } - } else { - distribution_index = 0; - } - // now extract pdf - - AngleEnergy* angleEnergyPtr = distribution_[distribution_index].get(); - return angleEnergyPtr->sample_energy_and_pdf(E_in, mu, E_out, seed); + return sample_dist(E_in, seed).sample_energy_and_pdf(E_in, mu, E_out, seed); } } // namespace openmc diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index 1e648bce003..0416047cf15 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -10,6 +10,7 @@ #include "openmc/endf.h" #include "openmc/hdf5_interface.h" +#include "openmc/math_functions.h" #include "openmc/random_lcg.h" #include "openmc/search.h" @@ -152,25 +153,13 @@ CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group) distribution_.push_back(std::move(d)); } // incoming energies } - -void CorrelatedAngleEnergy::sample( - double E_in, double& E_out, double& mu, uint64_t* seed) const +Distribution& CorrelatedAngleEnergy::sample_dist( + double E_in, double& E_out, uint64_t* seed) const { - // Find energy bin and calculate interpolation factor -- if the energy is - // outside the range of the tabulated energies, choose the first or last bins - auto n_energy_in = energy_.size(); + // Find energy bin and calculate interpolation factor int i; double r; - if (E_in < energy_[0]) { - i = 0; - r = 0.0; - } else if (E_in > energy_[n_energy_in - 1]) { - i = n_energy_in - 2; - r = 1.0; - } else { - i = lower_bound_index(energy_.begin(), energy_.end(), E_in); - r = (E_in - energy_[i]) / (energy_[i + 1] - energy_[i]); - } + get_energy_index(energy_, E_in, i, r); // Sample between the ith and [i+1]th bin int l = r > prn(seed) ? i + 1 : i; @@ -257,122 +246,22 @@ void CorrelatedAngleEnergy::sample( // Find correlated angular distribution for closest outgoing energy bin if (r1 - c_k < c_k1 - r1 || distribution_[l].interpolation == Interpolation::histogram) { - mu = distribution_[l].angle[k]->sample(seed); + return *distribution_[l].angle[k]; } else { - mu = distribution_[l].angle[k + 1]->sample(seed); + return *distribution_[l].angle[k + 1]; } } +void CorrelatedAngleEnergy::sample( + double E_in, double& E_out, double& mu, uint64_t* seed) const +{ + mu = sample_dist(E_in, E_out, seed).sample(seed); +} + double CorrelatedAngleEnergy::sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { - // Find energy bin and calculate interpolation factor -- if the energy is - // outside the range of the tabulated energies, choose the first or last bins - auto n_energy_in = energy_.size(); - int i; - double r; - if (E_in < energy_[0]) { - i = 0; - r = 0.0; - } else if (E_in > energy_[n_energy_in - 1]) { - i = n_energy_in - 2; - r = 1.0; - } else { - i = lower_bound_index(energy_.begin(), energy_.end(), E_in); - r = (E_in - energy_[i]) / (energy_[i + 1] - energy_[i]); - } - - // Sample between the ith and [i+1]th bin - int l = r > prn(seed) ? i + 1 : i; - - // Interpolation for energy E1 and EK - int n_energy_out = distribution_[i].e_out.size(); - int n_discrete = distribution_[i].n_discrete; - double E_i_1 = distribution_[i].e_out[n_discrete]; - double E_i_K = distribution_[i].e_out[n_energy_out - 1]; - - n_energy_out = distribution_[i + 1].e_out.size(); - n_discrete = distribution_[i + 1].n_discrete; - double E_i1_1 = distribution_[i + 1].e_out[n_discrete]; - double E_i1_K = distribution_[i + 1].e_out[n_energy_out - 1]; - - double E_1 = E_i_1 + r * (E_i1_1 - E_i_1); - double E_K = E_i_K + r * (E_i1_K - E_i_K); - - // Determine outgoing energy bin - n_energy_out = distribution_[l].e_out.size(); - n_discrete = distribution_[l].n_discrete; - double r1 = prn(seed); - double c_k = distribution_[l].c[0]; - int k = 0; - int end = n_energy_out - 2; - - // Discrete portion - for (int j = 0; j < n_discrete; ++j) { - k = j; - c_k = distribution_[l].c[k]; - if (r1 < c_k) { - end = j; - break; - } - } - - // Continuous portion - double c_k1; - for (int j = n_discrete; j < end; ++j) { - k = j; - c_k1 = distribution_[l].c[k + 1]; - if (r1 < c_k1) - break; - k = j + 1; - c_k = c_k1; - } - - double E_l_k = distribution_[l].e_out[k]; - double p_l_k = distribution_[l].p[k]; - if (distribution_[l].interpolation == Interpolation::histogram) { - // Histogram interpolation - if (p_l_k > 0.0 && k >= n_discrete) { - E_out = E_l_k + (r1 - c_k) / p_l_k; - } else { - E_out = E_l_k; - } - - } else if (distribution_[l].interpolation == Interpolation::lin_lin) { - // Linear-linear interpolation - double E_l_k1 = distribution_[l].e_out[k + 1]; - double p_l_k1 = distribution_[l].p[k + 1]; - - double frac = (p_l_k1 - p_l_k) / (E_l_k1 - E_l_k); - if (frac == 0.0) { - E_out = E_l_k + (r1 - c_k) / p_l_k; - } else { - E_out = - E_l_k + - (std::sqrt(std::max(0.0, p_l_k * p_l_k + 2.0 * frac * (r1 - c_k))) - - p_l_k) / - frac; - } - } - - // Now interpolate between incident energy bins i and i + 1 - if (k >= n_discrete) { - if (l == i) { - E_out = E_1 + (E_out - E_i_1) * (E_K - E_1) / (E_i_K - E_i_1); - } else { - E_out = E_1 + (E_out - E_i1_1) * (E_K - E_1) / (E_i1_K - E_i1_1); - } - } - - double pdf; // assuming the data in lab frame! - if (r1 - c_k < c_k1 - r1 || - distribution_[l].interpolation == Interpolation::histogram) { - pdf = distribution_[l].angle[k]->evaluate(mu); - } else { - pdf = distribution_[l].angle[k + 1]->evaluate(mu); - } - - return pdf; + return sample_dist(E_in, E_out, seed).evaluate(mu); } } // namespace openmc diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index b8ebe29b8ca..43a40c0cd4e 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -9,6 +9,7 @@ #include "xtensor/xview.hpp" #include "openmc/hdf5_interface.h" +#include "openmc/math_functions.h" #include "openmc/random_dist.h" #include "openmc/random_lcg.h" #include "openmc/search.h" @@ -114,24 +115,13 @@ KalbachMann::KalbachMann(hid_t group) } // incoming energies } -void KalbachMann::sample( - double E_in, double& E_out, double& mu, uint64_t* seed) const +void KalbachMann::sample_params( + double E_in, double& E_out, double& km_a, double& km_r, uint64_t* seed) const { - // Find energy bin and calculate interpolation factor -- if the energy is - // outside the range of the tabulated energies, choose the first or last bins - auto n_energy_in = energy_.size(); + // Find energy bin and calculate interpolation factor int i; double r; - if (E_in < energy_[0]) { - i = 0; - r = 0.0; - } else if (E_in > energy_[n_energy_in - 1]) { - i = n_energy_in - 2; - r = 1.0; - } else { - i = lower_bound_index(energy_.begin(), energy_.end(), E_in); - r = (E_in - energy_[i]) / (energy_[i + 1] - energy_[i]); - } + get_energy_index(energy_, E_in, i, r); // Sample between the ith and [i+1]th bin int l = r > prn(seed) ? i + 1 : i; @@ -181,7 +171,6 @@ void KalbachMann::sample( double E_l_k = distribution_[l].e_out[k]; double p_l_k = distribution_[l].p[k]; - double km_r, km_a; if (distribution_[l].interpolation == Interpolation::histogram) { // Histogram interpolation if (p_l_k > 0.0 && k >= n_discrete) { @@ -227,6 +216,13 @@ void KalbachMann::sample( E_out = E_1 + (E_out - E_i1_1) * (E_K - E_1) / (E_i1_K - E_i1_1); } } +} + +void KalbachMann::sample( + double E_in, double& E_out, double& mu, uint64_t* seed) const +{ + double km_r, km_a; + sample_params(E_in, E_out, km_a, km_r, seed); // Sampled correlated angle from Kalbach-Mann parameters if (prn(seed) > km_r) { @@ -240,123 +236,12 @@ void KalbachMann::sample( double KalbachMann::sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { - // Find energy bin and calculate interpolation factor -- if the energy is - // outside the range of the tabulated energies, choose the first or last bins - - // double E_out; - auto n_energy_in = energy_.size(); - int i; - double r; - if (E_in < energy_[0]) { - i = 0; - r = 0.0; - } else if (E_in > energy_[n_energy_in - 1]) { - i = n_energy_in - 2; - r = 1.0; - } else { - i = lower_bound_index(energy_.begin(), energy_.end(), E_in); - r = (E_in - energy_[i]) / (energy_[i + 1] - energy_[i]); - } - - // Sample between the ith and [i+1]th bin - int l = r > prn(seed) ? i + 1 : i; - - // Interpolation for energy E1 and EK - int n_energy_out = distribution_[i].e_out.size(); - int n_discrete = distribution_[i].n_discrete; - double E_i_1 = distribution_[i].e_out[n_discrete]; - double E_i_K = distribution_[i].e_out[n_energy_out - 1]; - - n_energy_out = distribution_[i + 1].e_out.size(); - n_discrete = distribution_[i + 1].n_discrete; - double E_i1_1 = distribution_[i + 1].e_out[n_discrete]; - double E_i1_K = distribution_[i + 1].e_out[n_energy_out - 1]; - - double E_1 = E_i_1 + r * (E_i1_1 - E_i_1); - double E_K = E_i_K + r * (E_i1_K - E_i_K); - - // Determine outgoing energy bin - n_energy_out = distribution_[l].e_out.size(); - n_discrete = distribution_[l].n_discrete; - double r1 = prn(seed); - double c_k = distribution_[l].c[0]; - int k = 0; - int end = n_energy_out - 2; - - // Discrete portion - for (int j = 0; j < n_discrete; ++j) { - k = j; - c_k = distribution_[l].c[k]; - if (r1 < c_k) { - end = j; - break; - } - } - - // Continuous portion - double c_k1; - for (int j = n_discrete; j < end; ++j) { - k = j; - c_k1 = distribution_[l].c[k + 1]; - if (r1 < c_k1) - break; - k = j + 1; - c_k = c_k1; - } - - double E_l_k = distribution_[l].e_out[k]; - double p_l_k = distribution_[l].p[k]; double km_r, km_a; - if (distribution_[l].interpolation == Interpolation::histogram) { - // Histogram interpolation - if (p_l_k > 0.0 && k >= n_discrete) { - E_out = E_l_k + (r1 - c_k) / p_l_k; - } else { - E_out = E_l_k; - } - // Determine Kalbach-Mann parameters - km_r = distribution_[l].r[k]; - km_a = distribution_[l].a[k]; - - } else { - // Linear-linear interpolation - double E_l_k1 = distribution_[l].e_out[k + 1]; - double p_l_k1 = distribution_[l].p[k + 1]; - - double frac = (p_l_k1 - p_l_k) / (E_l_k1 - E_l_k); - if (frac == 0.0) { - E_out = E_l_k + (r1 - c_k) / p_l_k; - } else { - E_out = - E_l_k + - (std::sqrt(std::max(0.0, p_l_k * p_l_k + 2.0 * frac * (r1 - c_k))) - - p_l_k) / - frac; - } - // Linear-linear interpolation - // Determine Kalbach-Mann parameters - km_r = distribution_[l].r[k] + - (E_out - E_l_k) / (E_l_k1 - E_l_k) * - (distribution_[l].r[k + 1] - distribution_[l].r[k]); - km_a = distribution_[l].a[k] + - (E_out - E_l_k) / (E_l_k1 - E_l_k) * - (distribution_[l].a[k + 1] - distribution_[l].a[k]); - } - - // Now interpolate between incident energy bins i and i + 1 - if (k >= n_discrete) { - if (l == i) { - E_out = E_1 + (E_out - E_i_1) * (E_K - E_1) / (E_i_K - E_i_1); - } else { - E_out = E_1 + (E_out - E_i1_1) * (E_K - E_1) / (E_i1_K - E_i1_1); - } - } + sample_params(E_in, E_out, km_a, km_r, seed); // https://docs.openmc.org/en/latest/methods/neutron_physics.html#equation-KM-pdf-angle - double pdf = km_a / (2 * std::sinh(km_a)) * - (std::cosh(km_a * mu) + km_r * std::sinh(km_a * mu)); - - return pdf; + return km_a / (2 * std::sinh(km_a)) * + (std::cosh(km_a * mu) + km_r * std::sinh(km_a * mu)); } } // namespace openmc diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index bc170f1d8b8..72f0b0b92d0 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -22,13 +22,8 @@ NBodyPhaseSpace::NBodyPhaseSpace(hid_t group) read_attribute(group, "q_value", Q_); } -void NBodyPhaseSpace::sample( - double E_in, double& E_out, double& mu, uint64_t* seed) const +double NBodyPhaseSpace::sample_energy(double E_in, uint64_t* seed) const { - // By definition, the distribution of the angle is isotropic for an N-body - // phase space distribution - mu = uniform_distribution(-1., 1., seed); - // Determine E_max parameter double Ap = mass_ratio_; double E_max = (Ap - 1.0) / Ap * (A_ / (A_ + 1.0) * E_in + Q_); @@ -64,48 +59,23 @@ void NBodyPhaseSpace::sample( // Now determine v and E_out double v = x / (x + y); - E_out = E_max * v; + return E_max * v; } -double NBodyPhaseSpace::sample_energy_and_pdf( - double E_in, double mu, double& E_out, uint64_t* seed) const +void NBodyPhaseSpace::sample( + double E_in, double& E_out, double& mu, uint64_t* seed) const { - // 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, seed); + // By definition, the distribution of the angle is isotropic for an N-body + // phase space distribution + mu = uniform_distribution(-1., 1., seed); - double y; - double r1, r2, r3, r4, r5, r6; - switch (n_bodies_) { - case 3: - y = maxwell_spectrum(1.0, seed); - break; - case 4: - r1 = prn(seed); - r2 = prn(seed); - r3 = prn(seed); - y = -std::log(r1 * r2 * r3); - break; - case 5: - 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: - fatal_error("N-body phase space with >5 bodies."); - } + E_out = sample_energy(E_in, seed); +} - // Now determine v and E_out - double v = x / (x + y); - E_out = E_max * v; +double NBodyPhaseSpace::sample_energy_and_pdf( + double E_in, double mu, double& E_out, uint64_t* seed) const +{ + E_out = sample_energy(E_in, seed); return 0.5; } diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 0c26e4d2d82..760dcd60ddc 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -1,6 +1,7 @@ #include "openmc/secondary_thermal.h" #include "openmc/hdf5_interface.h" +#include "openmc/math_functions.h" #include "openmc/random_lcg.h" #include "openmc/search.h" #include "openmc/vector.h" @@ -12,20 +13,6 @@ namespace openmc { -// Helper function to get index on incident energy grid -void get_energy_index( - const vector& energies, double E, int& i, double& f) -{ - // Get index and interpolation factor for elastic grid - i = 0; - f = 0.0; - if (E >= energies.front()) { - i = lower_bound_index(energies.begin(), energies.end(), E); - if (i + 1 < energies.size()) - f = (E - energies[i]) / (energies[i + 1] - energies[i]); - } -} - double get_pdf_discrete( const vector& mu, const vector& w, double mu_0) { @@ -148,25 +135,21 @@ IncoherentElasticAE::IncoherentElasticAE(hid_t group) void IncoherentElasticAE::sample( double E_in, double& E_out, double& mu, uint64_t* seed) const { + E_out = E_in; + // 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(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; } double IncoherentElasticAE::sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { - // Sample angle by inverting the distribution in ENDF-102, Eq. 7.4 - double c = 2 * E_in * debye_waller_; E_out = E_in; + // Sample angle by inverting the distribution in ENDF-102, Eq. 7.4 + double c = 2 * E_in * debye_waller_; double A = c / (1 - std::exp(-2.0 * c)); // normalization factor - double pdf = A * std::exp(-c * (1 - mu)); - return pdf; - - // Energy doesn't change in elastic scattering (ENDF-102, Eq. 7.4) + return A * std::exp(-c * (1 - mu)); } //============================================================================== @@ -257,8 +240,8 @@ IncoherentInelasticAEDiscrete::IncoherentInelasticAEDiscrete( read_dataset(group, "skewed", skewed_); } -void IncoherentInelasticAEDiscrete::sample( - double E_in, double& E_out, double& mu, uint64_t* seed) const +void IncoherentInelasticAEDiscrete::sample_params( + double E_in, double& E_out, int& j, uint64_t* seed) const { // Get index and interpolation factor for inelastic grid int i; @@ -272,7 +255,6 @@ void IncoherentInelasticAEDiscrete::sample( // for the second and second to last bins, relative to a normal bin // probability of 1). Otherwise, each bin is equally probable. - int j; int n = energy_out_.shape()[1]; if (!skewed_) { // All bins equally likely @@ -304,6 +286,18 @@ void IncoherentInelasticAEDiscrete::sample( // Outgoing energy E_out = (1 - f) * E_ij + f * E_i1j; +} + +void IncoherentInelasticAEDiscrete::sample( + double E_in, double& E_out, double& mu, uint64_t* seed) const +{ + // Get index and interpolation factor for inelastic grid + int i; + double f; + get_energy_index(energy_, E_in, i, f); + + int j; + sample_params(E_in, E_out, j, seed); // Sample outgoing cosine bin int m = mu_out_.shape()[2]; @@ -325,37 +319,8 @@ double IncoherentInelasticAEDiscrete::sample_energy_and_pdf( double f; get_energy_index(energy_, E_in, i, f); int j; - int n = energy_out_.shape()[1]; - if (!skewed_) { - // All bins equally likely - j = prn(seed) * n; - } else { - // Distribution skewed away from edge points - double r = prn(seed) * (n - 3); - if (r > 1.0) { - // equally likely N-4 middle bins - j = r + 1; - } else if (r > 0.6) { - // second to last bin has relative probability of 0.4 - j = n - 2; - } else if (r > 0.5) { - // last bin has relative probability of 0.1 - j = n - 1; - } else if (r > 0.1) { - // second bin has relative probability of 0.4 - j = 1; - } else { - // first bin has relative probability of 0.1 - j = 0; - } - } - - // Determine outgoing energy corresponding to E_in[i] and E_in[i+1] - double E_ij = energy_out_(i, j); - double E_i1j = energy_out_(i + 1, j); + sample_params(E_in, E_out, j, seed); - // Outgoing energy - E_out = (1 - f) * E_ij + f * E_i1j; int m = mu_out_.shape()[2]; std::vector mu_vector; @@ -411,24 +376,23 @@ IncoherentInelasticAE::IncoherentInelasticAE(hid_t group) } } -void IncoherentInelasticAE::sample( - double E_in, double& E_out, double& mu, uint64_t* seed) const +void IncoherentInelasticAE::sample_params( + double E_in, double& E_out, double& f, int& l, int& j, uint64_t* seed) const { // Get index and interpolation factor for inelastic grid int i; - double f; - get_energy_index(energy_, E_in, i, f); + double f0; + get_energy_index(energy_, E_in, i, f0); // Pick closer energy based on interpolation factor - int l = f > 0.5 ? i + 1 : i; + l = f0 > 0.5 ? i + 1 : i; // Determine outgoing energy bin // (First reset n_energy_out to the right value) - auto n = distribution_[l].n_e_out; + int n = distribution_[l].n_e_out; double r1 = prn(seed); double c_j = distribution_[l].e_out_cdf[0]; double c_j1; - std::size_t j; for (j = 0; j < n - 1; ++j) { c_j1 = distribution_[l].e_out_cdf[j + 1]; if (r1 < c_j1) @@ -466,6 +430,15 @@ void IncoherentInelasticAE::sample( E_out += E_in - E_l; } + f = (r1 - c_j) / (c_j1 - c_j); +} +void IncoherentInelasticAE::sample( + double E_in, double& E_out, double& mu, uint64_t* seed) const +{ + double f; + int l, j; + sample_params(E_in, E_out, f, l, j, seed); + // Sample outgoing cosine bin int n_mu = distribution_[l].mu.shape()[1]; std::size_t k = prn(seed) * n_mu; @@ -474,7 +447,6 @@ void IncoherentInelasticAE::sample( // a bin of width 0.5*min(mu[k] - mu[k-1], mu[k+1] - mu[k]) centered on the // discrete mu value itself. const auto& mu_l = distribution_[l].mu; - f = (r1 - c_j) / (c_j1 - c_j); // Interpolate kth mu value between distributions at energies j and j+1 mu = mu_l(j, k) + f * (mu_l(j + 1, k) - mu_l(j, k)); @@ -501,63 +473,12 @@ void IncoherentInelasticAE::sample( double IncoherentInelasticAE::sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { - // Get index and interpolation factor for inelastic grid - int i; double f; - get_energy_index(energy_, E_in, i, f); - - // Pick closer energy based on interpolation factor - int l = f > 0.5 ? i + 1 : i; - - // Determine outgoing energy bin - // (First reset n_energy_out to the right value) - auto n = distribution_[l].n_e_out; - double r1 = prn(seed); - double c_j = distribution_[l].e_out_cdf[0]; - double c_j1; - std::size_t j; - for (j = 0; j < n - 1; ++j) { - c_j1 = distribution_[l].e_out_cdf[j + 1]; - if (r1 < c_j1) - break; - c_j = c_j1; - } + int l, j; + sample_params(E_in, E_out, f, l, j, seed); - // check to make sure j is <= n_energy_out - 2 - j = std::min(j, n - 2); - - // Get the data to interpolate between - double E_l_j = distribution_[l].e_out[j]; - double p_l_j = distribution_[l].e_out_pdf[j]; - - // Next part assumes linear-linear interpolation in standard - double E_l_j1 = distribution_[l].e_out[j + 1]; - double p_l_j1 = distribution_[l].e_out_pdf[j + 1]; - - // Find secondary energy (variable E) - double frac = (p_l_j1 - p_l_j) / (E_l_j1 - E_l_j); - if (frac == 0.0) { - E_out = E_l_j + (r1 - c_j) / p_l_j; - } else { - E_out = E_l_j + - (std::sqrt(std::max(0.0, p_l_j * p_l_j + 2.0 * frac * (r1 - c_j))) - - p_l_j) / - frac; - } - - // Adjustment of outgoing energy - double E_l = energy_[l]; - if (E_out < 0.5 * E_l) { - E_out *= 2.0 * E_in / E_l - 1.0; - } else { - E_out += E_in - E_l; - } - - // Sample outgoing cosine bin int n_mu = distribution_[l].mu.shape()[1]; const auto& mu_l = distribution_[l].mu; - f = (r1 - c_j) / (c_j1 - c_j); - std::vector mu_vector; for (int k = 0; k < n_mu; ++k) { @@ -590,32 +511,30 @@ MixedElasticAE::MixedElasticAE( close_group(incoherent_group); } -void MixedElasticAE::sample( - double E_in, double& E_out, double& mu, uint64_t* seed) const +const AngleEnergy& MixedElasticAE::sample_dist( + double E_in, uint64_t* seed) const { // Evaluate coherent and incoherent elastic cross sections double xs_coh = coherent_xs_(E_in); double xs_incoh = incoherent_xs_(E_in); if (prn(seed) * (xs_coh + xs_incoh) < xs_coh) { - coherent_dist_.sample(E_in, E_out, mu, seed); + return coherent_dist_; } else { - incoherent_dist_->sample(E_in, E_out, mu, seed); + return *incoherent_dist_; } } +void MixedElasticAE::sample( + double E_in, double& E_out, double& mu, uint64_t* seed) const +{ + sample_dist(E_in, seed).sample(E_in, E_out, mu, seed); +} + double MixedElasticAE::sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { - // Evaluate coherent and incoherent elastic cross sections - double xs_coh = coherent_xs_(E_in); - double xs_incoh = incoherent_xs_(E_in); - - if (prn(seed) * (xs_coh + xs_incoh) < xs_coh) { - return coherent_dist_.sample_energy_and_pdf(E_in, mu, E_out, seed); - } else { - return incoherent_dist_->sample_energy_and_pdf(E_in, mu, E_out, seed); - } + return sample_dist(E_in, seed).sample_energy_and_pdf(E_in, mu, E_out, seed); } } // namespace openmc diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index 27adf5a172e..5746f824d17 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -70,14 +70,12 @@ double UncorrelatedAngleEnergy::sample_energy_and_pdf( { // Sample outgoing energy E_out = energy_->sample(E_in, seed); - double pdf; if (!angle_.empty()) { - pdf = angle_.evaluate(E_in, mu); + return angle_.evaluate(E_in, mu); } else { // no angle distribution given => assume isotropic for all energies - pdf = 0.5; + return 0.5; } - return pdf; } } // namespace openmc diff --git a/src/thermal.cpp b/src/thermal.cpp index 8791db5c745..1a06d1d19a3 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -296,16 +296,21 @@ void ThermalData::calculate_xs( *inelastic = (*inelastic_.xs)(E); } -void ThermalData::sample(const NuclideMicroXS& micro_xs, double E, - double* E_out, double* mu, uint64_t* seed) +AngleEnergy& ThermalData::sample_dist( + const NuclideMicroXS& micro_xs, double E, uint64_t* seed) const { // Determine whether inelastic or elastic scattering will occur if (prn(seed) < micro_xs.thermal_elastic / micro_xs.thermal) { - elastic_.distribution->sample(E, *E_out, *mu, seed); + return *elastic_.distribution; } else { - inelastic_.distribution->sample(E, *E_out, *mu, seed); + return *inelastic_.distribution; } +} +void ThermalData::sample(const NuclideMicroXS& micro_xs, double E, + double* E_out, double* mu, uint64_t* seed) const +{ + sample_dist(micro_xs, E, seed).sample(E, *E_out, *mu, seed); // Because of floating-point roundoff, it may be possible for mu to be // outside of the range [-1,1). In these cases, we just set mu to exactly // -1 or 1 @@ -316,15 +321,8 @@ void ThermalData::sample(const NuclideMicroXS& micro_xs, double E, double ThermalData::sample_energy_and_pdf(const NuclideMicroXS& micro_xs, double E_in, double mu, double& E_out, uint64_t* seed) const { - AngleEnergy* angleEnergyPtr; - - if (prn(seed) < micro_xs.thermal_elastic / micro_xs.thermal) { - angleEnergyPtr = elastic_.distribution.get(); - } else { - angleEnergyPtr = inelastic_.distribution.get(); - } - - return angleEnergyPtr->sample_energy_and_pdf(E_in, mu, E_out, seed); + return sample_dist(micro_xs, E_in, seed) + .sample_energy_and_pdf(E_in, mu, E_out, seed); } void free_memory_thermal() From c52ad4f5b83c04d7aea36096da8ac3ee4ca71348 Mon Sep 17 00:00:00 2001 From: GuySten Date: Fri, 16 Jan 2026 12:58:32 +0200 Subject: [PATCH 81/88] further simplification --- include/openmc/secondary_thermal.h | 46 ++++++++++++++++++ src/secondary_thermal.cpp | 75 +++++++----------------------- 2 files changed, 64 insertions(+), 57 deletions(-) diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index ff6d7c09d65..e21fe20946f 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -194,6 +194,52 @@ class MixedElasticAE : public AngleEnergy { const Function1D& incoherent_xs_; //!< Polymorphic ref. to incoherent XS }; +struct DoubleVector { + double data; + double& operator[](size_t index) { return data; } +} + +typename double +get_pdf_discrete(const vector& mu, const T& w, double mu_0) +{ + // Make sure mu is in range [-1,1] + if (std::abs(mu_0) > 1.0) + mu_0 = std::copysign(1.0, mu_0); + double a0; + double a1; + double b0; + double b1; + int32_t ai = -1; + int32_t bi = -1; + if (mu_0 > mu[0]) { + ai = lower_bound_index(mu.begin(), mu.end(), mu_0); + a0 = mu[ai]; + a1 = (ai > 1) ? mu[ai - 1] : -1.0; + } else { + a0 = -1.0; + a1 = -1.0; + } + if (mu_0 < mu[mu.size() - 1]) { + bi = upper_bound_index(mu.begin(), mu.end(), mu_0); + b0 = mu[bi]; + b1 = (bi < mu.size() - 1) ? mu[bi + 1] : 1.0; + } else { + b0 = 1.0; + b1 = 1.0; + } + + // Calculate Delta_a and Delta_b + double delta_a = 0.5 * std::min(b0 - a0, a0 - a1); + double delta_b = 0.5 * std::min(b1 - b0, b0 - a0); + + if (mu_0 < a0 + delta_a) + return w[ai] / (2.0 * delta_a); + else if (mu_0 + delta_b < b0) + return w[bi] / (2.0 * delta_b); + else + return 0.0; +} + } // namespace openmc #endif // OPENMC_SECONDARY_THERMAL_H diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 760dcd60ddc..111cd21f3df 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -13,50 +13,9 @@ namespace openmc { -double get_pdf_discrete( - const vector& mu, const vector& w, double mu_0) -{ - // Make sure mu is in range [-1,1] - if (std::abs(mu_0) > 1.0) - mu_0 = std::copysign(1.0, mu_0); - double a0; - double a1; - double b0; - double b1; - int32_t ai = -1; - int32_t bi = -1; - if (mu_0 > mu[0]) { - ai = lower_bound_index(mu.begin(), mu.end(), mu_0); - a0 = mu[ai]; - a1 = (ai > 1) ? mu[ai - 1] : -1.0; - } else { - a0 = -1.0; - a1 = -1.0; - } - if (mu_0 < mu[mu.size() - 1]) { - bi = upper_bound_index(mu.begin(), mu.end(), mu_0); - b0 = mu[bi]; - b1 = (bi < mu.size() - 1) ? mu[bi + 1] : 1.0; - } else { - b0 = 1.0; - b1 = 1.0; - } - - // Calculate Delta_a and Delta_b - double delta_a = 0.5 * std::min(b0 - a0, a0 - a1); - double delta_b = 0.5 * std::min(b1 - b0, b0 - a0); - - if (mu_0 < a0 + delta_a) - return w[ai] / (2.0 * delta_a); - else if (mu_0 + delta_b < b0) - return w[bi] / (2.0 * delta_b); - else - return 0.0; -} - double get_pdf_discrete(const vector& mu, double mu_0) { - vector w(mu.size(), 1.0 / mu.size()); + DoubleVector w {1.0 / mu.size()}; return get_pdf_discrete(mu, w, mu_0); } @@ -104,23 +63,25 @@ double CoherentElasticAE::sample_energy_and_pdf( return 0; } - const int i = lower_bound_index(energies.begin(), energies.end(), E_in); - vector energies_cut(energies.begin(), energies.begin() + i + 1); - vector factors_cut(factors.begin(), factors.begin() + i + 1); + const int n = upper_bound_index(energies.begin(), energies.end(), E_in); + vector energies_cut(energies.begin(), energies.begin() + n); + vector factors_cut(factors.begin(), factors.begin() + n); + + vector mu_vector; + mu_vector.reserve(n); - vector mu_vector_rev; - std::transform(energies_cut.begin(), energies_cut.end(), - std::back_inserter(mu_vector_rev), + std::transform(std::advance(energies.rbegin(), n - 1), energies.rend(), + std::back_inserter(mu_vector), [E_in](double Ei) { return 1 - 2 * Ei / E_in; }); - vector mu_vector(mu_vector_rev.rbegin(), mu_vector_rev.rend()); - - auto f = xt::adapt(factors_cut, { - factors_cut.size(), - }); - auto weights = xt::diff(f); - weights /= xt::sum(weights); - vector w(weights.begin(), weights.end()); - return get_pdf_discrete(mu_vector, w, mu); + + vector weights; + weights.reserve(n); + + weights.emplace_back(factors[0] / factors[n]); + for (int i = 1; i <= n; ++i) { + weights.emplace_back((factors[i] - factors[i - 1]) / factors[n]); + } + return get_pdf_discrete(mu_vector, weights, mu); } //============================================================================== From f9d6d0fddba21cd6e5494917910e76df5016016e Mon Sep 17 00:00:00 2001 From: GuySten Date: Fri, 16 Jan 2026 13:04:23 +0200 Subject: [PATCH 82/88] further simplification --- src/secondary_thermal.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 111cd21f3df..f657916d4ca 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -64,8 +64,6 @@ double CoherentElasticAE::sample_energy_and_pdf( } const int n = upper_bound_index(energies.begin(), energies.end(), E_in); - vector energies_cut(energies.begin(), energies.begin() + n); - vector factors_cut(factors.begin(), factors.begin() + n); vector mu_vector; mu_vector.reserve(n); From dc38dc54f6dd6f0da83bd3e86495e36d88f0a80c Mon Sep 17 00:00:00 2001 From: GuySten Date: Fri, 16 Jan 2026 13:15:00 +0200 Subject: [PATCH 83/88] further simplification --- include/openmc/secondary_thermal.h | 9 +++++---- src/secondary_thermal.cpp | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index e21fe20946f..db5e2e0fab9 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -6,6 +6,7 @@ #include "openmc/angle_energy.h" #include "openmc/endf.h" +#include "openmc/search.h" #include "openmc/secondary_correlated.h" #include "openmc/vector.h" @@ -196,11 +197,11 @@ class MixedElasticAE : public AngleEnergy { struct DoubleVector { double data; - double& operator[](size_t index) { return data; } -} + const double& operator[](size_t index) const { return data; } +}; -typename double -get_pdf_discrete(const vector& mu, const T& w, double mu_0) +template +double get_pdf_discrete(const vector& mu, const T& w, double mu_0) { // Make sure mu is in range [-1,1] if (std::abs(mu_0) > 1.0) diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index f657916d4ca..314d3ed4b46 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -68,7 +68,7 @@ double CoherentElasticAE::sample_energy_and_pdf( vector mu_vector; mu_vector.reserve(n); - std::transform(std::advance(energies.rbegin(), n - 1), energies.rend(), + std::transform(energies.rbegin() + n - 1, energies.rend(), std::back_inserter(mu_vector), [E_in](double Ei) { return 1 - 2 * Ei / E_in; }); From 02aea311ef8994d3b962cea610b44c2643f05dcf Mon Sep 17 00:00:00 2001 From: GuySten Date: Fri, 16 Jan 2026 13:33:09 +0200 Subject: [PATCH 84/88] reserve vector sizes --- src/secondary_thermal.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 314d3ed4b46..351804d7758 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -177,6 +177,7 @@ double IncoherentElasticAEDiscrete::sample_energy_and_pdf( int n_mu = mu_out_.shape()[1]; std::vector mu_vector; + mu_vector.reserve(n_mu); for (int k = 0; k < n_mu; ++k) { double mu_k = mu_out_(i, k) + f * (mu_out_(i + 1, k) - mu_out_(i, k)); @@ -282,6 +283,7 @@ double IncoherentInelasticAEDiscrete::sample_energy_and_pdf( int m = mu_out_.shape()[2]; std::vector mu_vector; + mu_vector.reserve(n_mu); for (int k = 0; k < m; ++k) { double mu_ijk = mu_out_(i, j, k); From f14005a81c7e54e1debb3f2fd51d23b854299141 Mon Sep 17 00:00:00 2001 From: GuySten <62616591+GuySten@users.noreply.github.com> Date: Sat, 17 Jan 2026 18:35:16 +0200 Subject: [PATCH 85/88] Fix mu_vector reservation size in secondary_thermal.cpp --- src/secondary_thermal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 351804d7758..240bbf8cf36 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -283,7 +283,7 @@ double IncoherentInelasticAEDiscrete::sample_energy_and_pdf( int m = mu_out_.shape()[2]; std::vector mu_vector; - mu_vector.reserve(n_mu); + mu_vector.reserve(m); for (int k = 0; k < m; ++k) { double mu_ijk = mu_out_(i, j, k); From 903af78a115a066809071f7e4255f28a2b81f145 Mon Sep 17 00:00:00 2001 From: GuySten Date: Sat, 17 Jan 2026 19:50:30 +0200 Subject: [PATCH 86/88] clang format --- include/openmc/secondary_thermal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index db5e2e0fab9..b7de6ab4e61 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -197,7 +197,7 @@ class MixedElasticAE : public AngleEnergy { struct DoubleVector { double data; - const double& operator[](size_t index) const { return data; } + const double& operator[](size_t index) const { return data; } }; template From 64e23e380cdbdf3b5cd20ca7bf5a35e68347e3f4 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 29 Jan 2026 20:02:02 +0200 Subject: [PATCH 87/88] fix for invalid energy distribution in elastic scattering case --- src/secondary_uncorrelated.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index 5746f824d17..ec2af710258 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -69,7 +69,12 @@ double UncorrelatedAngleEnergy::sample_energy_and_pdf( double E_in, double mu, double& E_out, uint64_t* seed) const { // Sample outgoing energy - E_out = energy_->sample(E_in, seed); + if (energy_ != nullptr) { + E_out = energy_->sample(E_in, seed); + } else { + E_out = E_in; + } + if (!angle_.empty()) { return angle_.evaluate(E_in, mu); } else { From 1f36c89ce7d02fe82c36fbbc3f6729b91e804e1a Mon Sep 17 00:00:00 2001 From: GuySten <62616591+GuySten@users.noreply.github.com> Date: Fri, 30 Jan 2026 00:45:16 +0200 Subject: [PATCH 88/88] Refactor probability sampling logic in reaction_product Removed unnecessary break statement in probability sampling logic. --- src/reaction_product.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index 398f72e7967..6cfd164a01d 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -117,10 +117,8 @@ AngleEnergy& ReactionProduct::sample_dist(double E_in, uint64_t* seed) const prob += applicability_[i](E_in); // If i-th distribution is sampled, sample energy from the distribution - if (c <= prob) { + if (c <= prob) return *distribution_[i]; - break; - } } } else { // If only one distribution is present, go ahead and sample it