Repository navigation
Add stat:sum field to MCPL files for proper weight normalization #3522
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
6979b4b
Add stat:sum field to MCPL files for proper weight normalization (#3514)
bpolania 787ba30
Address PR review comments
bpolania 58b1c92
Fix clang-format version 15 compatibility
bpolania e86a06f
Fix Python test to work with actual OpenMC simulation
bpolania a255618
Fix stat:sum to use total source particles, not file particle count
bpolania 2a93f20
Apply clang-format formatting
bpolania a52b668
Account for gen_per_batch
paulromano 5f583fb
Simplify test
paulromano File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| #include <catch2/catch_test_macros.hpp> | ||
| #include <cstdio> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| #include "openmc/bank.h" | ||
| #include "openmc/mcpl_interface.h" | ||
|
|
||
| // Test the MCPL stat:sum functionality (issue #3514) | ||
| TEST_CASE("MCPL stat:sum field") | ||
| { | ||
| // Check if MCPL interface is available | ||
| if (!openmc::is_mcpl_interface_available()) { | ||
| SKIP("MCPL library not available"); | ||
| } | ||
|
|
||
| SECTION("stat:sum field is written to MCPL files") | ||
| { | ||
| // Create a temporary filename | ||
| std::string filename = "test_stat_sum.mcpl"; | ||
|
|
||
| // Create some test particles | ||
| std::vector<openmc::SourceSite> source_bank(100); | ||
| std::vector<int64_t> bank_index = {0, 100}; // 100 particles total | ||
|
|
||
| // Initialize test particles | ||
| for (int i = 0; i < 100; ++i) { | ||
| source_bank[i].particle = openmc::ParticleType::neutron; | ||
| source_bank[i].r = {i * 0.1, i * 0.2, i * 0.3}; | ||
| source_bank[i].u = {0.0, 0.0, 1.0}; | ||
| source_bank[i].E = 2.0e6; // 2 MeV | ||
| source_bank[i].time = 0.0; | ||
| source_bank[i].wgt = 1.0; | ||
| } | ||
|
|
||
| // Write the MCPL file | ||
| openmc::write_mcpl_source_point(filename.c_str(), source_bank, bank_index); | ||
|
|
||
| // Verify the file was created | ||
| FILE* f = std::fopen(filename.c_str(), "r"); | ||
| REQUIRE(f != nullptr); | ||
| std::fclose(f); | ||
|
|
||
| // Read the file back to check stat:sum | ||
| // Note: This would require mcpl_open_file and checking the header | ||
| // Since we can't easily read MCPL headers in C++ without the full MCPL API, | ||
| // we rely on the Python test to verify the actual content | ||
|
|
||
| // Clean up | ||
| std::remove(filename.c_str()); | ||
| } | ||
|
|
||
| SECTION("stat:sum uses correct particle count") | ||
| { | ||
| std::string filename = "test_count.mcpl"; | ||
|
|
||
| // Test with different particle counts | ||
| std::vector<int> test_counts = {1, 10, 100, 1000}; | ||
|
|
||
| for (int count : test_counts) { | ||
| std::vector<openmc::SourceSite> source_bank(count); | ||
| std::vector<int64_t> bank_index = {0, count}; | ||
|
|
||
| // Initialize particles | ||
| for (int i = 0; i < count; ++i) { | ||
| source_bank[i].particle = openmc::ParticleType::neutron; | ||
| source_bank[i].r = {0.0, 0.0, 0.0}; | ||
| source_bank[i].u = {0.0, 0.0, 1.0}; | ||
| source_bank[i].E = 1.0e6; | ||
| source_bank[i].time = 0.0; | ||
| source_bank[i].wgt = 1.0; | ||
| } | ||
|
|
||
| // Write MCPL file | ||
| openmc::write_mcpl_source_point( | ||
| filename.c_str(), source_bank, bank_index); | ||
|
|
||
| // The stat:sum should equal count (verified by Python test) | ||
| // Here we just verify the file was created successfully | ||
| FILE* f = std::fopen(filename.c_str(), "r"); | ||
| REQUIRE(f != nullptr); | ||
| std::fclose(f); | ||
|
|
||
| // Clean up | ||
| std::remove(filename.c_str()); | ||
| } | ||
| } | ||
|
|
||
| SECTION("stat:sum handles empty particle bank") | ||
| { | ||
| std::string filename = "test_empty.mcpl"; | ||
|
|
||
| // Create empty particle bank | ||
| std::vector<openmc::SourceSite> source_bank; | ||
| std::vector<int64_t> bank_index = {0}; | ||
|
|
||
| // This should still create a valid MCPL file with stat:sum = 0 | ||
| openmc::write_mcpl_source_point(filename.c_str(), source_bank, bank_index); | ||
|
|
||
| // Verify file was created | ||
| FILE* f = std::fopen(filename.c_str(), "r"); | ||
| REQUIRE(f != nullptr); | ||
| std::fclose(f); | ||
|
|
||
| // Clean up | ||
| std::remove(filename.c_str()); | ||
| } | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| #!/usr/bin/env python | ||
| """Test for MCPL stat:sum functionality (issue #3514)""" | ||
|
|
||
| import os | ||
| import shutil | ||
| import tempfile | ||
| import pytest | ||
| import openmc | ||
|
|
||
| # Skip test if MCPL is not available | ||
| pytestmark = pytest.mark.skipif( | ||
| shutil.which("mcpl-config") is None, | ||
| reason="mcpl-config command not found in PATH; MCPL is likely not available." | ||
| ) | ||
|
|
||
|
|
||
| def test_mcpl_stat_sum_field(): | ||
| """Test that MCPL files contain proper stat:sum field with particle count.""" | ||
|
|
||
| # Only run if mcpl module is available for verification | ||
| mcpl = pytest.importorskip("mcpl") | ||
|
|
||
| with tempfile.TemporaryDirectory() as tmpdir: | ||
| # Create a simple model | ||
| model = openmc.Model() | ||
|
|
||
| # Create a simple material | ||
| mat = openmc.Material() | ||
| mat.add_nuclide('U235', 1.0) | ||
| mat.set_density('g/cm3', 10.0) | ||
| model.materials = [mat] | ||
|
|
||
| # Create a simple geometry (sphere) | ||
| sphere = openmc.Sphere(r=10.0, boundary_type='vacuum') | ||
| cell = openmc.Cell(fill=mat, region=-sphere) | ||
| model.geometry = openmc.Geometry([cell]) | ||
|
|
||
| # Configure settings for MCPL output | ||
| model.settings = openmc.Settings() | ||
| model.settings.batches = 5 | ||
| model.settings.inactive = 2 | ||
| model.settings.particles = 1000 | ||
| model.settings.source = openmc.Source(space=openmc.stats.Point()) | ||
|
|
||
| # Enable MCPL source file writing | ||
| model.settings.source_point = {'mcpl': True, 'separate': True} | ||
|
|
||
| # Run the simulation | ||
| cwd = os.getcwd() | ||
| try: | ||
| os.chdir(tmpdir) | ||
| model.run(output=False) | ||
|
|
||
| # Find the MCPL file | ||
| import glob | ||
| mcpl_files = glob.glob('source.*.mcpl*') | ||
| assert len(mcpl_files) > 0, "No MCPL source files were created" | ||
|
|
||
| # Open and check the MCPL file | ||
| mcpl_file = mcpl_files[0] | ||
| with mcpl.MCPLFile(mcpl_file) as f: | ||
| # Check if stat:sum field exists | ||
| # The stat:sum should be accessible through the header | ||
| # Look for the "openmc_np1" key | ||
| comments = f.comments if hasattr(f, 'comments') else [] | ||
|
tkittel marked this conversation as resolved.
Resolved
|
||
|
|
||
| # Check for stat:sum in comments (MCPL stores these as comments) | ||
| stat_sum_found = False | ||
| stat_sum_value = None | ||
|
|
||
| for comment in comments: | ||
|
tkittel marked this conversation as resolved.
Resolved
|
||
| if 'stat:sum:openmc_np1' in comment: | ||
| stat_sum_found = True | ||
| # Extract the value | ||
| parts = comment.split(':') | ||
| if len(parts) >= 4: | ||
| stat_sum_value = float(parts[3].strip()) | ||
| break | ||
|
|
||
| assert stat_sum_found, "stat:sum:openmc_np1 field not found in MCPL file" | ||
|
|
||
| # The value should be the total number of source particles | ||
| # For 5 batches with 1000 particles each = 5000 total | ||
| expected_particles = model.settings.batches * model.settings.particles | ||
|
|
||
| # If stat:sum was properly updated, it should equal expected_particles | ||
| # If it's still -1, the update before closing didn't work | ||
| assert stat_sum_value != -1, "stat:sum was not updated from initial -1 value" | ||
| assert stat_sum_value == expected_particles, \ | ||
| f"stat:sum value {stat_sum_value} doesn't match expected {expected_particles}" | ||
|
|
||
| finally: | ||
| os.chdir(cwd) | ||
|
|
||
|
|
||
| def test_mcpl_stat_sum_crash_safety(): | ||
| """Test that incomplete MCPL files have stat:sum = -1.""" | ||
|
|
||
| # This test would verify that if file creation is interrupted, | ||
| # the stat:sum field remains at -1 to indicate incomplete file | ||
| # This is harder to test without mocking the MCPL library internals | ||
|
|
||
| # For now, we can at least document the expected behavior: | ||
| # 1. When mcpl_create_outfile is called, stat:sum should be set to -1 | ||
| # 2. Only when mcpl_close_outfile is called should it be updated | ||
| # 3. If the program crashes between these calls, stat:sum remains -1 | ||
|
|
||
| # This could be tested with a C++ unit test that directly uses the | ||
| # mcpl_interface functions and simulates a crash | ||
| pass | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| # Allow running this test directly | ||
| test_mcpl_stat_sum_field() | ||
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.
Uh oh!
There was an error while loading. Please reload this page.