Repository navigation
Conversation
Add a <birth_mesh> settings element (Settings.birth_mesh) that tags every fission site banked in an eigenvalue calculation with the bin of a mesh that contains the birth position, Particle::r_born(), of the neutron that produced the site, or with -1 if that position is outside the mesh. Any type of mesh can be used. The tag is the 0-based bin that a MeshFilter on the mesh gives for the birth position, which is the binning of MeshBornFilter. No translation or rotation is applied: these are attributes of a filter, not of a mesh, so a MeshFilter with a translation corresponds to a birth mesh that is itself moved. The tags of a saved source bank can thus be combined with the site positions, energies, times and delayed groups to form fission-matrix estimates in post-processing. Continuous-energy and multigroup fission sites are both tagged; the bin is computed once per fission event. Tagging consumes no random numbers and changes no scoring, so transport and tally results are unchanged. In fixed-source mode, where no fission sites are banked, the element is ignored with a warning. The tag is a new SourceSite member, birth_mesh_bin, which fills the existing tail padding of the struct (sizeof(SourceSite) is unchanged) and is added to the MPI datatype and to the openmc.lib ctypes mirror. It is not part of the source_bank compound type. Instead, the tags are written with the existing write_bank_dataset() template as a separate int32 birth_mesh_bin dataset, aligned by index with source_bank, together with a birth_mesh_id attribute, in state points that contain the source bank and in the source point files source.<batch>.h5 and source.h5. A state point of a tagged run records birth_mesh_id even when the source bank is written separately. Surface source files, the initial source file and MCPL files carry no tags. read_source_bank() reads the tags back for a restart or a file source. The dataset must be one-dimensional with the extent of source_bank, otherwise the run stops with a fatal error. The tags are read through a memory dataspace sized to the buffer, selecting this process's share of the sites when the bank is distributed. The state point and source file format version is bumped to 18.3. Files written by earlier versions still load in Python and as file sources: StatePoint checks only the major version and returns None for birth_mesh_bin and birth_mesh_id when a file has no tags, and read_source_bank() handles the particle codes of any file as before. As for earlier minor version bumps, a restart still requires a state point of the current version. ParticleList.export_to_hdf5() now writes version 18.3 too. Tests: the data-independent multigroup regression test birth_mesh uses two fissile slabs separated by an absorber that no neutron crosses except with probability <= e^-100 (and 2^-64 per sampled flight distance, for a random number of exactly 0), so that every tag must equal the bin of its own site on a two-bin mesh. Finer regular and nonuniform rectilinear meshes show that the tags come from the birth position rather than from the site. The test also checks which files carry tags, a restart (through openmc.lib), the rejection of tag datasets that are one entry too short or too long on restart and for a file source, and fixed-source mode. Unit tests cover the Settings XML round trip, including a cylindrical birth mesh, and StatePoint.birth_mesh_bin and StatePoint.birth_mesh_id.
Add tests of the fission bank birth-mesh tags: - test_source_site (Catch2), also run on two MPI ranks as test_source_site_mpi: the MPI datatype of SourceSite holds every byte of the struct, and sites sent from one rank to another keep every member. - test_birth_mesh_lib_source_bank: every field of openmc.lib.source_bank() equals the source bank and the tags written to the state point, and each tag is the bin of the source site from which the neutron that produced the site was started. - test_birth_mesh_any_mesh: in a problem of eight decoupled fuel islands, the tag of every site is the bin that a MeshFilter gives in its island, for a regular, a translated regular, a rectilinear, a cylindrical and a spherical mesh, and for a regular mesh with a translated filter. - birth_mesh_ce: the two-slab checks of birth_mesh in continuous-energy transport, with U-235 and H-1 slabs separated by B-10, and the comparison of the tags with the bins of the source sites from which the histories were started. test_birth_mesh_restart also checks that a restart run reads the tags without errors from HDF5, and test_birth_mesh_source_files that ParticleList.export_to_hdf5() writes the format version of the source files written by OpenMC.
Add test_birth_mesh_ce_file_source: a tagged continuous-energy run writes source.h5, and an eigenvalue run with a FileSource on that file is initialized through openmc.lib. Each site sampled for the first batch must have the tag of the file site that it copies, which checks that read_source_bank() copies the tags into the sites when the whole bank is read. Like the other openmc.lib tests, it is skipped under MPI.
The random ray solver banks no fission sites, so a <birth_mesh> element tagged nothing in a random ray eigenvalue run, while its state points still recorded a birth_mesh_id. The element is now ignored with a warning, as in fixed-source mode, and no birth_mesh_id is written. MCPL source point files have no place for the tags, which were dropped without notice. When an eigenvalue run with a birth mesh writes its source points to separate MCPL files, a warning now says so. It is printed once, by the master process, when the settings are read, and not at every source point written.
Add test_birth_mesh_random_ray: with the random ray solver, a <birth_mesh> element is ignored with a warning, printed once by each process, and the state point records no birth_mesh_id and no tags. Add test_birth_mesh_mcpl_warning: the warning that MCPL source point files carry no tags is given exactly once, by the master process, for an eigenvalue run that writes its source points to separate MCPL files, and not for HDF5 source points, for source points kept in the state point, when source points are not written, in fixed-source mode or with the random ray solver. Source points are requested at a batch that the run does not reach, so that these cases need no MCPL library. A case that writes MCPL source points at two batches is skipped without mcpl-config, as the other MCPL tests are.
The warning that MCPL source point files carry no birth-mesh tags required the source to be written to separate files. With overwrite_latest, finalize_batch() writes the latest source at every batch even when the source is otherwise not written, so a run with write set to false and MCPL output dropped the tags without the warning. The warning now follows both writes: the source points of the given batches when the source is written to separate files, and the latest source with overwrite_latest.
Extend test_birth_mesh_mcpl_warning. With overwrite_latest the latest source is written to an MCPL file at every batch, also when write is False, so that run gives the warning once. An empty list of source point batches and HDF5 output with overwrite_latest give none. Without the MCPL library, the overwrite_latest run stops where it writes the first MCPL file, after the warning; the test accepts that stop, and with the library it checks that source.mcpl is written.
|
@ondrejch Thanks a lot for your recent contributions. I wanted to personally let you know that the @openmc-dev/technical-committee has decided to enforce a limit on the number of open PRs (5) from any given individual in order to more effectively deal with the high volume of PRs that the project is receiving. Our team has limited bandwidth and we want to make sure that we're able to clear out the backlog of older PRs. In the meantime, this means that because you have 6 open PRs you won't be able to open further ones without the existing ones being resolved. Rest assured, we do intend to get to them but we ask for your patience as it may take some time in light of the long backlog. |
|
Thanks for letting me know, Paul. That makes sense, and I won't open any new PRs until some of these are resolved. In case it helps with the order, I'd suggest the three bug fixes first (#4180, fission sites rejected by the time cutoff; #4182, the double filter weighting with EnergyoutFilter and DelayedGroupFilter; and #4191, the multigroup FileSource groups of #4183), then #4178 (the lifetime clock and delayed group of (n,xn) and split neutrons), and then #4195, which is stacked on #4182, and #4199. |
|
Thanks for the contribution! Before going further, I wanted to check whether this can already be done with existing tallies. A neutron started from the source bank has its fission site as its birth position. So a tally combining a tally = openmc.Tally(name='fission matrix')
tally.filters = [openmc.MeshBornFilter(mesh), openmc.MeshFilter(mesh)]
tally.scores = ['nu-fission']
# after the run
F = sp.get_tally(name='fission matrix').mean.reshape(n_bins, n_bins)Would this cover your use case? |
|
Thanks, Guy. Yes, for the fission matrix itself that tally is the right tool, and it is what I use: my TFM exports are built from The tags would only add site-level data: a saved source bank could be re-binned on the destination side (site position, energy, delayed group) after the run, without a new run. I don't need that at the moment, and on its own it isn't worth a new |
Description
Summary
This PR adds a
<birth_mesh>settings element, exposed in Python asSettings.birth_mesh. When it is set in an eigenvalue calculation, every fission site banked in the fission bank, in continuous-energy and in multigroup mode, is tagged with a bin of that mesh: the bin that contains the birth position of the neutron that produced the site (Particle::r_born()).MeshFilteron the same mesh gives for the birth position, which is the binning ofMeshBornFilter, or-1if that position is outside the mesh.MeshFilterwith a translation corresponds to a birth mesh that is itself moved.birth_mesh_bin, aligned by index withsource_bank, together with abirth_mesh_idattribute. This happens in state points that contain the source bank and in the source point filessource.<batch>.h5andsource.h5. On a restart or with a file source, the tags are read back. In Python,StatePoint.birth_mesh_binreturns the tags andStatePoint.birth_mesh_idthe mesh ID.Commits
birth_mesh, and the unit tests.SourceSiteMPI datatype on one and two ranks, anopenmc.libtest of the ctypes mirror and of the birth positions, a transport test of six mesh types against aMeshFilter, the continuous-energy regression testbirth_mesh_ce, and two added checks in existing tests (see "Tests added in the second and third commits").FileSourcereads the tags with the sites.birth_mesh_idis written. An eigenvalue run with a birth mesh that writes its source points to separate MCPL files prints one warning that these files carry no tags.overwrite_latest, the latest source is written at every batch, also whenwriteis false, and the warning of the fourth commit missed that case. The warning now covers both writes, and an empty list of source point batches no longer gives it.Motivation
The fission matrix couples the region where a neutron is born to the region where it causes fission.
MeshBornFilter×MeshFiltertallies give the matrix itself. Several fission-matrix methods also need the individual sites, though. One example is the transient fission matrix method (Laureau et al., Annals of Nuclear Energy 85, 2015), which uses the time structure and delayed-group structure of the matrix. The source bank already stores the position, energy, time, and delayed group of each site, but not where the neutron that produced the site was born. A birth tag adds that, so a saved source bank holds the joint data(birth bin, site position, energy, time, delayed group)for every site. Post-processing can then form fission-matrix estimates and their time and delayed-group structure on any site binning, with no further runs or tallies.The notion of birth and the binning match
MeshBornFilter, so tags and tallies on the same mesh can be compared directly.Design
Where the tag comes from.
create_fission_sites()computessettings::birth_mesh->get_bin(p.r_born())once per fission event, before the loop over the sampled sites, and stores it in every site. This happens in bothphysics.cpp(continuous energy) andphysics_mg.cpp(multigroup), when a birth mesh is set. It uses no random numbers and changes no scoring, so the transport and the tally results are unchanged.What "birth" means. It is the position at which the neutron was started from a site. For a neutron from the source bank, that is its own fission site. For the extra neutrons of (n,xn) reactions, it is the collision point, and for particles created by weight-window splitting, the split point. This is the same convention, and the same limitation, as
MeshBornFilter, and the docs say so.Mesh types and bins. Tagging only needs
Mesh::get_bin(), the call through whichMeshFilterandMeshBornFilterbin a position, so the birth mesh can be any mesh defined insettings.xml. When the element is read, the mesh is prepared for point location (prepare_for_point_location()), asMeshFilter::set_mesh()and the weight windows do. AMeshFilterapplies its translation and rotation to the position before it bins it, and aMeshBornFilterits translation to the birth position. The birth mesh applies neither, because translation and rotation are attributes of those filters, not of the mesh. A tag on a mesh moved by a vector t therefore equals the bin of aMeshFilterwith translation t on the unmoved mesh. The rule is stated in the same terms in the commit message, the comment insettings.cpp, the<birth_mesh>entry ofio_formats/settings.rst, theSettings.birth_meshdocstring, and a new paragraph in the user's guide ("Generation of Output Files"). The second commit tests it in transport against aMeshFilterin the same run, for regular, translated regular, rectilinear, cylindrical, and spherical meshes, and for a translated filter (see "Tests"). Unstructured meshes are not tested here, since the builds have neither DAGMC nor libMesh.Storage in
SourceSite. The new memberint birth_mesh_bin {-1}goes aftern_collision, in what was tail padding:sizeof(SourceSite)stays 136 bytes, the new member sits at offset 132, and all other members keep their offsets. The member is added to the MPI datatype ininitialize_mpi()(16 entries instead of 15), so the tags travel with the sites insynchronize_bank(), and to theopenmc.libctypes mirror_SourceSite, soopenmc.lib.source_bank()gains abirth_mesh_binfield. Measured on develop aa4afc4, in serial and MPI builds:developsizeof(SourceSite)birth_mesh_binctypes.sizeof(_SourceSite)With #4178, which adds
double lifetimeaftern_collision, the new member fills the 4 bytes of padding beforelifetime, and the size stays at #4178's 144 bytes (see "Related PRs").A separate dataset, not a new field of the
source_bankcompound type.read_source_bank()refuses a file whose compound member names differ from the current ones ("Source site attributes in file do not match ..."). A new compound field would therefore make every existing state point and source file unreadable as a restart or file source. It would also change surface source files, the initial source file, and the Python readers. A separate dataset avoids all of this:Tags are written only for the eigenvalue source bank:
source.<batch>.h5andsource.h5.birth_mesh_id: written to every state point of a tagged run, even when the source bank is written to a separate file, so the file still records which mesh the tags in the separate file refer to.Writing.
write_source_bank()copies the tags of the local sites into an int32 buffer and writes it with the existingwrite_bank_dataset()template (bank_io.h), usingH5T_NATIVE_INT32and, with MPI,MPI_INT32_T. The tags therefore take exactly the same parallel HDF5 and MPI gather paths as the source bank. The buffer has the size of the local source bank, which the template also uses as the master's receive buffer.Reading. When
birth_mesh_binexists,read_source_bank()checks that it is one-dimensional with the length ofsource_bankand stops with a fatal error otherwise. It reads the dataset through a memory dataspace sized to the buffer, selecting this process's share of the sites when the bank is distributed (restart). Without the length check, a dataset that is one entry too long would be read silently on restart.Fixed-source mode and random ray. Neither a fixed-source calculation nor the random ray solver banks sites in the fission bank, so in both the element is ignored with a warning, every process prints it, and nothing is tagged or written,
birth_mesh_idincluded. Before the fourth commit, a random ray eigenvalue run kept the mesh, tagged nothing, and still recordedbirth_mesh_idin its state points.MCPL source points. An MCPL file has no place for the tags.
finalize_batch()writes source points in MCPL format in two cases: at the given batches to separate files, and withoverwrite_latestthe latest source at every batch, also whenwriteis false. When an eigenvalue run with a birth mesh is set up for either, a warning says that the tags are not written to MCPL source point files. The master process prints it once, when the settings are read, not at every source point. Which batches the run reaches is known only during the run, so a requested batch counts as written; an empty list of batches gives no warning.Format version.
VERSION_STATEPOINTgoes from 18.2 to 18.3, because state point and source files can now contain a new dataset and attribute. Source files share this version, andParticleList.export_to_hdf5()writes[18, 3]too.docs/source/io_formats/statepoint.rstandsource.rstgive the new version, andsource.rstnow states the version explicitly. The<birth_mesh>element is documented insettings.rst, in alphabetical order, with the bin numbering, the meaning of birth, and which files carry tags.Tests
Regression test
tests/regression_tests/birth_mesh(first commit)Model. Two one-group fissile slabs (5 cm each) are separated by a 10 cm pure absorber (Σt = Σa = 10 /cm), with reflective outer boundaries. A neutron that enters the absorber is absorbed at its first collision there, so to reach the other slab it needs at least 10 cm of uncollided path in the absorber. No neutron crosses it except with probability ≤ e^-100 per neutron (and 2^-64 per sampled flight distance, for a random number of exactly 0, which gives an infinite flight). The birth position of the neutron that produced a site is therefore in the slab of the site.
Checks. Five test functions, six test cases:
test_birth_mesh_eigenvaluesource_bank, and equal toStatePoint.birth_mesh_bin;birth_mesh_idandStatePoint.birth_mesh_idare the mesh ID. The surface source file and the initial source file have no tags and nobirth_mesh_id.test_birth_mesh_source_filessourcepoint = {'separate': True, 'overwrite': True}: the state point hasbirth_mesh_idbut no source bank and no tags, soStatePoint.birth_mesh_binisNoneandStatePoint.birth_mesh_idis the mesh ID.source.6.h5andsource.h5carry identical, correct tags.test_birth_mesh_birth_position[False]/[True]test_birth_mesh_restartstatepoint.3.h5reads the tags and tags the new sites. Throughopenmc.lib, the restarted source bank holds the file's tags. Tag datasets one entry too short and one entry too long are rejected with the length error, both on restart and as a file source.test_birth_mesh_fixed_sourcebirth_mesh_id, andStatePoint.birth_mesh_idisNone.Why it needs no nuclear data and no reference files. The test runs in multigroup mode with a library that it writes itself, so it needs neither
OPENMC_CROSS_SECTIONSnor the NNDC library. Every assertion follows from the geometry or from the file layout, not from stored numbers, so there is noinputs_true.datorresults_true.dat. Apart from the e^-100 bound above, it does not depend on the random number sequence, the platform, or strict FP. It handles--mpi; theopenmc.libpart is skipped under MPI.Tests added in the second and third commits
The second commit adds
tests/cpp_unit_tests/test_source_site.cpp(registered intests/cpp_unit_tests/CMakeLists.txt), two tests and two checks intests/regression_tests/birth_mesh/test.py, and the continuous-energy testtests/regression_tests/birth_mesh_ce. The third commit adds a fifth test tobirth_mesh_ce. None of these tests needs reference files.test_source_site, Catch2). On an MPI build, it first checks thatmpi::source_sitecovers every byte ofSourceSite: its extent, its true extent, and its size (MPI_Type_size) all equalsizeof(SourceSite). Withbirth_mesh_binthe struct has no padding, so a datatype that leaves out a member or part of one, or gives a member a type of another size, holds fewer or more bytes than the struct. It then fills every byte of 4 sites per rank with a pattern that depends on the rank, the site, and the byte, sets every member to a distinct value, and sends the sites to the next rank withMPI_Sendrecv. The received sites must equal the sent ones member by member and byte for byte, and the 2 sites after them in the receive buffer must be untouched. The byte comparison also covers members that the test does not name, such as Keep the lifetime clock and delayed group of (n,xn) and split neutrons #4178'slifetime. CTest runs it on one rank astest_source_siteand on two ranks astest_source_site_mpi, with--order decl, because Catch2 3.16.0, the version that CI builds, orders test cases randomly on each rank. On a build without MPI, it is skipped.openmc.liblayout (test_birth_mesh_lib_source_bank). It runs the multigroup two-slab problem throughopenmc.lib, with a regular mesh of 2.5 cm bins along x and 5 cm bins along y that leaves the outer half of the right slab outside. After the last batch, the fieldsr,u,E,time,wgt,delayed_group,surf_id,particle, andbirth_mesh_binofopenmc.lib.source_bank()must equal thesource_bankandbirth_mesh_bindatasets of the state point, so a member of the ctypes mirror at a wrong offset or of a wrong type shows up as a mismatch. The tags must include-1and at least two bins. The test also compares each tag with the bin of the source site from which the neutron that produced the site was started (parent_idindexes the source bank of the last batch): with these data no secondary neutrons are created, so that source site is the neutron's birth position, and the two bins must be equal for every site, while the bin of the site itself differs for some.test_birth_mesh_any_mesh, 6 cases). Eight 10 cm fuel islands sit in the absorber of the two-slab data (Σt = Σa = 10 /cm), which fills the rest of a 120 cm × 120 cm × 10 cm box with vacuum faces along x and y and reflective faces along z. Any two islands are at least 18 cm (180 mean free paths) apart, so a site and the birth position of the neutron that produced it lie in the same island, by the argument above. Each island lies inside one element of the birth mesh or wholly outside the mesh. A tally[CellFilter(islands), MeshFilter(mesh)]with a collision estimator gives, in the same run, the bin of each island: exactly one nonzero bin per island inside the mesh, distinct for distinct islands, and none for an island outside. Every site must lie in one island, every island must hold sites, and every tag must equal the bin of its island, or-1outside the mesh. The six cases are a regular 4 × 4 × 1 mesh; the same mesh moved by (30, −30, 0) cm, which leaves three islands outside; the moved mesh checked against aMeshFilterwith that translation on the unmoved mesh; a nonuniform rectilinear mesh; a cylindrical mesh; and a spherical mesh, the last two with four azimuthal sectors and an origin off the center. The reference is theMeshFilteritself, not a Python reimplementation of the bin numbering. In all six cases, the bins that the tally gives the islands agree with bins computed from the geometry before the test was first run.birth_mesh_ce, 5 tests). The multigroup tests cannot reach the continuous-energy tagging line inphysics.cpp; this test does, with the NNDC library that CI uses. Two 10 cm slabs of U-235 and H-1 are separated by 10 cm of B-10 at 50 g/cm³. The total cross section of B-10 is at least 1.4 b below 20 MeV, so the layer is more than 40 mean free paths thick at every energy, and a neutron born in one slab causes fissions only in that slab. The checks are those of the multigroup test: with one bin per slab, every tag equals the bin of its own site; with two bins per slab along x and two along y, some tags differ from the bin of the site and none leaves the slab; with a mesh over the left half, the sites in the right slab are tagged-1. A fourth test runs the problem throughopenmc.liband compares each tag with the bin of the source site from which its history was started. A neutron created by an (n,2n) or similar reaction in that history is born at the reaction instead, so the test requires at least 99 % of the tags to equal that bin, and more than 10 % to differ from the bin of the site. In a diagnostic run, 1 of the 1000 tags differed from it; the neutron that produced that site was such a secondary, and its tag was the bin of its own birth point. A fifth test, from the third commit, covers the path by which a file source reads the tags, where the bank is read whole rather than distributed as on restart. A tagged run writessource.h5, and a second run with aFileSourceon that file is initialized throughopenmc.lib; aftersimulation_init(), every site of the first batch must carry the tag of the file site at the same position. It uses continuous energy because ondevelopa multigroupFileSourcereads the stored group index as an energy (Multigroup FileSource reads the group index stored in OpenMC source files as an energy in eV #4183). The same checks were first made by hand, measured on develop 3cded0f with an earlier version of the first commit and ENDF/B-VII.1 data (k = 1.278, 4000 sites): with two bins per slab, 1714 of the 4000 tags differed from the site bin, and none left its slab.test_birth_mesh_restartrequires that the restart run prints no HDF5 diagnostics, andtest_birth_mesh_source_filesthatParticleList.export_to_hdf5()writes the format version of the source files that OpenMC writes.The
openmc.libparts oftest_birth_mesh_lib_source_bank,test_birth_mesh_ce_birth_position,test_birth_mesh_ce_file_source, andtest_birth_mesh_restartrun in the pytest process, always in history mode, also under--event. Under--mpi, the first three tests are skipped, and so is theopenmc.libpart of the fourth.On develop aa4afc4 with the first two commits,
birth_mesh(13 tests),birth_mesh_ce(4),test_settings.py(3), andtest_statepoint.py(2) pass in history and--eventmode, 22 of 22. With--mpi(2 ranks × 2 threads, through the wrapper described under "MPI runs", withmpi4pyavailable), 20 pass and the 2openmc.libtests are skipped. With the third commit,birth_mesh(13) andbirth_mesh_ce(5) pass in history and--eventmode, 18 of 18, and with--mpi15 pass and the 3openmc.libtests are skipped.Tests of the random ray and MCPL behavior (fifth and seventh commits)
Both tests are in
tests/regression_tests/birth_mesh/test.py, use the multigroup two-slab problem, run OpenMC as a subprocess in every mode, and count the warnings in its output.test_birth_mesh_random_ray. The problem solved with the random ray solver and a birth mesh: the random ray warning is printed once by each process, neither the fixed-source warning nor the MCPL warning is printed, and the state point has nobirth_mesh_idand nobirth_mesh_bin.test_birth_mesh_mcpl_warning, 10 cases. The MCPL warning must be printed exactly once, by the master process, in three cases, and never in the seven others:overwrite_latestwithwritefalse, from the seventh commit;overwrite_latestwith HDF5 output (source.h5must hold tags); an empty list of batches; MCPL withseparatefalse (the state point must hold tags); MCPL withwritefalse; fixed-source mode (with the fixed-source warning); and the random ray solver (with the random ray warning).mcpl-config, as the other MCPL tests are. Theoverwrite_latestcase writes an MCPL file at every batch, so without the library the run stops at the first write, after the warning; the test accepts that stop, and only with the error that the MCPL library is not available. With the library, it checks thatsource.mcplis written.At the fourth commit, before its tests,
birth_mesh,birth_mesh_ce,test_settings.py, andtest_statepoint.py(23 tests) pass in history mode; with--mpi, 20 pass and the 3openmc.libtests are skipped. At the sixth commit, with the tests of the fifth, the same files (31 tests) give 30 passed and 1 skipped in history mode, and 27 passed and 4 skipped with--mpi. At the seventh,birth_mesh(24 tests) gives 23 passed and 1 skipped in history and--eventmode, and 22 passed and 2 skipped with--mpi. The skipped tests are the MCPL case above and, under--mpi, theopenmc.libtests. These runs were made on develop 3aec0ed; the--mpiruns usedmpi4pyand the wrapper described under "MPI runs".Unit tests (first commit)
tests/unit_tests/test_settings.py:Settings.birth_meshis part of the full export/import round trip. A newtest_birth_meshchecks the defaultNone, rejects a bare mesh ID, writes no element when unset, writes a mesh shared withufs_meshonly once, round-trips aCylindricalMeshbirth mesh, and raisesValueErrorfor an undefined mesh ID.tests/unit_tests/test_statepoint.py:StatePoint.birth_mesh_binandStatePoint.birth_mesh_idon hand-written files: tags present (the ID is returned as anint); tagged but with a separate source file (ID present, tagsNone); untagged (bothNone). These files have version[18, 0], so the test also covers older minor versions.Mutation check
Measured on develop 3cded0f, with an earlier version of the first commit. Each mutation edits one spot of C++ in a scratch tree of the commit and rebuilds; then the
birth_meshtest is run. All 11 are detected, and the unmutated tree passes:p.r()instead ofp.r_born()birth_position(both meshes)eigenvalue,source_files,birth_position,restartread_source_bank()ignores the tag datasetrestartrestartsource_filesbirth_mesh_idwritten only when the source bank is in the state pointsource_filesfixed_sourcebirth_position[True](rectilinear)For the tests of the second and third commits, on develop aa4afc4, each mutation below was built and run against this PR's tests: the C++ tests with
cteston MPI builds, and the Python tests in history mode. Every one is detected. In the table, the test names drop the prefixtest_birth_mesh_.birth_mesh_binleft out of the MPI datatype, givenMPI_DOUBLE, or given the displacement ofn_collisiontest_source_site,test_source_site_mpin_collisionandbirth_mesh_binswapped, orbirth_mesh_binleft outlib_source_bank,restart,ce_birth_positionbirth_mesh_binasc_uintlib_source_bankp.r()birth_position[False],birth_position[True],lib_source_bankp.r_last()lib_source_bankp.r()ce_two_bins_per_slab,ce_birth_positionp.r_last()ce_birth_positionfixed_source)any_mesh[rectilinear],any_mesh[cylindrical],any_mesh[spherical],birth_position[True]any_meshcases,birth_position[True],lib_source_bank,ce_two_bins_per_slab,ce_birth_positionlib_source_bank,any_mesh[translated],any_mesh[translated_filter],ce_left_halfrestartce_file_source(third commit)restartrestartParticleList.export_to_hdf5()writes 18.2, orVERSION_STATEPOINTstays 18.2 while it writes 18.3source_filesOn top of #4178, with the same tests:
birth_mesh_binafterlifetime(sizeof(SourceSite)152)test_source_site,test_source_site_mpitest_source_site,test_source_site_mpi,test_secondary_bank_mpilifetimeatdisp[15]in a 17-entry datatypetest_source_site(segmentation fault),test_source_site_mpi,test_secondary_banktest_source_site(segmentation fault),test_source_site_mpi,test_secondary_bank,test_secondary_bank_mpilifetimeasMPI_INTtest_source_site,test_source_site_mpi,test_secondary_bank_mpibirth_mesh_binasMPI_DOUBLEtest_source_site,test_source_site_mpi(test_secondary_bank_mpipasses)birth_mesh_binandlifetimeswapped, orbirth_mesh_binleft outlib_source_bank,restart,ce_birth_positionce_file_source(third commit)For the fourth and sixth commits, each mutation below was built and run against the tests of the fifth and seventh commits, in history mode, and under
--mpiwhere stated. Every one is detected; the two that drop the master-process guard are detected only under--mpi.random_ray,mcpl_warning[random_ray]fixed_source,random_ray,mcpl_warning[fixed_source],mcpl_warning[random_ray]birth_meshmcpl_warning[mcpl]mcpl_warning[mcpl]mcpl_warning[hdf5], and with the sixth commit alsomcpl_warning[overwrite_hdf5]separateconditionmcpl_warning[not_separate]writecondition (fourth commit)mcpl_warning[not_written]mcpl_warning[fixed_source],mcpl_warning[random_ray]--mpi:mcpl_warning[mcpl], and with the sixth commit alsomcpl_warning[overwrite_not_written]overwrite_latestdropped from the condition, as in the fourth commit, orwritestill required for itmcpl_warning[overwrite_not_written]mcpl_warning[empty_batches]mcpl_warning[mcpl]Three changes are not detected:
VERSION_STATEPOINTandParticleList.export_to_hdf5(), since no test pins a version number;openmc.libunder MPI could see them, and the tests runopenmc.libonly without MPI.Compatibility checks across builds
Measured on develop 3cded0f, comparing an earlier version of the first commit with a build of
developat that commit (version 18.2) and with the Python package of that commit:openmc.StatePointon an untagged 18.2 state point from the base buildbirth_mesh_binandbirth_mesh_idareNoneParticleList.from_hdf5on an 18.2 source fileParticleList.export_to_hdf5()file, as its file sourcek_generation, tally results, andsource_bankbitwise identicalThe multigroup file sources in these checks widen
energy_boundsbecause of #4183: ondevelop, a multigroupFileSourcereads the group index that OpenMC stores inEas an energy in eV, so the default bounds reject the group-0 sites of these one-group files, and widening the bounds gives the right group here only because the lowest group edge is 0 (#4191 proposes a fix).Test suite
The test set of CI (
tests/test_matplotlib_import.py tests/unit_tests tests/regression_tests) was run on develop aa4afc4 and on this branch on that base at its first commit. All runs used the same environment: the NNDC HDF5 library (itscross_sections.xmlmatches the md5 intests/conftest.py) andOMP_NUM_THREADS=2, in three pytest processes per run (the unit tests, and the regression tests split in two). All builds are RelWithDebInfo with strict FP and OpenMP on; the MPI build uses parallel HDF5.--event--mpi --mpi-np=2(2 ranks x 2 threads)No test failed. The 8 extra passes in each mode are this PR's 6 regression test cases and its 2 new unit tests (
test_settings.py::test_birth_meshandtest_statepoint.py::test_birth_mesh_tags). The skipped tests and the tests with errors are the same on both sides, and every error is raised at setup by the environment:OPENMC_ENDF_DATA, which was not set (test_data_*.py,test_deplete_chain.py, andtest_endf.py).cpp_driver,source_dlopen, andsource_parameterized_dlopen) build against an installed OpenMC, and these builds were not installed.mpi4py, which was not installed.This PR's own tests, at its first commit, run on their own (
birth_mesh,test_settings.py, andtest_statepoint.py; 11 tests): all 11 pass in history and--eventmode. With--mpi(2 ranks x 2 threads), 10 pass, andtest_settings.py::test_properties_file_load, which this PR does not change, errors at setup becausempi4pyis missing, as ondevelop; with the system'smpi4py3.1.5 made available, it passes on both. These 11 tests are among the 22 run with the first two commits (see "Tests added in the second and third commits").MPI runs. The MPI runs used Open MPI 4.1.6, through a small wrapper around
mpiexecthat unsets theOMPI_*,PMIX_*,OPAL_*, andORTE_*environment variables before it callsmpiexec. An MPI initialization inside the pytest process leaves these variables in that process, and without the wrapper every latermpiexec -n 2 openmcin it aborts, ondevelopand on this PR alike. Withoutmpi4py, that initialization is the in-processopenmc.lib.init()of the CMFD regression tests (a singletonMPI_Initunder Open MPI). Withmpi4pyinstalled, it is already the import ofopenmc.lib, whichtests/conftest.pyperforms at collection:openmc.lib.coreimportsopenmc.mpi, which importsmpi4py.MPI. The runs at the first commit kept process binding off (OMPI_MCA_hwloc_base_binding_policy=none, which their wrapper preserves); the runs with the second and third commits used Open MPI's default binding.Reference files. Regenerating every regression reference with
pytest --update tests/regression_tests(history mode) on develop aa4afc4 and on this branch at its first commit gives byte-identical files, apart from run-to-run differences and the format version:collision_track, the fourdeplete_*directories,microxs,surface_source,surface_source_write). All 58 also differ between two runs ofdevelop.develop. In 29 of them only timestamps differ: the wall-clockdepletion timedataset of the depletion results (22 files) and the modification times in HDF5 object headers (7 files), which also differ between the two runs ofdevelop. The other 23 are surface source references (surface_sourceandsurface_source_writecases 01 to 21 and a01), whose rootversionattribute is written as[18, 3]instead of[18, 2]; apart from that, they differ only in object-header times.No committed reference file is changed by this PR. A later
--updatewould write version 18.3 into those 23 files; their tests compare only thesource_bankdataset, so the committed files still pass.Other checks
ctestat the first commit, on the serial and the MPI build: 14 tests, 13 passed,test_mcpl_stat_sumskipped because MCPL is not enabled. With the second commit: 15 tests on the serial build, 13 passed, withtest_mcpl_stat_sumandtest_source_site(which needs MPI) skipped; and 16 on the MPI build, 15 passed, withtest_mcpl_stat_sumskipped, built once with the system's Catch2 3.4.0 and once with Catch2 3.16.0, the version pinned incmake/Dependencies.cmake. On the Catch2 3.16.0 build,test_source_site_mpipassed 20 runs out of 20, each with 90 assertions in 2 test cases per rank. The third commit changes no C++ file, and gives the same counts.--dry-run --Werror) reports no changes for the 10 C++ files that the first commit changes. pyflakes 3.2.0 on its 8 changed Python files and pycodestyle 2.11.1 on its Python diff report nothing. For the second commit, clang-format reports no changes fortest_source_site.cpp, and pyflakes on the two changed test files and pycodestyle on the Python diff report nothing; for the third commit, pyflakes onbirth_mesh_ce/test.pyand pycodestyle on its diff report nothing.ctest15 tests on the serial build (13 passed,test_mcpl_stat_sumandtest_source_siteskipped) and 16 on the MPI build (15 passed,test_mcpl_stat_sumskipped). clang-format 18.1.8 reports no changes forsrc/settings.cppandinclude/openmc/settings.h, and pyflakes onopenmc/settings.pyandbirth_mesh/test.pyand pycodestyle on the Python diff report nothing.openmc.examples.random_ray_pin_cell()and a birth mesh, the run prints the random ray warning once, and its state point has nobirth_mesh_id; before the fourth commit it printed no warning and recorded the ID. With the Monte Carlo solver, MCPL source points at batches 3 to 5 give one MCPL warning, and the run then stops at the first MCPL write, since the MCPL library is not installed. The same holds withoverwrite_latestandwritefalse, which before the sixth commit gave no warning; an empty list of batches gave a warning before it and gives none since. HDF5 source points give no warning, and their files hold tags.docs/source/io_formatsgave no warnings for the changed sections. It was not repeated after the wording of the mesh rule changed inio_formats/settings.rst, for the new user's guide paragraph, or for the random ray sentences of the fourth commit.On top of #4178. The resolution described under "Related PRs" was built and tested on #4178's commits applied to develop aa4afc4:
sizeof(SourceSite)144,birth_mesh_binat 132,lifetimeat 136, every other offset as ondevelop; the MPI datatype has 17 entries with extent and true extent 144;ctypes.sizeof(_SourceSite)144, every offset equal to the C++ one. Keep the lifetime clock and delayed group of (n,xn) and split neutrons #4178 alone gives the same size, with 4 bytes of padding at offset 132.ctestat the first commit: 16 tests on the serial build and 17 on the MPI build, all passed except the skippedtest_mcpl_stat_sum, among them Keep the lifetime clock and delayed group of (n,xn) and split neutrons #4178'stest_particle,test_secondary_bank, and two-ranktest_secondary_bank_mpi.birth_meshpasses in history,--event, and--mpimode, and the IFP regression tests andtest_ifp_weight_windows.pypass in history mode.tests/cpp_unit_tests/CMakeLists.txt, is resolved as described under "Related PRs"):ctestruns 17 tests on the serial build, 15 passed and 2 skipped (test_mcpl_stat_sumandtest_source_site), and 19 on the MPI build, 18 passed andtest_mcpl_stat_sumskipped, among themtest_source_site_mpiand Keep the lifetime clock and delayed group of (n,xn) and split neutrons #4178'stest_secondary_bank_mpi. Intest_source_site, the byte comparison coverslifetime, and the datatype covers all 144 bytes.birth_mesh,birth_mesh_ce,test_settings.py, andtest_statepoint.pygive the same results as without Keep the lifetime clock and delayed group of (n,xn) and split neutrons #4178: 22 of 22 pass in history and--eventmode, and with--mpi20 pass and the 2openmc.libtests are skipped.ctestgives the same counts, andbirth_meshandbirth_mesh_cegive the same results as without Keep the lifetime clock and delayed group of (n,xn) and split neutrons #4178: 18 of 18 in history and--eventmode, and with--mpi15 passed and the 3openmc.libtests skipped.ctest17 tests on the serial build (15 passed, 2 skipped) and 19 on the MPI build (18 passed,test_mcpl_stat_sumskipped), andbirth_meshgives the same results as without Keep the lifetime clock and delayed group of (n,xn) and split neutrons #4178: 23 passed and 1 skipped in history and--eventmode, and 22 passed and 2 skipped with--mpi.Rebase onto 3aec0ed. Unless another base is named, the runs reported in this section for the first three commits were made with this branch on develop aa4afc4. The rebase of those commits onto develop 3aec0ed (#4189 and #4190) changed only
openmc/data/reaction.pyandopenmc/model/model.py, which this PR does not touch; each commit has the same diff as before. The fourth to seventh commits were made on 3aec0ed. At the rebased third commit:ctest15 tests, 13 passed, withtest_mcpl_stat_sumandtest_source_siteskipped;birth_mesh,birth_mesh_ce,test_settings.py, andtest_statepoint.py; 23 tests) pass in history mode;Not run (CI configurations not reproduced):
mpi4py; the runs of this PR's own tests with the second and third commits had the system'smpi4py3.1.5.test_birth_mesh_mcpl_warning[mcpl_written]was skipped, and theoverwrite_latestcase stopped, as its test allows, at the first MCPL write.OPENMC_ENDF_DATAor an installed OpenMC (errors on both sides, listed above).<birth_mesh>element and its warnings; at each of them, this PR's own tests were run on their own (see "Tests added in the second and third commits" and "Tests of the random ray and MCPL behavior").--eventand MPI runs of the CI test set.Compatibility
birth_mesh_bindataset andbirth_mesh_idattribute; nothing is removed or changed, and untagged runs write the same datasets as before.openmc.StatePointchecks only the major version, so 18.0–18.2 files still load.ParticleList.from_hdf5()does not check the version.read_source_bank()handles files of any version as before; the particle-code check isminor >= 2within major 18.statepoint_version_check()requires an exact match, as for the 18.1 → 18.2 bump in Refactor ParticleType to use PDG Monte Carlo numbering scheme #3756. A restart from a state point written bydevelop(18.2) is therefore refused, and so is the reverse. This PR does not change that rule.openmc_properties_export()also writesVERSION_STATEPOINTas its version attribute, soproperties.h5files now say 18.3. No reader checks it.Settings.birth_mesh(anyopenmc.MeshBase, defaultNone),StatePoint.birth_mesh_bin(numpy.ndarrayorNone) andStatePoint.birth_mesh_id(intorNone).openmc.lib.source_bank()records gain abirth_mesh_binfield.SourceSitegainsbirth_mesh_bin(default-1) in its former tail padding; all other members keep their offsets and the size is unchanged.write_source_point(),write_h5_source_point(), andwrite_source_bank()gain a trailingbool birth_mesh_tags = false, so existing calls still compile and behave as before; the signatures change at the ABI level. New global:settings::birth_mesh(const Mesh*). Compiled source libraries need no source changes, but should be rebuilt against the new headers.<birth_mesh>element is ignored with a warning in fixed-source mode and with the random ray solver. An eigenvalue run with a birth mesh that writes MCPL source points prints one warning.Related PRs
This PR is based on
developand does not depend on any other PR. Several of my open PRs touch the same files:SourceSitemember aftern_collision(double lifetime). The two PRs conflict textually ininclude/openmc/particle_data.h,openmc/lib/core.py, andsrc/initialize.cpp. Whichever is merged second keeps both members in the ordern_collision,birth_mesh_bin,lifetime, in the struct, in_SourceSite, and in the MPI datatype. One part of the merge needs a manual edit: both PRs grow the MPI datatype from 15 to 16 entries in the same way, so git merges the four count lines ofinitialize_mpi()(disp[16], the loop bound, the block-count list, and the count passed toMPI_Type_create_struct()) without a conflict, at 16. They must become 17, withlifetimeatdisp[16]; otherwise the datatype misses one of the two members. With this order,birth_mesh_binfills the 4 bytes of padding that Keep the lifetime clock and delayed group of (n,xn) and split neutrons #4178 leaves beforelifetime, andsizeof(SourceSite)stays at Keep the lifetime clock and delayed group of (n,xn) and split neutrons #4178's 144. With the second commit,tests/cpp_unit_tests/CMakeLists.txtconflicts as well, because both PRs register Catch2 tests at the same two places; whichever is merged second keeps both, Keep the lifetime clock and delayed group of (n,xn) and split neutrons #4178's entries first. This resolution is tested (see "On top of Keep the lifetime clock and delayed group of (n,xn) and split neutrons #4178"). A datatype left at the auto-merged 16 entries fails this PR'stest_source_siteandtest_source_site_mpiand Keep the lifetime clock and delayed group of (n,xn) and split neutrons #4178'stest_secondary_bank_mpi.boolparameter towrite_source_point()andwrite_h5_source_point(), and a file attribute in the same block ofwrite_h5_source_point(), so they conflict textually ininclude/openmc/state_point.h,src/state_point.cpp,src/simulation.cpp,docs/source/io_formats/source.rst, anddocs/source/io_formats/statepoint.rst. Whichever is merged second keeps both: both trailing parameters, the first-merged PR's parameter first; both attributes in the same block; both arguments at the two call sites infinalize_batch(); and both documentation entries.create_fission_sites()insrc/physics.cppandsrc/physics_mg.cpp, where this PR computes the tag before that loop. The two conflict textually there. Keeping both puts the tag computation between Fix analog fission tallies for sites rejected by the time cutoff #4180's newrecord_fission_neutronlambda and its new loop header; the assignment of the tag inside the loop merges without a conflict. I have not built or tested this merge.docs/source/io_formats/statepoint.rstandsrc/simulation.cppwith it, in other places.None of these PRs changes
VERSION_STATEPOINT.AI Assistance
Code, tests, analysis, reviews and text:
Review of the pull request formalities:
Review of the change:
Checklist
Summary by CodeRabbit
-1when outside the mesh.