1
1
import yaml
2
2
from pathlib import Path
3
+ import warnings
3
4
from abc import ABC , abstractmethod
4
5
import numpy as np
6
+ import xarray as xr
7
+ import h5py
5
8
from .validate import validate_benchmark
6
9
7
10
import openmc
11
+ import pydagmc
8
12
from cad_to_dagmc import CadToDagmc
9
13
10
14
@@ -54,6 +58,16 @@ def _build_model(self):
54
58
"""Build the whole model for the benchmark."""
55
59
pass
56
60
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
+
57
71
def _read_metadata (self ):
58
72
"""Read metadata from the benchmark specification."""
59
73
metadata = self ._benchmark_spec ['metadata' ]
@@ -175,9 +189,13 @@ def build_mesh(cad_file: str, material_tags, set_size: dict, global_mesh_size_mi
175
189
# Get the STEP file
176
190
cad_file = LFS_DIR / "benchmarks" / f"{ geometry_data ['cad_file' ]} "
177
191
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 )
181
199
182
200
# download the h5m file
183
201
# download_from_drive(benchmark_name=self.name, file_format='h5m')
@@ -251,7 +269,7 @@ def angular_conversion(values, units):
251
269
angular_sources = []
252
270
# Openmc needs one source per angle bin:
253
271
angles = source ['angular_energy_distribution' ]
254
- abins = np .array (angles ['angle' ]['bins' ])
272
+ abins = np .cos (angles ['angle' ]['bins' ])
255
273
for i in range (len (abins )- 1 ):
256
274
lb = angular_conversion (
257
275
abins [i ], angles ['angle' ]['units' ])
@@ -344,10 +362,13 @@ def _build_settings(self):
344
362
settings_data = self ._benchmark_spec ['settings' ]
345
363
346
364
settings = openmc .Settings ()
347
- if settings_data ['run_mode' ] == 'fixed source ' :
365
+ if settings_data ['run_mode' ] == 'fixed_source ' :
348
366
settings .run_mode = 'fixed source'
349
367
elif settings_data ['run_mode' ] == 'k-eigenvalue' :
350
368
settings .run_mode = 'eigenvalue'
369
+ else :
370
+ raise ValueError (
371
+ f"Unsupported run mode: { settings_data ['run_mode' ]} " )
351
372
settings .batches = int (settings_data ['batches' ])
352
373
settings .particles = int (settings_data ['particles_per_batch' ])
353
374
settings .photon_transport = settings_data ['photon_transport' ]
@@ -356,7 +377,7 @@ def _build_settings(self):
356
377
# electron treatment
357
378
settings .output = {'tallies' : False }
358
379
359
- source = self .build_source ()
380
+ source = self ._build_source ()
360
381
settings .source = source
361
382
362
383
return settings
@@ -373,3 +394,78 @@ def _build_model(self):
373
394
tallies = tallies
374
395
)
375
396
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
0 commit comments