sdf-xarray 0.7.4__tar.gz → 0.8.0__tar.gz

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.
Files changed (68) hide show
  1. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/PKG-INFO +1 -1
  2. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/animation.md +2 -2
  3. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/animating/animate_1D_density.py +2 -2
  4. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/animating/animate_1D_poynting_flux.py +2 -2
  5. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/animating/animate_2D_dist_fn_multispecies.py +1 -1
  6. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/animating/animate_2D_dist_fn_multispecies_alternative.py +1 -1
  7. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/animating/animate_2D_poynting_flux.py +2 -2
  8. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/live_demo.ipynb +2 -2
  9. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/plotting/plot_1D_density.py +1 -1
  10. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/plotting/plot_1D_poynting_flux.py +1 -1
  11. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/plotting/plot_2D_density.py +1 -1
  12. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/plotting/plot_2D_dist_fn_multispecies.py +1 -1
  13. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/plotting/plot_2D_poynting_flux.py +1 -1
  14. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/plotting/plot_time_temperature.py +1 -1
  15. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/plotting/plot_x_px_scatter.py +1 -1
  16. sdf_xarray-0.8.0/docs/epoch_workshop_2026/workshop_details.md +7 -0
  17. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/unit_conversion.md +4 -4
  18. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/pyproject.toml +2 -0
  19. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/src/sdf_xarray/_version.py +3 -3
  20. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/src/sdf_xarray/dataset_accessor.py +30 -18
  21. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/tests/test_epoch_dataset_accessor.py +43 -1
  22. sdf_xarray-0.7.4/docs/epoch_workshop_2026/workshop_details.md +0 -7
  23. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/.github/workflows/build_publish.yml +0 -0
  24. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/.github/workflows/lint.yml +0 -0
  25. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/.github/workflows/tests.yml +0 -0
  26. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/.gitignore +0 -0
  27. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/.gitmodules +0 -0
  28. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/.readthedocs.yaml +0 -0
  29. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/CITATION.cff +0 -0
  30. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/CMakeLists.txt +0 -0
  31. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/CONTRIBUTING.md +0 -0
  32. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/LICENCE +0 -0
  33. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/README.md +0 -0
  34. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/.gitignore +0 -0
  35. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/_static/BEAM.png +0 -0
  36. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/_static/PlasmaFAIR.svg +0 -0
  37. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/_static/force_render_dark_xarray_objects.css +0 -0
  38. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/_static/sdf-xarray-logo-dark.svg +0 -0
  39. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/_static/sdf-xarray-logo-light.svg +0 -0
  40. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/_templates/custom-class-template.rst +0 -0
  41. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/_templates/custom-module-template.rst +0 -0
  42. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/api.md +0 -0
  43. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/conf.py +0 -0
  44. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/contributing.md +0 -0
  45. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/animations.md +0 -0
  46. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/histograms/plot_hist_ke.py +0 -0
  47. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/histograms/plot_hist_ke_probe.py +0 -0
  48. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/histograms/plot_hist_px.py +0 -0
  49. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/histograms.md +0 -0
  50. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/epoch_workshop_2026/plots.md +0 -0
  51. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/index.md +0 -0
  52. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/installation.md +0 -0
  53. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/known_issues.md +0 -0
  54. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/loading_data.md +0 -0
  55. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/make.bat +0 -0
  56. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/understanding_datasets.md +0 -0
  57. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/docs/why_sdf_xarray.md +0 -0
  58. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/src/sdf_xarray/__init__.py +0 -0
  59. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/src/sdf_xarray/csdf.pxd +0 -0
  60. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/src/sdf_xarray/dataarray_accessor.py +0 -0
  61. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/src/sdf_xarray/download.py +0 -0
  62. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/src/sdf_xarray/plotting.py +0 -0
  63. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/src/sdf_xarray/sdf_interface.pyx +0 -0
  64. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/tests/test_cython.py +0 -0
  65. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/tests/test_dataset.py +0 -0
  66. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/tests/test_datatree.py +0 -0
  67. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/tests/test_epoch_dataarray_accessor.py +0 -0
  68. {sdf_xarray-0.7.4 → sdf_xarray-0.8.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sdf-xarray
3
- Version: 0.7.4
3
+ Version: 0.8.0
4
4
  Summary: Provides a backend for xarray to read SDF files as created by the EPOCH plasma PIC code.
5
5
  Author-Email: Peter Hill <peter.hill@york.ac.uk>, Joel Adams <joel.adams@york.ac.uk>, Shaun Doherty <shaun.doherty@york.ac.uk>, Chris Herdman <chris.herdman@york.ac.uk>, Liam Pattinson <liam.pattinson@york.ac.uk>, Sviatoslav Shekhanov <sviatoslav.shekhanov@york.ac.uk>
6
6
  License-Expression: BSD-3-Clause
@@ -170,8 +170,8 @@ Some functionality such as `aspect` and `size` are not fully implemented yet.
170
170
  ds = sdfxr.open_mfdataset("tutorial_dataset_2d/*.sdf")
171
171
 
172
172
  # Change the units of the coordinates
173
- ds = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid", "Y_Grid_mid"])
174
- ds = ds.epoch.rescale_coords(1e15, "fs", ["time"])
173
+ ds = ds.epoch.rescale_coords("µm", ["X_Grid_mid", "Y_Grid_mid"])
174
+ ds = ds.epoch.rescale_coords("fs", ["time"])
175
175
  ds["time"].attrs["long_name"] = "t"
176
176
 
177
177
  # Change units and name of the variable
@@ -6,9 +6,9 @@ input_dir = Path("datasets/1_1_drifting_bunch")
6
6
  ds = sdfxr.open_mfdataset(input_dir)
7
7
 
8
8
  # Convert the time to femtoseconds
9
- ds = ds.epoch.rescale_coords(1e15, "fs", "time")
9
+ ds = ds.epoch.rescale_coords("fs", "time", 1e15)
10
10
  # Convert the x and y coords to microns
11
- ds = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid"])
11
+ ds = ds.epoch.rescale_coords("µm", ["X_Grid_mid"], 1e6)
12
12
 
13
13
  anim = ds["Derived_Number_Density"].epoch.animate()
14
14
 
@@ -8,9 +8,9 @@ input_dir = Path("datasets/3_3_Gaussian_1d_laser")
8
8
  ds = sdfxr.open_mfdataset(input_dir)
9
9
 
10
10
  # Convert the time to femtoseconds
11
- ds = ds.epoch.rescale_coords(1e15, "fs", "time")
11
+ ds = ds.epoch.rescale_coords("fs", "time", 1e15)
12
12
  # Convert the x and y coords to microns
13
- ds = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid"])
13
+ ds = ds.epoch.rescale_coords("µm", ["X_Grid_mid"], 1e6)
14
14
 
15
15
  # Calculate Poynting flux magnitude
16
16
  flux_magnitude = np.sqrt(
@@ -8,7 +8,7 @@ ds = sdfxr.open_mfdataset(
8
8
  )
9
9
 
10
10
  # Rescale coords to account for kilometers
11
- ds = ds.epoch.rescale_coords(1e-3, "km", ["X_x_px_Left"])
11
+ ds = ds.epoch.rescale_coords("km", ["X_x_px_Left"], 1e-3)
12
12
 
13
13
  # Sum phase-space of species "Left" and "Right" in "x_px" distribution function
14
14
  # NOTE: We only use the values from the right distribution function as if we inherit
@@ -11,7 +11,7 @@ ds = sdfxr.open_mfdataset(
11
11
  )
12
12
 
13
13
  # Rescale coords to account for kilometers
14
- ds = ds.epoch.rescale_coords(1e-3, "km", ["X_x_px_Left"])
14
+ ds = ds.epoch.rescale_coords("km", ["X_x_px_Left"], 1e-3)
15
15
 
16
16
  # Sum phase-space of species "Left" and "Right" in "x_px" distribution function
17
17
  # NOTE: We only use the values from the right distribution function as if we inherit
@@ -8,9 +8,9 @@ input_dir = Path("datasets/3_5_Gaussian_beam")
8
8
  ds = sdfxr.open_mfdataset(input_dir)
9
9
 
10
10
  # Convert the time to femtoseconds
11
- ds = ds.epoch.rescale_coords(1e15, "fs", "time")
11
+ ds = ds.epoch.rescale_coords("fs", "time", 1e15)
12
12
  # Convert the x and y coords to microns
13
- ds = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid", "Y_Grid_mid"])
13
+ ds = ds.epoch.rescale_coords("µm", ["X_Grid_mid", "Y_Grid_mid"], 1e6)
14
14
 
15
15
  # Calculate Poynting flux magnitude
16
16
  flux_magnitude = np.sqrt(
@@ -3800,7 +3800,7 @@
3800
3800
  },
3801
3801
  {
3802
3802
  "cell_type": "code",
3803
- "execution_count": 10,
3803
+ "execution_count": null,
3804
3804
  "id": "2ef27498",
3805
3805
  "metadata": {
3806
3806
  "colab": {
@@ -3833,7 +3833,7 @@
3833
3833
  }
3834
3834
  ],
3835
3835
  "source": [
3836
- "ds = ds.epoch.rescale_coords(1e6, \"µm\", [\"X_Grid_mid\", \"Y_Grid_mid\"])\n",
3836
+ "ds = ds.epoch.rescale_coords(\"µm\", [\"X_Grid_mid\", \"Y_Grid_mid\"], 1e6)\n",
3837
3837
  "ds[\"Derived_Number_Density_Electron\"].isel(time=0).epoch.plot()"
3838
3838
  ]
3839
3839
  },
@@ -8,7 +8,7 @@ import sdf_xarray as sdfxr
8
8
  input_dir = Path("datasets/1_1_drifting_bunch")
9
9
 
10
10
  ds = sdfxr.open_dataset(input_dir / "0000.sdf")
11
- ds = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid"])
11
+ ds = ds.epoch.rescale_coords("µm", ["X_Grid_mid"], 1e6)
12
12
 
13
13
  ds["Derived_Number_Density"].epoch.plot()
14
14
  plt.tight_layout()
@@ -10,7 +10,7 @@ input_dir = Path("datasets/3_3_Gaussian_1d_laser")
10
10
  ds = sdfxr.open_dataset(input_dir / "0020.sdf")
11
11
 
12
12
  # Convert the x and y coords to microns
13
- ds = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid"])
13
+ ds = ds.epoch.rescale_coords("µm", ["X_Grid_mid"], 1e6)
14
14
 
15
15
  # Calculate Poynting flux magnitude
16
16
  flux_magnitude = np.sqrt(
@@ -8,7 +8,7 @@ import sdf_xarray as sdfxr
8
8
  input_dir = Path("datasets/4_3_basic_target")
9
9
 
10
10
  ds = sdfxr.open_dataset(input_dir / "0000.sdf")
11
- ds = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid", "Y_Grid_mid"])
11
+ ds = ds.epoch.rescale_coords("µm", ["X_Grid_mid", "Y_Grid_mid"], 1e6)
12
12
 
13
13
  ds["Derived_Number_Density"].epoch.plot()
14
14
  plt.tight_layout()
@@ -8,7 +8,7 @@ import sdf_xarray as sdfxr
8
8
  input_dir = Path("datasets/2_1_two_stream_instability")
9
9
  ds = sdfxr.open_dataset(input_dir / "0000.sdf")
10
10
 
11
- ds = ds.epoch.rescale_coords(1e-3, "km", ["X_x_px_Left"])
11
+ ds = ds.epoch.rescale_coords("km", ["X_x_px_Left"], 1e-3)
12
12
 
13
13
  # Sum phase-space of species "Left" and "Right" in "x_px" distribution function
14
14
  # NOTE: We only use the values from the right distribution function as if we inherit
@@ -10,7 +10,7 @@ input_dir = Path("datasets/3_5_Gaussian_beam")
10
10
  ds = sdfxr.open_dataset(input_dir / "0001.sdf")
11
11
 
12
12
  # Convert the x and y coords to microns
13
- ds = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid", "Y_Grid_mid"])
13
+ ds = ds.epoch.rescale_coords("µm", ["X_Grid_mid", "Y_Grid_mid"], 1e6)
14
14
 
15
15
  # Calculate Poynting flux magnitude
16
16
  flux_magnitude = np.sqrt(
@@ -13,7 +13,7 @@ input_dir = Path("datasets/4_2_self_heating")
13
13
  ds = sdfxr.open_mfdataset(input_dir)
14
14
 
15
15
  # Convert the time to femtoseconds
16
- ds = ds.epoch.rescale_coords(1e15, "fs", "time")
16
+ ds = ds.epoch.rescale_coords("fs", "time", 1e15)
17
17
 
18
18
  # Averate temperature over all spatial cells at each time-step
19
19
  kB = 1.380649e-23
@@ -9,7 +9,7 @@ input_dir = Path("datasets/5_2_subsets")
9
9
  ds = sdfxr.open_dataset(input_dir / "0000.sdf", keep_particles=True)
10
10
 
11
11
  ds = ds.epoch.rescale_coords(
12
- 1e6, "µm", ["X_Particles_subset_Refluxers_Electron", "Y_Target_mid"]
12
+ "µm", ["X_Particles_subset_Refluxers_Electron", "Y_Target_mid"], 1e6
13
13
  )
14
14
 
15
15
  x = ds["X_Particles_subset_Refluxers_Electron"]
@@ -0,0 +1,7 @@
1
+ # Workshop Details
2
+
3
+ The workshop consisted of a series of talks and practical sessions delivered at the University of York over the course of 2 days (23-24 April). It covered setting up both `EPOCH` and `sdf-xarray` along with delving into the details of how PIC codes work. The timetable is located [here](https://epochpic.github.io/quickstart/epoch_workshop_2026.html) and recordings of the presentations can be found on [YouTube](https://youtube.com/playlist?list=PLXfw-OhAIheRtwRqrGZvMqFuQ2GrIBLVl).
4
+
5
+ ## Live Demo
6
+
7
+ As part of the start of the workshop a live demo covering the key features of `sdf-xarray` was performed. You can download this jupyter notebook in order to follow along <path:live_demo.ipynb>. Alternatively, if you have a Google account you can use the online [Google Colab notebook](https://colab.research.google.com/drive/1FTKj6gUizKjcL1ZSK4sNKlWKs5mzrD2g?usp=sharing).
@@ -30,7 +30,7 @@ plt.rcParams.update({
30
30
  For simple scaling and unit relabelling of coordinates (e.g., converting meters to microns),
31
31
  the most straightforward approach is to use the [`xarray.Dataset.epoch.rescale_coords`](project:#sdf_xarray.dataset_accessor.EpochAccessor.rescale_coords) dataset accessor.
32
32
  This function scales the coordinate values by a given multiplier and updates the
33
- `"units"` attribute in one step.
33
+ `"units"` attribute in one step. If the multiplier is not specified then the conversion parameter is inferred from the units and converted automatically using `pint` (see [](#unit-conversion-with-pint-xarray) for details).
34
34
 
35
35
  ### Rescaling grid coordinates
36
36
 
@@ -41,7 +41,7 @@ We can use the [`xarray.Dataset.epoch.rescale_coords`](project:#sdf_xarray.datas
41
41
  fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))
42
42
 
43
43
  ds = sdfxr.open_mfdataset("tutorial_dataset_2d/*.sdf")
44
- ds_in_microns = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid", "Y_Grid_mid"])
44
+ ds_in_microns = ds.epoch.rescale_coords("µm", ["X_Grid_mid", "Y_Grid_mid"], 1e6)
45
45
 
46
46
  ds["Derived_Number_Density_Electron"].isel(time=0).epoch.plot(ax=ax1)
47
47
  ax1.set_title("Original X Coordinate (m)")
@@ -55,7 +55,7 @@ fig.tight_layout()
55
55
  ### Rescaling time coordinate
56
56
 
57
57
  We can also use the [`xarray.Dataset.epoch.rescale_coords`](project:#sdf_xarray.dataset_accessor.EpochAccessor.rescale_coords) method to convert the time coordinate from
58
- seconds (`s`) to femto-seconds (`fs`) by applying a multiplier of `1e15`.
58
+ seconds (`s`) to femto-seconds (`fs`).
59
59
 
60
60
  ```{code-cell} ipython3
61
61
  ds = sdfxr.open_mfdataset("tutorial_dataset_2d/*.sdf")
@@ -63,7 +63,7 @@ ds["time"]
63
63
  ```
64
64
 
65
65
  ```{code-cell} ipython3
66
- ds = ds.epoch.rescale_coords(1e15, "fs", "time")
66
+ ds = ds.epoch.rescale_coords("fs", "time")
67
67
  ds["time"]
68
68
  ```
69
69
 
@@ -78,6 +78,8 @@ test = [
78
78
  "matplotlib",
79
79
  "pooch>=1.8.2",
80
80
  "tqdm",
81
+ "pint",
82
+ "pint-xarray",
81
83
  ]
82
84
 
83
85
  [project.entry-points."xarray.backends"]
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.7.4'
22
- __version_tuple__ = version_tuple = (0, 7, 4)
21
+ __version__ = version = '0.8.0'
22
+ __version_tuple__ = version_tuple = (0, 8, 0)
23
23
 
24
- __commit_id__ = commit_id = 'gc521760ee'
24
+ __commit_id__ = commit_id = 'gbd2a24a3a'
@@ -19,37 +19,39 @@ class EpochAccessor:
19
19
 
20
20
  def rescale_coords(
21
21
  self,
22
- multiplier: float,
23
22
  unit_label: str,
24
23
  coord_names: str | list[str],
24
+ multiplier: float | None = None,
25
25
  ) -> xr.Dataset:
26
26
  """
27
- Rescales specified X and Y coordinates in the Dataset by a given multiplier
28
- and updates the unit label attribute.
27
+ Rescales specified coordinates in a Dataset by a given unit. If the
28
+ multiplier is not specified then the coordinates are automatically
29
+ scaled using `pint <https://pint.readthedocs.io/en/stable>`_, if the multiplier is specified then it will be
30
+ used to rescale the coordinate.
29
31
 
30
32
  Parameters
31
33
  ----------
32
- multiplier : float
33
- The factor by which to multiply the coordinate values (e.g., 1e6 for meters to microns).
34
34
  unit_label : str
35
35
  The new unit label for the coordinates (e.g., "µm").
36
36
  coord_names : str or list of str
37
37
  The name(s) of the coordinate variable(s) to rescale.
38
38
  If a string, only that coordinate is rescaled.
39
39
  If a list, all listed coordinates are rescaled.
40
-
41
- Returns
42
- -------
43
- xr.Dataset
44
- A new Dataset with the updated and rescaled coordinates.
40
+ multiplier : float or None
41
+ The factor by which to multiply the coordinate values (e.g., 1e6
42
+ for meters to microns). If not specified then ``pint`` is used to
43
+ rescale the units automatically.
45
44
 
46
45
  Examples
47
46
  --------
48
- # Convert X, Y, and Z from meters to microns
49
- >>> ds_in_microns = ds.epoch.rescale_coords(1e6, "µm", coord_names=["X_Grid", "Y_Grid", "Z_Grid"])
50
-
51
- # Convert only X to millimeters
52
- >>> ds_in_mm = ds.epoch.rescale_coords(1000, "mm", coord_names="X_Grid")
47
+ >>> # Convert X, Y, and Z from meters to microns using pint
48
+ >>> ds_in_microns = ds.epoch.rescale_coords("µm", coord_names=["X_Grid", "Y_Grid", "Z_Grid"])
49
+ >>>
50
+ >>> # Convert X, Y, and Z from meters to microns
51
+ >>> ds_in_microns = ds.epoch.rescale_coords("µm", coord_names=["X_Grid", "Y_Grid", "Z_Grid"], 1e6)
52
+ >>>
53
+ >>> # Convert time to femtoseconds
54
+ >>> ds_in_mm = ds.epoch.rescale_coords("fs", coord_names="time")
53
55
  """
54
56
 
55
57
  ds = self._ds
@@ -72,9 +74,19 @@ class EpochAccessor:
72
74
 
73
75
  coord_original = ds[coord_name]
74
76
 
75
- coord_rescaled = coord_original * multiplier
76
- coord_rescaled.attrs = coord_original.attrs.copy()
77
- coord_rescaled.attrs["units"] = unit_label
77
+ if multiplier is not None:
78
+ coord_rescaled = coord_original * multiplier
79
+ coord_rescaled.attrs = coord_original.attrs.copy()
80
+ coord_rescaled.attrs["units"] = unit_label
81
+ else:
82
+ coord_rescaled: xr.DataArray = (
83
+ coord_original.pint.quantify(coord_original.attrs["units"])
84
+ .pint.to(unit_label)
85
+ .pint.dequantify()
86
+ )
87
+ # Ensure the unit label follows the same naming convension the
88
+ # user has specified and not the one given by pint
89
+ coord_rescaled.attrs["units"] = unit_label
78
90
 
79
91
  new_coords[coord_name] = coord_rescaled
80
92
 
@@ -50,6 +50,32 @@ def test_rescale_coords_X():
50
50
  assert ds_rescaled["Z_Grid_mid"].attrs["full_name"] == "Grid/Grid_mid"
51
51
 
52
52
 
53
+ def test_rescale_coords_X_auto():
54
+ unit_label = "mm"
55
+
56
+ with xr.open_dataset(TEST_FILES_DIR_3D / "0000.sdf") as ds:
57
+ ds_rescaled = ds.epoch.rescale_coords(
58
+ unit_label=unit_label,
59
+ coord_names="X_Grid_mid",
60
+ )
61
+
62
+ expected_x = ds["X_Grid_mid"].values * 1e3
63
+ assert np.allclose(ds_rescaled["X_Grid_mid"].values, expected_x)
64
+ assert ds_rescaled["X_Grid_mid"].attrs["units"] == unit_label
65
+ assert ds_rescaled["X_Grid_mid"].attrs["long_name"] == "X"
66
+ assert ds_rescaled["X_Grid_mid"].attrs["full_name"] == "Grid/Grid_mid"
67
+
68
+ assert np.allclose(ds_rescaled["Y_Grid_mid"].values, ds["Y_Grid_mid"].values)
69
+ assert ds_rescaled["Y_Grid_mid"].attrs["units"] == "m"
70
+ assert ds_rescaled["Y_Grid_mid"].attrs["long_name"] == "Y"
71
+ assert ds_rescaled["Y_Grid_mid"].attrs["full_name"] == "Grid/Grid_mid"
72
+
73
+ assert np.allclose(ds_rescaled["Z_Grid_mid"].values, ds["Z_Grid_mid"].values)
74
+ assert ds_rescaled["Z_Grid_mid"].attrs["units"] == "m"
75
+ assert ds_rescaled["Z_Grid_mid"].attrs["long_name"] == "Z"
76
+ assert ds_rescaled["Z_Grid_mid"].attrs["full_name"] == "Grid/Grid_mid"
77
+
78
+
53
79
  def test_rescale_coords_X_Y():
54
80
  multiplier = 1e2
55
81
  unit_label = "cm"
@@ -142,7 +168,7 @@ def test_rescale_coords_non_existent_coord():
142
168
 
143
169
 
144
170
  def test_rescale_coords_time():
145
- multiplier = 1e-15
171
+ multiplier = 1e15
146
172
  unit_label = "fs"
147
173
 
148
174
  with open_mfdataset(TEST_FILES_DIR_3D.glob("*.sdf")) as ds:
@@ -159,6 +185,22 @@ def test_rescale_coords_time():
159
185
  assert ds_rescaled["time"].attrs["full_name"] == "time"
160
186
 
161
187
 
188
+ def test_rescale_coords_time_auto():
189
+ unit_label = "fs"
190
+
191
+ with open_mfdataset(TEST_FILES_DIR_3D.glob("*.sdf")) as ds:
192
+ ds_rescaled = ds.epoch.rescale_coords(
193
+ unit_label=unit_label,
194
+ coord_names="time",
195
+ )
196
+
197
+ expected_time = ds["time"].values * 1e15
198
+ assert np.allclose(ds_rescaled["time"].values, expected_time)
199
+ assert ds_rescaled["time"].attrs["units"] == unit_label
200
+ assert ds_rescaled["time"].attrs["long_name"] == "Time"
201
+ assert ds_rescaled["time"].attrs["full_name"] == "time"
202
+
203
+
162
204
  def test_animate_multiple_accessor():
163
205
  with open_mfdataset(TEST_FILES_DIR_1D.glob("*.sdf")) as ds:
164
206
  assert hasattr(ds, "epoch")
@@ -1,7 +0,0 @@
1
- # Workshop Details
2
-
3
- The workshop consists of a series of talks and practical sessions delivered at the University of York over the course of 2 days (23-24 April). It covers setting up both EPOCH and sdf-xarray along with delving into the details of how PIC codes work. The timetable can be found here <https://epochpic.github.io/quickstart/epoch_workshop_2026.html>.
4
-
5
- ## Live Demo
6
-
7
- As part of the start of the workshop we will have a live demo covering the key features of sdf-xarray. Please download this jupyter notebook in order to follow along <path:live_demo.ipynb>. Alternatively, if you have a Google account you can use the online [Google Colab notebook](https://colab.research.google.com/drive/1FTKj6gUizKjcL1ZSK4sNKlWKs5mzrD2g?usp=sharing).
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes