def build_database(source_pckl='EPICS2023.pckl', output_pckl='geim_data.pckl'):
"""
Loads raw EPICS parser output, compiles CubicSplines and relaxation tables,
integrates physics_base formulations, and exports the final simulation database.
"""
print(f"Loading raw parser data from {source_pckl}...")
with open(source_pckl, 'rb') as fi:
Z, M, DATA = pickle.load(fi)
print("Compiling atomic subshell configurations...")
subshells = DATA[0][0][91][912][0]['shell']
shell_labels = ['1s¹⁄₂','2s¹⁄₂','2p¹⁄₂','2p³⁄₂','3s¹⁄₂','3p¹⁄₂','3p³⁄₂','3d³⁄₂','3d⁵⁄₂','4s¹⁄₂','4p¹⁄₂','4p³⁄₂']
# Pack basic configurations
shell_names = dict(zip(subshells, shell_labels))
n_elec = dict(zip(subshells, DATA[0][0][91][912][0][' num_electrons']))
e_bind = dict(zip(subshells, DATA[0][0][91][913][0]['binding_energy']))
e_loc = dict(zip(subshells, DATA[0][0][92][935][0]['energy to atom']))
print("Compiling photon integrated interaction splines...")
t_coh = DATA[7][0][71][0][0]
t_inc = DATA[7][0][72][0][0]
t_pe = DATA[7][0][73][0][0]
splines = {
'COHERENT': CubicSpline(t_coh['Eγ'], t_coh['σ_coherent'], extrapolate=False),
'INCOHERENT': CubicSpline(t_inc['Eγ'], t_inc['σ_incoherent'], extrapolate=False),
'PHOTOELECT': CubicSpline(t_pe['Eγ'], t_pe['σ_photoelect'], extrapolate=False),
'FormFactor': CubicSpline(DATA[7][0][93][941][0]['x'], DATA[7][0][93][941][0]['form_factor '], extrapolate=True),
'Scatt_Func': CubicSpline(DATA[7][0][93][942][0]['x'], DATA[7][0][93][942][0]['scatter_func'], extrapolate=True),
'ImAnScFact': CubicSpline(DATA[7][0][93][943][0]['Eγ'], DATA[7][0][93][943][0]['Im_an_sc_fac'], extrapolate=True),
'ReAnScFact': CubicSpline(DATA[7][0][93][944][0]['Eγ'], DATA[7][0][93][944][0]['Re_an_sc_fac'], extrapolate=True)
}
print("Compiling electron integrated interaction splines...")
splines.update({
'BREMSSTRAH': CubicSpline(DATA[9][0][82][0][0]['Ee'], DATA[9][0][82][0][0]['σ_bremsstrah'], extrapolate=True),
'AvEeBremss': CubicSpline(DATA[9][9][82][10][0]['Ee'], DATA[9][9][82][10][0]['<Ee_bremsst>'], extrapolate=True),
'AvEγBremss': CubicSpline(DATA[9][7][82][10][0]['Ee'], DATA[9][7][82][10][0]['<Eγ_bremsst>'], extrapolate=True),
'ELASTTRANS': CubicSpline(DATA[9][0][7][0][0]['Ee'], DATA[9][0][7][0][0]['σ_elastic_trans'], extrapolate=True),
'ELALASCATT': CubicSpline(DATA[9][0][8][0][0]['Ee'], DATA[9][0][8][0][0]['σ_LA_elastic_sc'], extrapolate=True),
'ELASTSCATT': CubicSpline(DATA[9][0][10][0][0]['Ee'], DATA[9][0][10][0][0]['σ_elastic_scatt'], extrapolate=True)
})
print("Compiling shell-specific interaction splines...")
phel_shell, ionz_shell, reco_ebind = {}, {}, {}
for ss in subshells:
t_phel = DATA[7][0][73][0][ss]
phel_shell[ss] = CubicSpline(t_phel['Eγ'][1:], t_phel['σ_photoelect'][1:], extrapolate=False)
t_ionz = DATA[9][0][81][0][ss]
ionz_shell[ss] = CubicSpline(t_ionz['Ee'][1:], t_ionz['σ_ionization'][1:], extrapolate=False)
t_reco = DATA[9][19][81][10][ss]
reco_ebind[ss] = CubicSpline(t_reco['Ee'], t_reco['<Ee_recoil>'] / e_bind[ss], extrapolate=False)
splines['PHEL_SHELL'] = phel_shell
splines['IONZ_SHELL'] = ionz_shell
splines['RECO_EBIND'] = reco_ebind
# --- GEIM MULTI-SHELL COMPTON HYBRID CALIBRATION ---
print("Compiling calibrated shell-specific Compton cross-section splines...")
compton_grid_Eγ = t_inc['Eγ'] # Keep the exact, unmodified original EPICS2023 grid
comp_shell = {}
for ss in subshells:
Eb = e_bind[ss]
Ne = n_elec[ss]
# Allocate the calibrated array over the exact master grid size
shell_σ_calibrated = np.zeros(len(compton_grid_Eγ))
for idx, Eγ in enumerate(compton_grid_Eγ):
# If the photon energy is below or equal to the binding threshold,
# the cross-section is strictly physical zero.
if Eγ <= Eb:
shell_σ_calibrated[idx] = 0.0
continue
σ_epics_total = t_inc['σ_incoherent'][idx]
# Sum up theoretical bound-shell cross-sections for the denominator
Σ_step = 0.0
for current_ss in subshells:
Σ_step += physics_base.σ_KN_bound_shell(Eγ, e_bind[current_ss], n_elec[current_ss])
# Compute analytical weight for the current target subshell
σ_target_theory = physics_base.σ_KN_bound_shell(Eγ, Eb, Ne)
if Σ_step > 1e-6:
shell_σ_calibrated[idx] = σ_epics_total * (σ_target_theory / Σ_step)
else:
shell_σ_calibrated[idx] = 0.0
# Construct the CubicSpline using the unbroken master energetic grid.
# This guarantees alignment and synchronization across all atomic processes.
comp_shell[ss] = CubicSpline(compton_grid_Eγ, shell_σ_calibrated, extrapolate=False)
splines['COMP_SHELL'] = comp_shell
print("Computing atomic relaxation cascade tables...")
relaxation_table = calculate_relaxation_table(subshells, DATA)
# Pack everything into the final compiled dict structure
compiled_db = {
'Z': Z,
'A_raw': M,
'subshells': subshells,
'shell_names': shell_names,
'n_elec': n_elec,
'e_bind': e_bind,
'e_loc': e_loc,
'splines': splines,
'relaxation': relaxation_table
}
print(f"Saving compiled performance database to {output_pckl}...")
with open(output_pckl, 'wb') as fo:
pickle.dump(compiled_db, fo, protocol=pickle.HIGHEST_PROTOCOL)
print("Database built successfully!\n")