Skip to content

Commit ffa451d

Browse files
SteSegdavidepettinarieepeterson
authored
Added postprocess() and run() methods to Benchmark class (#58)
* Benchmark build methods -> private * + _build_model() abstractmethod * + _build_model abstractmethod * + _build_model in OpenmcBenchmark * + self.model in OpenmcBenchmark constructor * Benchmark build methods -> private * + _build_model() abstractmethod * + _build_model abstractmethod * + _build_model in OpenmcBenchmark * + self.model in OpenmcBenchmark constructor * fix tests * fix test * + abstractmethod postprocess * okt specs tally surf id -> 7 * + self._build_source() fix in build settings * _build_geometry check if mesh.h5m is already present * + warning if mesh.h5m already present * update todo.md * + xarray as dependency * + tally postprocess, df -> xarray * updated todo file * + store results in benchmark_results.h5 * update todo * + run() abstractmethod * + run() method in OpenmcBenchmark * + throw error if unsupported run mode * update todo * + pydagmc dependency * - pydagmc dependency * + fix polar angle -> cos(angle) source * + pydagmc as dependency * fix pydagmc dependency * update tests * + fix tests * typo in surfaces_by_id * updated todo * drop 'surface' col in result df * mode='a' in to_netcdf result method * add vectorial logo * change logo * added 3rd dimension to result xarray * + small fix on xarray definition * Experimental configuration of OKTAVIAN experiments * OKTAVIAN general information * references * add al results * + point to rignt lfs submodule branch * specify branch in submodule * + lfs change * test cad visualization * test for cad visualization * update name for oktavian al benchmark * deleted wrong file * change src * change references * add .glb file for cad visualization * add html page * add new reference style * add interactive view * + abstractmethod postprocess * okt specs tally surf id -> 7 * + self._build_source() fix in build settings * _build_geometry check if mesh.h5m is already present * + warning if mesh.h5m already present * update todo.md * + xarray as dependency * + tally postprocess, df -> xarray * updated todo file * + store results in benchmark_results.h5 * update todo * + run() abstractmethod * + run() method in OpenmcBenchmark * + throw error if unsupported run mode * update todo * + pydagmc dependency * - pydagmc dependency * + fix polar angle -> cos(angle) source * + pydagmc as dependency * fix pydagmc dependency * update tests * + fix tests * typo in surfaces_by_id * updated todo * drop 'surface' col in result df * mode='a' in to_netcdf result method * added 3rd dimension to result xarray * + small fix on xarray definition --------- Co-authored-by: davidepettinari <[email protected]> Co-authored-by: Davide Pettinari <[email protected]> Co-authored-by: Ethan Peterson <[email protected]>
1 parent 017b5a6 commit ffa451d

File tree

5 files changed

+127
-12
lines changed

5 files changed

+127
-12
lines changed

TODO.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
11
# To do
22
### Manage CAD geometry
33

4-
### Benchmark modules
4+
5+
## Benchmarks API
6+
- Add capability of merging multiple surface results for oktavian (should happen only with surface tallies)
7+
- Add expected tally results shape in `schema` and `specifications` to deal with the point here above
8+
- Try to manage results as numpy arrays instead of pandas dataframes in `OpenmcBenchmark.postprocess()`
9+
- Should we use `h5netcdf` engine for saving/opening datasets in `h5` files or stick with the default `netcdf4`?
10+
- Do we wanna use `makefun` decorator for wrapped methods (e.g. `OpenmcBenchmark.run()`)?
511

612
## Tests
713
- Add tests to everything
814

915
## Notebooks
16+
- Use `specifications` validation against `schema` function and script
17+
- Open and inspect a `specifications` file (just `metadata`)
18+
- Instantiate an `OpenmcBenchmark` object, build model, run simulation
19+
- Postprocessing and visualization
1020

11-
## clean postprocessing
21+
## Documentation
22+
- introduction and motivation
23+
- V&V explanation
24+
- Definition of benchmark `specifications`
25+
- Benchmarks
26+
- Oktavian description
27+
- Oktavian results
1228

1329
## Miscellanea
1430

15-
1631
## Questions

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ dependencies = [
2222
"openapi_schema_validator",
2323
"pydantic",
2424
"numpy",
25+
"xarray",
2526
"tables",
2627
"cad-to-dagmc",
28+
"pydagmc @ git+https://github.com/svalinn/pydagmc.git"
2729
]
2830

2931
[tool.setuptools.package-data]

src/openmc_fusion_benchmarks/benchmark.py

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import yaml
22
from pathlib import Path
3+
import warnings
34
from abc import ABC, abstractmethod
45
import numpy as np
6+
import xarray as xr
7+
import h5py
58
from .validate import validate_benchmark
69

710
import openmc
11+
import pydagmc
812
from cad_to_dagmc import CadToDagmc
913

1014

@@ -54,6 +58,16 @@ def _build_model(self):
5458
"""Build the whole model for the benchmark."""
5559
pass
5660

61+
@abstractmethod
62+
def postprocess(self):
63+
"""Post-process the model after running."""
64+
pass
65+
66+
@abstractmethod
67+
def run(self):
68+
"""Run the benchmark simulation."""
69+
pass
70+
5771
def _read_metadata(self):
5872
"""Read metadata from the benchmark specification."""
5973
metadata = self._benchmark_spec['metadata']
@@ -175,9 +189,13 @@ def build_mesh(cad_file: str, material_tags, set_size: dict, global_mesh_size_mi
175189
# Get the STEP file
176190
cad_file = LFS_DIR / "benchmarks" / f"{geometry_data['cad_file']}"
177191

178-
# Generate the mesh
179-
build_mesh(cad_file=cad_file, material_tags=material_tags, set_size=set_size,
180-
global_mesh_size_min=global_mesh_size_min, global_mesh_size_max=global_mesh_size_max)
192+
# Generate the mesh if mesh.h5m not already present
193+
if Path("mesh.h5m").exists():
194+
warnings.warn(
195+
f"Mesh file already exists. Skipping mesh generation.")
196+
else:
197+
build_mesh(cad_file=cad_file, material_tags=material_tags, set_size=set_size,
198+
global_mesh_size_min=global_mesh_size_min, global_mesh_size_max=global_mesh_size_max)
181199

182200
# download the h5m file
183201
# download_from_drive(benchmark_name=self.name, file_format='h5m')
@@ -251,7 +269,7 @@ def angular_conversion(values, units):
251269
angular_sources = []
252270
# Openmc needs one source per angle bin:
253271
angles = source['angular_energy_distribution']
254-
abins = np.array(angles['angle']['bins'])
272+
abins = np.cos(angles['angle']['bins'])
255273
for i in range(len(abins)-1):
256274
lb = angular_conversion(
257275
abins[i], angles['angle']['units'])
@@ -344,10 +362,13 @@ def _build_settings(self):
344362
settings_data = self._benchmark_spec['settings']
345363

346364
settings = openmc.Settings()
347-
if settings_data['run_mode'] == 'fixed source':
365+
if settings_data['run_mode'] == 'fixed_source':
348366
settings.run_mode = 'fixed source'
349367
elif settings_data['run_mode'] == 'k-eigenvalue':
350368
settings.run_mode = 'eigenvalue'
369+
else:
370+
raise ValueError(
371+
f"Unsupported run mode: {settings_data['run_mode']}")
351372
settings.batches = int(settings_data['batches'])
352373
settings.particles = int(settings_data['particles_per_batch'])
353374
settings.photon_transport = settings_data['photon_transport']
@@ -356,7 +377,7 @@ def _build_settings(self):
356377
# electron treatment
357378
settings.output = {'tallies': False}
358379

359-
source = self.build_source()
380+
source = self._build_source()
360381
settings.source = source
361382

362383
return settings
@@ -373,3 +394,78 @@ def _build_model(self):
373394
tallies=tallies
374395
)
375396
return model
397+
398+
def postprocess(self, statepoint: str = 'statepoint.100.h5', mesh: str = 'mesh.h5m'):
399+
"""Post-process the model after running."""
400+
# Retrieve tallies data from specifications
401+
tallies_data = self._benchmark_spec['tallies']
402+
# Read openmc statepoint file
403+
sp = openmc.StatePoint(statepoint)
404+
# Read mesh file
405+
mesh = pydagmc.DAGModel(mesh)
406+
407+
# Cycle tallies in specifications
408+
for spec_t in tallies_data:
409+
# Get corresponding tally from statepoint
410+
df = sp.get_tally(name=spec_t['name']).get_pandas_dataframe()
411+
412+
# Preparing tally dataframe
413+
df = df.drop(columns=['surface', 'cell', 'particle', 'nuclide',
414+
'score', 'energyfunction'], errors='ignore')
415+
# Cyle tally filters
416+
norm = 1
417+
for f in spec_t['filters']:
418+
if f['type'] == 'cell':
419+
# Get cell volumes for normalization
420+
norm = [mesh.volumes_by_id[v].area for v in f['values']]
421+
elif f['type'] == 'surface':
422+
# Get surface areas for normalization
423+
norm = [mesh.surfaces_by_id[v].area for v in f['values']]
424+
elif f['type'] == 'material':
425+
raise NotImplementedError(
426+
'Material filter not implemented in postprocess yet.')
427+
428+
# Normalize the tally data
429+
df['mean'] = df['mean'] / norm
430+
df['std. dev.'] = df['std. dev.'] / norm
431+
432+
# Convert to xarray and add dimensions
433+
t = xr.DataArray(
434+
df.values[np.newaxis, :, :], # shape: (1, r, c)
435+
dims=["case", "row", "column"],
436+
coords={
437+
"case": ["0"],
438+
"column": df.columns,
439+
"row": np.arange(df.shape[0])
440+
},
441+
name=spec_t['name']
442+
)
443+
444+
# Save the tally data to a netCDF file
445+
t.to_netcdf(f"benchmark_results.h5",
446+
group=f"{spec_t['name']}", mode='a')
447+
448+
# Add some metadata attributes
449+
with h5py.File(f"benchmark_results.h5", "a") as f:
450+
f.attrs['benchmark_name'] = self.name
451+
# f.attrs['benchmark_version'] = self._benchmark_spec['metadata'].get(
452+
# 'version', 'N/A')
453+
# f.attrs['description'] = self._benchmark_spec['metadata'].get(
454+
# 'description', 'N/A')
455+
# f.attrs['title'] = self._benchmark_spec['metadata'].get(
456+
# 'title', 'N/A')
457+
# f.attrs['literature'] = self._benchmark_spec['metadata'].get(
458+
# 'references', [])
459+
f.attrs['code'] = f"openmc {sp.version[0]}.{sp.version[1]}.{sp.version[2]}"
460+
461+
return
462+
463+
def run(self, *args, **kwargs):
464+
"""Run the benchmark simulation."""
465+
# Run the OpenMC model
466+
statepoint = self.model.run(*args, **kwargs)
467+
468+
# Post-process the results
469+
self.postprocess(statepoint=statepoint)
470+
471+
return

src/openmc_fusion_benchmarks/benchmarks/oktavian_al/specifications.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ tallies:
146146
scores: [current]
147147
filters:
148148
- type: surface
149-
values: [6]
149+
values: [7]
150150
- type: energy
151151
values: [97122., 101090., 105210., 109500., 113970., 118620.,
152152
123470., 128500., 133750., 139210., 144890., 150800.,
@@ -179,7 +179,7 @@ tallies:
179179
scores: [current]
180180
filters:
181181
- type: surface
182-
values: [6]
182+
values: [7]
183183
- type: energy
184184
values: [500000., 600000., 700000., 800000., 900000., 1000000.,
185185
1100000., 1200000., 1300000., 1400000., 1500000., 1600000.,

test/test_benchmark.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ def _build_source(self): return "source"
1414
def _build_settings(self): return "settings"
1515
def _build_tallies(self): return "tallies"
1616
def _build_model(self): return "model"
17+
def postprocess(self): return "postprocess"
18+
def run(self): return "run"
1719

1820

1921
@pytest.fixture
@@ -23,7 +25,7 @@ def valid_yaml():
2325
"materials": [],
2426
"geometry": {},
2527
"sources": [],
26-
"tallies": []
28+
"tallies": [],
2729
})
2830

2931

0 commit comments

Comments
 (0)