Skip to content

Commit

Permalink
Several bugfixes for apm_app cameca examples, heuristics for leap mod…
Browse files Browse the repository at this point in the history
…el and pulse mode
  • Loading branch information
atomprobe-tc committed Jan 30, 2025
1 parent 04f5ac8 commit 94dc834
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 26 deletions.
2 changes: 1 addition & 1 deletion src/pynxtools_apm/configurations/cameca_cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
("atom_probe/reconstruction/quality", "fQuality"),
("atom_probe/reconstruction/primary_element", "fPrimaryElement"),
("measurement/instrument/local_electrode/name", "fApertureName"),
("measurement/instrument/instrument_name", "fAtomProbeName"),
("measurement/instrument/name", "fAtomProbeName"),
("measurement/instrument/fabrication/model", "fLeapModel"),
("measurement/instrument/fabrication/serial_number", "fSerialNumber"),
(
Expand Down
2 changes: 1 addition & 1 deletion src/pynxtools_apm/configurations/eln_cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
"prefix_src": "instrument/",
"map": [
"status",
"instrument_name",
("name", "instrument_name"),
"location",
("fabrication/vendor", "fabrication_vendor"),
("fabrication/model", "fabrication_model"),
Expand Down
4 changes: 1 addition & 3 deletions src/pynxtools_apm/utils/generate_synthetic_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,9 +410,7 @@ def emulate_instrument_header(self, template: dict) -> dict:
# check if required fields exists and are valid
# print("Parsing instrument header...")
trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/"
template[f"{trg}instrument_name"] = str(
f"test instrument {np.random.choice(100, 1)[0]}"
)
template[f"{trg}name"] = str(f"test instrument {np.random.choice(100, 1)[0]}")
template[f"{trg}flight_path_length"] = np.float64(
np.random.normal(loc=1.0, scale=0.05)
)
Expand Down
97 changes: 76 additions & 21 deletions src/pynxtools_apm/utils/oasis_apsuite_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,56 @@ def parse_comments(self, template: dict) -> dict:
template[f"/ENTRY[entry{self.entry_id}]/experiment_description"] = free_text
return template

def assume_pulse_mode(self, template: dict) -> dict:
"""Assume the pulse mode from specific values."""
# WHETHER OR NOT THIS ASSUMPTION IS RIGHT has not been confirmed by Cameca
# the assumption is here only implemented to show a typical example to
# technology partners where challenges exists and offer an elaborated guess
# for RDM that should however better be confirmed by tech partners and would
# profit from having qualifiers attached to how sure one is for each mapped
# quantity and concept
if "fInitialPulserFreq" in self.yml:
if self.yml["fInitialPulserFreq"] < 0.0:
pulse_mode = "laser"
elif self.yml[f"fInitialPulserFreq"] >= 0.0:
pulse_mode = "voltage"
else:
pulse_mode = "unknown"
template[
f"/ENTRY[entry{self.entry_id}]/measurement/instrument/pulser/pulse_mode"
] = pulse_mode
return template

def assume_leap_model_enum_for_rdm(self, template: dict) -> dict:
"""Try to recover the APT instrument model avoiding possible typos via enum."""
# WHETHER OR NOT THIS ASSUMPTION IS RIGHT has not been confirmed by Cameca
# we rely on examples here
# ["Inspico", "3DAP, "LAWATAP", "LEAP 3000 Si", "LEAP 3000X Si",
# "LEAP 3000 HR", "LEAP 3000X HR", "LEAP 4000 Si", "LEAP 4000X Si",
# "LEAP 4000 HR", "LEAP 4000X HR",
# "LEAP 5000 XS", "LEAP 5000 XR", "LEAP 5000 R", "EIKOS", "EIKOS-UV",
# "LEAP 6000 XR", "LEAP INVIZO", "Photonic AP", "TeraSAT", "TAPHR",
# "Modular AP", "Titanium APT", "Extreme UV APT", "unknown"]
if "fLeapModel" in self.yml:
for fleap_model, enum_value in [
("10", "LEAP 4000X Si"),
("11", "LEAP 4000 HR"),
("12", "LEAP 4000X HR"),
("14", "LEAP 5000 XS"),
("15", "LEAP 4000 XHR"),
("16", "LEAP 5000 XR"),
("17", "LEAP INVIZO"),
]:
if fleap_model == self.yml[f"LeapModel"]:
template[
f"/ENTRY[entry{self.entry_id}]/measurement/instrument/type"
] = enum_value
return template
template[f"/ENTRY[entry{self.entry_id}]/measurement/instrument/type"] = (
"unknown"
)
return template

def parse_acquisition_mode(self, template: dict) -> dict:
return template

Expand All @@ -125,7 +175,7 @@ def parse_ranging_definitions(self, template: dict) -> dict:
if src in self.yml:
if isinstance(self.yml[src], list):
unique_elements = set()
add_unknown_iontype(template, entry_id=1)
# add_unknown_iontype(template, entry_id=1)
ion_id = 0

for rng_def in self.yml[src]:
Expand All @@ -142,33 +192,37 @@ def parse_ranging_definitions(self, template: dict) -> dict:
]
):
continue
line = rng_def["fRngComposition"].strip()
tmp = re.split(r"[\s=]+", line)

atoms = []
for entry in tmp:
element_multiplicity = re.split(r":+", entry)
if len(element_multiplicity) != 2:
raise ValueError(
f"Line {line}, element multiplicity is not "
f"correctly formatted {len(element_multiplicity)}!"
)
if (
element_multiplicity[0] in chemical_symbols[1::]
and np.uint32(element_multiplicity[1])
< MAX_NUMBER_OF_ION_SPECIES
):
# TODO::check if range is significant
symbol = element_multiplicity[0]
atoms += [symbol] * int(element_multiplicity[1])
unique_elements.add(symbol)
line = rng_def["fRngComposition"].strip()
if line != "":
tmp = re.split(r"[\s=]+", line)
for entry in tmp:
element_multiplicity = re.split(r":+", entry)
if len(element_multiplicity) != 2:
raise ValueError(
f"Line {line}, element multiplicity is not "
f"correctly formatted {len(element_multiplicity)}!"
)
if (
element_multiplicity[0] in chemical_symbols[1::]
and np.uint32(element_multiplicity[1])
< MAX_NUMBER_OF_ION_SPECIES
):
# TODO::check if range is significant
symbol = element_multiplicity[0]
atoms += [symbol] * int(element_multiplicity[1])
unique_elements.add(symbol)

ion = NxIon(nuclide_hash=create_nuclide_hash(atoms), charge_state=0)
ion.add_range(
rng_def["fMassToChargeLow"], rng_def["fMassToChargeHigh"]
)
ion.comment = NxField(rng_def["fRngName"], "")
ion.comment = NxField(rng_def["fRngName"].strip(), "")
ion.apply_combinatorics()
ion.update_human_readable_name()
if ion.name.values == "" and rng_def["fRngName"].strip() != "":
ion.name.values = rng_def["fRngName"].strip()
# print(ion.report())

trg = f"/ENTRY[entry{self.entry_id}]/atom_probe/ranging/peak_identification/ionID[ion{ion_id}]/"
Expand Down Expand Up @@ -282,7 +336,8 @@ def parse(self, template: dict) -> dict:
self.parse_event_statistics(template)
self.parse_versions(template)
self.parse_comments(template)
# self.parse_acquisition_mode(template)
self.assume_pulse_mode(template)
self.assume_leap_model_enum_for_rdm(template)
self.parse_ranging_definitions(template)
identifier = [self.entry_id, 1]
add_specific_metadata_pint(APM_CAMECA_TO_NEXUS, self.yml, identifier, template)
Expand Down

0 comments on commit 94dc834

Please sign in to comment.