Fix a bug in time cutoff behavior (#3526)

This commit is contained in:
GuySten 2025-08-18 16:40:54 +02:00 committed by GitHub
parent c5a7173864
commit 68e894c4b0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 37 additions and 47 deletions

View file

@ -44,34 +44,30 @@ namespace openmc {
double Particle::speed() const
{
// Determine mass in eV/c^2
double mass;
switch (this->type()) {
case ParticleType::neutron:
mass = MASS_NEUTRON_EV;
break;
case ParticleType::photon:
mass = 0.0;
break;
case ParticleType::electron:
case ParticleType::positron:
mass = MASS_ELECTRON_EV;
break;
}
if (this->E() < 1.0e-9 * mass) {
// If the energy is much smaller than the mass, revert to non-relativistic
// formula. The 1e-9 criterion is specifically chosen as the point below
// which the error from using the non-relativistic formula is less than the
// round-off eror when using the relativistic formula (see analysis at
// https://gist.github.com/paulromano/da3b473fe3df33de94b265bdff0c7817)
return C_LIGHT * std::sqrt(2 * this->E() / mass);
if (settings::run_CE) {
// Determine mass in eV/c^2
double mass;
switch (this->type()) {
case ParticleType::neutron:
mass = MASS_NEUTRON_EV;
break;
case ParticleType::photon:
mass = 0.0;
break;
case ParticleType::electron:
case ParticleType::positron:
mass = MASS_ELECTRON_EV;
break;
}
// Equivalent to C * sqrt(1-(m/(m+E))^2) without problem at E<<m:
return C_LIGHT * std::sqrt(this->E() * (this->E() + 2 * mass)) /
(this->E() + mass);
} else {
// Calculate inverse of Lorentz factor
const double inv_gamma = mass / (this->E() + mass);
// Calculate speed via v = c * sqrt(1 - γ^-2)
return C_LIGHT * std::sqrt(1 - inv_gamma * inv_gamma);
auto& macro_xs = data::mg.macro_xs_[this->material()];
int macro_t = this->mg_xs_cache().t;
int macro_a = macro_xs.get_angle_index(this->u());
return 1.0 / macro_xs.get_xs(MgxsType::INVERSE_VELOCITY, this->g(), nullptr,
nullptr, nullptr, macro_t, macro_a);
}
}
@ -241,8 +237,14 @@ void Particle::event_advance()
collision_distance() = -std::log(prn(current_seed())) / macro_xs().total;
}
// Select smaller of the two distances
double distance = std::min(boundary().distance(), collision_distance());
double speed = this->speed();
double time_cutoff = settings::time_cutoff[static_cast<int>(type())];
double distance_cutoff =
(time_cutoff < INFTY) ? (time_cutoff - time()) * speed : INFTY;
// Select smaller of the three distances
double distance =
std::min({boundary().distance(), collision_distance(), distance_cutoff});
// Advance particle in space and time
// Short-term solution until the surface source is revised and we can use
@ -250,23 +252,10 @@ void Particle::event_advance()
for (int j = 0; j < n_coord(); ++j) {
coord(j).r() += distance * coord(j).u();
}
double dt = distance / this->speed();
double dt = distance / speed;
this->time() += dt;
this->lifetime() += dt;
// Kill particle if its time exceeds the cutoff
bool hit_time_boundary = false;
double time_cutoff = settings::time_cutoff[static_cast<int>(type())];
if (time() > time_cutoff) {
double dt = time() - time_cutoff;
time() = time_cutoff;
lifetime() = time_cutoff;
double push_back_distance = speed() * dt;
this->move_distance(-push_back_distance);
hit_time_boundary = true;
}
// Score track-length tallies
if (!model::active_tracklength_tallies.empty()) {
score_tracklength_tally(*this, distance);
@ -284,7 +273,7 @@ void Particle::event_advance()
}
// Set particle weight to zero if it hit the time boundary
if (hit_time_boundary) {
if (distance == distance_cutoff) {
wgt() = 0.0;
}
}

View file

@ -137,6 +137,7 @@ void create_fission_sites(Particle& p)
SourceSite site;
site.r = p.r();
site.particle = ParticleType::neutron;
site.time = p.time();
site.wgt = 1. / weight;
site.parent_id = p.id();
site.progeny_id = p.n_progeny()++;

View file

@ -1,5 +1,5 @@
tally 1:
2.000000E+03
4.000000E+05
1.383148E+02
1.913099E+03
0.000000E+00
0.000000E+00

View file

@ -620,7 +620,7 @@ def test_mesh_material_volumes_serialize():
assert new_volumes.by_element(3) == [(2, 1.0)]
def test_raytrace_mesh_infinite_loop():
def test_raytrace_mesh_infinite_loop(run_in_tmpdir):
# Create a model with one large spherical cell
sphere = openmc.Sphere(r=100, boundary_type='vacuum')
cell = openmc.Cell(region=-sphere)