pynxtools-apm 0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,45 @@
1
+ <!--# APM reader
2
+
3
+ ## Getting started - usage related to NOMAD Oasis
4
+ This parser can be used to map numerical data and metadata inside frequently
5
+ used file formats of atom probe tomography and related field-ion microscopy
6
+ software tools into a NeXus HDF5 file which complies with a specific version
7
+ of the NXapm application definition.
8
+
9
+ If you are using a NOMAD Oasis, you can use this tool as follows.
10
+ 1. Log-in to a NOMAD Oasis and navigate to the *Create Uploads* tab.
11
+ 2. Select apm example from the list of available options.
12
+ 3. Drag-and-drop the file with your reconstructed dataset via pos, epos, or apt,
13
+ and add your ranging definitions file via rrng, or rng, or the fig files from
14
+ Peter Felfer's Erlangen atom-probe-toolbox.
15
+ 3. Edit the electronic lab notebook (ELN) schema inside the NOMAD Oasis and click the
16
+ save button in the NOMAD Oasis GUI to save the data that you have entered into
17
+ the ELN template. Clicking *save* will trigger the automatic generation
18
+ of an eln_data.yaml file. Second, the clicking will trigger a run of the
19
+ dataconverter/apm parser which generates a NXapm NeXus file based on the data
20
+ in the reconstruction and ranging file and the eln_data.yaml file. Afterwards,
21
+ the file will be displayed in the GUI and show up in the upload section of your Oasis.
22
+ By default the converter performs a strong loss-less compression on the input
23
+ as many of the stack data store integers with a low entropy. The compression may
24
+ take some time. You can inspect the progress of the conversion in the console from
25
+ which you started the NOMAD Oasis appworker.
26
+ 4. If successful, a NeXus (nxs) file will appear in your upload. You can explore
27
+ its content with the H5Web tools inside the NOMAD Oasis GUI and click interactively
28
+ through the data including default plots. For atom probe these are a 3D discretized
29
+ view of your reconstruction using 1nm rectangular binning and the mass spectrum
30
+ with a default 0.01 Da binning an no additional corrections.
31
+ If unsuccessful, the console from where you started the NOMAD appworker can help
32
+ you with identifying if problems occurred.
33
+
34
+ ## A request to take action by the technology partners
35
+ In fact, while the above-mentioned file formats and corresponding commercial software is routinely
36
+ used by numerous atom probers every day, the actual knowledge about the I/O routines has always
37
+ been on a few developers shoulders. In fact many of the open-source readers for file formats were
38
+ reverse-engineered. This is challenging because the amount of documentation that is available
39
+ for some file formats is neither exhaustive nor documented enough in detail.
40
+
41
+ This limits developers to decide and design how to best implement possible mappings of
42
+ specific binary numerical data and metadata into specifically named fields. We wish that
43
+ intensified exchange between technology partners like AMETEK/Cameca and the atom probe
44
+ community can help to improve the situation. Everybody is very welcomed to leave us
45
+ comments in the issue list (or drop us an email) to exchange specifically about this topic.-->
File without changes
File without changes
@@ -0,0 +1,345 @@
1
+ #
2
+ # Copyright The NOMAD Authors.
3
+ #
4
+ # This file is part of NOMAD. See https://nomad-lab.eu for further info.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ """Utilities for working with NeXus concepts encoded as Python dicts in the concepts dir."""
19
+
20
+ from datetime import datetime
21
+ import pytz
22
+ import flatdict as fd
23
+ import numpy as np
24
+
25
+ from pynxtools_apm.utils.string_conversions import string_to_number, rchop
26
+ from pynxtools_apm.utils.interpret_boolean import try_interpret_as_boolean
27
+ from pynxtools_apm.utils.get_file_checksum import get_sha256_of_file_content
28
+
29
+
30
+ def variadic_path_to_specific_path(path: str, instance_identifier: list):
31
+ """Transforms a variadic path to an actual path with instances."""
32
+ if (path is not None) and (path != ""):
33
+ narguments = path.count("*")
34
+ if narguments == 0: # path is not variadic
35
+ return path
36
+ if len(instance_identifier) >= narguments:
37
+ tmp = path.split("*")
38
+ if len(tmp) == narguments + 1:
39
+ nx_specific_path = ""
40
+ for idx in range(0, narguments):
41
+ nx_specific_path += f"{tmp[idx]}{instance_identifier[idx]}"
42
+ idx += 1
43
+ nx_specific_path += f"{tmp[-1]}"
44
+ return nx_specific_path
45
+ return None
46
+
47
+
48
+ def add_specific_metadata(
49
+ concept_mapping: dict, orgmeta: fd.FlatDict, identifier: list, template: dict
50
+ ) -> dict:
51
+ """Map specific concept src on specific NeXus concept trg.
52
+
53
+ concept_mapping: translation dict how trg and src are to be mapped
54
+ orgmeta: instance data of src concepts
55
+ identifier: list of identifier to resolve variadic paths
56
+ template: instance data resulting from a resolved src to trg concept mapping
57
+ """
58
+ if "prefix_trg" in concept_mapping:
59
+ variadic_prefix_trg = concept_mapping["prefix_trg"]
60
+ elif "prefix" in concept_mapping:
61
+ variadic_prefix_trg = concept_mapping["prefix"]
62
+ else:
63
+ raise KeyError(f"Neither prefix nor prefix_trg found in concept_mapping!")
64
+
65
+ if "prefix_src" in concept_mapping:
66
+ prefix_src = concept_mapping["prefix_src"]
67
+ else:
68
+ prefix_src = ""
69
+
70
+ # process all mapping functors
71
+ # (in graphical programming these are also referred to as filters or nodes), i.e.
72
+ # an agent that gets some input does some (maybe abstract mapping) and returns an output
73
+ # as the mapping can be abstract we call it functor
74
+ if "use" in concept_mapping:
75
+ for entry in concept_mapping["use"]:
76
+ if isinstance(entry, tuple):
77
+ if len(entry) == 2:
78
+ if isinstance(entry[1], str) and entry[1] == "":
79
+ continue
80
+ trg = variadic_path_to_specific_path(
81
+ f"{variadic_prefix_trg}/{entry[0]}", identifier
82
+ )
83
+ template[f"{trg}"] = entry[1]
84
+ if "map" in concept_mapping:
85
+ for entry in concept_mapping["map"]:
86
+ if isinstance(entry, str):
87
+ if f"{prefix_src}{entry}" not in orgmeta:
88
+ continue
89
+ if (
90
+ isinstance(orgmeta[f"{prefix_src}{entry}"], str)
91
+ and orgmeta[f"{prefix_src}{entry}"] == ""
92
+ ):
93
+ continue
94
+ trg = variadic_path_to_specific_path(
95
+ f"{variadic_prefix_trg}/{entry}", identifier
96
+ )
97
+ template[f"{trg}"] = orgmeta[f"{prefix_src}{entry}"]
98
+ if isinstance(entry, tuple):
99
+ if len(entry) == 2:
100
+ if isinstance(entry[0], str):
101
+ if f"{prefix_src}{entry[1]}" not in orgmeta:
102
+ continue
103
+ if (orgmeta[f"{prefix_src}{entry[1]}"], str) and orgmeta[
104
+ f"{prefix_src}{entry[1]}"
105
+ ] == "":
106
+ continue
107
+ trg = variadic_path_to_specific_path(
108
+ f"{variadic_prefix_trg}/{entry[0]}", identifier
109
+ )
110
+ template[f"{trg}"] = orgmeta[f"{prefix_src}{entry[1]}"]
111
+ if "map_to_str" in concept_mapping:
112
+ for entry in concept_mapping["map_to_str"]:
113
+ if isinstance(entry, str):
114
+ if f"{prefix_src}{entry}" not in orgmeta:
115
+ continue
116
+ if (
117
+ isinstance(orgmeta[f"{prefix_src}{entry}"], str)
118
+ and orgmeta[f"{prefix_src}{entry}"] == ""
119
+ ):
120
+ continue
121
+ trg = variadic_path_to_specific_path(
122
+ f"{variadic_prefix_trg}/{entry}", identifier
123
+ )
124
+ template[f"{trg}"] = orgmeta[f"{prefix_src}{entry}"]
125
+ if isinstance(entry, tuple):
126
+ if len(entry) == 2:
127
+ if all(isinstance(elem, str) for elem in entry):
128
+ if f"{prefix_src}{entry[1]}" not in orgmeta:
129
+ continue
130
+ if (
131
+ isinstance(orgmeta[f"{prefix_src}{entry[1]}"], str)
132
+ and orgmeta[f"{prefix_src}{entry[1]}"] == ""
133
+ ):
134
+ continue
135
+ trg = variadic_path_to_specific_path(
136
+ f"{variadic_prefix_trg}/{entry[0]}", identifier
137
+ )
138
+ template[f"{trg}"] = orgmeta[f"{prefix_src}{entry[1]}"]
139
+ if "map_to_bool" in concept_mapping:
140
+ for entry in concept_mapping["map_to_bool"]:
141
+ if isinstance(entry, str):
142
+ if f"{prefix_src}{entry[0]}" not in orgmeta:
143
+ continue
144
+ trg = variadic_path_to_specific_path(
145
+ f"{variadic_prefix_trg}/{entry[0]}", identifier
146
+ )
147
+ template[f"{trg}"] = try_interpret_as_boolean(
148
+ orgmeta[f"{prefix_src}{entry[0]}"]
149
+ )
150
+ if isinstance(entry, tuple):
151
+ if len(entry) == 2:
152
+ if all(isinstance(elem, str) for elem in entry):
153
+ if f"{prefix_src}{entry[1]}" not in orgmeta:
154
+ continue
155
+ trg = variadic_path_to_specific_path(
156
+ f"{variadic_prefix_trg}/{entry[0]}", identifier
157
+ )
158
+ template[f"{trg}"] = try_interpret_as_boolean(
159
+ orgmeta[f"{prefix_src}{entry[1]}"]
160
+ )
161
+ if "map_to_real" in concept_mapping:
162
+ for entry in concept_mapping["map_to_real"]:
163
+ if isinstance(entry, str):
164
+ if isinstance(entry[0], str):
165
+ if f"{prefix_src}{entry[0]}" not in orgmeta:
166
+ continue
167
+ if (
168
+ isinstance(orgmeta[f"{prefix_src}{entry[0]}"], str)
169
+ and orgmeta[f"{prefix_src}{entry[0]}"] == ""
170
+ ):
171
+ continue
172
+ trg = variadic_path_to_specific_path(
173
+ f"{variadic_prefix_trg}/{entry[0]}", identifier
174
+ )
175
+ template[f"{trg}"] = string_to_number(
176
+ orgmeta[f"{prefix_src}{entry[0]}"]
177
+ )
178
+ if isinstance(entry, tuple):
179
+ if len(entry) == 2:
180
+ if all(isinstance(elem, str) for elem in entry):
181
+ if f"{prefix_src}{entry[1]}" not in orgmeta:
182
+ continue
183
+ if (
184
+ isinstance(orgmeta[f"{prefix_src}{entry[0]}"], str)
185
+ and orgmeta[f"{prefix_src}{entry[1]}"] == ""
186
+ ):
187
+ continue
188
+ trg = variadic_path_to_specific_path(
189
+ f"{variadic_prefix_trg}/{entry[0]}", identifier
190
+ )
191
+ template[f"{trg}"] = string_to_number(
192
+ orgmeta[f"{prefix_src}{entry[1]}"]
193
+ )
194
+ elif isinstance(entry[0], str) and isinstance(entry[1], list):
195
+ if not all(
196
+ (
197
+ isinstance(value, str)
198
+ and f"{prefix_src}{value}" in orgmeta
199
+ )
200
+ for value in entry[1]
201
+ ):
202
+ continue
203
+ if not all(
204
+ (
205
+ isinstance(orgmeta[f"{prefix_src}{value}"], str)
206
+ and orgmeta[f"{prefix_src}{value}"] != ""
207
+ )
208
+ for value in entry[1]
209
+ ):
210
+ continue
211
+ trg = variadic_path_to_specific_path(
212
+ f"{variadic_prefix_trg}/{entry[0]}", identifier
213
+ )
214
+ res = []
215
+ for value in entry[1]:
216
+ res.append(
217
+ string_to_number(orgmeta[f"{prefix_src}{value}"])
218
+ )
219
+ template[f"{trg}"] = np.asarray(res, np.float64)
220
+ if "map_to_real_and_multiply" in concept_mapping:
221
+ for entry in concept_mapping["map_to_real_and_multiply"]:
222
+ if isinstance(entry, tuple):
223
+ if len(entry) == 3:
224
+ if (
225
+ isinstance(entry[0], str)
226
+ and isinstance(entry[1], str)
227
+ and isinstance(entry[2], float)
228
+ ):
229
+ if f"{prefix_src}{entry[1]}" not in orgmeta:
230
+ continue
231
+ if (
232
+ isinstance(orgmeta[f"{prefix_src}{entry[1]}"], str)
233
+ and orgmeta[f"{prefix_src}{entry[1]}"] == ""
234
+ ):
235
+ continue
236
+ trg = variadic_path_to_specific_path(
237
+ f"{variadic_prefix_trg}/{entry[0]}", identifier
238
+ )
239
+ template[f"{trg}"] = entry[2] * string_to_number(
240
+ orgmeta[f"{prefix_src}{entry[1]}"]
241
+ )
242
+ if "map_to_real_and_join" in concept_mapping:
243
+ for entry in concept_mapping["map_to_real_and_join"]:
244
+ if isinstance(entry, tuple):
245
+ if len(entry) == 2:
246
+ if isinstance(entry[0], str) and isinstance(entry[1], list):
247
+ if not all(
248
+ (
249
+ isinstance(value, str)
250
+ and f"{prefix_src}{value}" in orgmeta
251
+ )
252
+ for value in entry[1]
253
+ ):
254
+ continue
255
+ if not all(
256
+ (
257
+ isinstance(orgmeta[f"{prefix_src}{value}"], str)
258
+ and orgmeta[f"{prefix_src}{value}"] != ""
259
+ )
260
+ for value in entry[1]
261
+ ):
262
+ continue
263
+ res = []
264
+ for value in entry[1]:
265
+ res.append(
266
+ string_to_number(orgmeta[f"{prefix_src}{value}"])
267
+ )
268
+ trg = variadic_path_to_specific_path(
269
+ f"{variadic_prefix_trg}/{entry[0]}", identifier
270
+ )
271
+ template[f"{trg}"] = np.asarray(res)
272
+ # we may need to be more specific with the return datatype here, currently default python float
273
+ if "unix_to_iso8601" in concept_mapping:
274
+ for entry in concept_mapping["unix_to_iso8601"]:
275
+ if isinstance(entry, tuple):
276
+ if (
277
+ 2 <= len(entry) <= 3
278
+ ): # trg, src, timestamp or empty string (meaning utc)
279
+ if all(isinstance(elem, str) for elem in entry):
280
+ if f"{prefix_src}{entry[1]}" not in orgmeta:
281
+ continue
282
+ if (
283
+ isinstance(orgmeta[f"{prefix_src}{entry[1]}"], str)
284
+ and orgmeta[f"{prefix_src}{entry[1]}"] == ""
285
+ ):
286
+ continue
287
+ tzone = "UTC"
288
+ if len(entry) == 3:
289
+ # if not isinstance(entry[2], str):
290
+ # raise TypeError(f"{tzone} needs to be of type string!")
291
+ tzone = entry[2]
292
+ if tzone not in pytz.all_timezones:
293
+ raise ValueError(
294
+ f"{tzone} is not a timezone in pytz.all_timezones!"
295
+ )
296
+ trg = variadic_path_to_specific_path(
297
+ f"{variadic_prefix_trg}/{entry[0]}", identifier
298
+ )
299
+ template[f"{trg}"] = datetime.fromtimestamp(
300
+ int(orgmeta[f"{prefix_src}{entry[1]}"]),
301
+ tz=pytz.timezone(tzone),
302
+ ).isoformat()
303
+ if "join_str" in concept_mapping: # currently also joining empty strings
304
+ for entry in concept_mapping["join_str"]:
305
+ if isinstance(entry, tuple):
306
+ if len(entry) == 2:
307
+ if isinstance(entry[0], str) and isinstance(entry[1], list):
308
+ if not all(
309
+ (
310
+ isinstance(value, str)
311
+ and f"{prefix_src}{value}" in orgmeta
312
+ )
313
+ for value in entry[1]
314
+ ):
315
+ continue
316
+ trg = variadic_path_to_specific_path(
317
+ f"{variadic_prefix_trg}/{entry[0]}", identifier
318
+ )
319
+ res = []
320
+ for value in entry[1]:
321
+ res.append(orgmeta[f"{prefix_src}{value}"])
322
+ template[f"{trg}"] = " ".join(res)
323
+ if "sha256" in concept_mapping:
324
+ for entry in concept_mapping["sha256"]:
325
+ if isinstance(entry, tuple):
326
+ if len(entry) == 2:
327
+ if not all(isinstance(elem, str) for elem in entry):
328
+ continue
329
+ if f"{prefix_src}{entry[1]}" not in orgmeta:
330
+ continue
331
+ if orgmeta[f"{prefix_src}{entry[1]}"] == "":
332
+ continue
333
+ trg = variadic_path_to_specific_path(
334
+ f"{variadic_prefix_trg}/{entry[0]}", identifier
335
+ )
336
+ with open(orgmeta[f"{prefix_src}{entry[1]}"], "rb") as fp:
337
+ template[f"{rchop(trg, 'checksum')}checksum"] = (
338
+ get_sha256_of_file_content(fp)
339
+ )
340
+ template[f"{rchop(trg, 'checksum')}type"] = "file"
341
+ template[f"{rchop(trg, 'checksum')}path"] = orgmeta[
342
+ f"{prefix_src}{entry[1]}"
343
+ ]
344
+ template[f"{rchop(trg, 'checksum')}algorithm"] = "sha256"
345
+ return template
@@ -0,0 +1,189 @@
1
+ #
2
+ # Copyright The NOMAD Authors.
3
+ #
4
+ # This file is part of NOMAD. See https://nomad-lab.eu for further info.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ """Dict mapping custom schema instances from eln_data.yaml file on concepts in NXapm."""
19
+
20
+ APM_ENTRY_TO_NEXUS = {
21
+ "prefix_trg": "/ENTRY[entry*]",
22
+ "prefix_src": "entry/",
23
+ "map_to_str": [
24
+ "run_number",
25
+ "operation_mode",
26
+ "start_time",
27
+ "end_time",
28
+ "experiment_description",
29
+ ("experiment_alias", "run_number"),
30
+ ],
31
+ }
32
+
33
+
34
+ APM_SAMPLE_TO_NEXUS = {
35
+ "prefix_trg": "/ENTRY[entry*]/sample",
36
+ "prefix_src": "sample/",
37
+ "map": [
38
+ ("grain_diameter", "grain_diameter/value"),
39
+ ("grain_diameter_error", "grain_diameter_error/value"),
40
+ ("heat_treatment_temperature", "heat_treatment_temperature/value"),
41
+ ("heat_treatment_temperature_error", "heat_treatment_temperature_error/value"),
42
+ ("heat_treatment_quenching_rate", "heat_treatment_quenching_rate/value"),
43
+ (
44
+ "heat_treatment_quenching_rate_error",
45
+ "heat_treatment_quenching_rate_error/value",
46
+ ),
47
+ ],
48
+ "map_to_str": [
49
+ "alias",
50
+ "description",
51
+ "method",
52
+ ("grain_diameter/@units", "grain_diameter/unit"),
53
+ ("grain_diameter_error/@units", "grain_diameter/unit"),
54
+ ("heat_treatment_temperature/@units", "heat_treatment_temperature/unit"),
55
+ (
56
+ "heat_treatment_temperature_error/@units",
57
+ "heat_treatment_temperature_error/unit",
58
+ ),
59
+ ("heat_treatment_quenching_rate/@units", "heat_treatment_quenching_rate/unit"),
60
+ (
61
+ "heat_treatment_quenching_rate_error/@units",
62
+ "heat_treatment_quenching_rate_error/unit",
63
+ ),
64
+ ],
65
+ }
66
+
67
+
68
+ APM_SPECIMEN_TO_NEXUS = {
69
+ "prefix_trg": "/ENTRY[entry*]/specimen",
70
+ "prefix_src": "specimen/",
71
+ "map": [
72
+ ("initial_radius", "initial_radius/value"),
73
+ ("shank_angle", "shank_angle/value"),
74
+ ],
75
+ "map_to_bool": ["is_polycrystalline", "is_amorphous"],
76
+ "map_to_str": [
77
+ "alias",
78
+ "preparation_date",
79
+ "description",
80
+ "method",
81
+ ("initial_radius/@units", "initial_radius/unit"),
82
+ ("shank_angle/@units", "shank_angle/unit"),
83
+ ],
84
+ }
85
+
86
+
87
+ APM_INSTRUMENT_STATIC_TO_NEXUS = {
88
+ "prefix_trg": "/ENTRY[entry*]/measurement/instrument",
89
+ "prefix_src": "atom_probe/",
90
+ "map": [("analysis_chamber/flight_path", "nominal_flight_path/value")],
91
+ "map_to_str": [
92
+ "status",
93
+ "instrument_name",
94
+ "location",
95
+ ("FABRICATION[fabrication]/vendor", "fabrication_vendor"),
96
+ ("FABRICATION[fabrication]/model", "fabrication_model"),
97
+ ("FABRICATION[fabrication]/identifier", "fabrication_identifier"),
98
+ ("reflectron/status", "reflectron_status"),
99
+ ("local_electrode/name", "local_electrode_name"),
100
+ ("pulser/pulse_mode", "pulser/pulse_mode"),
101
+ ("analysis_chamber/flight_path/@units", "nominal_flight_path/unit"),
102
+ ],
103
+ }
104
+
105
+
106
+ APM_INSTRUMENT_DYNAMIC_TO_NEXUS = {
107
+ "prefix_trg": "/ENTRY[entry*]/measurement/event_data_apm_set/EVENT_DATA_APM[event_data_apm]/instrument",
108
+ "prefix_src": "atom_probe/",
109
+ "use": [("control/target_detection_rate/@units", "ions/pulse")],
110
+ "map": [
111
+ ("control/target_detection_rate", "target_detection_rate"),
112
+ ("pulser/pulse_frequency", "pulser/pulse_frequency/value"),
113
+ ("pulser/pulse_fraction", "pulser/pulse_fraction"),
114
+ ("analysis_chamber/chamber_pressure", "chamber_pressure/value"),
115
+ ("stage_lab/base_temperature", "base_temperature/value"),
116
+ ],
117
+ "map_to_str": [
118
+ ("control/evaporation_control", "evaporation_control"),
119
+ ("pulser/pulse_frequency/@units", "pulser/pulse_frequency/unit"),
120
+ ("analysis_chamber/chamber_pressure/@units", "chamber_pressure/unit"),
121
+ ("stage_lab/base_temperature/@units", "base_temperature/unit"),
122
+ ],
123
+ }
124
+
125
+
126
+ APM_RANGE_TO_NEXUS = {
127
+ "prefix_trg": "/ENTRY[entry*]/atom_probe/ranging",
128
+ "prefix_src": "ranging/",
129
+ "map_to_str": [
130
+ ("programID[program1]/program", "program"),
131
+ ("programID[program1]/program/@version", "program_version"),
132
+ ],
133
+ }
134
+
135
+
136
+ APM_RECON_TO_NEXUS = {
137
+ "prefix_trg": "/ENTRY[entry*]/atom_probe/reconstruction",
138
+ "prefix_src": "reconstruction/",
139
+ "map": [("field_of_view", "field_of_view/value")],
140
+ "map_to_str": [
141
+ "protocol_name",
142
+ "crystallographic_calibration",
143
+ "parameter",
144
+ ("programID[program1]/program", "program"),
145
+ ("programID[program1]/program/@version", "program_version"),
146
+ ("field_of_view/@units", "field_of_view/unit"),
147
+ ],
148
+ }
149
+
150
+
151
+ APM_WORKFLOW_TO_NEXUS = {
152
+ "prefix_trg": "/ENTRY[entry*]/atom_probe",
153
+ "prefix_src": "workflow/",
154
+ "sha256": [
155
+ ("raw_data/SERIALIZED[serialized]/checksum", "raw_dat_file"),
156
+ ("hit_finding/SERIALIZED[serialized]/checksum", "hit_dat_file"),
157
+ ("reconstruction/config/checksum", "recon_cfg_file"),
158
+ ],
159
+ }
160
+
161
+ # NeXus concept specific mapping tables which require special treatment as the current
162
+ # NOMAD Oasis custom schema implementation delivers them as a list of dictionaries instead
163
+ # of a directly flattenable list of key, value pairs
164
+
165
+ APM_USER_TO_NEXUS = {
166
+ "prefix_trg": "/ENTRY[entry*]/USER[user*]",
167
+ "map_to_str": [
168
+ "name",
169
+ "affiliation",
170
+ "address",
171
+ "email",
172
+ "telephone_number",
173
+ "role",
174
+ "social_media_name",
175
+ "social_media_platform",
176
+ ],
177
+ }
178
+
179
+
180
+ APM_IDENTIFIER_TO_NEXUS = {
181
+ "prefix_trg": "/ENTRY[entry*]/USER[user*]",
182
+ "use": [
183
+ ("IDENTIFIER[identifier]/is_persistent", False),
184
+ ("IDENTIFIER[identifier]/service", "orcid"),
185
+ ],
186
+ "map_to_str": [
187
+ ("IDENTIFIER[identifier]/identifier", "orcid"),
188
+ ],
189
+ }
@@ -0,0 +1,94 @@
1
+ #
2
+ # Copyright The NOMAD Authors.
3
+ #
4
+ # This file is part of NOMAD. See https://nomad-lab.eu for further info.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ """Dict mapping values for a specifically configured NOMAD Oasis."""
19
+
20
+ # currently by virtue of design NOMAD Oasis specific examples show how different tools and
21
+ # services can be specifically coupled and implemented so that they work together
22
+ # currently we assume that the ELN provides all those pieces of information to instantiate
23
+ # a NeXus data artifact which technology-partner-specific files or database blobs can not
24
+ # deliver. Effectively a reader uses the eln_data.yaml generic ELN output to fill in these
25
+ # missing pieces of information while typically heavy data (tensors etc) are translated
26
+ # and written from the technology-partner files
27
+ # for large application definitions this can lead to a practical inconvenience:
28
+ # the ELN that has to be exposed to the user is complex and has many fields to fill in
29
+ # just to assure that all information are included in the ELN output and thus consumable
30
+ # by the dataconverter
31
+ # taking the perspective of a specific lab where a specific version of an ELN provided by
32
+ # or running in addition to NOMAD Oasis is used many pieces of information might not change
33
+ # or administrators do not wish to expose this via the end user ELN in an effort to reduce
34
+ # the complexity for end users and make entering of repetitiv information obsolete
35
+
36
+ # this is the scenario for which deployment_specific mapping shines
37
+ # parsing of deployment specific details in the apm reader is currently implemented
38
+ # such that it executes after reading generic ELN data (eventually available entries)
39
+ # in the template get overwritten
40
+
41
+ import datetime as dt
42
+
43
+ from pynxtools_apm.utils.versioning import NX_APM_ADEF_NAME
44
+
45
+
46
+ APM_OASISCONFIG_TO_NEXUS = {
47
+ "prefix_trg": "/ENTRY[entry*]",
48
+ "use": [
49
+ ("definition", f"{NX_APM_ADEF_NAME}"),
50
+ (
51
+ "start_time",
52
+ f"{dt.datetime.now(dt.timezone.utc).isoformat().replace('+00:00', 'Z')}",
53
+ ),
54
+ ],
55
+ "map_to_str": [("operation_mode")],
56
+ }
57
+
58
+
59
+ APM_CSYS_MCSTASLIKE_TO_NEXUS = {
60
+ "prefix_trg": "/ENTRY[entry*]/coordinate_system_set/COORDINATE_SYSTEM[coordinate_system]",
61
+ "use": [
62
+ (
63
+ "alias",
64
+ "Following the idea of McStas that the z-axis points along the direction of an ion leaving the apex along the longest direction of the specimen",
65
+ ),
66
+ ("type", "cartesian"),
67
+ ("handedness", "right_handed"),
68
+ (
69
+ "x_direction",
70
+ "Direction 1 that is perpendicular to the z_direction for a right_handed cartesian",
71
+ ),
72
+ ("x_alias", "x-axis"),
73
+ (
74
+ "y_direction",
75
+ "Direction 2 that is perpendicular to the x_direction and the z_direction for a right_handed cartesian",
76
+ ),
77
+ ("y_alias", "y-axis"),
78
+ (
79
+ "z_direction",
80
+ "Direction of an ion travelling hypothetically exactly along the assumed axis that is parallel to the longest direction of the specimen",
81
+ ),
82
+ ("z_alias", "z-axis"),
83
+ (
84
+ "origin",
85
+ "E.g. a characteristic point e.g. initial apex or center of the base of the specimen or something else",
86
+ ),
87
+ ],
88
+ }
89
+
90
+
91
+ APM_EXAMPLE_TO_NEXUS = {
92
+ "prefix_trg": "/ENTRY[entry*]/CITE[cite*]",
93
+ "map_to_str": [("doi"), ("description")],
94
+ }