Source code for solrat.atom_model.multi_term_atom_model.object.precomputed_data
import os
from dataclasses import dataclass, field
from typing import Union
import pandas as pd
from solrat.engine.generators.merge_frame import Frame, FrameFactor
def _frame_to_csv(frame: Frame, path: str) -> None:
"""Save a Frame to CSV by evaluating all unmerged factors into a single 'coefficient' column."""
f = frame.copy()
# Merge any unmerged factors
for name in list(f.factors.keys()):
if not f.factors[name].merged:
f.merge_factor(name)
# Combine multiple merged factors into one column
if len(f.factors) > 1:
combined_name = f.combine_all_merged_factors()
else:
combined_name = next(iter(f.factors)) if f.factors else None
# Rename to 'coefficient' if needed
if combined_name is not None and combined_name != "coefficient":
f.frame = f.frame.rename(columns={combined_name: "coefficient"})
f.frame.to_csv(path, index=False)
def _frame_from_csv(path: str) -> Frame:
"""Load a Frame from CSV. The 'coefficient' column is registered as a merged factor."""
df = pd.read_csv(path)
frame = Frame(base_frame=df)
loop_cols = [c for c in df.columns if c != "coefficient"]
frame.factors["coefficient"] = FrameFactor(
name="coefficient",
dependencies=loop_cols,
merged=True,
)
return frame
_FILE_MAP = {
"coherence_decay_frame": "coherence_decay_n0.csv",
"coherence_decay_frame_n_1": "coherence_decay_n1.csv",
"absorption_frame": "absorption.csv",
"emission_e_frame": "emission_e.csv",
"emission_s_frame": "emission_s.csv",
"relaxation_e_frame": "relaxation_e.csv",
"relaxation_a_frame": "relaxation_a.csv",
"relaxation_s_frame": "relaxation_s.csv",
}
[docs]
@dataclass
class PrecomputedData:
"""
Container for precomputed atom-specific SEE frames.
Frames that do not depend on the radiation tensor or atmosphere parameters can be
precomputed once, saved to CSV via :meth:`save_to_directory`, and reloaded via
:meth:`load_from_directory`. Frames that still require the radiation tensor (absorption,
emission_s, relaxation_a, relaxation_s) are stored with the atom-specific factor already
evaluated; the radiation-tensor factor is applied per :meth:`fill_all_equations` call.
"""
coherence_decay_frame: Frame
absorption_frame: Frame
emission_e_frame: Frame
emission_s_frame: Frame
relaxation_e_frame: Frame
relaxation_a_frame: Frame
relaxation_s_frame: Frame
coherence_decay_frame_n_1: Union[Frame, None] = field(default=None)
[docs]
def save_to_directory(self, directory: str) -> None:
"""Save all frames to CSV files in *directory*."""
os.makedirs(directory, exist_ok=True)
for attr, filename in _FILE_MAP.items():
frame = getattr(self, attr, None)
if frame is not None:
_frame_to_csv(frame, os.path.join(directory, filename))
[docs]
@classmethod
def load_from_directory(cls, directory: str) -> "PrecomputedData":
"""Load all frames from CSV files in *directory*."""
def _load(filename: str) -> Frame:
return _frame_from_csv(os.path.join(directory, filename))
n1_path = os.path.join(directory, _FILE_MAP["coherence_decay_frame_n_1"])
return cls(
coherence_decay_frame=_load(_FILE_MAP["coherence_decay_frame"]),
absorption_frame=_load(_FILE_MAP["absorption_frame"]),
emission_e_frame=_load(_FILE_MAP["emission_e_frame"]),
emission_s_frame=_load(_FILE_MAP["emission_s_frame"]),
relaxation_e_frame=_load(_FILE_MAP["relaxation_e_frame"]),
relaxation_a_frame=_load(_FILE_MAP["relaxation_a_frame"]),
relaxation_s_frame=_load(_FILE_MAP["relaxation_s_frame"]),
coherence_decay_frame_n_1=(
_load(_FILE_MAP["coherence_decay_frame_n_1"]) if os.path.exists(n1_path) else None
),
)