diff --git a/src/pg_rad/configs/filepaths.py b/src/pg_rad/configs/filepaths.py index e8103af..385a92b 100644 --- a/src/pg_rad/configs/filepaths.py +++ b/src/pg_rad/configs/filepaths.py @@ -1,3 +1,4 @@ ATTENUATION_TABLE = 'attenuation_table.csv' +ISOTOPE_TABLE = 'isotopes.csv' TEST_EXP_DATA = 'test_path_coords.csv' LOGGING_CONFIG = 'logging.yml' diff --git a/src/pg_rad/data/isotopes.csv b/src/pg_rad/data/isotopes.csv new file mode 100644 index 0000000..282a3b9 --- /dev/null +++ b/src/pg_rad/data/isotopes.csv @@ -0,0 +1,6 @@ +isotope,gamma_energy_keV,branching_ratio +Cs134,604.7,0.976 +Cs134,795.9,0.855 +Cs137,661.657,0.851 +Co60,1173.228,1.0 +Co60,1332.5,1.0 \ No newline at end of file diff --git a/src/pg_rad/inputparser/parser.py b/src/pg_rad/inputparser/parser.py index 67fe80e..c6dc8b2 100644 --- a/src/pg_rad/inputparser/parser.py +++ b/src/pg_rad/inputparser/parser.py @@ -11,6 +11,7 @@ from pg_rad.exceptions.exceptions import ( InvalidYAMLError ) from pg_rad.configs import defaults +from pg_rad.isotopes.isotope import get_isotope from .specs import ( MetadataSpec, @@ -276,7 +277,9 @@ class ConfigParser: specs: List[PointSourceSpec] = [] for name, params in source_dict.items(): - required = {"activity_MBq", "isotope", "position"} + required = { + "activity_MBq", "isotope", "position", "gamma_energy_keV" + } if not isinstance(params, dict): raise InvalidConfigValueError( f"sources.{name} is not defined correctly." @@ -288,7 +291,10 @@ class ConfigParser: raise MissingConfigKeyError(name, missing) activity = params.get("activity_MBq") - isotope = params.get("isotope") + isotope_name = params.get("isotope") + gamma_energy_keV = params.get("gamma_energy_keV") + + isotope = get_isotope(isotope_name, gamma_energy_keV) if not isinstance(activity, int | float) or activity <= 0: raise InvalidConfigValueError( diff --git a/src/pg_rad/isotopes/isotope.py b/src/pg_rad/isotopes/isotope.py index ec501af..c73fc26 100644 --- a/src/pg_rad/isotopes/isotope.py +++ b/src/pg_rad/isotopes/isotope.py @@ -1,5 +1,8 @@ -from typing import Dict, Type +from importlib.resources import files +from pandas import read_csv + +from pg_rad.configs.filepaths import ISOTOPE_TABLE from pg_rad.exceptions.exceptions import InvalidIsotopeError from pg_rad.physics.attenuation import get_mass_attenuation_coeff @@ -30,22 +33,35 @@ class Isotope: self.mu_mass_air = get_mass_attenuation_coeff(E / 1000) -class CS137(Isotope): - def __init__(self): - super().__init__( - name="Cs-137", - E=661.66, - b=0.851 +def get_isotope(isotope: str, energy_gamma_keV: float) -> Isotope: + """Lazy factory function to create isotope objects.""" + path = files('pg_rad.data').joinpath(ISOTOPE_TABLE) + df = read_csv(path) + + isotope_df = df[df['isotope'] == isotope] + + if isotope_df.empty: + raise InvalidIsotopeError(f"No data available for isotope {isotope}.") + + tol = 0.01 * energy_gamma_keV + closest_energy = isotope_df[ + (isotope_df['gamma_energy_keV'] >= energy_gamma_keV - tol) & + (isotope_df['gamma_energy_keV'] <= energy_gamma_keV + tol) + ] + + if closest_energy.empty: + available_gammas = ', '.join( + str(x)+' keV' for x in isotope_df['gamma_energy_keV'].to_list() + ) + raise InvalidIsotopeError( + f"No gamma of {energy_gamma_keV}±{tol} keV " + f"found for isotope {isotope}. " + f"Available gammas are: {available_gammas}" ) - -preset_isotopes: Dict[str, Type[Isotope]] = { - "Cs137": CS137 -} - - -def get_isotope(isotope_str: str) -> Isotope: - """Lazy factory function to create isotope objects.""" - if isotope_str not in preset_isotopes: - raise InvalidIsotopeError(f"Unknown isotope: {isotope_str}") - return preset_isotopes[isotope_str]() + matched_row = closest_energy.iloc[0] + return Isotope( + name=isotope, + E=matched_row['gamma_energy_keV'], + b=matched_row['branching_ratio'] + ) diff --git a/src/pg_rad/main.py b/src/pg_rad/main.py index bfc741c..b31db63 100644 --- a/src/pg_rad/main.py +++ b/src/pg_rad/main.py @@ -59,7 +59,7 @@ def main(): length: 1000 segments: - straight - - turn_left + - turn_left: 45 direction: negative sources: @@ -67,6 +67,7 @@ def main(): activity_MBq: 100 position: [250, 100, 0] isotope: Cs137 + gamma_energy_keV: 661 detector: name: dummy diff --git a/src/pg_rad/objects/sources.py b/src/pg_rad/objects/sources.py index 74a0ad3..08e4146 100644 --- a/src/pg_rad/objects/sources.py +++ b/src/pg_rad/objects/sources.py @@ -12,7 +12,7 @@ class PointSource(BaseObject): def __init__( self, activity_MBq: int, - isotope: str, + isotope: Isotope, position: tuple[float, float, float] = (0, 0, 0), name: str | None = None, color: str = 'red' @@ -40,7 +40,7 @@ class PointSource(BaseObject): super().__init__(position, name, color) self.activity = activity_MBq - self.isotope: Isotope = get_isotope(isotope) + self.isotope = isotope logger.debug(f"Source created: {self.name}") diff --git a/src/pg_rad/plotting/result_plotter.py b/src/pg_rad/plotting/result_plotter.py index bcd4a65..5163913 100644 --- a/src/pg_rad/plotting/result_plotter.py +++ b/src/pg_rad/plotting/result_plotter.py @@ -105,7 +105,7 @@ class ResultPlotter: data = [ [ s.name, - s.isotope, + s.isotope+f" ({s.primary_gamma} keV)", s.activity, "("+", ".join(f"{val:.2f}" for val in s.position)+")", round(s.dist_from_path, 2) diff --git a/src/pg_rad/simulator/engine.py b/src/pg_rad/simulator/engine.py index 3af7d9e..a7d4d9a 100644 --- a/src/pg_rad/simulator/engine.py +++ b/src/pg_rad/simulator/engine.py @@ -62,6 +62,7 @@ class SimulationEngine: SourceOutput( s.name, s.isotope.name, + s.isotope.E, s.activity, s.pos, dist_to_path) diff --git a/src/pg_rad/simulator/outputs.py b/src/pg_rad/simulator/outputs.py index 395317f..5186a5e 100644 --- a/src/pg_rad/simulator/outputs.py +++ b/src/pg_rad/simulator/outputs.py @@ -13,6 +13,7 @@ class CountRateOutput: class SourceOutput: name: str isotope: str + primary_gamma: float activity: float position: Tuple[float, float, float] dist_from_path: float