Skip to content

Commit 5b0d040

Browse files
authored
Merge pull request #38 from openmc-data-storage/adding-transport-friendly-output
Adding transport friendly output
2 parents 9fafc57 + af121c1 commit 5b0d040

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

example_usage.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,9 @@
2929
filename=test_filename_1
3030
)
3131

32+
33+
result = odj.extract_cross_section_data(h5_filename='tests/Li6.h5', json_filename='li6_neutron.json')
34+
result = odj.extract_cross_section_data(h5_filename='tests/Li7.h5', json_filename='li7_neutron.json')
35+
36+
3237
# list(pathlib.Path('your_directory').glob('*.txt'))

src/openmc_data_to_json/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,8 @@
88
cross_section_h5_to_json,
99
reactions_in_h5,
1010
trim_zeros_from_front_and_back_of_list,
11+
)
12+
from .transport import (
13+
extract_cross_section_data,
14+
CompactJSONEncoder,
1115
)
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import json
2+
import numpy as np
3+
import openmc
4+
5+
class CompactJSONEncoder(json.JSONEncoder):
6+
"""A JSON Encoder that puts arrays of primitives on a single line."""
7+
8+
def __init__(self, *args, **kwargs):
9+
# Default to 2-space indentation if not specified
10+
if kwargs.get("indent") is None:
11+
kwargs["indent"] = 2
12+
super().__init__(*args, **kwargs)
13+
self.indentation_level = 0
14+
15+
def default(self, o):
16+
"""Handle NumPy types"""
17+
if isinstance(o, np.ndarray):
18+
return o.tolist()
19+
if isinstance(o, np.integer):
20+
return int(o)
21+
if isinstance(o, np.floating):
22+
return float(o)
23+
return json.JSONEncoder.default(self, o)
24+
25+
def encode(self, o):
26+
"""Encode JSON objects with special handling for arrays of primitives."""
27+
if isinstance(o, (list, tuple)):
28+
# Put arrays of primitives on a single line
29+
if all(isinstance(x, (int, float, bool, str, type(None))) for x in o):
30+
return "[" + ", ".join(json.dumps(el) for el in o) + "]"
31+
# For other containers, use standard formatting
32+
return self._encode_container(o, "[", "]")
33+
if isinstance(o, dict):
34+
if not o:
35+
return "{}"
36+
# Ensure keys are strings
37+
o = {str(k) if k is not None else "null": v for k, v in o.items()}
38+
if self.sort_keys:
39+
o = dict(sorted(o.items(), key=lambda x: x[0]))
40+
# Format dictionary
41+
return self._encode_container(o, "{", "}")
42+
# For primitive types, use standard JSON encoding
43+
return json.dumps(
44+
o,
45+
skipkeys=self.skipkeys,
46+
ensure_ascii=self.ensure_ascii,
47+
check_circular=self.check_circular,
48+
allow_nan=self.allow_nan,
49+
sort_keys=self.sort_keys,
50+
separators=(self.item_separator, self.key_separator),
51+
default=self.default,
52+
)
53+
54+
def _encode_container(self, container, open_char, close_char):
55+
"""Encode a container with proper indentation."""
56+
is_dict = isinstance(container, dict)
57+
58+
# Handle very small containers on single line (dicts with 1 item or less)
59+
if is_dict and len(container) <= 1:
60+
if not container:
61+
return "{}"
62+
key, value = next(iter(container.items()))
63+
return f"{{ {self.encode(key)}: {self.encode(value)} }}"
64+
65+
# Format container across multiple lines
66+
self.indentation_level += 1
67+
if is_dict:
68+
items = [f"{self.indent_str}{self.encode(k)}: {self.encode(v)}" for k, v in container.items()]
69+
else:
70+
items = [f"{self.indent_str}{self.encode(el)}" for el in container]
71+
self.indentation_level -= 1
72+
73+
return f"{open_char}\n" + ",\n".join(items) + f"\n{self.indent_str}{close_char}"
74+
75+
def iterencode(self, data, **kwargs):
76+
"""Required to also work with `json.dump`."""
77+
return self.encode(data)
78+
79+
@property
80+
def indent_str(self) -> str:
81+
"""Get the indentation string for the current level."""
82+
if isinstance(self.indent, int):
83+
return " " * (self.indentation_level * self.indent)
84+
elif isinstance(self.indent, str):
85+
return self.indentation_level * self.indent
86+
else:
87+
return ""
88+
89+
90+
def extract_cross_section_data(h5_filename, json_filename):
91+
result = {}
92+
93+
isotope_object = openmc.data.IncidentNeutron.from_hdf5(h5_filename)
94+
result['atomic_number'] = isotope_object.atomic_number
95+
result['atomic_symbol'] = isotope_object.atomic_symbol
96+
result['atomic_weight_ratio'] = isotope_object.atomic_weight_ratio
97+
result['mass_number'] = isotope_object.mass_number
98+
result['metastable'] = isotope_object.metastable
99+
result['nuclide'] = isotope_object.name
100+
result['temperatures'] = isotope_object.temperatures
101+
102+
result['reactions'] = {}
103+
104+
# Collect all unique temperatures first
105+
all_temperatures = set()
106+
for reaction in isotope_object.reactions.values():
107+
all_temperatures.update(reaction.xs.keys())
108+
109+
# Initialize the nested structure once
110+
for temperature in all_temperatures:
111+
result['reactions'][temperature] = {}
112+
113+
result['energy'] = {}
114+
for temperature, energy in isotope_object.energy.items():
115+
result['energy'][temperature] = energy
116+
117+
for reaction_mt, reaction in isotope_object.reactions.items():
118+
temperatures_and_tabulars=reaction.xs
119+
120+
for temperature, tabular in temperatures_and_tabulars.items():
121+
reaction_at_temperature_dict = {}
122+
123+
reaction_at_temperature_dict['interpolation'] = tabular.interpolation
124+
reaction_at_temperature_dict['threshold_idx'] = tabular._threshold_idx
125+
reaction_at_temperature_dict['xs'] = tabular.x
126+
127+
result['reactions'][temperature][reaction_mt] = reaction_at_temperature_dict
128+
129+
# print(f"Reaction MT: {reaction_mt}, Temperature: {temperature}, Interpolation: {tabular.interpolation}, Threshold Index: {tabular._threshold_idx}, len XS: {len(tabular.x)}")
130+
131+
with open(json_filename, 'w') as f:
132+
json.dump(result, f, cls=CompactJSONEncoder, indent=2)
133+
134+
return result

0 commit comments

Comments
 (0)