Skip to content

Commit

Permalink
docs + small changes
Browse files Browse the repository at this point in the history
  • Loading branch information
nulinspiratie committed Feb 22, 2024
1 parent d4e7be9 commit 695ca21
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 4 deletions.
53 changes: 53 additions & 0 deletions qualang_tools/results/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,56 @@ for i in range(len(freqs_external)): # Loop over the LO frequencies
# Process and plot the results
...
```


## Data handler
The `DataHandler` is used to easily save data once a measurement has been performed.
It saves data into an automatically generated folder with folder structure:
`{root_data_folder}/%Y-%m-%d/#{idx}_{name}_%H%M%S`.
- `root_data_folder` is the root folder for all data, defined once at the start
- `%Y-%m-%d`: All datasets are first ordered by date
- `{idx}`: Datasets are identified by an incrementer (starting at `#1`).
Whenever a save is performed, the index of the last saved dataset is determined and
increased by 1.
- `name`: Each data folder has a name
- `%H%M%S`: The time is also specified.
This structure can be changed in `DataHandler.folder_structure`.

Data is generally saved using the command `data_handler.save_data("msmt_name", data)`,
where `data` is a dictionary.
The data is saved to the json file `data.json` in the data folder, but nonserialisable
types are saved into separate files. The following nonserialisable types are currently
supported:
- Matplotlib figures
- Numpy arrays
- Xarrays

### Usage example
```python
# Assume a measurement has been performed, and all results are collected here
data = {
"T1": 5e-6,
"T1_figure": plt.figure(),
"IQ_array": np.array([[1, 2, 3], [4, 5, 6]])
}

# Initialize the DataHandler
data_handler = DataHandler(root_data_folder="C:/data")

# Save results
data_folder = data_handler.save_data("T1_measurement", data=data)
print(data_folder)
# C:/data/2024-02-24/#152_T1_measurement_095214
# This assumes the save was performed at 2024-02-24 at 09:52:14
```
After calling `data_handler.save_data()`, three files are created in `data_folder`:
- `T1_figure.png`
- `arrays.npz` containing all the numpy arrays
- `data.json` which contains:
```
{
"T1": 5e-06,
"T1_figure": "./T1_figure.png",
"IQ_array": "./arrays.npz#IQ_array"
}
```
4 changes: 3 additions & 1 deletion qualang_tools/results/data_handler/data_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def save_data(
data_filename: str = "data.json",
metadata_filename: str = "metadata.json",
data_processors: Sequence[DataProcessor] = (),
) -> None:
) -> Path:
"""Save data to a folder
:param data_folder: The folder where the data will be saved
Expand Down Expand Up @@ -53,6 +53,8 @@ def save_data(
for data_processor in data_processors:
data_processor.post_process(data_folder=data_folder)

return data_folder


class DataHandler:
default_data_processors = DEFAULT_DATA_PROCESSORS
Expand Down
9 changes: 6 additions & 3 deletions qualang_tools/results/data_handler/data_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ def post_process(self, data_folder: Path):


class NumpyArraySaver(DataProcessor):
min_size: int = 100
merge_arrays: bool = True
merged_array_name: str = "arrays.npz"

Expand All @@ -102,8 +101,6 @@ def process(self, data):
for keys, val in iterate_nested_dict(data):
if not isinstance(val, np.ndarray):
continue
elif self.min_size is not False and val.size < self.min_size:
continue

path = Path("/".join(keys))
self.data_arrays[path] = val
Expand All @@ -122,6 +119,9 @@ def post_process(self, data_folder: Path):
np.save(data_folder / path.with_suffix(".npy"), arr)


DEFAULT_DATA_PROCESSORS.append(NumpyArraySaver)


class XarraySaver(DataProcessor):
merge_arrays: bool = False
merged_array_name: str = "xarrays"
Expand Down Expand Up @@ -185,3 +185,6 @@ def post_process(self, data_folder: Path):
else:
for path, array in self.data_arrays.items():
array.to_netcdf(data_folder / path.with_suffix(self.file_suffix))


DEFAULT_DATA_PROCESSORS.append(XarraySaver)
15 changes: 15 additions & 0 deletions tests/data_handler/test_data_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,18 @@ def test_data_handler_matplotlib_processor(tmp_path):
assert file_data == {"a": 1, "b": 2, "c": 3, "my_fig": "./my_fig.png"}

assert (tmp_path / expected_data_folder / "my_fig.png").exists()


def test_custom(tmp_path):
from matplotlib import pyplot as plt
import numpy as np

data = {"T1": 5e-6, "T1_figure": plt.figure(), "IQ_array": np.array([[1, 2, 3], [4, 5, 6]])}

# Initialize the DataHandler
data_handler = DataHandler(root_data_folder=tmp_path)

# Save results
data_folder = data_handler.save_data("T1_measurement", data=data)

print(list(data_folder.iterdir()))

0 comments on commit 695ca21

Please sign in to comment.