Description
Digging into https://openmc.discourse.group/t/openmc-s-gamma-transport-with-ww-just-slow-or-am-i-doing-something-wrong/6477
has revealed a number of issues.
The first three could be considered a bug, the last two, more like feature enhancements.
1. Coincident Surface and Mesh Boundary
When a particle crosses a geometry surface with weight-window surface checkpoints on, OpenMC looks up its weight window by position only, and at that moment the particle sits exactly on the surface. If that surface is also a boundary of the weight-window mesh, planar meshes assign a point on a boundary to the lower-index element; on cylindrical and spherical meshes roundoff decides. So a particle moving in +x, +y or +z is checked against the window of the element it just left, and is not split; a particle moving the other way gets the correct window. The result is one-sided variance reduction: good statistics on the −x/−y/−z sides, poor on the + sides.
This is the central problem in:
https://openmc.discourse.group/t/openmc-s-gamma-transport-with-ww-just-slow-or-am-i-doing-something-wrong/6477
Examples of the problem:
demo_surface.py
demo_surface_material.py
demo_surface_material_surface_only.py
Options:
#4156
Which agrees with what my Agent recommended as a solution
2. No Surface Checkpoint at Lattice Crossings
With the surface checkpoint on, OpenMC applies no weight-window check when a particle crosses from one lattice element to the next, because the lattice branch of event_cross_surface never calls apply_weight_windows.
Consequences:
Self-evidently, not great to not have.
Examples of the problem:
demo_lattice.py
demo_lattice_material.py
demo_lattice_material_surface_only.py
Options:
After cross_lattice in the lattice branch, call apply_weight_windows(*this) when settings::weight_window_checkpoint_surface is set. The lookup must use the entered element, which #4156 provides. This fix needs to build on #4156
3. Track file with the shared secondary bank
With weight windows on, the default shared secondary bank writes split copies as separate tracks.h5 datasets with no link to their history, lets them consume the max_tracks quota, and gives each an empty leading particle track.
Consequences:
Bad track file when shared secondary bank is on (fixed-source runs with weight windows )
Options:
Carry write_track and the parent id in SourceSite, append the secondary's states to the parent's dataset or write parent_id as a dataset attribute; exclude secondaries from the max_tracks count; skip add_particle_track in initialize_particle_track when is_secondary.
4. Splitting on entering a Void, Rouletting on exiting the Void.
A surface checkpoint splits a particle on entering a void cell, producing copies with identical position and direction that stay identical until they reach material, so the split only multiplies void tracking.
If the window on the far side of the void is higher, the copies are rouletted as soon as they leave it.
Consequences:
An efficiency problem, not a correctness problem.
All this extra unneeded splitting and rouletting reduces performance and can compromise the effectiveness of the WW in certain situations around these voids.
Ex.: in a void with a window lower than its neighbours, OpenMC splits 4-for-1 on entry and roulettes on exit, ending with 0.67 particles of weight 1.5 per entering history, where a run without windows has 1.0 of weight 1.0. if we remove void splitting/rouletting; the particle enters the far side unchanged.
Examples:
demo_void.py
Options:
At a surface checkpoint, skip the split branch (keep roulette) when the cell entered is void: if (material() == MATERIAL_VOID) ... before the split in apply_weight_window, or a flag passed from event_cross_surface.
5. Weight Window checkpoint after N number of MFP
MCNP checks mesh-based weight windows after one mean free path of free flight in addition to collisions and surfaces (WWP nmfp, default 1), so the window is checked about once per mean free path even in cells spanning many mesh elements. OpenMC has no equivalent; a third checkpoint type is proposed.
Consequences:
As a dice rolling gambling man 🧙♂️🎲
I would wager there are cases where this behaviour improves weight window VR.
Options:
A third checkpoint in <weight_window_checkpoints>, e.g. <mean_free_path>1.0</mean_free_path>, applying only when weight windows are on: in Particle::event_advance, limit the step to nmfp / macro_xs().total, and at that point apply the weight window without a collision (a "pseudo-collision"), then continue the flight. Void has infinite mean free path, so the check never fires there
@GuySten ;
I entrust these to your good hands.
Environment
OpenMC latest develop branch
Linux
Description
Digging into https://openmc.discourse.group/t/openmc-s-gamma-transport-with-ww-just-slow-or-am-i-doing-something-wrong/6477
has revealed a number of issues.
The first three could be considered a bug, the last two, more like feature enhancements.
1. Coincident Surface and Mesh Boundary
When a particle crosses a geometry surface with weight-window surface checkpoints on, OpenMC looks up its weight window by position only, and at that moment the particle sits exactly on the surface. If that surface is also a boundary of the weight-window mesh, planar meshes assign a point on a boundary to the lower-index element; on cylindrical and spherical meshes roundoff decides. So a particle moving in +x, +y or +z is checked against the window of the element it just left, and is not split; a particle moving the other way gets the correct window. The result is one-sided variance reduction: good statistics on the −x/−y/−z sides, poor on the + sides.
This is the central problem in:
https://openmc.discourse.group/t/openmc-s-gamma-transport-with-ww-just-slow-or-am-i-doing-something-wrong/6477
Examples of the problem:
demo_surface.py
demo_surface_material.py
demo_surface_material_surface_only.py
Options:
#4156
Which agrees with what my Agent recommended as a solution
2. No Surface Checkpoint at Lattice Crossings
With the surface checkpoint on, OpenMC applies no weight-window check when a particle crosses from one lattice element to the next, because the lattice branch of event_cross_surface never calls apply_weight_windows.
Consequences:
Self-evidently, not great to not have.
Examples of the problem:
demo_lattice.py
demo_lattice_material.py
demo_lattice_material_surface_only.py
Options:
After
cross_latticein the lattice branch, callapply_weight_windows(*this)whensettings::weight_window_checkpoint_surfaceis set. The lookup must use the entered element, which #4156 provides. This fix needs to build on #41563. Track file with the shared secondary bank
With weight windows on, the default shared secondary bank writes split copies as separate tracks.h5 datasets with no link to their history, lets them consume the max_tracks quota, and gives each an empty leading particle track.
Consequences:
Bad track file when shared secondary bank is on (fixed-source runs with weight windows )
Options:
Carry
write_trackand the parent id inSourceSite, append the secondary's states to the parent's dataset or writeparent_idas a dataset attribute; exclude secondaries from themax_trackscount; skipadd_particle_trackininitialize_particle_trackwhenis_secondary.4. Splitting on entering a Void, Rouletting on exiting the Void.
A surface checkpoint splits a particle on entering a void cell, producing copies with identical position and direction that stay identical until they reach material, so the split only multiplies void tracking.
If the window on the far side of the void is higher, the copies are rouletted as soon as they leave it.
Consequences:
An efficiency problem, not a correctness problem.
All this extra unneeded splitting and rouletting reduces performance and can compromise the effectiveness of the WW in certain situations around these voids.
Ex.: in a void with a window lower than its neighbours, OpenMC splits 4-for-1 on entry and roulettes on exit, ending with 0.67 particles of weight 1.5 per entering history, where a run without windows has 1.0 of weight 1.0. if we remove void splitting/rouletting; the particle enters the far side unchanged.
Examples:
demo_void.py
Options:
At a surface checkpoint, skip the split branch (keep roulette) when the cell entered is void:
if (material() == MATERIAL_VOID) ...before the split inapply_weight_window, or a flag passed fromevent_cross_surface.5. Weight Window checkpoint after N number of MFP
MCNP checks mesh-based weight windows after one mean free path of free flight in addition to collisions and surfaces (WWP nmfp, default 1), so the window is checked about once per mean free path even in cells spanning many mesh elements. OpenMC has no equivalent; a third checkpoint type is proposed.
Consequences:
As a dice rolling gambling man 🧙♂️🎲
I would wager there are cases where this behaviour improves weight window VR.
Options:
A third checkpoint in
<weight_window_checkpoints>, e.g.<mean_free_path>1.0</mean_free_path>, applying only when weight windows are on: inParticle::event_advance, limit the step tonmfp / macro_xs().total, and at that point apply the weight window without a collision (a "pseudo-collision"), then continue the flight. Void has infinite mean free path, so the check never fires there@GuySten ;
I entrust these to your good hands.
Environment
OpenMC latest develop branch
Linux