From 323463efb698353417cdb60412c30013eae2b6e5 Mon Sep 17 00:00:00 2001 From: Laurenz Rettig <53396064+rettigl@users.noreply.github.com> Date: Mon, 12 Aug 2024 12:49:51 +0200 Subject: [PATCH] Remove mappings (#25) * remove mappings, and adopt eln retrieval to use path rather than key * enable tests temporarily * Add units to h5 test data * re-disable tests * Add tests for new eln data formalism * Cleanup eln testing file --------- Co-authored-by: domna --- pynxtools_mpes/mappings.py | 64 ----------- pynxtools_mpes/reader.py | 22 +--- scripts/regenerate_examples.sh | 8 +- tests/data/config_file.json | 6 +- tests/data/eln_data.yaml | 111 +------------------ tests/data/example.nxs | Bin 4375944 -> 4375944 bytes tests/data/example_eln.nxs | Bin 0 -> 4375816 bytes tests/data/xarray_saved_small_calibration.h5 | Bin 6533728 -> 6539872 bytes tests/test_reader.py | 27 +++-- 9 files changed, 36 insertions(+), 202 deletions(-) delete mode 100644 pynxtools_mpes/mappings.py create mode 100644 tests/data/example_eln.nxs diff --git a/pynxtools_mpes/mappings.py b/pynxtools_mpes/mappings.py deleted file mode 100644 index d769118..0000000 --- a/pynxtools_mpes/mappings.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Mapping dictionaries for the MPES conversion. -""" - -DEFAULT_UNITS = { - "X": "step", - "Y": "step", - "t": "step", - "tofVoltage": "V", - "extractorVoltage": "V", - "extractorCurrent": "A", - "cryoTemperature": "K", - "sampleTemperature": "K", - "dldTimeBinSize": "ns", - "delay": "ps", - "timeStamp": "s", - "energy": "eV", - "kx": "1/A", - "ky": "1/A", -} - -CONVERT_DICT = { - "Instrument": "INSTRUMENT[instrument]", - "Analyzer": "ELECTRONANALYSER[electronanalyser]", - "Manipulator": "MANIPULATOR[manipulator]", - "Beam": "beamTYPE[beam]", - "unit": "@units", - "Sample": "SAMPLE[sample]", - "Source": "sourceTYPE[source]", - "User": "USER[user]", - "energy_resolution": "energy_resolution/resolution", - "momentum_resolution": "RESOLUTION[momentum_resolution]/resolution", - "temporal_resolution": "RESOLUTION[temporal_resolution]/resolution", - "spatial_resolution": "RESOLUTION[spatial_resolution]/resolution", - "angular_resolution": "RESOLUTION[angular_resolution]/resolution", - "sample_temperature": "temperature_sensor/value", - "drain_current": "drain_current_amperemeter/value", - "photon_energy": "energy", -} - -REPLACE_NESTED = { - "SAMPLE[sample]/chemical_formula": "SAMPLE[sample]/SUBSTANCE[substance]/molecular_formula_hill", - "sourceTYPE[source]/Probe": "sourceTYPE[source_probe]", - "sourceTYPE[source]/Pump": "sourceTYPE[source_pump]", - "beamTYPE[beam]/Probe": "beamTYPE[beam_probe]", - "beamTYPE[beam]/Pump": "beamTYPE[beam_pump]", - "sample_history": "history/notes/description", - "ELECTRONANALYSER[electronanalyser]/RESOLUTION[energy_resolution]": ( - "ELECTRONANALYSER[electronanalyser]/energy_resolution" - ), - "ELECTRONANALYSER[electronanalyser]/RESOLUTION[momentum_resolution]": ( - "ELECTRONANALYSER[electronanalyser]/momentum_resolution" - ), - "ELECTRONANALYSER[electronanalyser]/RESOLUTION[spatial_resolution]": ( - "ELECTRONANALYSER[electronanalyser]/spatial_resolution" - ), - "ELECTRONANALYSER[electronanalyser]/RESOLUTION[angular_resolution]": ( - "ELECTRONANALYSER[electronanalyser]/angular_resolution" - ), - "SAMPLE[sample]/gas_pressure": "INSTRUMENT[instrument]/pressure_gauge/value", - "SAMPLE[sample]/temperature": ( - "INSTRUMENT[instrument]/MANIPULATOR[manipulator]/temperature_sensor/value" - ), -} diff --git a/pynxtools_mpes/reader.py b/pynxtools_mpes/reader.py index f0cdc62..fb5ad1e 100644 --- a/pynxtools_mpes/reader.py +++ b/pynxtools_mpes/reader.py @@ -27,8 +27,6 @@ from pynxtools.dataconverter.readers.multi.reader import MultiFormatReader from pynxtools.dataconverter.readers.utils import parse_yml -from pynxtools_mpes.mappings import CONVERT_DICT, DEFAULT_UNITS, REPLACE_NESTED - logger = logging.getLogger("pynxtools") @@ -121,15 +119,15 @@ def h5_to_xarray(faddr: str, mode: str = "r") -> xr.DataArray: f"ax{axis}" ].attrs["unit"] except (KeyError, TypeError): - xarray[bin_names[axis]].attrs["unit"] = DEFAULT_UNITS[bin_names[axis]] + xarray[bin_names[axis]].attrs["unit"] = "" try: xarray.attrs["units"] = h5_file["binned"]["BinnedData"].attrs["units"] xarray.attrs["long_name"] = h5_file["binned"]["BinnedData"].attrs[ "long_name" ] except (KeyError, TypeError): - xarray.attrs["units"] = "counts" - xarray.attrs["long_name"] = "photoelectron counts" + xarray.attrs["units"] = "" + xarray.attrs["long_name"] = "" if metadata is not None: xarray.attrs["metadata"] = metadata @@ -146,8 +144,7 @@ def iterate_dictionary(dic, key_string): if not len(keys) == 1: return iterate_dictionary(dic[keys[0]], keys[1]) else: - raise KeyError - return None + return None def rgetattr(obj, attr): @@ -196,8 +193,6 @@ def set_config_file(self, file_path: str) -> Dict[str, Any]: def handle_eln_file(self, file_path: str) -> Dict[str, Any]: self.eln_data = parse_yml( file_path, - convert_dict=CONVERT_DICT, - replace_nested=REPLACE_NESTED, parent_key="/ENTRY", ) @@ -208,9 +203,7 @@ def get_eln_data(self, key: str, path: str) -> Any: if self.eln_data is None: return None - return self.eln_data.get( - key.replace(f"/ENTRY[{self.callbacks.entry_name}]", "/ENTRY") - ) + return self.eln_data.get(path) def handle_objects(self, objects: Tuple[Any]) -> Dict[str, Any]: if isinstance(objects, xr.DataArray): @@ -253,10 +246,7 @@ def get_data_dims(self, key: str, path: str) -> List[str]: return list(map(str, self.data_xarray.dims)) def get_attr(self, key: str, path: str) -> Any: - try: - return iterate_dictionary(self.data_xarray.attrs, path) - except KeyError: - logger.info(f"Path {path} not found. Skipping the entry.") + return iterate_dictionary(self.data_xarray.attrs, path) READER = MPESReader diff --git a/scripts/regenerate_examples.sh b/scripts/regenerate_examples.sh index f7295b9..83bfbc1 100755 --- a/scripts/regenerate_examples.sh +++ b/scripts/regenerate_examples.sh @@ -7,7 +7,13 @@ function update_mpes_example { dataconverter xarray_saved_small_calibration.h5 config_file.json --reader $READER --nxdl $NXDL --output example.nxs } +function update_mpes_eln_example { + echo "Update mpes example with eln file" + dataconverter xarray_saved_small_calibration.h5 config_file.json eln_data.yaml --reader $READER --nxdl $NXDL --output example_eln.nxs +} + project_dir=$(dirname $(dirname $(realpath $0))) cd $project_dir/tests/data -update_mpes_example \ No newline at end of file +update_mpes_example +update_mpes_eln_example \ No newline at end of file diff --git a/tests/data/config_file.json b/tests/data/config_file.json index 6a412c5..a8bfd3a 100644 --- a/tests/data/config_file.json +++ b/tests/data/config_file.json @@ -2,7 +2,7 @@ "/@default": "entry", "/ENTRY/@default": "data", "/ENTRY/definition": "NXmpes", - "/ENTRY/title": "@attrs:metadata/entry_title", + "/ENTRY/title": "['@eln:/ENTRY/title', '@attrs:metadata/entry_title']", "/ENTRY/start_time": "@attrs:metadata/timing/acquisition_start", "/ENTRY/experiment_institution": "Fritz Haber Institute - Max Planck Society", "/ENTRY/experiment_facility": "Time Resolved ARPES", @@ -24,8 +24,8 @@ "name": "Time-of-flight momentum microscope equipped delay line detector, at the endstation of the high rep-rate HHG source at FHI", "name/@short_name": "TR-ARPES @ FHI", "energy_resolution": { - "resolution": 140.0, - "resolution/@units": "meV", + "resolution": "['@eln:/ENTRY/Instrument/energy_resolution', '140.0']", + "resolution/@units": "['@eln:/ENTRY/Instrument/energy_resolution/@units', 'meV']", "physical_quantity": "energy", "type": "estimated" }, diff --git a/tests/data/eln_data.yaml b/tests/data/eln_data.yaml index d6a61b1..19d9af6 100644 --- a/tests/data/eln_data.yaml +++ b/tests/data/eln_data.yaml @@ -1,109 +1,8 @@ -title: Valence Band Dynamics - 1030 nm linear p-polarized pump, 0.6 mJ/cm2 absorbed fluence +title: Title from ELN file Instrument: energy_resolution: - unit: meV - value: 140.0 - momentum_resolution: - unit: 1/angstrom - value: 0.08 - temporal_resolution: - unit: fs - value: 35.0 - Analyzer: - energy_resolution: - unit: eV - value: 110.0 - momentum_resolution: - unit: 1/angstrom - value: 0.08 - slow_axes: delay - spatial_resolution: - unit: µm - value: 10.0 - Manipulator: - sample_temperature: - unit: K - value: 300.0 - Source: - Probe: - frequency: - unit: KHz - value: 500.0 - photon_energy: - unit: eV - value: 21.7 - Pump: - frequency: - unit: KHz - value: 500.0 - photon_energy: - unit: eV - value: 1.55 - Beam: - Probe: - extent: - unit: µm - value: - - 80.0 - - 80.0 - incident_energy: - unit: eV - value: 21.7 - incident_energy_spread: - unit: eV - value: 0.11 - incident_polarization: - - 1 - - 1 - - 0 - - 0 - pulse_duration: - unit: fs - value: 20.0 - Pump: - extent: - unit: µm - value: - - 230.0 - - 265.0 - incident_energy: - unit: eV - value: 1.55 - incident_energy_spread: - unit: eV - value: 0.08 - incident_polarization: - - 1 - - -1 - - 0 - - 0 - incident_wavelength: - unit: nm - value: 800.0 - average_power: - unit: mW - value: 300.0 - pulse_energy: - unit: µJ - value: 0.6 - fluence: - unit: mJ / cm ** 2 - value: 0.15 - pulse_duration: - unit: fs - value: 35.0 + unit: eV + value: 0.14 Sample: - chemical_formula: WSe2 - description: Sample - name: WSe2 Single Crystal - preparation_date: "2019-01-13T09:00:00+00:00" - pressure: - unit: bar - value: 5.0e-14 - sample_history: Cleaved -User: - address: Faradayweg 4-6, 14915 Berlin - affiliation: Fritz Haber Institute of the Max Planck Society - email: maklar@fhi-berlin.mpg.de - name: Julian Maklar - role: Principal Investigator + name: My ELN sample name + preparation_date: "2019-01-13T09:00:00+00:00" \ No newline at end of file diff --git a/tests/data/example.nxs b/tests/data/example.nxs index 8cca44b216d144ea914f1474648aefbb919d29b1..cbf0321f272197cefeb0e81180ac34ac8e4c6372 100644 GIT binary patch delta 203 zcmZw7NlwCG0D#em^Nbao07X<#5$s~q9w~#tXi{f!=|z=k!=-KJ9h2acc7-O;n0z$V=X66ooVYhci~dkm7cz9 zu^@3Hb!*_xP$qZp!J{Y7Uc7!6lc+ZAg`^phJPp(OS(;^G(2bKkj_V)qQBke@H~joY H#pl-_R5Cqw delta 203 zcmZw7NiqXb007VuQ_Qr)lpu&P692b!I@;VPqv|bEl|8qxP^#R;X*htz+r80vTp9g& zF`;78lxZ_&Rn3{VV9}CgD^{&ZtgETpuxZP-9Su#p_UvmpaOghTFYV{qAap`ngwT9{P0D)hzwrH& HlpmkJQ}aD+ diff --git a/tests/data/example_eln.nxs b/tests/data/example_eln.nxs new file mode 100644 index 0000000000000000000000000000000000000000..d7baa113b62653d8c934d7d79ea0d6aed15d7592 GIT binary patch literal 4375816 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