diff --git a/examples/data/synthesis/synthesize_seeds_advanced.py b/examples/data/synthesis/synthesize_seeds_advanced.py new file mode 100644 index 00000000..bf5d532d --- /dev/null +++ b/examples/data/synthesis/synthesize_seeds_advanced.py @@ -0,0 +1,81 @@ +# Copyright 2021 National Technology & Engineering Solutions of Sandia, LLC (NTESS). +# Under the terms of Contract DE-NA0003525 with NTESS, +# the U.S. Government retains certain rights in this software. +"""This example demonstrates how to generate synthetic seeds from GADRAS using PyRIID's +configuration expansion features.""" +import yaml + +from riid.data.synthetic.seed import SeedSynthesizer +from riid.gadras.api import get_expanded_config, validate_inject_config + +seed_synth_config = """ +--- +gamma_detector: + name: Generic\\NaI\\2x4x16 + parameters: + distance_cm: + - 10 + - 100 + - 1000 + height_cm: 100 + dead_time_per_pulse: 5 + latitude_deg: 35.0 + longitude_deg: 253.4 + elevation_m: 1620 +sources: + - isotope: Cs137 + configurations: + - Cs137,100uCi + - name: Cs137 + activity: + - 1 + - 0.5 + activity_units: Ci + shielding_atomic_number: + min: 10 + max: 40.0 + dist: uniform + num_samples: 5 + shielding_aerial_density: + mean: 120 + std: 2 + num_samples: 5 + - isotope: Cosmic + configurations: + - Cosmic + - isotope: K40 + configurations: + - PotassiumInSoil + - isotope: Ra226 + configurations: + - UraniumInSoil + - isotope: Th232 + configurations: + - ThoriumInSoil +... +""" +seed_synth_config = yaml.safe_load(seed_synth_config) +validate_inject_config(seed_synth_config) +from pprint import pprint +pprint(len(get_expanded_config(seed_synth_config)["sources"][0]["configurations"])) + +try: + seeds_ss = SeedSynthesizer().generate( + seed_synth_config, + verbose=True + ) + print(seeds_ss) + + # At this point, you could save out the seeds via: + seeds_ss.to_hdf("seeds.h5") + + # or start separating your backgrounds from foreground for use with the StaticSynthesizer + fg_seeds_ss, bg_seeds_ss = seeds_ss.split_fg_and_bg() + + print(fg_seeds_ss) + print(bg_seeds_ss) + + fg_seeds_ss.to_hdf("./fg_seeds.h5") + bg_seeds_ss.to_hdf("./bg_seeds.h5") +except FileNotFoundError: + pass # Happens when not on Windows diff --git a/riid/data/synthetic/seed.py b/riid/data/synthetic/seed.py index f15454a2..3a448b17 100644 --- a/riid/data/synthetic/seed.py +++ b/riid/data/synthetic/seed.py @@ -15,7 +15,7 @@ from riid.data.sampleset import SampleSet, _get_utc_timestamp, read_pcf from riid.gadras.api import (DETECTOR_PARAMS, GADRAS_ASSEMBLY_PATH, INJECT_PARAMS, SourceInjector, get_gadras_api, - validate_inject_config) + get_inject_setups, validate_inject_config) class SeedSynthesizer(): @@ -65,7 +65,7 @@ def _set_detector_parameters(self, gadras_api, new_parameters: dict, verbose=Fal def generate(self, config: Union[str, dict], normalize_spectra: bool = True, normalize_sources: bool = True, - dry_run=False, verbose: bool = False) -> SampleSet: + verbose: bool = False) -> SampleSet: """Produce a `SampleSet` containing foreground and/or background seeds using GADRAS based on the given inject configuration. @@ -75,8 +75,6 @@ def generate(self, config: Union[str, dict], file which deserialized as a dictionary normalize_spectra: whether to divide each row of `SampleSet.spectra` by each row's sum normalize_sources: whether to divide each row of `SampleSet.sources` by each row's sum - dry_run: when False, actually perform inject(s), otherwise simply report info about - what would hypothetically happen verbose: whether to show detailed output Returns: @@ -95,30 +93,25 @@ def generate(self, config: Union[str, dict], validate_inject_config(config) + setups = get_inject_setups(config) with self._cwd(GADRAS_ASSEMBLY_PATH): gadras_api = get_gadras_api() detector_name = config["gamma_detector"]["name"] - new_detector_parameters = config["gamma_detector"]["parameters"] gadras_api.detectorSetCurrent(detector_name) original_detector_parameters = self._get_detector_parameters(gadras_api) - now = _get_utc_timestamp().replace(":", "_") # replace() prevents error on Windows - - rel_output_path = f"{now}_sources.pcf" - source_list = [] - detector_setups = [new_detector_parameters] # TODO: generate all detector_setups source_injector = SourceInjector(gadras_api) - try: - for d in detector_setups: - self._set_detector_parameters(gadras_api, d, verbose, dry_run) - - if dry_run: - continue - + if verbose: + print(f"Obtaining sources for '{detector_name}'") + + for s in setups: + new_detector_parameters = s["gamma_detector"]["parameters"] + now = _get_utc_timestamp().replace(":", "_") # replace() prevents error on Windows + rel_output_path = f"{now}_sources.pcf" + source_list = [] + try: + self._set_detector_parameters(gadras_api, new_detector_parameters, verbose) # TODO: propagate dry_run to injectors - # Source injects - if verbose: - print(f"Obtaining sources for '{detector_name}'") pcf_abs_path = source_injector.generate( config, rel_output_path, @@ -130,22 +123,13 @@ def generate(self, config: Union[str, dict], if normalize_spectra: seeds_ss.normalize() source_list.append(seeds_ss) - - if verbose: - print() - - if dry_run: - return None - - except Exception as e: - # Try to restore .dat file to original state even when an error occurs - if not dry_run: + except Exception as e: + # Try to restore .dat file to original state even when an error occurs self._set_detector_parameters(gadras_api, original_detector_parameters) - raise e + raise e # Restore .dat file to original state - if not dry_run: - self._set_detector_parameters(gadras_api, original_detector_parameters) + self._set_detector_parameters(gadras_api, original_detector_parameters) ss = SampleSet() ss.concat(source_list) diff --git a/riid/gadras/api.py b/riid/gadras/api.py index 8c2405b5..187dc95a 100644 --- a/riid/gadras/api.py +++ b/riid/gadras/api.py @@ -535,15 +535,19 @@ def get_expanded_config(config: dict) -> dict: return expanded_config -def get_detector_setups(expanded_config: dict): - """ - Permutate the lists of values in the expanded config to +def get_detector_setups(expanded_config: dict) -> list: + """Permutate the lists of values in the expanded config to generate a list of detector setups. + + Args: + expanded_config: a dictionary representing an expanded seed synthesis configuration + + Returns: + A list of detector setups """ detector_params = expanded_config["gamma_detector"]["parameters"] list_of_parameters_values = [x for x in detector_params.values()] parameter_permutations = list(itertools.product(*list_of_parameters_values)) - detector_setups = [] for perm in parameter_permutations: setup = copy.deepcopy(expanded_config["gamma_detector"]) @@ -554,13 +558,17 @@ def get_detector_setups(expanded_config: dict): return detector_setups -def get_inject_setups(config: dict): - """ - Creates a list of dictionaries containing the individual detector_setups with - expanded sources. +def get_inject_setups(config: dict) -> list: + """Get a list of fully expanded synthesis configurations from an initial, + collapsed configuration. + + Args: + config: a dictionary representing a collapsed seed synthesis configuration + + Returns: + A list of expanded configurations """ expanded_config = get_expanded_config(config) - detector_setups = get_detector_setups(expanded_config) inject_setups = [] for detector_setup in detector_setups: diff --git a/riid/gadras/api_schema.json b/riid/gadras/api_schema.json index 4908a314..3dac6043 100644 --- a/riid/gadras/api_schema.json +++ b/riid/gadras/api_schema.json @@ -48,7 +48,6 @@ "elevation_m": { "description": "Elevation, in meters", "$ref": "#/$defs/detector_properties_types" - } }, "required": [ @@ -73,18 +72,22 @@ "items": { "type": "object", "properties": { - "isotope": { - "type": "string" - }, - "configurations": { - "type": "array", - "items": { - "anyOf": [ - {"type": "string"}, - {"$ref": "#/$defs/source_config_type"} - ] + "isotope": { + "type": "string" + }, + "configurations": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/$defs/source_config_type" + } + ] + } } - } }, "additionalProperties": false } @@ -100,20 +103,34 @@ "type": "array", "items": { "anyOf": [ - {"type":"number"}, - {"$ref": "#/$defs/sample_range"}, - {"$ref": "#/$defs/sample_norm"} + { + "type": "number" + }, + { + "$ref": "#/$defs/sample_range" + }, + { + "$ref": "#/$defs/sample_norm" + } ] } }, - {"$ref":"#/$defs/sample_range"}, - {"$ref":"#/$defs/sample_norm"} + { + "$ref": "#/$defs/sample_range" + }, + { + "$ref": "#/$defs/sample_norm" + } ] }, - "sample_range": { "type": "object", - "required": [ "min", "max", "dist", "num_samples" ], + "required": [ + "min", + "max", + "dist", + "num_samples" + ], "properties": { "min": { "type": "number", @@ -140,7 +157,11 @@ }, "sample_norm": { "type": "object", - "required": [ "mean", "std", "num_samples"], + "required": [ + "mean", + "std", + "num_samples" + ], "properties": { "mean": { "type": "number", @@ -157,40 +178,78 @@ }, "additionalProperties": false }, - "source_config_type":{ + "source_config_type": { "type": "object", - "required": ["name"], + "required": [ + "name" + ], "properties": { "name": { "type": "string" }, "activity": { "anyOf": [ - {"type": "number"}, - {"type": "array","items": {"type":"number"}}, - {"$ref": "#/$defs/sample_range"}, - {"$ref": "#/$defs/sample_norm"} + { + "type": "number" + }, + { + "type": "array", + "items": { + "type": "number" + } + }, + { + "$ref": "#/$defs/sample_range" + }, + { + "$ref": "#/$defs/sample_norm" + } ] }, "activity_units": { "type": "string", - "enum": ["Ci", "uCi", "Bq"] + "enum": [ + "Ci", + "uCi", + "Bq" + ] }, "shielding_atomic_number": { "anyOf": [ - {"type": "number"}, - {"type": "array","items": {"type":"number"}}, - {"$ref": "#/$defs/sample_range"}, - {"$ref": "#/$defs/sample_norm"} - + { + "type": "number" + }, + { + "type": "array", + "items": { + "type": "number" + } + }, + { + "$ref": "#/$defs/sample_range" + }, + { + "$ref": "#/$defs/sample_norm" + } ] }, - "shielding_areal_density": { + "shielding_aerial_density": { "anyOf": [ - {"type": "number"}, - {"type": "array","items": {"type":"number"}}, - {"$ref": "#/$defs/sample_range"}, - {"$ref": "#/$defs/sample_norm"} + { + "type": "number" + }, + { + "type": "array", + "items": { + "type": "number" + } + }, + { + "$ref": "#/$defs/sample_range" + }, + { + "$ref": "#/$defs/sample_norm" + } ] } },