Replace grep-style test with proper pytest that runs OpenMC simulation

Co-authored-by: jtramm <1009059+jtramm@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-07-10 22:41:28 +00:00
parent ab0cfbad52
commit e90e6ff020

View file

@ -1,155 +1,159 @@
"""
Test module for verifying the fix for multiple point sources in same subdivided
source region in the Random Ray Solver.
Test for the Random Ray Solver multiple point sources error detection fix.
This test addresses the issue where multiple point sources placed in the same
subdivided source region would silently overwrite each other. The fix adds
proper error detection to prevent this condition.
The tests verify:
1. Multiple point sources in the same subdivided region trigger an error
2. Multiple point sources in different subdivided regions work correctly
3. Multiple point sources work correctly when mesh subdivision is disabled
Note: These tests require a fully built OpenMC C++ executable to run the
actual simulations. In CI environments where the executable is available,
the full error detection will be tested.
This test verifies the fix for issue #3470 where multiple point sources
placed in the same subdivided source region would silently overwrite each other.
The fix adds proper error detection to prevent this condition.
"""
import pytest
import openmc
from openmc.examples import random_ray_three_region_cube
def test_error_detection_setup():
"""Test that demonstrates the setup for detecting multiple point sources error.
def test_multiple_point_sources_same_location_error():
"""Test that multiple point sources at the same location trigger an error
when mesh subdivision is enabled."""
This test documents the scenario that should trigger the error:
- Random ray solver with mesh subdivision enabled
- Multiple point sources at the same location (same mesh bin)
- Should raise RuntimeError with specific message
# Create a basic random ray model
model = random_ray_three_region_cube()
The actual error detection happens in the C++ code in:
src/random_ray/flat_source_domain.cpp:convert_external_sources()
"""
# Enable mesh subdivision - this is required to trigger the error
mesh = openmc.RegularMesh()
mesh.dimension = (4, 4, 4)
mesh.lower_left = (0.0, 0.0, 0.0)
mesh.upper_right = (30.0, 30.0, 30.0)
# Expected error message from C++ code
expected_error_msg = (
"Multiple point sources detected in the same subdivided source "
"region.This is not currently supported in the random ray solver."
# Get the source universe to apply mesh subdivision
source_universe = model.geometry.get_universes_by_name('source universe')[0]
model.settings.random_ray['source_region_meshes'] = [
(mesh, [source_universe])
]
# Create multiple point sources at the exact same location
# This should trigger the error when mesh subdivision is enabled
point_location = openmc.stats.Point(xyz=(5.0, 5.0, 5.0))
energy_dist = openmc.stats.Discrete([10.0], [1.0])
source1 = openmc.IndependentSource(
space=point_location,
energy=energy_dist,
strength=1.0
)
source2 = openmc.IndependentSource(
space=point_location, # Same location
energy=energy_dist,
strength=1.0
)
# Document the test scenario
test_scenario = {
'solver': 'random_ray',
'mesh_subdivision': True,
'point_sources': [
{'location': (5.0, 5.0, 5.0), 'strength': 1.0},
{'location': (5.0, 5.0, 5.0), 'strength': 1.0} # Same location
],
'expected_result': 'RuntimeError',
'expected_message': expected_error_msg
}
# Set the multiple point sources
model.settings.source = [source1, source2]
# Verify the test scenario is well-defined
assert test_scenario['solver'] == 'random_ray'
assert test_scenario['mesh_subdivision'] is True
assert len(test_scenario['point_sources']) == 2
assert (test_scenario['point_sources'][0]['location'] ==
test_scenario['point_sources'][1]['location'])
assert test_scenario['expected_result'] == 'RuntimeError'
assert 'Multiple point sources detected' in test_scenario['expected_message']
# Very short simulation to just trigger initialization
model.settings.batches = 1
model.settings.inactive = 0
model.settings.particles = 10
# The error should be triggered during initialization/conversion of external sources
with pytest.raises(RuntimeError, match=r"Multiple point sources detected.*same subdivided.*source region"):
model.run()
def test_valid_scenarios_setup():
"""Test scenarios that should NOT trigger the error."""
def test_multiple_point_sources_different_locations_success():
"""Test that multiple point sources at different locations work correctly
with mesh subdivision enabled."""
valid_scenarios = [
{
'description': 'Different locations with mesh subdivision',
'solver': 'random_ray',
'mesh_subdivision': True,
'point_sources': [
{'location': (2.0, 2.0, 2.0), 'strength': 1.0},
{'location': (8.0, 8.0, 8.0), 'strength': 1.0} # Different location
],
'expected_result': 'Success'
},
{
'description': 'Same location without mesh subdivision',
'solver': 'random_ray',
'mesh_subdivision': False,
'point_sources': [
{'location': (5.0, 5.0, 5.0), 'strength': 1.0},
{'location': (5.0, 5.0, 5.0), 'strength': 1.0} # Same location OK
],
'expected_result': 'Success'
}
# Create a basic random ray model
model = random_ray_three_region_cube()
# Enable mesh subdivision
mesh = openmc.RegularMesh()
mesh.dimension = (4, 4, 4)
mesh.lower_left = (0.0, 0.0, 0.0)
mesh.upper_right = (30.0, 30.0, 30.0)
# Get the source universe to apply mesh subdivision
source_universe = model.geometry.get_universes_by_name('source universe')[0]
model.settings.random_ray['source_region_meshes'] = [
(mesh, [source_universe])
]
# Verify all valid scenarios are properly defined
for scenario in valid_scenarios:
assert scenario['solver'] == 'random_ray'
assert len(scenario['point_sources']) == 2
assert scenario['expected_result'] == 'Success'
# Create multiple point sources at different locations
# This should work fine
point_location1 = openmc.stats.Point(xyz=(2.0, 2.0, 2.0))
point_location2 = openmc.stats.Point(xyz=(8.0, 8.0, 8.0))
energy_dist = openmc.stats.Discrete([10.0], [1.0])
# Verify the scenarios are actually different
assert valid_scenarios[0]['mesh_subdivision'] != valid_scenarios[1]['mesh_subdivision']
source1 = openmc.IndependentSource(
space=point_location1,
energy=energy_dist,
strength=1.0
)
source2 = openmc.IndependentSource(
space=point_location2, # Different location
energy=energy_dist,
strength=1.0
)
# Set the multiple point sources
model.settings.source = [source1, source2]
# Very short simulation to just verify it initializes correctly
model.settings.batches = 1
model.settings.inactive = 0
model.settings.particles = 10
# This should not raise an error
try:
model.run()
except RuntimeError as e:
# If we get a RuntimeError, it should NOT be about multiple point sources
assert "Multiple point sources detected" not in str(e)
# Re-raise any other errors (could be due to test environment)
raise
def test_fix_implementation_verification():
"""Verify the fix implementation details."""
def test_multiple_point_sources_no_mesh_subdivision_success():
"""Test that multiple point sources at the same location work correctly
when mesh subdivision is disabled."""
# The fix should be in this file
fix_file = 'src/random_ray/flat_source_domain.cpp'
# Create a basic random ray model without mesh subdivision
model = random_ray_three_region_cube()
# The fix should be in this method
fix_method = 'convert_external_sources'
# Explicitly ensure no mesh subdivision is configured
# (default behavior, but being explicit for clarity)
if 'source_region_meshes' in model.settings.random_ray:
del model.settings.random_ray['source_region_meshes']
# The fix should use this logic pattern
expected_logic = [
'SourceRegionKey key {sr, mesh_bin}',
'auto it = point_source_map_.find(key)',
'if (it != point_source_map_.end())',
'fatal_error("Multiple point sources detected...")',
'point_source_map_[key] = es'
]
# Create multiple point sources at the same location
# This should work fine without mesh subdivision
point_location = openmc.stats.Point(xyz=(5.0, 5.0, 5.0))
energy_dist = openmc.stats.Discrete([10.0], [1.0])
fix_info = {
'file': fix_file,
'method': fix_method,
'logic_pattern': expected_logic,
'lines_added': 5, # Minimal change
'approach': 'Error detection instead of feature support'
}
source1 = openmc.IndependentSource(
space=point_location,
energy=energy_dist,
strength=1.0
)
source2 = openmc.IndependentSource(
space=point_location, # Same location, but no mesh subdivision
energy=energy_dist,
strength=1.0
)
# Verify fix metadata
assert fix_info['file'].endswith('flat_source_domain.cpp')
assert fix_info['method'] == 'convert_external_sources'
assert len(fix_info['logic_pattern']) == 5
assert fix_info['lines_added'] == 5
assert 'Error detection' in fix_info['approach']
# Set the multiple point sources
model.settings.source = [source1, source2]
print(f"Fix implemented in: {fix_info['file']}")
print(f"Method: {fix_info['method']}")
print(f"Lines added: {fix_info['lines_added']}")
print(f"Approach: {fix_info['approach']}")
# Integration test placeholder for when OpenMC is fully available
def test_multiple_point_sources_error_detection_integration():
"""Integration test for the actual error detection.
# Very short simulation to just verify it initializes correctly
model.settings.batches = 1
model.settings.inactive = 0
model.settings.particles = 10
This test should be run in environments where OpenMC C++ executable
is built and available. It will create the actual error scenario and
verify the RuntimeError is raised.
Currently marked as expected to be skipped in environments without
the full OpenMC installation.
"""
pytest.skip("Requires full OpenMC C++ build - integration test placeholder")
# When OpenMC is available, this test would:
# 1. Create a model with random ray solver
# 2. Enable mesh subdivision
# 3. Add multiple point sources at same location
# 4. Call model.run() and expect RuntimeError
# 5. Verify error message contains expected text
# This should not raise an error about multiple point sources
try:
model.run()
except RuntimeError as e:
# If we get a RuntimeError, it should NOT be about multiple point sources
assert "Multiple point sources detected" not in str(e)
# Re-raise any other errors (could be due to test environment)
raise