From 2281d13f0250f3b0235263abaaee33b657c788a0 Mon Sep 17 00:00:00 2001 From: Melek Derman <48313913+melekderman@users.noreply.github.com> Date: Thu, 16 Apr 2026 00:23:04 -0400 Subject: [PATCH 1/8] Add util.py for data library generation --- tools/data_library_generator/electron/util.py | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 tools/data_library_generator/electron/util.py diff --git a/tools/data_library_generator/electron/util.py b/tools/data_library_generator/electron/util.py new file mode 100644 index 000000000..84a920f29 --- /dev/null +++ b/tools/data_library_generator/electron/util.py @@ -0,0 +1,213 @@ +import ACEtk +import h5py +import numpy as np + + +def print_error(message): + print(f"\n [ERROR]: {message}\n") + exit() + + +def print_note(message): + print(f"\n [NOTE]: {message}\n") + + +def decode_epr_zaid(name: str): + """ + Decode an EPR ACE ZAID like '1000.14p' into atomic number Z. + All EPR tables are elemental: ZAID = Z * 1000 (A = 000). + Returns Z. + """ + zaid_str, _ = name.split(".") + Z = int(zaid_str) // 1000 + return Z + + +def load_elastic_angular_distribution(block, h5_group: h5py.Group): + """ + Load elastic angular distribution block into HDF5 group. + Stores incident energy grid, cosine grid, PDF, and CDF per energy. + """ + NE = block.number_of_energies + + energies = np.array(block.energies) + dataset = h5_group.create_dataset("energy", data=energies) + dataset.attrs["unit"] = "MeV" + + offset = np.zeros(NE, dtype=int) + cosines = [] + pdf = [] + cdf = [] + for i in range(NE): + dist = block.distribution(i + 1) + offset[i] = len(cosines) + cosines.extend(dist.cosines) + pdf.extend(dist.pdf) + cdf.extend(dist.cdf) + + h5_group.create_dataset("offset", data=offset) + h5_group.create_dataset("cosine", data=np.array(cosines)) + h5_group.create_dataset("pdf", data=np.array(pdf)) + h5_group.create_dataset("cdf", data=np.array(cdf)) + + +def load_bremsstrahlung(block, h5_group: h5py.Group): + """ + Load bremsstrahlung energy distribution block into HDF5 group. + Stores outgoing photon energy PDF for each incident electron energy. + """ + NE = block.number_of_energies + + energies = np.array(block.energies) + dataset = h5_group.create_dataset("energy", data=energies) + dataset.attrs["unit"] = "MeV" + + offset = np.zeros(NE, dtype=int) + energy_out = [] + pdf = [] + for i in range(NE): + dist = block.distribution(i + 1) + offset[i] = len(energy_out) + energy_out.extend(dist.outgoing_energies) + pdf.extend(dist.pdf) + + h5_group.create_dataset("offset", data=offset) + dataset = h5_group.create_dataset("energy_out", data=np.array(energy_out)) + dataset.attrs["unit"] = "MeV" + h5_group.create_dataset("pdf", data=np.array(pdf)) + + +def load_electroionization_subshell(block, h5_group: h5py.Group): + """ + Load electroionization energy distribution for a single subshell. + Stores outgoing (knock-on) electron energy PDF per incident energy. + """ + NE = block.number_of_energies + + energies = np.array(block.energies) + dataset = h5_group.create_dataset("energy", data=energies) + dataset.attrs["unit"] = "MeV" + + offset = np.zeros(NE, dtype=int) + energy_out = [] + pdf = [] + for i in range(NE): + dist = block.distribution(i + 1) + offset[i] = len(energy_out) + energy_out.extend(dist.outgoing_energies) + pdf.extend(dist.pdf) + + h5_group.create_dataset("offset", data=offset) + dataset = h5_group.create_dataset("energy_out", data=np.array(energy_out)) + dataset.attrs["unit"] = "MeV" + h5_group.create_dataset("pdf", data=np.array(pdf)) + + +# ============================================================================= +# Constants +# ============================================================================= + +SYMBOL_TO_Z = { + "H": 1, + "He": 2, + "Li": 3, + "Be": 4, + "B": 5, + "C": 6, + "N": 7, + "O": 8, + "F": 9, + "Ne": 10, + "Na": 11, + "Mg": 12, + "Al": 13, + "Si": 14, + "P": 15, + "S": 16, + "Cl": 17, + "Ar": 18, + "K": 19, + "Ca": 20, + "Sc": 21, + "Ti": 22, + "V": 23, + "Cr": 24, + "Mn": 25, + "Fe": 26, + "Co": 27, + "Ni": 28, + "Cu": 29, + "Zn": 30, + "Ga": 31, + "Ge": 32, + "As": 33, + "Se": 34, + "Br": 35, + "Kr": 36, + "Rb": 37, + "Sr": 38, + "Y": 39, + "Zr": 40, + "Nb": 41, + "Mo": 42, + "Tc": 43, + "Ru": 44, + "Rh": 45, + "Pd": 46, + "Ag": 47, + "Cd": 48, + "In": 49, + "Sn": 50, + "Sb": 51, + "Te": 52, + "I": 53, + "Xe": 54, + "Cs": 55, + "Ba": 56, + "La": 57, + "Ce": 58, + "Pr": 59, + "Nd": 60, + "Pm": 61, + "Sm": 62, + "Eu": 63, + "Gd": 64, + "Tb": 65, + "Dy": 66, + "Ho": 67, + "Er": 68, + "Tm": 69, + "Yb": 70, + "Lu": 71, + "Hf": 72, + "Ta": 73, + "W": 74, + "Re": 75, + "Os": 76, + "Ir": 77, + "Pt": 78, + "Au": 79, + "Hg": 80, + "Tl": 81, + "Pb": 82, + "Bi": 83, + "Po": 84, + "At": 85, + "Rn": 86, + "Fr": 87, + "Ra": 88, + "Ac": 89, + "Th": 90, + "Pa": 91, + "U": 92, + "Np": 93, + "Pu": 94, + "Am": 95, + "Cm": 96, + "Bk": 97, + "Cf": 98, + "Es": 99, + "Fm": 100, +} + +Z_TO_SYMBOL = {value: key for key, value in SYMBOL_TO_Z.items()} From 9b343dff6cb89765d6f0b39f9cc85cf7b0aa639f Mon Sep 17 00:00:00 2001 From: Melek Derman <48313913+melekderman@users.noreply.github.com> Date: Thu, 16 Apr 2026 00:24:05 -0400 Subject: [PATCH 2/8] Add MC/DC electron data generation script This script generates MC/DC electron data by reading ACE files and creating HDF5 datasets for various properties, including cross sections and atomic relaxation data. --- .../electron/generate.py | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 tools/data_library_generator/electron/generate.py diff --git a/tools/data_library_generator/electron/generate.py b/tools/data_library_generator/electron/generate.py new file mode 100644 index 000000000..3fb3959c3 --- /dev/null +++ b/tools/data_library_generator/electron/generate.py @@ -0,0 +1,216 @@ +import ACEtk +import argparse +import h5py +import numpy as np +import os + +from tqdm import tqdm + +#### + +import util_electron +from util_electron import print_error, print_note + +parser = argparse.ArgumentParser(description="MC/DC electron data generator") +parser.add_argument("--rewrite", dest="rewrite", action="store_true", default=False) +parser.add_argument("--verbose", dest="verbose", action="store_true", default=False) +args, unargs = parser.parse_known_args() +rewrite = args.rewrite +verbose = args.verbose + +# Directories +output_dir = os.getenv("MCDC_LIB_ELECTRON") +ace_file = os.getenv("MCDC_ACELIB_ELECTRON") +xsdir_file = os.getenv("MCDC_ACELIB_ELECTRON_XSDIR") + +if output_dir is None: + print_error("Environment variable $MCDC_LIB_ELECTRON is not set") +if ace_file is None: + print_error("Environment variable $MCDC_ACELIB_ELECTRON is not set") +if xsdir_file is None: + print_error("Environment variable $MCDC_ACELIB_ELECTRON_XSDIR is not set") + +# Create output directory if needed +os.makedirs(output_dir, exist_ok=True) +print(f"\nACE file : {ace_file}") +print(f"Xsdir file : {xsdir_file}") +print(f"Output dir : {output_dir}\n") + +# Read xsdir and collect EPR entries +xsdir = ACEtk.Xsdir.from_file(xsdir_file) +target_entries = [] +for entry in xsdir.entries: + if not entry.zaid.endswith(".14p"): + continue + + Z = util_electron.decode_epr_zaid(entry.zaid) + symbol = util_electron.Z_TO_SYMBOL[Z] + mcdc_name = f"{symbol}.h5" + + if not rewrite and os.path.exists(f"{output_dir}/{mcdc_name}"): + continue + + target_entries.append((entry, Z, symbol, mcdc_name)) + +# Loop over all elements +pbar = tqdm( + target_entries, + disable=verbose, + bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt}{postfix}", +) +for entry, Z, symbol, mcdc_name in pbar: + + if not rewrite and os.path.exists(f"{output_dir}/{mcdc_name}"): + continue + + if verbose: + print("\n" + "=" * 80 + "\n") + print(f"Create {mcdc_name} from {entry.zaid}\n") + pbar.set_postfix_str(f"{mcdc_name}") + + # Load ACE table + ace_table = ACEtk.PhotoatomicTable.from_file(ace_file, entry.file_start_line) + + # Create MC/DC file + file = h5py.File(f"{output_dir}/{mcdc_name}", "w") + + # ================================================================================== + # Basic properties + # ================================================================================== + + header = ace_table.header + file.attrs["source_title"] = header.title + file.attrs["source_date"] = header.date + + file.create_dataset("element_symbol", data=symbol) + file.create_dataset("atomic_number", data=Z) + file.create_dataset("atomic_weight_ratio", data=ace_table.atomic_weight_ratio) + + # ================================================================================== + # Principal cross sections (energy grid + xs for all reactions) + # ================================================================================== + + xs0_block = ace_table.principal_cross_section_block + + energy_grid = np.array(xs0_block.energies) + dataset = file.create_dataset("energy_grid", data=energy_grid) + dataset.attrs["unit"] = "MeV" + + xs_group = file.create_group("cross_sections") + + dataset = xs_group.create_dataset("elastic", data=np.array(xs0_block.elastic)) + dataset.attrs["unit"] = "barns" + + dataset = xs_group.create_dataset("bremsstrahlung", data=np.array(xs0_block.bremsstrahlung)) + dataset.attrs["unit"] = "barns" + + dataset = xs_group.create_dataset("excitation", data=np.array(xs0_block.excitation)) + dataset.attrs["unit"] = "barns" + + dataset = xs_group.create_dataset("electroionization", data=np.array(xs0_block.electroionization)) + dataset.attrs["unit"] = "barns" + + dataset = xs_group.create_dataset("total", data=np.array(xs0_block.total)) + dataset.attrs["unit"] = "barns" + + # ================================================================================== + # Elastic angular distribution + # ================================================================================== + + angle_group = file.create_group("elastic_angular_distribution") + util_electron.load_elastic_angular_distribution( + ace_table.elastic_angular_distribution_block, angle_group + ) + + # ================================================================================== + # Excitation energy loss + # ================================================================================== + + excit_block = ace_table.excitation_block + excit_group = file.create_group("excitation") + + energies = np.array(excit_block.energies) + dataset = excit_group.create_dataset("energy", data=energies) + dataset.attrs["unit"] = "MeV" + + dataset = excit_group.create_dataset("energy_loss", data=np.array(excit_block.energy_loss)) + dataset.attrs["unit"] = "MeV" + + # ================================================================================== + # Bremsstrahlung energy distribution + # ================================================================================== + + brems_group = file.create_group("bremsstrahlung") + util_electron.load_bremsstrahlung(ace_table.bremsstrahlung_block, brems_group) + + # ================================================================================== + # Electroionization: per-subshell cross sections and energy distributions + # ================================================================================== + + subshell_block = ace_table.electron_subshell_block + N_subshells = subshell_block.number_of_subshells + + ioniz_group = file.create_group("electroionization") + + for i in range(N_subshells): + idx = i + 1 + shell_group = ioniz_group.create_group(f"subshell_{idx}") + + # Binding energy and cross section + binding_energy = subshell_block.binding_energy(idx) + dataset = shell_group.create_dataset("binding_energy", data=binding_energy) + dataset.attrs["unit"] = "MeV" + + xs = np.array(subshell_block.cross_section(idx)) + dataset = shell_group.create_dataset("xs", data=xs) + dataset.attrs["unit"] = "barns" + + # Knock-on electron energy distribution + energy_dist_group = shell_group.create_group("energy_distribution") + util_electron.load_electroionization_subshell( + subshell_block.energy_distribution(idx), energy_dist_group + ) + + if verbose: + print(f" Z={Z} ({symbol}): {N_subshells} subshells") + + # ================================================================================== + # Atomic relaxation (subshell transition data) + # ================================================================================== + + relaxation_block = ace_table.subshell_transition_data_block + relaxation_group = file.create_group("atomic_relaxation") + + for i in range(N_subshells): + idx = i + 1 + data = relaxation_block.transition_data(idx) + shell_group = relaxation_group.create_group(f"subshell_{idx}") + + N_transitions = data.number_of_transitions + shell_group.create_dataset("number_of_transitions", data=N_transitions) + + if N_transitions > 0: + primary_shells = [] + secondary_shells = [] + energies = [] + probabilities = [] + for j in range(N_transitions): + jdx = j + 1 + primary_shells.append(data.primary_subshell(jdx)) + secondary_shells.append(data.secondary_subshell(jdx)) + energies.append(data.energy(jdx)) + probabilities.append(data.probability(jdx)) + + shell_group.create_dataset("primary_subshell", data=np.array(primary_shells)) + shell_group.create_dataset("secondary_subshell", data=np.array(secondary_shells)) + dataset = shell_group.create_dataset("energy", data=np.array(energies)) + dataset.attrs["unit"] = "MeV" + shell_group.create_dataset("probability", data=np.array(probabilities)) + + # ================================================================================== + # Finalize + # ================================================================================== + + file.close() + +print("") From 62614afcbc2c32a8a30353f3398526c677050e45 Mon Sep 17 00:00:00 2001 From: Melek Derman <48313913+melekderman@users.noreply.github.com> Date: Thu, 16 Apr 2026 00:28:31 -0400 Subject: [PATCH 3/8] Add README for MC/DC Electron Data Library Generator Added detailed README for MC/DC Electron Data Library Generator, including prerequisites, environment variables, usage instructions, and output schema. --- .../data_library_generator/electron/README.md | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 tools/data_library_generator/electron/README.md diff --git a/tools/data_library_generator/electron/README.md b/tools/data_library_generator/electron/README.md new file mode 100644 index 000000000..909ea572b --- /dev/null +++ b/tools/data_library_generator/electron/README.md @@ -0,0 +1,93 @@ +# MC/DC Electron Data Library Generator +Converts EPRDATA14 ACE-format electron/photon/relaxation data into MC/DC's +per-element HDF5 format for continuous-energy electron transport. + +## Prerequisites +```bash +pip install ACEtk h5py numpy tqdm +``` +You need the EPRDATA14 library (available from [LANL Nuclear Data](https://nucleardata.lanl.gov/ace/eprdata14)). + +## Environment Variables +| Variable | Description | +|------------------------------|----------------------------------------------------| +| `MCDC_ACELIB_ELECTRON` | Path to the EPRDATA14 data file. | +| `MCDC_ACELIB_ELECTRON_XSDIR` | Path to the EPRDATA14 `xsdir` file. | +| `MCDC_LIB_ELECTRON` | Path to the output directory for MC/DC HDF5 files. | + +## Usage +```bash +export MCDC_ACELIB_ELECTRON=/path/to/eprdata14/eprdata14 +export MCDC_ACELIB_ELECTRON_XSDIR=/path/to/eprdata14/xsdir +export MCDC_LIB_ELECTRON=/path/to/mcdc/electron/library + +python generate_electron.py # Convert only missing elements +python generate_electron.py --rewrite # Regenerate all files +python generate_electron.py --verbose # Print detailed per-element info +``` + +## What it Does +For each element (Z=1 to Z=100) in the EPRDATA14 library, the generator: +1. Reads the xsdir to locate each elemental table within the single EPRDATA14 file. +2. Extracts the principal cross section energy grid and pointwise cross sections + (elastic, bremsstrahlung, excitation, electroionization) for all reaction channels. +3. Extracts tabulated elastic angular distributions (cosine PDFs) per incident energy (MT-528). +4. Extracts excitation energy loss as a function of incident energy (MT-527). +5. Extracts bremsstrahlung outgoing photon energy distributions per incident energy (MT-526). +6. Extracts per-subshell electroionization cross sections and knock-on electron + energy distributions (MT-534 for K-shell, MT-535+ for higher shells). +7. Extracts atomic relaxation (fluorescence and Auger) transition data per subshell. +8. Writes a single HDF5 file per element (e.g., `Al.h5`). + +## Output HDF5 Schema +``` +.h5 +├── element_symbol (string) +├── atomic_number (int) +├── atomic_weight_ratio (float) +├── electron_reactions/ +│ ├── xs_energy_grid (1-D array, MeV) +│ ├── elastic_scattering/ +│ │ └── MT-528/ +│ │ ├── xs (1-D array, barns) +│ │ └── angular_cosine_distribution/ +│ │ ├── energy (1-D array, MeV) +│ │ ├── offset (1-D array, int) +│ │ ├── cosine (1-D array) +│ │ ├── pdf (1-D array) +│ │ └── cdf (1-D array) +│ ├── excitation/ +│ │ └── MT-527/ +│ │ ├── xs (1-D array, barns) +│ │ └── energy_loss/ +│ │ ├── energy (1-D array, MeV) +│ │ └── energy_loss (1-D array, MeV) +│ ├── bremsstrahlung/ +│ │ └── MT-526/ +│ │ ├── xs (1-D array, barns) +│ │ └── energy_distribution/ +│ │ ├── energy (1-D array, MeV) +│ │ ├── offset (1-D array, int) +│ │ ├── energy_out (1-D array, MeV) +│ │ └── pdf (1-D array) +│ └── electroionization/ +│ └── MT-534/ (K-shell; MT-535, MT-536, ... for higher shells) +│ ├── xs (1-D array, barns) +│ ├── binding_energy (float, MeV) +│ └── energy_distribution/ +│ ├── energy (1-D array, MeV) +│ ├── offset (1-D array, int) +│ ├── energy_out (1-D array, MeV) +│ └── pdf (1-D array) +└── atomic_relaxation/ + └── MT-534/ (K-shell; MT-535, MT-536, ... for higher shells) + ├── number_of_transitions (int) + ├── primary_subshell (1-D array, int) + ├── secondary_subshell (1-D array, int) + ├── energy (1-D array, MeV) + └── probability (1-D array) +``` + +## See Also +- [Continuous Energy Theory Guide](../../docs/source/theory/cont_energy.rst) +- [Installation Guide — CE Library Configuration](../../docs/source/install.rst) From 96662ccd3fd06defd6c25806051c87083bf21211 Mon Sep 17 00:00:00 2001 From: Melek Derman <48313913+melekderman@users.noreply.github.com> Date: Thu, 16 Apr 2026 00:29:04 -0400 Subject: [PATCH 4/8] Organize electron reactions into specific groups Refactor electron reaction groups and datasets in generate.py --- .../electron/generate.py | 113 +++++++++++------- 1 file changed, 68 insertions(+), 45 deletions(-) diff --git a/tools/data_library_generator/electron/generate.py b/tools/data_library_generator/electron/generate.py index 3fb3959c3..a213ed952 100644 --- a/tools/data_library_generator/electron/generate.py +++ b/tools/data_library_generator/electron/generate.py @@ -87,37 +87,73 @@ file.create_dataset("atomic_weight_ratio", data=ace_table.atomic_weight_ratio) # ================================================================================== - # Principal cross sections (energy grid + xs for all reactions) + # Reaction groups # ================================================================================== + # Elastic scattering : MT-528 + # Excitation : MT-527 + # Bremsstrahlung : MT-526 + # Electroionization : MT-534 (K), MT-535 (L1), MT-536 (L2), ... - xs0_block = ace_table.principal_cross_section_block + reactions = file.create_group("electron_reactions") - energy_grid = np.array(xs0_block.energies) - dataset = file.create_dataset("energy_grid", data=energy_grid) - dataset.attrs["unit"] = "MeV" + elastic_group = reactions.create_group("elastic_scattering") + excitation_group = reactions.create_group("excitation") + bremsstrahlung_group = reactions.create_group("bremsstrahlung") + electroionization_group = reactions.create_group("electroionization") + + # MT groups + elastic_group.create_group("MT-528").attrs["MT"] = 528 + excitation_group.create_group("MT-527").attrs["MT"] = 527 + bremsstrahlung_group.create_group("MT-526").attrs["MT"] = 526 + + subshell_block = ace_table.electron_subshell_block + N_subshells = subshell_block.number_of_subshells + electroionization_MTs = [534 + i for i in range(N_subshells)] + + for i, MT in enumerate(electroionization_MTs): + electroionization_group.create_group(f"MT-{MT:03}").attrs["MT"] = MT + + if verbose: + print(f" Reaction group MTs") + print(f" - Elastic scattering MT : [528]") + print(f" - Excitation MT : [527]") + print(f" - Bremsstrahlung MT : [526]") + print(f" - Electroionization MTs : {electroionization_MTs}") + + # ================================================================================== + # Cross sections + # ================================================================================== - xs_group = file.create_group("cross_sections") + xs0_block = ace_table.principal_cross_section_block - dataset = xs_group.create_dataset("elastic", data=np.array(xs0_block.elastic)) - dataset.attrs["unit"] = "barns" + xs_energy = np.array(xs0_block.energies) + dataset = reactions.create_dataset("xs_energy_grid", data=xs_energy) + dataset.attrs["unit"] = "MeV" - dataset = xs_group.create_dataset("bremsstrahlung", data=np.array(xs0_block.bremsstrahlung)) - dataset.attrs["unit"] = "barns" + # Elastic + xs = elastic_group.create_dataset("MT-528/xs", data=np.array(xs0_block.elastic)) + xs.attrs["unit"] = "barns" - dataset = xs_group.create_dataset("excitation", data=np.array(xs0_block.excitation)) - dataset.attrs["unit"] = "barns" + # Excitation + xs = excitation_group.create_dataset("MT-527/xs", data=np.array(xs0_block.excitation)) + xs.attrs["unit"] = "barns" - dataset = xs_group.create_dataset("electroionization", data=np.array(xs0_block.electroionization)) - dataset.attrs["unit"] = "barns" + # Bremsstrahlung + xs = bremsstrahlung_group.create_dataset("MT-526/xs", data=np.array(xs0_block.bremsstrahlung)) + xs.attrs["unit"] = "barns" - dataset = xs_group.create_dataset("total", data=np.array(xs0_block.total)) - dataset.attrs["unit"] = "barns" + # Electroionization per subshell + for i, MT in enumerate(electroionization_MTs): + xs = electroionization_group.create_dataset( + f"MT-{MT:03}/xs", data=np.array(subshell_block.cross_section(i + 1)) + ) + xs.attrs["unit"] = "barns" # ================================================================================== # Elastic angular distribution # ================================================================================== - angle_group = file.create_group("elastic_angular_distribution") + angle_group = elastic_group.create_group("MT-528/angular_cosine_distribution") util_electron.load_elastic_angular_distribution( ace_table.elastic_angular_distribution_block, angle_group ) @@ -127,7 +163,7 @@ # ================================================================================== excit_block = ace_table.excitation_block - excit_group = file.create_group("excitation") + excit_group = excitation_group.create_group("MT-527/energy_loss") energies = np.array(excit_block.energies) dataset = excit_group.create_dataset("energy", data=energies) @@ -140,40 +176,26 @@ # Bremsstrahlung energy distribution # ================================================================================== - brems_group = file.create_group("bremsstrahlung") + brems_group = bremsstrahlung_group.create_group("MT-526/energy_distribution") util_electron.load_bremsstrahlung(ace_table.bremsstrahlung_block, brems_group) # ================================================================================== - # Electroionization: per-subshell cross sections and energy distributions + # Electroionization: binding energy and knock-on electron energy distributions # ================================================================================== - subshell_block = ace_table.electron_subshell_block - N_subshells = subshell_block.number_of_subshells - - ioniz_group = file.create_group("electroionization") - - for i in range(N_subshells): + for i, MT in enumerate(electroionization_MTs): idx = i + 1 - shell_group = ioniz_group.create_group(f"subshell_{idx}") + MT_group = electroionization_group[f"MT-{MT:03}"] - # Binding energy and cross section binding_energy = subshell_block.binding_energy(idx) - dataset = shell_group.create_dataset("binding_energy", data=binding_energy) + dataset = MT_group.create_dataset("binding_energy", data=binding_energy) dataset.attrs["unit"] = "MeV" - xs = np.array(subshell_block.cross_section(idx)) - dataset = shell_group.create_dataset("xs", data=xs) - dataset.attrs["unit"] = "barns" - - # Knock-on electron energy distribution - energy_dist_group = shell_group.create_group("energy_distribution") + energy_dist_group = MT_group.create_group("energy_distribution") util_electron.load_electroionization_subshell( subshell_block.energy_distribution(idx), energy_dist_group ) - if verbose: - print(f" Z={Z} ({symbol}): {N_subshells} subshells") - # ================================================================================== # Atomic relaxation (subshell transition data) # ================================================================================== @@ -181,13 +203,14 @@ relaxation_block = ace_table.subshell_transition_data_block relaxation_group = file.create_group("atomic_relaxation") - for i in range(N_subshells): + for i, MT in enumerate(electroionization_MTs): idx = i + 1 data = relaxation_block.transition_data(idx) - shell_group = relaxation_group.create_group(f"subshell_{idx}") + MT_group = relaxation_group.create_group(f"MT-{MT:03}") + MT_group.attrs["MT"] = MT N_transitions = data.number_of_transitions - shell_group.create_dataset("number_of_transitions", data=N_transitions) + MT_group.create_dataset("number_of_transitions", data=N_transitions) if N_transitions > 0: primary_shells = [] @@ -201,11 +224,11 @@ energies.append(data.energy(jdx)) probabilities.append(data.probability(jdx)) - shell_group.create_dataset("primary_subshell", data=np.array(primary_shells)) - shell_group.create_dataset("secondary_subshell", data=np.array(secondary_shells)) - dataset = shell_group.create_dataset("energy", data=np.array(energies)) + MT_group.create_dataset("primary_subshell", data=np.array(primary_shells)) + MT_group.create_dataset("secondary_subshell", data=np.array(secondary_shells)) + dataset = MT_group.create_dataset("energy", data=np.array(energies)) dataset.attrs["unit"] = "MeV" - shell_group.create_dataset("probability", data=np.array(probabilities)) + MT_group.create_dataset("probability", data=np.array(probabilities)) # ================================================================================== # Finalize From c9a7950cc6c7f9e865df6746ef3f5bf8bc55a197 Mon Sep 17 00:00:00 2001 From: Melek Derman <48313913+melekderman@users.noreply.github.com> Date: Thu, 16 Apr 2026 00:48:59 -0400 Subject: [PATCH 5/8] Updated (correct) version with MTs- Refactor generate.py for ACE file --- .../electron/generate.py | 107 ++++++++++-------- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/tools/data_library_generator/electron/generate.py b/tools/data_library_generator/electron/generate.py index a213ed952..cd4735a7b 100644 --- a/tools/data_library_generator/electron/generate.py +++ b/tools/data_library_generator/electron/generate.py @@ -20,7 +20,7 @@ # Directories output_dir = os.getenv("MCDC_LIB_ELECTRON") -ace_file = os.getenv("MCDC_ACELIB_ELECTRON") +ace_file = os.getenv("MCDC_ACELIB_ELECTRON") xsdir_file = os.getenv("MCDC_ACELIB_ELECTRON_XSDIR") if output_dir is None: @@ -36,21 +36,32 @@ print(f"Xsdir file : {xsdir_file}") print(f"Output dir : {output_dir}\n") -# Read xsdir and collect EPR entries -xsdir = ACEtk.Xsdir.from_file(xsdir_file) +# Parse xsdir manually (EPRDATA14 xsdir has no header block) +# Format: zaid awr filename access filetype start_line table_length ... +all_entries = [] +with open(xsdir_file) as f: + for line in f: + parts = line.split() + if len(parts) < 6: + continue + zaid = parts[0] + start_line = int(parts[5]) + all_entries.append((zaid, start_line)) + +# Select target entries target_entries = [] -for entry in xsdir.entries: - if not entry.zaid.endswith(".14p"): +for zaid, start_line in all_entries: + if not zaid.endswith(".14p"): continue - Z = util_electron.decode_epr_zaid(entry.zaid) + Z = util_electron.decode_epr_zaid(zaid) symbol = util_electron.Z_TO_SYMBOL[Z] mcdc_name = f"{symbol}.h5" if not rewrite and os.path.exists(f"{output_dir}/{mcdc_name}"): continue - target_entries.append((entry, Z, symbol, mcdc_name)) + target_entries.append((zaid, start_line, Z, symbol, mcdc_name)) # Loop over all elements pbar = tqdm( @@ -58,18 +69,18 @@ disable=verbose, bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt}{postfix}", ) -for entry, Z, symbol, mcdc_name in pbar: +for zaid, start_line, Z, symbol, mcdc_name in pbar: if not rewrite and os.path.exists(f"{output_dir}/{mcdc_name}"): continue if verbose: print("\n" + "=" * 80 + "\n") - print(f"Create {mcdc_name} from {entry.zaid}\n") + print(f"Create {mcdc_name} from {zaid}\n") pbar.set_postfix_str(f"{mcdc_name}") # Load ACE table - ace_table = ACEtk.PhotoatomicTable.from_file(ace_file, entry.file_start_line) + ace_table = ACEtk.PhotoatomicTable.from_concatenated_file(ace_file, start_line) # Create MC/DC file file = h5py.File(f"{output_dir}/{mcdc_name}", "w") @@ -96,21 +107,20 @@ reactions = file.create_group("electron_reactions") - elastic_group = reactions.create_group("elastic_scattering") - excitation_group = reactions.create_group("excitation") - bremsstrahlung_group = reactions.create_group("bremsstrahlung") + elastic_group = reactions.create_group("elastic_scattering") + excitation_group = reactions.create_group("excitation") + bremsstrahlung_group = reactions.create_group("bremsstrahlung") electroionization_group = reactions.create_group("electroionization") - # MT groups - elastic_group.create_group("MT-528").attrs["MT"] = 528 - excitation_group.create_group("MT-527").attrs["MT"] = 527 + elastic_group.create_group("MT-528").attrs["MT"] = 528 + excitation_group.create_group("MT-527").attrs["MT"] = 527 bremsstrahlung_group.create_group("MT-526").attrs["MT"] = 526 - subshell_block = ace_table.electron_subshell_block - N_subshells = subshell_block.number_of_subshells + subsh_block = ace_table.electron_subshell_block + N_subshells = subsh_block.number_electron_subshells electroionization_MTs = [534 + i for i in range(N_subshells)] - for i, MT in enumerate(electroionization_MTs): + for MT in electroionization_MTs: electroionization_group.create_group(f"MT-{MT:03}").attrs["MT"] = MT if verbose: @@ -124,28 +134,24 @@ # Cross sections # ================================================================================== - xs0_block = ace_table.principal_cross_section_block + xs0_block = ace_table.electron_cross_section_block xs_energy = np.array(xs0_block.energies) dataset = reactions.create_dataset("xs_energy_grid", data=xs_energy) dataset.attrs["unit"] = "MeV" - # Elastic xs = elastic_group.create_dataset("MT-528/xs", data=np.array(xs0_block.elastic)) xs.attrs["unit"] = "barns" - # Excitation xs = excitation_group.create_dataset("MT-527/xs", data=np.array(xs0_block.excitation)) xs.attrs["unit"] = "barns" - # Bremsstrahlung xs = bremsstrahlung_group.create_dataset("MT-526/xs", data=np.array(xs0_block.bremsstrahlung)) xs.attrs["unit"] = "barns" - # Electroionization per subshell for i, MT in enumerate(electroionization_MTs): xs = electroionization_group.create_dataset( - f"MT-{MT:03}/xs", data=np.array(subshell_block.cross_section(i + 1)) + f"MT-{MT:03}/xs", data=np.array(xs0_block.electroionisation(i + 1)) ) xs.attrs["unit"] = "barns" @@ -155,21 +161,22 @@ angle_group = elastic_group.create_group("MT-528/angular_cosine_distribution") util_electron.load_elastic_angular_distribution( - ace_table.elastic_angular_distribution_block, angle_group + ace_table.electron_elastic_angular_distribution_block, angle_group ) # ================================================================================== # Excitation energy loss # ================================================================================== - excit_block = ace_table.excitation_block + excit_block = ace_table.electron_excitation_energy_loss_block excit_group = excitation_group.create_group("MT-527/energy_loss") - energies = np.array(excit_block.energies) - dataset = excit_group.create_dataset("energy", data=energies) + dataset = excit_group.create_dataset("energy", data=np.array(excit_block.energies)) dataset.attrs["unit"] = "MeV" - dataset = excit_group.create_dataset("energy_loss", data=np.array(excit_block.energy_loss)) + dataset = excit_group.create_dataset( + "excitation_energy_loss", data=np.array(excit_block.excitation_energy_loss) + ) dataset.attrs["unit"] = "MeV" # ================================================================================== @@ -177,7 +184,9 @@ # ================================================================================== brems_group = bremsstrahlung_group.create_group("MT-526/energy_distribution") - util_electron.load_bremsstrahlung(ace_table.bremsstrahlung_block, brems_group) + util_electron.load_bremsstrahlung( + ace_table.bremsstrahlung_energy_distribution_block, brems_group + ) # ================================================================================== # Electroionization: binding energy and knock-on electron energy distributions @@ -187,45 +196,47 @@ idx = i + 1 MT_group = electroionization_group[f"MT-{MT:03}"] - binding_energy = subshell_block.binding_energy(idx) - dataset = MT_group.create_dataset("binding_energy", data=binding_energy) + dataset = MT_group.create_dataset( + "binding_energy", data=subsh_block.binding_energy(idx) + ) dataset.attrs["unit"] = "MeV" energy_dist_group = MT_group.create_group("energy_distribution") util_electron.load_electroionization_subshell( - subshell_block.energy_distribution(idx), energy_dist_group + ace_table.electroionisation_energy_distribution_block(idx), energy_dist_group ) # ================================================================================== # Atomic relaxation (subshell transition data) # ================================================================================== - relaxation_block = ace_table.subshell_transition_data_block + relax_block = ace_table.subshell_transition_data_block relaxation_group = file.create_group("atomic_relaxation") for i, MT in enumerate(electroionization_MTs): idx = i + 1 - data = relaxation_block.transition_data(idx) + td = relax_block.transition_data(idx) MT_group = relaxation_group.create_group(f"MT-{MT:03}") MT_group.attrs["MT"] = MT - N_transitions = data.number_of_transitions + N_transitions = td.number_transitions MT_group.create_dataset("number_of_transitions", data=N_transitions) if N_transitions > 0: - primary_shells = [] - secondary_shells = [] - energies = [] - probabilities = [] + primary_designators = [] + secondary_designators = [] + energies = [] + probabilities = [] for j in range(N_transitions): jdx = j + 1 - primary_shells.append(data.primary_subshell(jdx)) - secondary_shells.append(data.secondary_subshell(jdx)) - energies.append(data.energy(jdx)) - probabilities.append(data.probability(jdx)) - - MT_group.create_dataset("primary_subshell", data=np.array(primary_shells)) - MT_group.create_dataset("secondary_subshell", data=np.array(secondary_shells)) + t = td.transition(jdx) + primary_designators.append(td.primary_designator(jdx)) + secondary_designators.append(td.secondary_designator(jdx)) + energies.append(td.energy(jdx)) + probabilities.append(td.probability(jdx)) + + MT_group.create_dataset("primary_designator", data=np.array(primary_designators)) + MT_group.create_dataset("secondary_designator", data=np.array(secondary_designators)) dataset = MT_group.create_dataset("energy", data=np.array(energies)) dataset.attrs["unit"] = "MeV" MT_group.create_dataset("probability", data=np.array(probabilities)) From 1bc4deb80e22cbb6c824f5474b2e4d160819f5c7 Mon Sep 17 00:00:00 2001 From: Melek Derman <48313913+melekderman@users.noreply.github.com> Date: Thu, 16 Apr 2026 00:50:50 -0400 Subject: [PATCH 6/8] Corrected/updated version with CDFs Updated functions to store CDF instead of PDF for various energy distributions. --- tools/data_library_generator/electron/util.py | 42 ++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/tools/data_library_generator/electron/util.py b/tools/data_library_generator/electron/util.py index 84a920f29..91de4264c 100644 --- a/tools/data_library_generator/electron/util.py +++ b/tools/data_library_generator/electron/util.py @@ -26,81 +26,75 @@ def decode_epr_zaid(name: str): def load_elastic_angular_distribution(block, h5_group: h5py.Group): """ Load elastic angular distribution block into HDF5 group. - Stores incident energy grid, cosine grid, PDF, and CDF per energy. + Stores incident energy grid, cosine grid, and CDF per energy. + Note: ACEtk provides CDF only (no PDF) for electron elastic angular data. """ - NE = block.number_of_energies - energies = np.array(block.energies) dataset = h5_group.create_dataset("energy", data=energies) dataset.attrs["unit"] = "MeV" + NE = len(energies) offset = np.zeros(NE, dtype=int) cosines = [] - pdf = [] cdf = [] - for i in range(NE): - dist = block.distribution(i + 1) + for i, dist in enumerate(block.distributions): offset[i] = len(cosines) cosines.extend(dist.cosines) - pdf.extend(dist.pdf) cdf.extend(dist.cdf) h5_group.create_dataset("offset", data=offset) h5_group.create_dataset("cosine", data=np.array(cosines)) - h5_group.create_dataset("pdf", data=np.array(pdf)) h5_group.create_dataset("cdf", data=np.array(cdf)) def load_bremsstrahlung(block, h5_group: h5py.Group): """ Load bremsstrahlung energy distribution block into HDF5 group. - Stores outgoing photon energy PDF for each incident electron energy. + Stores outgoing photon energy CDF for each incident electron energy. + Note: ACEtk provides CDF only (no PDF) for bremsstrahlung distributions. """ - NE = block.number_of_energies - energies = np.array(block.energies) dataset = h5_group.create_dataset("energy", data=energies) dataset.attrs["unit"] = "MeV" + NE = len(energies) offset = np.zeros(NE, dtype=int) energy_out = [] - pdf = [] - for i in range(NE): - dist = block.distribution(i + 1) + cdf = [] + for i, dist in enumerate(block.distributions): offset[i] = len(energy_out) energy_out.extend(dist.outgoing_energies) - pdf.extend(dist.pdf) + cdf.extend(dist.cdf) h5_group.create_dataset("offset", data=offset) dataset = h5_group.create_dataset("energy_out", data=np.array(energy_out)) dataset.attrs["unit"] = "MeV" - h5_group.create_dataset("pdf", data=np.array(pdf)) + h5_group.create_dataset("cdf", data=np.array(cdf)) def load_electroionization_subshell(block, h5_group: h5py.Group): """ Load electroionization energy distribution for a single subshell. - Stores outgoing (knock-on) electron energy PDF per incident energy. + Stores outgoing (knock-on) electron energy CDF per incident energy. + Note: ACEtk provides CDF only (no PDF) for electroionization distributions. """ - NE = block.number_of_energies - energies = np.array(block.energies) dataset = h5_group.create_dataset("energy", data=energies) dataset.attrs["unit"] = "MeV" + NE = len(energies) offset = np.zeros(NE, dtype=int) energy_out = [] - pdf = [] - for i in range(NE): - dist = block.distribution(i + 1) + cdf = [] + for i, dist in enumerate(block.distributions): offset[i] = len(energy_out) energy_out.extend(dist.outgoing_energies) - pdf.extend(dist.pdf) + cdf.extend(dist.cdf) h5_group.create_dataset("offset", data=offset) dataset = h5_group.create_dataset("energy_out", data=np.array(energy_out)) dataset.attrs["unit"] = "MeV" - h5_group.create_dataset("pdf", data=np.array(pdf)) + h5_group.create_dataset("cdf", data=np.array(cdf)) # ============================================================================= From 3471324497c7b7788c1f34c3db2f9b3060573659 Mon Sep 17 00:00:00 2001 From: Melek Derman Date: Wed, 15 Apr 2026 22:07:00 -0700 Subject: [PATCH 7/8] last changes after testing --- .../data_library_generator/electron/README.md | 27 +++---- .../electron/generate.py | 81 +++++++++---------- 2 files changed, 51 insertions(+), 57 deletions(-) diff --git a/tools/data_library_generator/electron/README.md b/tools/data_library_generator/electron/README.md index 909ea572b..f2916651d 100644 --- a/tools/data_library_generator/electron/README.md +++ b/tools/data_library_generator/electron/README.md @@ -9,16 +9,14 @@ pip install ACEtk h5py numpy tqdm You need the EPRDATA14 library (available from [LANL Nuclear Data](https://nucleardata.lanl.gov/ace/eprdata14)). ## Environment Variables -| Variable | Description | -|------------------------------|----------------------------------------------------| -| `MCDC_ACELIB_ELECTRON` | Path to the EPRDATA14 data file. | -| `MCDC_ACELIB_ELECTRON_XSDIR` | Path to the EPRDATA14 `xsdir` file. | -| `MCDC_LIB_ELECTRON` | Path to the output directory for MC/DC HDF5 files. | +| Variable | Description | +|------------------------|----------------------------------------------------| +| `MCDC_ACELIB_ELECTRON` | Path to the EPRDATA14 data file. | +| `MCDC_LIB_ELECTRON` | Path to the output directory for MC/DC HDF5 files. | ## Usage ```bash export MCDC_ACELIB_ELECTRON=/path/to/eprdata14/eprdata14 -export MCDC_ACELIB_ELECTRON_XSDIR=/path/to/eprdata14/xsdir export MCDC_LIB_ELECTRON=/path/to/mcdc/electron/library python generate_electron.py # Convert only missing elements @@ -28,10 +26,10 @@ python generate_electron.py --verbose # Print detailed per-element info ## What it Does For each element (Z=1 to Z=100) in the EPRDATA14 library, the generator: -1. Reads the xsdir to locate each elemental table within the single EPRDATA14 file. +1. Loads all elemental tables from the single concatenated EPRDATA14 file. 2. Extracts the principal cross section energy grid and pointwise cross sections (elastic, bremsstrahlung, excitation, electroionization) for all reaction channels. -3. Extracts tabulated elastic angular distributions (cosine PDFs) per incident energy (MT-528). +3. Extracts tabulated elastic angular distributions (cosine CDFs) per incident energy (MT-528). 4. Extracts excitation energy loss as a function of incident energy (MT-527). 5. Extracts bremsstrahlung outgoing photon energy distributions per incident energy (MT-526). 6. Extracts per-subshell electroionization cross sections and knock-on electron @@ -54,14 +52,13 @@ For each element (Z=1 to Z=100) in the EPRDATA14 library, the generator: │ │ ├── energy (1-D array, MeV) │ │ ├── offset (1-D array, int) │ │ ├── cosine (1-D array) -│ │ ├── pdf (1-D array) │ │ └── cdf (1-D array) │ ├── excitation/ │ │ └── MT-527/ │ │ ├── xs (1-D array, barns) │ │ └── energy_loss/ │ │ ├── energy (1-D array, MeV) -│ │ └── energy_loss (1-D array, MeV) +│ │ └── excitation_energy_loss (1-D array, MeV) │ ├── bremsstrahlung/ │ │ └── MT-526/ │ │ ├── xs (1-D array, barns) @@ -69,7 +66,7 @@ For each element (Z=1 to Z=100) in the EPRDATA14 library, the generator: │ │ ├── energy (1-D array, MeV) │ │ ├── offset (1-D array, int) │ │ ├── energy_out (1-D array, MeV) -│ │ └── pdf (1-D array) +│ │ └── cdf (1-D array) │ └── electroionization/ │ └── MT-534/ (K-shell; MT-535, MT-536, ... for higher shells) │ ├── xs (1-D array, barns) @@ -78,16 +75,16 @@ For each element (Z=1 to Z=100) in the EPRDATA14 library, the generator: │ ├── energy (1-D array, MeV) │ ├── offset (1-D array, int) │ ├── energy_out (1-D array, MeV) -│ └── pdf (1-D array) +│ └── cdf (1-D array) └── atomic_relaxation/ └── MT-534/ (K-shell; MT-535, MT-536, ... for higher shells) ├── number_of_transitions (int) - ├── primary_subshell (1-D array, int) - ├── secondary_subshell (1-D array, int) + ├── primary_designator (1-D array, int) + ├── secondary_designator (1-D array, int) ├── energy (1-D array, MeV) └── probability (1-D array) ``` ## See Also - [Continuous Energy Theory Guide](../../docs/source/theory/cont_energy.rst) -- [Installation Guide — CE Library Configuration](../../docs/source/install.rst) +- [Installation Guide — CE Library Configuration](../../docs/source/install.rst) \ No newline at end of file diff --git a/tools/data_library_generator/electron/generate.py b/tools/data_library_generator/electron/generate.py index cd4735a7b..d99397c14 100644 --- a/tools/data_library_generator/electron/generate.py +++ b/tools/data_library_generator/electron/generate.py @@ -8,8 +8,8 @@ #### -import util_electron -from util_electron import print_error, print_note +import util +from util import print_error, print_note parser = argparse.ArgumentParser(description="MC/DC electron data generator") parser.add_argument("--rewrite", dest="rewrite", action="store_true", default=False) @@ -20,48 +20,36 @@ # Directories output_dir = os.getenv("MCDC_LIB_ELECTRON") -ace_file = os.getenv("MCDC_ACELIB_ELECTRON") -xsdir_file = os.getenv("MCDC_ACELIB_ELECTRON_XSDIR") - +ace_file = os.getenv("MCDC_ACELIB_ELECTRON") if output_dir is None: print_error("Environment variable $MCDC_LIB_ELECTRON is not set") if ace_file is None: print_error("Environment variable $MCDC_ACELIB_ELECTRON is not set") -if xsdir_file is None: - print_error("Environment variable $MCDC_ACELIB_ELECTRON_XSDIR is not set") - # Create output directory if needed os.makedirs(output_dir, exist_ok=True) print(f"\nACE file : {ace_file}") -print(f"Xsdir file : {xsdir_file}") print(f"Output dir : {output_dir}\n") -# Parse xsdir manually (EPRDATA14 xsdir has no header block) -# Format: zaid awr filename access filetype start_line table_length ... -all_entries = [] -with open(xsdir_file) as f: - for line in f: - parts = line.split() - if len(parts) < 6: - continue - zaid = parts[0] - start_line = int(parts[5]) - all_entries.append((zaid, start_line)) +# Load all tables from the concatenated EPRDATA14 file +print("Loading EPRDATA14 tables...") +all_tables = ACEtk.PhotoatomicTable.from_concatenated_file(ace_file) +table_map = {t.zaid: t for t in all_tables} +print(f"Loaded {len(table_map)} tables\n") # Select target entries target_entries = [] -for zaid, start_line in all_entries: +for zaid in table_map: if not zaid.endswith(".14p"): continue - Z = util_electron.decode_epr_zaid(zaid) - symbol = util_electron.Z_TO_SYMBOL[Z] + Z = util.decode_epr_zaid(zaid) + symbol = util.Z_TO_SYMBOL[Z] mcdc_name = f"{symbol}.h5" if not rewrite and os.path.exists(f"{output_dir}/{mcdc_name}"): continue - target_entries.append((zaid, start_line, Z, symbol, mcdc_name)) + target_entries.append((zaid, Z, symbol, mcdc_name)) # Loop over all elements pbar = tqdm( @@ -69,7 +57,7 @@ disable=verbose, bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt}{postfix}", ) -for zaid, start_line, Z, symbol, mcdc_name in pbar: +for zaid, Z, symbol, mcdc_name in pbar: if not rewrite and os.path.exists(f"{output_dir}/{mcdc_name}"): continue @@ -80,7 +68,7 @@ pbar.set_postfix_str(f"{mcdc_name}") # Load ACE table - ace_table = ACEtk.PhotoatomicTable.from_concatenated_file(ace_file, start_line) + ace_table = table_map[zaid] # Create MC/DC file file = h5py.File(f"{output_dir}/{mcdc_name}", "w") @@ -107,13 +95,13 @@ reactions = file.create_group("electron_reactions") - elastic_group = reactions.create_group("elastic_scattering") - excitation_group = reactions.create_group("excitation") - bremsstrahlung_group = reactions.create_group("bremsstrahlung") + elastic_group = reactions.create_group("elastic_scattering") + excitation_group = reactions.create_group("excitation") + bremsstrahlung_group = reactions.create_group("bremsstrahlung") electroionization_group = reactions.create_group("electroionization") - elastic_group.create_group("MT-528").attrs["MT"] = 528 - excitation_group.create_group("MT-527").attrs["MT"] = 527 + elastic_group.create_group("MT-528").attrs["MT"] = 528 + excitation_group.create_group("MT-527").attrs["MT"] = 527 bremsstrahlung_group.create_group("MT-526").attrs["MT"] = 526 subsh_block = ace_table.electron_subshell_block @@ -143,10 +131,14 @@ xs = elastic_group.create_dataset("MT-528/xs", data=np.array(xs0_block.elastic)) xs.attrs["unit"] = "barns" - xs = excitation_group.create_dataset("MT-527/xs", data=np.array(xs0_block.excitation)) + xs = excitation_group.create_dataset( + "MT-527/xs", data=np.array(xs0_block.excitation) + ) xs.attrs["unit"] = "barns" - xs = bremsstrahlung_group.create_dataset("MT-526/xs", data=np.array(xs0_block.bremsstrahlung)) + xs = bremsstrahlung_group.create_dataset( + "MT-526/xs", data=np.array(xs0_block.bremsstrahlung) + ) xs.attrs["unit"] = "barns" for i, MT in enumerate(electroionization_MTs): @@ -160,7 +152,7 @@ # ================================================================================== angle_group = elastic_group.create_group("MT-528/angular_cosine_distribution") - util_electron.load_elastic_angular_distribution( + util.load_elastic_angular_distribution( ace_table.electron_elastic_angular_distribution_block, angle_group ) @@ -184,7 +176,7 @@ # ================================================================================== brems_group = bremsstrahlung_group.create_group("MT-526/energy_distribution") - util_electron.load_bremsstrahlung( + util.load_bremsstrahlung( ace_table.bremsstrahlung_energy_distribution_block, brems_group ) @@ -202,8 +194,9 @@ dataset.attrs["unit"] = "MeV" energy_dist_group = MT_group.create_group("energy_distribution") - util_electron.load_electroionization_subshell( - ace_table.electroionisation_energy_distribution_block(idx), energy_dist_group + util.load_electroionization_subshell( + ace_table.electroionisation_energy_distribution_block(idx), + energy_dist_group, ) # ================================================================================== @@ -223,10 +216,10 @@ MT_group.create_dataset("number_of_transitions", data=N_transitions) if N_transitions > 0: - primary_designators = [] + primary_designators = [] secondary_designators = [] - energies = [] - probabilities = [] + energies = [] + probabilities = [] for j in range(N_transitions): jdx = j + 1 t = td.transition(jdx) @@ -235,8 +228,12 @@ energies.append(td.energy(jdx)) probabilities.append(td.probability(jdx)) - MT_group.create_dataset("primary_designator", data=np.array(primary_designators)) - MT_group.create_dataset("secondary_designator", data=np.array(secondary_designators)) + MT_group.create_dataset( + "primary_designator", data=np.array(primary_designators) + ) + MT_group.create_dataset( + "secondary_designator", data=np.array(secondary_designators) + ) dataset = MT_group.create_dataset("energy", data=np.array(energies)) dataset.attrs["unit"] = "MeV" MT_group.create_dataset("probability", data=np.array(probabilities)) From 0a1b4c5c76d91a967c238f7721b3073f06dc1634 Mon Sep 17 00:00:00 2001 From: Melek Derman Date: Tue, 28 Apr 2026 18:12:44 -0700 Subject: [PATCH 8/8] last updates - tested --- .../data_library_generator/electron/README.md | 22 ++++++++--------- .../electron/generate.py | 24 +++++++++---------- .../{ => neutron}/README.md | 18 +++++++------- .../{ => neutron}/generate.py | 8 +++---- .../{ => neutron}/util.py | 0 5 files changed, 35 insertions(+), 37 deletions(-) rename tools/data_library_generator/{ => neutron}/README.md (81%) rename tools/data_library_generator/{ => neutron}/generate.py (98%) rename tools/data_library_generator/{ => neutron}/util.py (100%) diff --git a/tools/data_library_generator/electron/README.md b/tools/data_library_generator/electron/README.md index f2916651d..7c6724207 100644 --- a/tools/data_library_generator/electron/README.md +++ b/tools/data_library_generator/electron/README.md @@ -3,10 +3,10 @@ Converts EPRDATA14 ACE-format electron/photon/relaxation data into MC/DC's per-element HDF5 format for continuous-energy electron transport. ## Prerequisites -```bash -pip install ACEtk h5py numpy tqdm -``` -You need the EPRDATA14 library (available from [LANL Nuclear Data](https://nucleardata.lanl.gov/ace/eprdata14)). + +- Installing ACEtk from source: [link](https://github.com/njoy/ACEtk) +- Dependencies: `pip install h5py numpy tqdm` +- You need the EPRDATA14 library (available from [LANL Nuclear Data](https://nucleardata.lanl.gov/ace/eprdata14)). ## Environment Variables | Variable | Description | @@ -16,23 +16,23 @@ You need the EPRDATA14 library (available from [LANL Nuclear Data](https://nucle ## Usage ```bash -export MCDC_ACELIB_ELECTRON=/path/to/eprdata14/eprdata14 +export MCDC_ACELIB_ELECTRON=/path/to/eprdata14/eprdata14/eprdata14 export MCDC_LIB_ELECTRON=/path/to/mcdc/electron/library -python generate_electron.py # Convert only missing elements -python generate_electron.py --rewrite # Regenerate all files -python generate_electron.py --verbose # Print detailed per-element info +python generate.py # Convert only missing elements +python generate.py --rewrite # Regenerate all files +python generate.py --verbose # Print detailed per-element info ``` ## What it Does For each element (Z=1 to Z=100) in the EPRDATA14 library, the generator: 1. Loads all elemental tables from the single concatenated EPRDATA14 file. 2. Extracts the principal cross section energy grid and pointwise cross sections - (elastic, bremsstrahlung, excitation, electroionization) for all reaction channels. + (elastic, bremsstrahlung, excitation, ionization) for all reaction channels. 3. Extracts tabulated elastic angular distributions (cosine CDFs) per incident energy (MT-528). 4. Extracts excitation energy loss as a function of incident energy (MT-527). 5. Extracts bremsstrahlung outgoing photon energy distributions per incident energy (MT-526). -6. Extracts per-subshell electroionization cross sections and knock-on electron +6. Extracts per-subshell ionization cross sections and knock-on electron energy distributions (MT-534 for K-shell, MT-535+ for higher shells). 7. Extracts atomic relaxation (fluorescence and Auger) transition data per subshell. 8. Writes a single HDF5 file per element (e.g., `Al.h5`). @@ -67,7 +67,7 @@ For each element (Z=1 to Z=100) in the EPRDATA14 library, the generator: │ │ ├── offset (1-D array, int) │ │ ├── energy_out (1-D array, MeV) │ │ └── cdf (1-D array) -│ └── electroionization/ +│ └── ionization/ │ └── MT-534/ (K-shell; MT-535, MT-536, ... for higher shells) │ ├── xs (1-D array, barns) │ ├── binding_energy (float, MeV) diff --git a/tools/data_library_generator/electron/generate.py b/tools/data_library_generator/electron/generate.py index d99397c14..6de516b96 100644 --- a/tools/data_library_generator/electron/generate.py +++ b/tools/data_library_generator/electron/generate.py @@ -91,14 +91,14 @@ # Elastic scattering : MT-528 # Excitation : MT-527 # Bremsstrahlung : MT-526 - # Electroionization : MT-534 (K), MT-535 (L1), MT-536 (L2), ... + # Ionization : MT-534 (K), MT-535 (L1), MT-536 (L2), ... reactions = file.create_group("electron_reactions") elastic_group = reactions.create_group("elastic_scattering") excitation_group = reactions.create_group("excitation") bremsstrahlung_group = reactions.create_group("bremsstrahlung") - electroionization_group = reactions.create_group("electroionization") + ionization_group = reactions.create_group("ionization") elastic_group.create_group("MT-528").attrs["MT"] = 528 excitation_group.create_group("MT-527").attrs["MT"] = 527 @@ -106,17 +106,17 @@ subsh_block = ace_table.electron_subshell_block N_subshells = subsh_block.number_electron_subshells - electroionization_MTs = [534 + i for i in range(N_subshells)] + ionization_MTs = [534 + i for i in range(N_subshells)] - for MT in electroionization_MTs: - electroionization_group.create_group(f"MT-{MT:03}").attrs["MT"] = MT + for MT in ionization_MTs: + ionization_group.create_group(f"MT-{MT:03}").attrs["MT"] = MT if verbose: print(f" Reaction group MTs") print(f" - Elastic scattering MT : [528]") print(f" - Excitation MT : [527]") print(f" - Bremsstrahlung MT : [526]") - print(f" - Electroionization MTs : {electroionization_MTs}") + print(f" - Ionization MTs : {ionization_MTs}") # ================================================================================== # Cross sections @@ -141,8 +141,8 @@ ) xs.attrs["unit"] = "barns" - for i, MT in enumerate(electroionization_MTs): - xs = electroionization_group.create_dataset( + for i, MT in enumerate(ionization_MTs): + xs = ionization_group.create_dataset( f"MT-{MT:03}/xs", data=np.array(xs0_block.electroionisation(i + 1)) ) xs.attrs["unit"] = "barns" @@ -181,12 +181,12 @@ ) # ================================================================================== - # Electroionization: binding energy and knock-on electron energy distributions + # Ionization: binding energy and knock-on electron energy distributions # ================================================================================== - for i, MT in enumerate(electroionization_MTs): + for i, MT in enumerate(ionization_MTs): idx = i + 1 - MT_group = electroionization_group[f"MT-{MT:03}"] + MT_group = ionization_group[f"MT-{MT:03}"] dataset = MT_group.create_dataset( "binding_energy", data=subsh_block.binding_energy(idx) @@ -206,7 +206,7 @@ relax_block = ace_table.subshell_transition_data_block relaxation_group = file.create_group("atomic_relaxation") - for i, MT in enumerate(electroionization_MTs): + for i, MT in enumerate(ionization_MTs): idx = i + 1 td = relax_block.transition_data(idx) MT_group = relaxation_group.create_group(f"MT-{MT:03}") diff --git a/tools/data_library_generator/README.md b/tools/data_library_generator/neutron/README.md similarity index 81% rename from tools/data_library_generator/README.md rename to tools/data_library_generator/neutron/README.md index 43065cd76..91ac4b18e 100644 --- a/tools/data_library_generator/README.md +++ b/tools/data_library_generator/neutron/README.md @@ -5,24 +5,22 @@ for continuous-energy neutron transport. ## Prerequisites -```bash -pip install ACEtk h5py numpy tqdm -``` - -You need a collection of ACE files (e.g., from NJOY or an ENDF/B distribution). +- Installing ACEtk from source: [link](https://github.com/njoy/ACEtk) +- Dependencies: `pip install h5py numpy tqdm` +- You need a collection of ACE files (e.g., from NJOY or an ENDF/B distribution). ## Environment Variables | Variable | Description | |---------------|-------------------------------------------------------| -| `MCDC_ACELIB` | Path to the directory containing your ACE files. | -| `MCDC_LIB` | Path to the output directory for MC/DC HDF5 files. | +| `MCDC_ACELIB_NEUTRON` | Path to the directory containing your ACE files. | +| `MCDC_LIB_NEUTRON` | Path to the output directory for MC/DC HDF5 files. | ## Usage ```bash -export MCDC_ACELIB=/path/to/ace/files -export MCDC_LIB=/path/to/mcdc/library +export MCDC_ACELIB_NEUTRON=/path/to/ace/files +export MCDC_LIB_NEUTRON=/path/to/mcdc/library python generate.py # Convert only missing nuclides python generate.py --rewrite # Regenerate all files @@ -31,7 +29,7 @@ python generate.py --verbose # Print detailed per-nuclide info ## What it Does -For each ACE file in `$MCDC_ACELIB`, the generator: +For each ACE file in `$MCDC_ACELIB_NEUTRON`, the generator: 1. Reads the ACE header to identify the nuclide (Z, A, isomeric state) and temperature. 2. Extracts pointwise cross sections (elastic, capture, inelastic, fission) and the energy grid. diff --git a/tools/data_library_generator/generate.py b/tools/data_library_generator/neutron/generate.py similarity index 98% rename from tools/data_library_generator/generate.py rename to tools/data_library_generator/neutron/generate.py index 06a63dc76..94202f7fc 100644 --- a/tools/data_library_generator/generate.py +++ b/tools/data_library_generator/neutron/generate.py @@ -19,13 +19,13 @@ verbose = args.verbose # Directories -output_dir = os.getenv("MCDC_LIB") -ace_dir = os.getenv("MCDC_ACELIB") +output_dir = os.getenv("MCDC_LIB_NEUTRON") +ace_dir = os.getenv("MCDC_ACELIB_NEUTRON") if output_dir is None: - print_error("Environment variable $MCDC_LIB is not set") + print_error("Environment variable $MCDC_LIB_NEUTRON is not set") if ace_dir is None: - print_error("Environment variable $MCDC_ACELIB is not set") + print_error("Environment variable $MCDC_ACELIB_NEUTRON is not set") # Create output directory if needed os.makedirs(output_dir, exist_ok=True) diff --git a/tools/data_library_generator/util.py b/tools/data_library_generator/neutron/util.py similarity index 100% rename from tools/data_library_generator/util.py rename to tools/data_library_generator/neutron/util.py