Repository navigation
Conversation
Particle::from_source() reset the lifetime clock of every particle that it started, while time() was restored from the source site. The extra neutrons of an (n,xn) reaction with an integral yield, banked by create_secondary(), and the neutrons made by weight-window splitting, banked by split(), therefore restarted their clock at zero, although they continue the generation of the neutron that produced them. For a non-integral yield the reaction multiplies the weight of the incident neutron instead, which keeps its clock, so the two representations of the same reaction gave different lifetimes. The clock is read only by the iterated fission probability (IFP) method, which stores p.lifetime() for each fission site and scores it with ifp-time-numerator. The reset biased the effective generation time low in models with (n,xn) reactions, and much more with weight windows, where each neutron created by a split lost the time elapsed before the split. SourceSite gains a lifetime field that defaults to zero. create_secondary() sets it to the clock of the particle when a neutron banks a neutron, split() sets it when a neutron is split, and from_source() restores it. Fission sites are built from a default SourceSite in create_fission_sites(), so fission neutrons still start at zero, as do external source sites, the sites of photons, electrons and positrons, and neutrons banked by other particles. The field is added to the MPI datatype of SourceSite and to the ctypes structure in openmc.lib. The source and surface source file formats do not change. A C++ unit test checks the banked and restored clocks, and a Python unit test checks that weight-window splitting leaves the IFP generation time unchanged. Before this change, the generation time with splitting in that test was 14.3% lower with NNDC data, and 13.4% lower on average over 5 seeds with ENDF/B-VII.1 data. The ifp-time-numerator references of the ifp regression tests increase by 1.3e-4 relative, because U-235 has (n,2n) and (n,3n) reactions; all other reference values are unchanged.
Add a C++ unit test that banks split and (n,2n) neutrons, photons, electrons and positrons with the settings of a fixed-source run with a shared secondary bank, redistributes the bank with synchronize_global_secondary_bank() and revives every site with event_revive_from_secondary(). A revived neutron must resume the lifetime of the neutron that banked it, while the other particles and a site that is not a secondary, such as a fission site, start at zero. When OpenMC is built with MPI, the test also checks that the extent of the SourceSite MPI datatype equals sizeof(SourceSite), and CTest runs it a second time on two ranks, so that sites banked on the first rank are sent to the second one.
Particle::split() copies the lifetime clock and the collision count of the neutron that it splits, but not its delayed group, so the banked copies of a neutron born from a delayed neutron precursor started with delayed_group = 0. IFP stores p.delayed_group() for each fission site and ifp-beta-numerator scores it, so with weight-window splitting the copies of a delayed neutron counted as prompt and the effective delayed neutron fraction was biased low. split() now copies delayed_group for neutrons, and create_secondary() copies it when a neutron banks a neutron, under the same rule as the lifetime clock: a neutron that continues the generation of the neutron that produced it keeps both. For (n,xn) reactions, this makes integral yields, which bank copies, consistent with non-integral yields, which keep the incident neutron and its delayed group. Fission sites still set their own delayed group, and the sites of other particles keep 0. Surface source and collision track sites of such neutrons now record the delayed group of the neutron they came from; the file formats do not change. In the weight-window model of test_ifp_weight_windows.py, beta_eff with splitting was 24% lower than without (ENDF/B-VII.1 data, 5 seeds); with this change the two agree within 0.6%. A C++ unit test checks the banked and restored delayed group. No reference result changes: the ifp regression models have no weight windows, and no U-235 delayed neutron reaches the (n,2n) threshold.
Add a C++ unit test in which neutrons born from precursors of delayed groups 1 to 4 bank a split neutron, an (n,2n) neutron, a photon, an electron and a positron, and a photon that was given a nonzero delayed group banks a split photon and a neutron. The bank is redistributed with synchronize_global_secondary_bank(), which sends the sites from the first rank to the second when CTest runs the test on two ranks, and every site is revived with event_revive_from_secondary(). A neutron banked by a neutron must resume the delayed group of that neutron and every other banked particle must have none, while a site that is not a secondary, such as a fission site, keeps its own. Add a Python unit test that starts every history from a source file site of delayed group 3 in a beryllium sphere with an iron shell and reads the delayed group of the particles that leave it from the surface source file. The extra neutrons of (n,2n) reactions at 14 MeV and the neutrons split by weight windows at 1 MeV are tested separately, each with a local and with a shared secondary bank, and the test follows the --event and --mpi options of the test suite. Every neutron must have delayed group 3 and every photon none. Before the previous commit, 640 of the 1581 neutrons that left the sphere in the (n,2n) run and 8116 of the 9099 in the splitting run had none (NNDC data, local bank).
State that the clock is in seconds, as the comments on the other time members of the header do. The text is shortened by one word so that the line stays within the column limit.
Add a Python unit test in which every neutron of the first generation of an eigenvalue run descends from a 14 MeV neutron that starts at t = 1 s and crosses 100 m of void before it reaches a beryllium slab in front of a U-235 slab. When the extra neutrons of (n,2n) reactions keep the lifetime clock of the neutron that produced them, every fission site of that generation records at least the flight time, so the ratio of the ifp-time-numerator to the ifp-denominator scored in the second generation is bounded below by it exactly, whatever the random numbers. The test checks that bound, computed with the non-relativistic speed, which exceeds the speed used in transport, and an upper bound of 1 ms that a clock copied from the absolute time would exceed. It follows the --event and --mpi options of the test suite.
Catch2 runs test cases in random order by default since version 3.9, and each process draws its own seed. With the Catch2 3.16 that CI fetches, the two ranks of test_secondary_bank_mpi could run the two test cases that call synchronize_global_secondary_bank in different orders. The collective calls then paired different test cases across the ranks, and the rank without banked sites failed on the site count of the other test case. Pass --order decl so that every rank runs the test cases in the same order.
5 tasks done
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes #4177
Problem
Two kinds of neutron are banked as
SourceSites and started again byParticle::from_source(), although they continue the generation of the neutron that produced them:create_secondary()ininelastic_scatter();split()inapply_weight_window().They lose two pieces of state that the iterated fission probability (IFP) method reads:
from_source()setslifetime() = 0.0for every particle, while it restorestime()from the site.ifp()storesp.lifetime()with each fission site, andifp-time-numeratorscores it. The IFP generation time is biased low.split()andcreate_secondary()do not copydelayed_group, so the copy starts with the site's default of 0. In continuous-energy mode a neutron'sdelayed_group()does not change during transport, so it is the neutron's birth tag.ifp()stores it with each fission site, andifp-beta-numeratorcounts a fission when the tag of its ancestor is nonzero. The copies of a delayed neutron therefore count as prompt, and the IFP beta_eff is biased low.The two (n,xn) paths of
inelastic_scatter()are also inconsistent. An integral yield banksyield - 1identical copies, which lose the clock and the tag. A non-integral yield multiplies the weight of the incident neutron instead, and that neutron keeps both.Size of the bias:
k-effective,
ifp-denominatorand every non-IFP tally are not affected.Physics
The IFP kinetics parameters follow the adjoint-weighted formulation of MCNP's KOPTS option [1], with
where F is the fission production operator and F_d its delayed part. Eq. (8) of [1] scores the Lambda numerator by track length. It sums track length over speed over every track of the original generation, each weighted by the asymptotic population of the progeny that carry the track's progenitor index. Section 2 of [1] (p. 2) treats particle splitting and (n,2n) reactions as branches of a history within the original generation, which then score separately. So a split or an (n,xn) reaction does not start a new generation.
The clock. OpenMC scores the same numerator with a clock instead of track lengths. Each fission is scored with the lifetime of the neutron that caused it, weighted by that fission's progeny in the asymptotic generation. This equals the track-length form in expectation only if the clock runs from the birth of the generation.
t_bfrom the birth of the generation to the branching event. IFP then losest_btimes that branch's progeny weight from the numerator.The delayed group. The beta_eff numerator weights each neutron born delayed in a generation by its asymptotic progeny. That progeny includes the progeny of every branch of the neutron's history: the neutron that remains after a split, its split copies, and the extra neutrons of an (n,xn) reaction. The birth tag must therefore follow every branch, as the clock does. A branch whose tag is reset to 0 has its progeny counted as the progeny of a prompt neutron, and the numerator loses it.
The non-integral-yield path already behaves this way. Multiplying the weight of the incident neutron is the expected value of banking copies, and the neutron keeps its clock and its tag. With this change the integral-yield path agrees with it.
[1] B. C. Kiedrowski, "Theory, Interface, Verification, Validation, and Performance of the Adjoint-Weighted Point Reactor Kinetics Parameter Calculations in MCNP," LA-UR-10-01700, Los Alamos National Laboratory (2010), https://mcnp.lanl.gov/pdf_files/TechReport_2010_LANL_LA-UR-10-01700_Kiedrowski.pdf
Design
double lifetime {0.0};is added at the end ofSourceSite, aftern_collision. The default of zero keeps the old behavior for every site that does not set it.delayed_groupis already a member, with a default of 0.create_secondary()setsbank.lifetime = lifetime()andbank.delayed_group = delayed_group()when a neutron banks a neutron (type.is_neutron() && this->type().is_neutron()). Its only neutron caller is the integral-yield branch ofinelastic_scatter(). Multigroup mode has nocreate_secondary()call, because its scattering multiplicity is a weight factor.split()sets both fields when the particle being split is a neutron. Its only caller isapply_weight_window().from_source()setslifetime() = src->lifetimeinstead of 0. It already setdelayed_group() = src->delayed_group. Every path that starts a particle from a site goes through it:event_revive_from_secondary());create_fission_sites()is not changed. It builds each site from a defaultSourceSite, so the clock starts at zero, and the site gets the delayed group sampled for its own emission. This holds in eigenvalue mode, in fixed-source mode withcreate_fission_neutrons(where the site goes to the secondary bank), and in continuous-energy and multigroup mode alike.IndependentSource, file sources and MCPL sources produce sites with a zero clock and a delayed group of 0. So do compiled sources, unless they set the fields.initialize_mpi()lists every field ofSourceSiteinmpi::source_site, and the newlifetimefield is its 16th entry.delayed_groupwas already in it.sizeof(SourceSite), and arrays of sites sent over MPI would be misread.openmc.lib.core._SourceSitegains thelifetimefield, so the structured arrays ofopenmc.lib.source_bank()andopenmc.lib.sample_external_source(as_array=True)keep matching the C++ layout.h5banktype()), and only its memory type usessizeof(SourceSite). The clock is in-memory state.delayed_groupis one of the 8 fields already (see "File contents" below).particle_restart.cpp), so the clock is not observable there.docs/source/usersguide/kinetics.rstsay when the IFP lifetime clock starts and that such a neutron also keeps the delayed group of the neutron that produced it.No IFP code is changed (
ifp.cpp,ifp.h,bank.cpp,eigenvalue.cpp,tally_scoring.cpp).The (n,xn) case
split()must copy the tag: a split only divides weight. Forcreate_secondary(), this PR copies it as well, for three reasons.The alternative is to leave (n,xn) neutrons at 0, on the ground that an (n,xn) neutron is a prompt neutron. We did not take it, for the reasons above.
In practice the choice matters little. In the local ENDF/B-VII.1 data, the delayed-neutron spectra of U-235 and U-238 end at 3.0 MeV. That is below the (n,2n) thresholds of U-235 (5.32 MeV) and U-238 (6.18 MeV), so a delayed neutron cannot cause an (n,2n) reaction in either. Be-9, with a threshold of about 1.75 MeV, is the exception that a delayed spectrum's tail can reach. The C++ test pins the copy. Leaving (n,xn) neutrons at 0 would be a one-line change in
create_secondary()and in the test.Readers of the particle's delayed group
This PR changes the value of
p.delayed_group()only for split and (n,xn) neutrons. These are all the code paths that read it:ifp()(src/ifp.cpp), scored byifp-beta-numeratorwith or without aDelayedGroupFilteradd_surf_source_to_bank()(src/particle.cpp), surface-source sitessrc/collision_track.cpp)print_particle()(src/output.cpp)Not affected:
delayed-nu-fissionscores and theDelayedGroupFilteron them read the group of the sites created at the collision (p.nu_bank()), not the particle's tag;create_fission_sites()use the created site;sort_bank()orders by parent and progeny IDs only;create_fission_sites()reads only the value it has just set (see "Multigroup mode" below);_SourceSitealready carrydelayed_group, and the HDF5 source and surface-source types are unchanged.File contents. Surface-source files and collision-track files keep their formats. Their content changes only for sites written by split or (n,xn) neutrons, which now carry the delayed group of the neutron that produced them instead of 0. In the
surface_source_write,surface_sourceandcollision_trackregression tests, the generated files are unchanged (see "Regression references" below).Multigroup mode (behavior before this PR, not changed). The multigroup
create_fission_sites()(src/physics_mg.cpp) also sets the colliding particle'sdelayed_group()to the group of each site it creates. After a multigroup fission collision, a particle'sdelayed_group()is therefore no longer its birth tag, and a later split copies that value, the same as the parent's. IFP does not read it there: the multigroupcreate_fission_sites()does not callifp(). Only the surface-source, collision-track and print readers above see it.Tests
C++ unit test,
tests/cpp_unit_tests/test_particle.cpp(new)3 test cases and 55 assertions, all exact. The neutron has
lifetime() = 1.25e-6s andtime() = 3.0e-6s:SourceSitehaslifetime == 0.0anddelayed_group == 0.create_secondary(..., neutron)andsplit()bank sites withlifetime == 1.25e-6andtime == 3.0e-6;split()of a photon, and a neutron banked by a photon banklifetime == 0.0and keeptime;from_source()of each banked site givesp.lifetime() == site.lifetime.delayed_group() = 4:delayed_group == 4;split()of a photon, and a neutron banked by a photon bank 0;from_source()andevent_revive_from_secondary()of each banked site restorep.delayed_group() == site.delayed_group.The lifetime test cases do not compile on
develop, which has noSourceSite::lifetime. The delayed-group test case fails on the lifetime-only part of this PR, withbank[i].delayed_group == 4evaluating to0 == 4.C++ unit test,
tests/cpp_unit_tests/test_secondary_bank.cpp(new)The test runs with the settings of a fixed-source run with a shared secondary bank.
synchronize_global_secondary_bank()and revives every site withevent_revive_from_secondary().With MPI, the test also checks that the extent of the
SourceSiteMPI datatype equalssizeof(SourceSite). The CTest entrytest_secondary_bank_mpiruns it on two ranks, so that sites banked on the first rank are sent to the second. This is the direct test of the MPI layout.The CTest entry passes
--order decl, because since version 3.9 Catch2 runs test cases in random order with a seed drawn by each process, and the two ranks must run the two test cases that callsynchronize_global_secondary_bank()in the same order. With the Catch2 3.16 that CI fetches, 10 of 20 local runs oftest_secondary_bank_mpifailed without the option, as the MPI CI job of this PR did, and 20 of 20 passed with it, with Catch2 3.16 and with 3.4.A second test case follows the delayed group through the same path.
synchronize_global_secondary_bank()(rank 0 to rank 1 undertest_secondary_bank_mpi) andevent_revive_from_secondary():On the lifetime-only part of this PR this case fails, serially and on 2 ranks, while the lifetime cases pass.
Python unit test,
tests/unit_tests/test_secondary_delayed_group.py(new)This test checks the delayed group in transport.
n2n: 14 MeV source neutrons without weight windows, so the only neutrons banked during transport are the extra neutrons of (n,2n) reactions;split: 1 MeV source neutrons, below the (n,2n) thresholds, with weight windows that split the neutrons colliding outside the central mesh cell into 10.--eventand--mpioptions of the test suite.The test passes with NNDC and ENDF/B-VII.1 data in history mode, with
--event, with--mpi(2 ranks) and with--mpi --event. On the lifetime-only part of this PR, all four cases fail in every mode. With NNDC data in history mode, these neutrons had delayed group 0:n2nsplitPython unit test,
tests/unit_tests/test_ifp_weight_windows.py(new)The test runs the U-235 sphere of the
ifpregression tests twice, without and with weight windows. It asserts|Lambda_ww / Lambda_analog - 1| <= 0.025. Settings: 50000 particles, 30 batches (10 inactive),ifp_n_generation = 1.Weight windows.
apply_weight_window()). So a window that is uniform in space never splits a weight-1 fission neutron, whatever its bounds.apply_weight_windows()), and the absolute bounds then apply.(i + j + k) % 4 == 0, has the window [0.05, 0.1], and the other cells have none (-1).Single-seed results, NNDC data, 2 threads, measured on
develop3cded0f before the rebase:develop3cded0fWith ENDF/B-VII.1 data the ratios are 0.869 (
develop, fails) and 1.0065 (this PR, passes). AtOMP_NUM_THREADS=2the test takes 8 s to 10 s in history mode and 17 s to 23 s with--event. It passes on the final branch with NNDC data, in both modes.Tolerance. The tolerance comes from a 5-seed study of this exact configuration (ENDF/B-VII.1 data, 5 seeds per arm, Welch t on the per-seed Lambda):
developdevelop(0.0267).ifp_n_generation = 5, the same study gives -13.6 % ondevelop(t = -35.1) and +0.17 % with this PR (t = 0.41, t_0.995 = 3.57).No beta_eff assertion. The same two runs give beta_eff, but at this size its per-seed scatter is too large for a test. Over 5 seeds the standard deviation of the beta_eff ratio with this PR is 1.8 %. A tolerance of 4 standard deviations (0.072) would exceed one fifth of the bias that it must detect (0.048), so the test asserts only Lambda. The delayed group is covered by the exact C++ tests and by the paired runs below.
beta_eff with weight windows
The model, weight windows and settings are those of the Python test, with ENDF/B-VII.1 data, 5 seeds per arm, 2 threads and a Welch t on the per-seed beta_eff. "Before" is the lifetime-only part of this PR, the first two commits. The lifetime part does not change beta_eff: in the paired runs of the lifetime study,
ifp-beta-numeratoragrees withdevelopto 4.5e-16 relative.ifp_n_generationWith one generation and a
DelayedGroupFilteron groups 1 to 6, every group is 19 % to 27 % low before (Welch t from -2.5 to -13.1), and within |t| <= 1.1 with this PR. With five generations every group is within |t| <= 1.91 with this PR.Python unit test,
tests/unit_tests/test_ifp_n2n_lifetime.py(new)This test checks the lifetime clock of (n,2n) neutrons in transport. The effect of (n,xn) reactions on the generation time is too small for a statistical comparison (see "Size of the bias" above), so the test checks an exact lower bound instead.
ifp_n_generation = 1. Every source neutron starts at the origin at t = 1 s with 14 MeV, moving along +z. It crosses 100 m of void before it reaches a 5 cm Be-9 slab (1.85 g/cm3), followed by a 10 cm U-235 slab (16.0 g/cm3). All cells are 30 cm wide in x and y, and every outer surface is a vacuum boundary. One tally without filters scoresifp-time-numerator(N) andifp-denominator(D).T_min = L / (c sqrt(2 E / (m c^2)))uses the non-relativistic speed, with the speed of light and neutron mass ofconstants.h. For every E > 0 that speed exceeds the relativistic speed that OpenMC uses in transport,c sqrt(E (E + 2 m c^2)) / (E + m c^2). T_min = 1.9323e-6 s is therefore below OpenMC's flight time of 1.9538e-6 s, which is 1.1 % longer.time()instead of the lifetime, because the source neutrons start at t = 1 s.--eventand--mpioptions of the test suite.If an extra neutron restarts its clock at the reaction, in the target, its fission sites record only the time since that reaction, and N/D falls below the bound. The void is convex, so a neutron that leaves the target cannot come back and add flight time.
Results, NNDC data, 2 threads, N/D / T_min. Seed 1 is the test's own run; seeds 2 to 5 run the same model with another seed.
developa5bc348 (same code as the base, 4e69968)bank.lifetime = lifetime();removed fromcreate_secondary()--eventand with--mpi(2 ranks).developand with the clock copy removed, the test fails in history mode, with--eventand with--mpi(2 ranks), withIFP mean lifetime (1.5870e-06 s) is below the flight time of the source neutrons (1.9323e-06 s).ifp-denominatorand the statepoint source banks are bitwise equal for all three, and N is bitwise equal betweendevelopand the build without the clock copy: only the time numerator of this PR differs. At 2 threads, in history mode,ifp-denominatorand the source banks are still bitwise equal, while k, and N betweendevelopand the build without the clock copy, differ only in the last bits, by up to about 2e-15 between builds as between two runs of the same build, from the order of the OpenMP reductions.create_secondary()already copies the clock, and the test passes.At
OMP_NUM_THREADS=2the test takes 0.11 s in history mode, 0.13 s with--eventand 0.5 s with--mpi. The--mpiruns used Open MPI 4.1.6 with process binding off, through a small wrapper aroundmpiexecthat unsets allOMPI_*,PMIX_*,OPAL_*andORTE_*environment variables but the binding policy (OMPI_MCA_hwloc_base_binding_policy) before it callsmpiexec.On the sixth commit,
ctestgives the counts listed under "Tests run on the final branch", serially and with MPI, andifp/total,ifp/groupwise,test_ifp_weight_windows.pyandtest_secondary_delayed_group.pypass in history mode.Regression references,
ifp/totalandifp/groupwiseBoth models are a bare U-235 sphere. Only the
ifp-time-numeratorrows of bothresults_true.datfiles change. They were regenerated with the NNDC library and a strict-FP build. Thedevelopbuild on the same machine reproduces the old references.ifp-time-numeratorAll other values in both files are unchanged, including k-effective,
ifp-beta-numeratorandifp-denominator, andinputs_true.datdoes not change.The delayed-group change leaves every reference unchanged. These models have no weight windows, so nothing is split, and no U-235 delayed neutron reaches the (n,2n) threshold. The lifetime-only part and the full PR were run with
--updateon ENDF/B-VII.1 data, atOMP_NUM_THREADS=2except where a test sets its own thread count. The generated files are identical for the tests below; in some HDF5 files only the version, commit and date attributes differ.ifp;weightwindows,weightwindows_fw_cadis,weightwindows_fw_cadis_local,weightwindows_fw_cadis_meshandweightwindows_pulse_height;surface_source_writeandmg_fixed_source_ww_fission_shared_secondary;surface_sourceandcollision_track, run at 1 thread to remove thread-scheduling differences.The one exception is
surface_source_write/case-a01, which runs at 2 threads by design. Its 24 sites differ in order only. They are equal after sorting, which is how the test compares them.Paired checks before and after
Each pair uses the same seed at 2 threads, and both builds are RelWithDebInfo with strict FP. The random walk is the same in both runs of a pair, so a paired difference is not statistical. The lifetime checks were run with
develop3cded0f as "before" and the lifetime commit as "after", before the rebase. The delayed-group checks were run on the rebased branch, with the first two commits as "before" and the full PR as "after".--update, with both builds, in two copies oftests/(ENDF/B-VII.1 data). Only the twoifp-time-numeratorrows above differ for a reason other than run-to-run variation.developat 2 threads, because it depends on thread scheduling:collision_track, the depletion tests,microxs,mgxs_library_hdf5,surface_source,surface_source_write,sourcepoint_latestandrandom_ray_diagonal_stabilization.cpp_driver,source_dlopenandsource_parameterized_dlopenneed an installed OpenMC;weightwindows_fw_cadis,weightwindows_fw_cadis_localandweightwindows_fw_cadis_mesh(flat, linear) fail on that machine only. On the machine of the full test suite under "Tests run on the final branch", they pass with both builds, and the regression part there has 307 passes.ifp/totalstatepoints (ENDF/B-VII.1).ifp-denominatorandifp-beta-numeratorare bitwise equal, and so is the source bank.ifp-time-numeratorincreases by 1.89e-4.ifp-denominator,ifp-beta-numeratorand the source bank are bitwise equal;developthe integral branch is low. 5 seeds cannot resolve the difference (+0.41 %, t = 1.11), but the paired run above measures it: 0.32 %.ifp-denominatorandifp-beta-numeratoragree to 4.5e-16;ifp-denominatorto 5.6e-16 andifp-time-numeratorto 2.9e-15;ifp-beta-numeratoris higher with this PR in every active batch of every run, by 27 % to 33 % in total;ifp-beta-numeratorandifp-denominatorare bitwise equal, k agrees to 8.2e-14 andifp-time-numeratorto 7.9e-15, so no delayed neutron caused an (n,xn) reaction in these runs;Tests run on the final branch
These were run locally (RelWithDebInfo, strict FP,
OMP_NUM_THREADS=2). The last four commits change only tests and a comment, so the product code is that of the third commit. The fourth commit compiles to the same code as the fifth, sixth and seventh, and these ran on it:ctest, the new Python test, theifpregression tests, the unit modules named below and the full test suite. The sixth commit addstest_ifp_n2n_lifetime.py, whose runs are given in its own subsection above, and the seventh passes--order decltotest_secondary_bank_mpi(see thetest_secondary_bank.cppsubsection). The MPI regression tests and the full set of unit modules ran on the third commit:ctest. 16 tests serially, of which 15 pass andtest_mcpl_stat_sumis skipped (no MCPL). With MPI, 17 tests, includingtest_secondary_bank_mpi; 16 pass and the same one is skipped.test_secondary_bankpasses on 1 and 2 ranks, with both of its delayed-group and lifetime cases.ifp/totalandifp/groupwisepass against the committed references, together withtest_ifp_weight_windows.py, in history mode and with--event.test_secondary_delayed_group.pypasses with NNDC and ENDF/B-VII.1 data, in history mode, with--event, with--mpi(2 ranks) and with--mpi --event.test_ifp_weight_windows.py,test_surface_source_write.pyandtest_source_file.pypass with it (25 passed, 2 skipped).-DOPENMC_USE_MPI=onpassesmg_fixed_source_ww_fission_shared_secondary,mg_basicandmg_basic_delayedwith--mpiat 2 ranks, against their committed references. The first is a fixed-source run with weight windows, fission neutrons and the shared secondary bank, so split neutrons cross ranks there.--updatecomparison on ENDF/B-VII.1 data described under "Regression references".test_ifp.pyandtest_ifp_weight_windows.py;test_collision_filter_ww.pyandweightwindows/;test_lib.py;test_source*.pymodules andtest_surface_source_write.py;test_uniform_source_sampling.pyandtest_deplete_external_source_rates.py.sizeof(SourceSite)is 144 bytes, andctypes.sizeof(openmc.lib.core._SourceSite)is also 144, withlifetimeat offset 136. Ondevelopboth are 136.openmc/lib/core.py.tests/test_matplotlib_import.py,tests/unit_testsandtests/regression_tests. History mode,OMP_NUM_THREADS=2, in 4 parallel shards, on Ubuntu 24.04 with an i7-8700K.develop4e69968, the same way: 1527 passed, 141 skipped, the same 64 errors and no failures. The 5 extra passes are the new tests.cpp_driver,source_dlopenandsource_parameterized_dlopenneed an installed OpenMC;OPENMC_ENDF_DATA(ENDF-6 files), which is not set here.--event: 119 passed (develop: 114);--mpion 2 ranks (OpenMPI): 134 passed (develop: 133).test_secondary_delayed_group.pyfailed, because they ran after tests that initializeopenmc.libin-process. Run alone with--mpi, they pass. Upstream'stest_tracks.pyfails the same way ondevelopwhen it runs aftertest_ww_gen.py.Not run:
OPENMC_ENDF_DATA);cpp_driver,source_dlopen,source_parameterized_dlopen), because nothing was installed.Compatibility
ifp-time-numerator) changes in eigenvalue models with integral-yield (n,xn) reactions or with weight-window splitting: by about 1e-4 relative for bare U-235, and by much more with splitting.ifp-beta-numerator) changes in eigenvalue models with weight-window splitting. A change from (n,xn) reactions needs delayed neutrons above an (n,2n) threshold, as in beryllium.SourceSitelayout. The struct grows from 136 to 144 bytes.openmc.libare updated with it.SourceSitemust be rebuilt against the new headers, as after Preserve collision count when splitting particles #3958.lifetimefield, in_SourceSiteand in the structured arrays returned byopenmc.lib.source_bank()andopenmc.lib.sample_external_source(as_array=True).Related issues and PRs
n_collisiontoSourceSite, its MPI datatype and_SourceSitein the same way, so that split particles keep their collision count.test_collision_filter_ww.py, an analog-versus-weight-window comparison like the new IFP test.time()for neutrons produced by (n,xn) reactions and fission. This PR does not changetime(). It keeps the IFP clock running through (n,xn) reactions and splits and restarts it at fission, as the adjoint-weighted definition requires.ifp()still storesp.lifetime()andp.delayed_group()there.SourceSite::parent_id, adds members aftern_collision, and editscreate_secondary(),split(),initialize_mpi()and_SourceSite.SourceSite,create_secondary()andsplit().ifp/nuclidewise.ifp-time-numeratortally (tally 1).kinetics.rstandparticle_data.h, in different places.split()withreplicate().AI Assistance
Checklist
Summary by CodeRabbit