From 63f7eb5742696b85e59918eeb72988e089388dda Mon Sep 17 00:00:00 2001 From: mkuehbach Date: Sat, 1 Feb 2025 00:21:37 +0100 Subject: [PATCH] Using refactored NXapm, possibly BREAKING CHANGE, next through testing with run-throughs --- .../configurations/cameca_cfg.py | 12 ++++----- src/pynxtools_apm/configurations/eln_cfg.py | 27 +++++++++---------- src/pynxtools_apm/configurations/oasis_cfg.py | 4 +-- .../utils/generate_synthetic_data.py | 22 +++++++++++++++ .../utils/oasis_apsuite_reader.py | 2 +- src/pynxtools_apm/utils/oasis_eln_reader.py | 5 ++-- 6 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/pynxtools_apm/configurations/cameca_cfg.py b/src/pynxtools_apm/configurations/cameca_cfg.py index ddea7e8..f3418ea 100644 --- a/src/pynxtools_apm/configurations/cameca_cfg.py +++ b/src/pynxtools_apm/configurations/cameca_cfg.py @@ -32,11 +32,11 @@ ("measurement/instrument/fabrication/model", "fLeapModel"), ("measurement/instrument/fabrication/serial_number", "fSerialNumber"), ( - "measurement/instrument/pulser/SOURCE[source*]/fabrication/model", + "measurement/instrument/pulser/sourceID[source*]/fabrication/model", "fLaserModelString", ), ( - "measurement/instrument/pulser/SOURCE[source*]/fabrication/serial_number", + "measurement/instrument/pulser/sourceID[source*]/fabrication/serial_number", "fLaserSerialNumber", ), ( @@ -73,18 +73,18 @@ ("atom_probe/reconstruction/tip_radius_zero", ureg.nanometer, "fTipRadius0"), ("atom_probe/reconstruction/voltage_zero", ureg.volt, "fVoltage0"), ( - "measurement/instrument/analysis_chamber/pressure", + "measurement/events/eventID[event*]/instrument/analysis_chamber/pressure", ureg.torr, "fAnalysisPressure", ), ( - "measurement/instrument/local_electrode/voltage", + "measurement/events/eventID[event*]/instrument/local_electrode/voltage", ureg.volt, "fAnodeAccelVoltage", ), ("elapsed_time", ureg.second, "fElapsedTime"), ( - "measurement/instrument/pulser/pulse_frequency", + "measurement/events/eventID[event*]/instrument/pulser/pulse_frequency", ureg.kilohertz, "fInitialPulserFreq", ), @@ -96,7 +96,7 @@ "fMaximumFlightPathMm", ), ( - "measurement/instrument/stage/specimen_temperature", + "measurement/events/eventID[event*]/instrument/stage/specimen_temperature", ureg.kelvin, "fSpecimenTemperature", ), diff --git a/src/pynxtools_apm/configurations/eln_cfg.py b/src/pynxtools_apm/configurations/eln_cfg.py index 95a56f2..2109ff7 100644 --- a/src/pynxtools_apm/configurations/eln_cfg.py +++ b/src/pynxtools_apm/configurations/eln_cfg.py @@ -22,21 +22,21 @@ APM_ENTRY_TO_NEXUS = { "prefix_trg": "/ENTRY[entry*]", "prefix_src": "entry/", - "map": [ - "run_number", + "map_to_str": [ "operation_mode", "start_time", "end_time", "experiment_description", ("experiment_alias", "run_number"), ], + "map_to_u4": ["run_number"], } APM_SAMPLE_TO_NEXUS = { "prefix_trg": "/ENTRY[entry*]/sample", "prefix_src": "sample/", - "map": [ + "map_to_str": [ "alias", "description", ("type", "method"), @@ -88,7 +88,7 @@ APM_SPECIMEN_TO_NEXUS = { "prefix_trg": "/ENTRY[entry*]/specimen", "prefix_src": "specimen/", - "map": [ + "map_to_str": [ "alias", "preparation_date", "description", @@ -116,16 +116,15 @@ APM_INSTRUMENT_STATIC_TO_NEXUS = { "prefix_trg": "/ENTRY[entry*]/measurement/instrument", "prefix_src": "instrument/", - "map": [ - "status", - ("name", "instrument_name"), + "map_to_str": [ + # "status", "location", + ("name", "instrument_name"), ("fabrication/vendor", "fabrication_vendor"), ("fabrication/model", "fabrication_model"), ("fabrication/identifier/identifier", "fabrication_identifier"), ("reflectron/status", "reflectron_status"), ("local_electrode/name", "local_electrode_name"), - ("pulser/pulse_mode", "pulser/pulse_mode"), ], "map_to_f8": [ ( @@ -139,11 +138,11 @@ APM_INSTRUMENT_DYNAMIC_TO_NEXUS = { - "prefix_trg": "/ENTRY[entry*]/measurement/event_data_apm_set/event_data_apm/instrument", + "prefix_trg": "/ENTRY[entry*]/measurement/events/eventID[event*]/instrument", "prefix_src": "instrument/", "use": [("control/target_detection_rate/@units", "ions/pulse")], - "map": [ - "pulser_pulse_mode", + "map_to_str": [ + ("pulser/pulse_mode", "pulser/pulse_mode"), ("control/evaporation_control", "evaporation_control"), ], "map_to_f8": [ @@ -156,7 +155,7 @@ ), ("pulser/pulse_fraction", "pulser/pulse_fraction"), ( - "analysis_chamber/chamber_pressure", + "analysis_chamber/pressure", ureg.bar, "chamber_pressure/value", "chamber_pressure/unit", @@ -174,7 +173,7 @@ APM_RANGE_TO_NEXUS = { "prefix_trg": "/ENTRY[entry*]/atom_probe/ranging", "prefix_src": "ranging/", - "map": [ + "map_to_str": [ ("programID[program1]/program", "program"), ("programID[program1]/program/@version", "program_version"), ], @@ -184,7 +183,7 @@ APM_RECON_TO_NEXUS = { "prefix_trg": "/ENTRY[entry*]/atom_probe/reconstruction", "prefix_src": "reconstruction/", - "map": [ + "map_to_str": [ "protocol_name", "crystallographic_calibration", "parameter", diff --git a/src/pynxtools_apm/configurations/oasis_cfg.py b/src/pynxtools_apm/configurations/oasis_cfg.py index bdab673..1685c04 100644 --- a/src/pynxtools_apm/configurations/oasis_cfg.py +++ b/src/pynxtools_apm/configurations/oasis_cfg.py @@ -49,7 +49,7 @@ f"{dt.datetime.now(dt.timezone.utc).isoformat().replace('+00:00', 'Z')}", ), ], - "map": ["operation_mode"], + "map_to_str": ["operation_mode"], } @@ -89,5 +89,5 @@ APM_EXAMPLE_TO_NEXUS = { "prefix_trg": "/ENTRY[entry*]/CITE[cite*]", "prefix_src": "", - "map": ["authors", "doi", "description", "url"], + "map_to_str": ["authors", "doi", "description", "url"], } diff --git a/src/pynxtools_apm/utils/generate_synthetic_data.py b/src/pynxtools_apm/utils/generate_synthetic_data.py index 89f3fed..9768a91 100644 --- a/src/pynxtools_apm/utils/generate_synthetic_data.py +++ b/src/pynxtools_apm/utils/generate_synthetic_data.py @@ -68,6 +68,8 @@ class ApmCreateExampleData: """A synthesized dataset meant to be used for development purposes only!.""" def __init__(self, synthesis_id): + """Construct class.""" + raise NotImplementedError() # assure deterministic behaviour of the PRNG np.random.seed(seed=synthesis_id) @@ -106,6 +108,7 @@ def create_reconstructed_positions(self): # assumptions: # identity orientation, no periodic boundary conditions + raise NotImplementedError() print(f"Using the following version of ase {ase.__version__}") xyz = np.asarray( FaceCenteredCubic( @@ -159,6 +162,7 @@ def place_atoms_from_periodic_table(self): create (hypothetical) charged molecular ions from them and evaluate their mass-to-charge-state ratio to be used as values in the example dataset.""" + raise NotImplementedError() # uniform random model for how many different ions # !! warning: for real world datasets this depends on real specimen composition @@ -268,6 +272,7 @@ def place_atoms_from_periodic_table(self): def composition_to_ranging_definitions(self, template: dict) -> dict: """Create ranging definitions based on composition.""" + raise NotImplementedError() assert len(self.nrm_composition) > 0, "Composition is not defined!" trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/ranging/" template[f"{trg}programID[program1]/program"] = NX_APM_EXEC_NAME @@ -303,6 +308,7 @@ def composition_to_ranging_definitions(self, template: dict) -> dict: def emulate_entry(self, template: dict) -> dict: """Copy data in entry section.""" + raise NotImplementedError() # check if required fields exists and are valid # print("Parsing entry...") trg = f"/ENTRY[entry{self.entry_id}]/" @@ -328,6 +334,7 @@ def emulate_entry(self, template: dict) -> dict: def emulate_user(self, template: dict) -> dict: """Copy data in user section.""" + raise NotImplementedError() # check if required fields exists and are valid # print("Parsing user...") prefix = f"/ENTRY[entry{self.entry_id}]/" @@ -367,6 +374,7 @@ def emulate_user(self, template: dict) -> dict: def emulate_specimen(self, template: dict) -> dict: """Copy data in specimen section.""" + raise NotImplementedError() # check if required fields exists and are valid # print("Parsing specimen...") trg = f"/ENTRY[entry{self.entry_id}]/specimen/" @@ -397,6 +405,7 @@ def emulate_specimen(self, template: dict) -> dict: def emulate_control_software(self, template: dict) -> dict: """Copy data in control software section.""" + raise NotImplementedError() # print("Parsing control software...") trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/control_software/" template[f"{trg}programID[program1]/program"] = "IVAS" @@ -407,6 +416,7 @@ def emulate_control_software(self, template: dict) -> dict: def emulate_instrument_header(self, template: dict) -> dict: """Copy data in instrument_header section.""" + raise NotImplementedError() # check if required fields exists and are valid # print("Parsing instrument header...") trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/" @@ -419,6 +429,7 @@ def emulate_instrument_header(self, template: dict) -> dict: def emulate_fabrication(self, template: dict) -> dict: """Copy data in fabrication section.""" + raise NotImplementedError() # print("Parsing fabrication...") trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/FABRICATION[fabrication]/" template[f"{trg}vendor"] = str( @@ -446,6 +457,7 @@ def emulate_fabrication(self, template: dict) -> dict: def emulate_analysis_chamber(self, template: dict) -> dict: """Copy data in analysis_chamber section.""" + raise NotImplementedError() # print("Parsing analysis chamber...") trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/analysis_chamber/" template[f"{trg}pressure"] = np.float64( @@ -456,6 +468,7 @@ def emulate_analysis_chamber(self, template: dict) -> dict: def emulate_reflectron(self, template: dict) -> dict: """Copy data in reflectron section.""" + raise NotImplementedError() # print("Parsing reflectron...") trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/REFLECTRON[reflectron]/" template[f"{trg}applied"] = bool(np.random.choice([0, 1], 1)[0]) @@ -463,6 +476,7 @@ def emulate_reflectron(self, template: dict) -> dict: def emulate_local_electrode(self, template: dict) -> dict: """Copy data in local_electrode section.""" + raise NotImplementedError() # print("Parsing local electrode...") trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/local_electrode/" template[f"{trg}name"] = str(f"electrode {np.random.choice(1000, 1)[0]}") @@ -470,6 +484,7 @@ def emulate_local_electrode(self, template: dict) -> dict: def emulate_detector(self, template: dict) -> dict: """Copy data in ion_detector section.""" + raise NotImplementedError() # print("Parsing detector...") trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/ion_detector/" detector_model_type = str(np.random.choice(["cameca", "mcp", "custom"], 1)[0]) @@ -483,6 +498,7 @@ def emulate_detector(self, template: dict) -> dict: def emulate_stage_lab(self, template: dict) -> dict: """Copy data in stage lab section.""" + raise NotImplementedError() # print("Parsing stage lab...") trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/stage_lab/" template[f"{trg}base_temperature"] = np.float64(10 + np.random.choice(50, 1)[0]) @@ -491,6 +507,7 @@ def emulate_stage_lab(self, template: dict) -> dict: def emulate_specimen_monitoring(self, template: dict) -> dict: """Copy data in specimen_monitoring section.""" + raise NotImplementedError() # print("Parsing specimen monitoring...") trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/specimen_monitoring/" eta = np.min((np.random.normal(loc=0.6, scale=0.1), 1.0)) @@ -503,6 +520,7 @@ def emulate_specimen_monitoring(self, template: dict) -> dict: def emulate_pulser(self, template: dict) -> dict: """Copy data in pulser section.""" + raise NotImplementedError() # print("Parsing pulser...") trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/pulser/" pulse_mode = str( @@ -537,6 +555,7 @@ def emulate_pulser(self, template: dict) -> dict: def emulate_reconstruction(self, template: dict) -> dict: """Copy data in reconstruction section.""" + raise NotImplementedError() # print("Parsing reconstruction...") trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/reconstruction/" src = f"/ENTRY[entry{self.entry_id}]/atom_probe/control_software/" @@ -555,6 +574,7 @@ def emulate_reconstruction(self, template: dict) -> dict: def emulate_ranging(self, template: dict) -> dict: """Copy data in ranging section.""" + raise NotImplementedError() # print("Parsing ranging...") trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/ranging/" src = f"/ENTRY[entry{self.entry_id}]/atom_probe/control_software/" @@ -568,6 +588,7 @@ def emulate_ranging(self, template: dict) -> dict: def emulate_random_input_from_eln(self, template: dict) -> dict: """Emulate random input as could come from an ELN.""" + raise NotImplementedError() self.emulate_entry(template) self.emulate_user(template) self.emulate_specimen(template) @@ -594,6 +615,7 @@ def emulate_random_input_from_eln(self, template: dict) -> dict: def synthesize(self, template: dict) -> dict: """Hand-over instantiated dataset to dataconverter template.""" + raise NotImplementedError() # heavy data, synthetic/mocked dataset for entry_id in np.arange(1, self.n_entries + 1): self.entry_id = entry_id diff --git a/src/pynxtools_apm/utils/oasis_apsuite_reader.py b/src/pynxtools_apm/utils/oasis_apsuite_reader.py index 3a985dd..2dad5c0 100644 --- a/src/pynxtools_apm/utils/oasis_apsuite_reader.py +++ b/src/pynxtools_apm/utils/oasis_apsuite_reader.py @@ -132,7 +132,7 @@ def assume_pulse_mode(self, template: dict) -> dict: else: pulse_mode = "unknown" template[ - f"/ENTRY[entry{self.entry_id}]/measurement/instrument/pulser/pulse_mode" + f"/ENTRY[entry{self.entry_id}]/measurement/events/eventID[event1]/instrument/pulser/pulse_mode" ] = pulse_mode return template diff --git a/src/pynxtools_apm/utils/oasis_eln_reader.py b/src/pynxtools_apm/utils/oasis_eln_reader.py index 6005768..8fe1a00 100644 --- a/src/pynxtools_apm/utils/oasis_eln_reader.py +++ b/src/pynxtools_apm/utils/oasis_eln_reader.py @@ -162,7 +162,8 @@ def parse_pulser_source(self, template: dict) -> dict: for ldct in self.yml[src]: trg_sta = ( f"/ENTRY[entry{self.entry_id}]/measurement/" - f"instrument/pulser/sourceID[source{laser_id}]" + f"events/eventID[event1]/instrument/" + f"pulser/sourceID[source{laser_id}]" ) if "name" in ldct: template[f"{trg_sta}/name"] = ldct["name"] @@ -174,7 +175,7 @@ def parse_pulser_source(self, template: dict) -> dict: trg_dyn = ( f"/ENTRY[entry{self.entry_id}]/measurement/" - f"event_data_apm_set/event_data_apm/instrument/" + f"events/eventID[event1]/instrument/" f"pulser/sourceID[source{laser_id}]" ) quantities = ["power", "pulse_energy"]