sdf-xarray 0.2.6__tar.gz → 0.3.1__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.

Potentially problematic release.


This version of sdf-xarray might be problematic. Click here for more details.

Files changed (131) hide show
  1. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/CONTRIBUTING.md +1 -1
  2. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/PKG-INFO +4 -1
  3. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/README.md +3 -0
  4. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/conf.py +2 -2
  5. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/getting_started.rst +28 -9
  6. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/key_functionality.rst +29 -9
  7. sdf_xarray-0.3.1/docs/tutorial_dataset_2d/0000.sdf +0 -0
  8. sdf_xarray-0.3.1/docs/tutorial_dataset_2d/0001.sdf +0 -0
  9. sdf_xarray-0.3.1/docs/tutorial_dataset_2d/0002.sdf +0 -0
  10. sdf_xarray-0.3.1/docs/tutorial_dataset_2d/0003.sdf +0 -0
  11. sdf_xarray-0.3.1/docs/tutorial_dataset_2d/0004.sdf +0 -0
  12. sdf_xarray-0.3.1/docs/tutorial_dataset_2d/0005.sdf +0 -0
  13. sdf_xarray-0.3.1/docs/tutorial_dataset_2d/input.deck +65 -0
  14. sdf_xarray-0.3.1/docs/unit_conversion.rst +228 -0
  15. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/src/sdf_xarray/__init__.py +47 -14
  16. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/src/sdf_xarray/_version.py +3 -3
  17. sdf_xarray-0.3.1/src/sdf_xarray/dataset_accessor.py +73 -0
  18. sdf_xarray-0.3.1/tests/example_files_3D/0000.sdf +0 -0
  19. sdf_xarray-0.3.1/tests/example_files_3D/0001.sdf +0 -0
  20. sdf_xarray-0.3.1/tests/example_files_3D/input.deck +54 -0
  21. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/test_basic.py +217 -19
  22. sdf_xarray-0.2.6/tests/test_epoch_accessor.py → sdf_xarray-0.3.1/tests/test_epoch_dataarray_accessor.py +69 -19
  23. sdf_xarray-0.3.1/tests/test_epoch_dataset_accessor.py +146 -0
  24. sdf_xarray-0.3.1/uv.lock +2610 -0
  25. sdf_xarray-0.2.6/docs/unit_conversion.rst +0 -176
  26. sdf_xarray-0.2.6/uv.lock +0 -2122
  27. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/.github/workflows/black.yml +0 -0
  28. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/.github/workflows/build_publish.yml +0 -0
  29. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/.github/workflows/lint.yml +0 -0
  30. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/.github/workflows/tests.yml +0 -0
  31. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/.gitignore +0 -0
  32. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/.gitmodules +0 -0
  33. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/.readthedocs.yaml +0 -0
  34. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/BEAM.png +0 -0
  35. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/CITATION.cff +0 -0
  36. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/CMakeLists.txt +0 -0
  37. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/LICENCE +0 -0
  38. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/PlasmaFAIR.svg +0 -0
  39. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/.gitignore +0 -0
  40. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/_templates/custom-class-template.rst +0 -0
  41. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/_templates/custom-module-template.rst +0 -0
  42. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/api.rst +0 -0
  43. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/contributing.rst +0 -0
  44. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/index.rst +0 -0
  45. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/known_issues.rst +0 -0
  46. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/make.bat +0 -0
  47. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0000.sdf +0 -0
  48. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0001.sdf +0 -0
  49. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0002.sdf +0 -0
  50. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0003.sdf +0 -0
  51. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0004.sdf +0 -0
  52. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0005.sdf +0 -0
  53. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0006.sdf +0 -0
  54. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0007.sdf +0 -0
  55. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0008.sdf +0 -0
  56. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0009.sdf +0 -0
  57. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0010.sdf +0 -0
  58. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0011.sdf +0 -0
  59. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0012.sdf +0 -0
  60. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0013.sdf +0 -0
  61. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0014.sdf +0 -0
  62. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0015.sdf +0 -0
  63. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0016.sdf +0 -0
  64. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0017.sdf +0 -0
  65. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0018.sdf +0 -0
  66. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0019.sdf +0 -0
  67. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0020.sdf +0 -0
  68. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0021.sdf +0 -0
  69. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0022.sdf +0 -0
  70. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0023.sdf +0 -0
  71. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0024.sdf +0 -0
  72. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0025.sdf +0 -0
  73. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0026.sdf +0 -0
  74. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0027.sdf +0 -0
  75. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0028.sdf +0 -0
  76. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0029.sdf +0 -0
  77. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0030.sdf +0 -0
  78. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0031.sdf +0 -0
  79. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0032.sdf +0 -0
  80. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0033.sdf +0 -0
  81. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0034.sdf +0 -0
  82. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0035.sdf +0 -0
  83. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0036.sdf +0 -0
  84. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0037.sdf +0 -0
  85. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0038.sdf +0 -0
  86. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0039.sdf +0 -0
  87. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/0040.sdf +0 -0
  88. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/deck.status +0 -0
  89. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/epoch1d.dat +0 -0
  90. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/input.deck +0 -0
  91. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/normal.visit +0 -0
  92. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/docs/tutorial_dataset_1d/restart.visit +0 -0
  93. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/pyproject.toml +0 -0
  94. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/src/sdf_xarray/csdf.pxd +0 -0
  95. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/src/sdf_xarray/plotting.py +0 -0
  96. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/src/sdf_xarray/sdf_interface.pyx +0 -0
  97. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_array_no_grids/0000.sdf +0 -0
  98. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_array_no_grids/0001.sdf +0 -0
  99. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_array_no_grids/README.md +0 -0
  100. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_array_no_grids/input.deck +0 -0
  101. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_dist_fn/0000.sdf +0 -0
  102. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_dist_fn/0001.sdf +0 -0
  103. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_dist_fn/0002.sdf +0 -0
  104. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_dist_fn/input.deck +0 -0
  105. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_1D/0000.sdf +0 -0
  106. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_1D/0001.sdf +0 -0
  107. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_1D/0002.sdf +0 -0
  108. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_1D/0003.sdf +0 -0
  109. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_1D/0004.sdf +0 -0
  110. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_1D/0005.sdf +0 -0
  111. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_1D/0006.sdf +0 -0
  112. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_1D/0007.sdf +0 -0
  113. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_1D/0008.sdf +0 -0
  114. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_1D/0009.sdf +0 -0
  115. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_1D/0010.sdf +0 -0
  116. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_1D/README.md +0 -0
  117. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_1D/input.deck +0 -0
  118. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_2D_moving_window/0000.sdf +0 -0
  119. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_2D_moving_window/0001.sdf +0 -0
  120. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_2D_moving_window/0002.sdf +0 -0
  121. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_2D_moving_window/0003.sdf +0 -0
  122. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_2D_moving_window/0004.sdf +0 -0
  123. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_files_2D_moving_window/input.deck +0 -0
  124. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_mismatched_files/0000.sdf +0 -0
  125. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_mismatched_files/0001.sdf +0 -0
  126. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_mismatched_files/0002.sdf +0 -0
  127. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_two_probes_2D/0000.sdf +0 -0
  128. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_two_probes_2D/0001.sdf +0 -0
  129. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_two_probes_2D/0002.sdf +0 -0
  130. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/example_two_probes_2D/input.deck +0 -0
  131. {sdf_xarray-0.2.6 → sdf_xarray-0.3.1}/tests/test_cython.py +0 -0
@@ -33,7 +33,7 @@ To run these tools locally, install the optional dependencies and run:
33
33
 
34
34
  ```bash
35
35
  pip install "sdf-xarray[lint]"
36
- ruff check
36
+ ruff check src tests
37
37
  ```
38
38
 
39
39
  ### Running and Adding Tests
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sdf-xarray
3
- Version: 0.2.6
3
+ Version: 0.3.1
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>
6
6
  License-Expression: BSD-3-Clause
@@ -61,6 +61,9 @@ sdf-xarray provides a backend for [xarray](https://xarray.dev) to read SDF files
61
61
  [EPOCH](https://epochpic.github.io) using the [SDF-C](https://github.com/epochpic/SDF_C) library.
62
62
  Part of [BEAM](#broad-epoch-analysis-modules-beam) (Broad EPOCH Analysis Modules).
63
63
 
64
+ > [!IMPORTANT]
65
+ > To install this package make sure you are using one of the Python versions listed above.
66
+
64
67
  ## Installation
65
68
 
66
69
  Install from PyPI with:
@@ -13,6 +13,9 @@ sdf-xarray provides a backend for [xarray](https://xarray.dev) to read SDF files
13
13
  [EPOCH](https://epochpic.github.io) using the [SDF-C](https://github.com/epochpic/SDF_C) library.
14
14
  Part of [BEAM](#broad-epoch-analysis-modules-beam) (Broad EPOCH Analysis Modules).
15
15
 
16
+ > [!IMPORTANT]
17
+ > To install this package make sure you are using one of the Python versions listed above.
18
+
16
19
  ## Installation
17
20
 
18
21
  Install from PyPI with:
@@ -8,9 +8,9 @@ from importlib.metadata import version as get_version
8
8
  from pathlib import Path
9
9
 
10
10
  with suppress(ImportError):
11
- import matplotlib
11
+ import matplotlib as mpl
12
12
 
13
- matplotlib.use("Agg")
13
+ mpl.use("Agg")
14
14
  # -- Project information -----------------------------------------------------
15
15
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
16
16
 
@@ -7,6 +7,15 @@
7
7
  Installation
8
8
  ------------
9
9
 
10
+ .. |python_versions_pypi| image:: https://img.shields.io/pypi/pyversions/sdf-xarray.svg
11
+ :alt: Supported Python versions
12
+ :target: https://pypi.org/project/sdf-xarray/
13
+
14
+ .. important::
15
+
16
+ To install this package, ensure that you are using one of the supported Python
17
+ versions: |python_versions_pypi|
18
+
10
19
  Install sdf-xarray from PyPI with:
11
20
 
12
21
  .. code-block:: bash
@@ -28,7 +37,7 @@ Usage
28
37
  `xarray`. There are several ways to load SDF files:
29
38
 
30
39
  - To load a single file, use :func:`xarray.open_dataset`.
31
- - To load multiple files, use :func:`xarray.open_mfdataset` or :func:`sdf_xarray.open_mfdataset`.
40
+ - To load multiple files, use :func:`xarray.open_mfdataset` or :func:`sdf_xarray.open_mfdataset` (Recommended).
32
41
  - To access the raw contents of a single SDF file, use :func:`sdf_xarray.sdf_interface.SDFFile`.
33
42
 
34
43
  .. note::
@@ -42,21 +51,32 @@ Basic usage:
42
51
  .. ipython:: python
43
52
 
44
53
  import xarray as xr
54
+ import sdf_xarray as sdfxr
45
55
  with xr.open_dataset("tutorial_dataset_1d/0010.sdf") as df:
46
56
  print(df["Electric_Field_Ex"])
47
57
 
48
58
  Multi file loading
49
59
  ~~~~~~~~~~~~~~~~~~
50
60
 
51
- To open a whole simulation at once, pass
52
- ``preprocess=sdf_xarray.SDFPreprocess()`` to `xarray.open_mfdataset`:
61
+ To open a whole simulation's files at once use the :func:`sdf_xarray.open_mfdataset` function:
62
+
63
+ .. ipython:: python
64
+
65
+ sdfxr.open_mfdataset("tutorial_dataset_1d/*.sdf")
66
+
67
+ You can alternatively open the dataset using the xarray's :func:`xarray.open_mfdataset`
68
+ along with the ``preprocess=sdfxr.SDFPreprocess()``:
53
69
 
54
70
  .. ipython:: python
55
71
 
56
- from sdf_xarray import SDFPreprocess
57
- xr.open_mfdataset("tutorial_dataset_1d/*.sdf", preprocess=SDFPreprocess())
72
+ xr.open_mfdataset(
73
+ "tutorial_dataset_1d/*.sdf",
74
+ join="outer",
75
+ compat="no_conflicts",
76
+ preprocess=sdfxr.SDFPreprocess()
77
+ )
58
78
 
59
- `SDFPreprocess` checks that all the files are from the same simulation, and
79
+ :class:`sdf_xarray.SDFPreprocess` checks that all the files are from the same simulation, and
60
80
  ensures there's a ``time`` dimension so the files are correctly concatenated.
61
81
 
62
82
  If your simulation has multiple ``output`` blocks so that not all variables are
@@ -64,12 +84,11 @@ output at every time step, then those variables will have ``NaN`` values at the
64
84
  corresponding time points.
65
85
 
66
86
  Alternatively, we can create a separate time dimensions for each ``output``
67
- block using `sdf_xarray.open_mfdataset` with ``separate_times=True``:
87
+ block using :func:`sdf_xarray.open_mfdataset` with ``separate_times=True``:
68
88
 
69
89
  .. ipython:: python
70
90
 
71
- from sdf_xarray import open_mfdataset
72
- open_mfdataset("tutorial_dataset_1d/*.sdf", separate_times=True)
91
+ sdfxr.open_mfdataset("tutorial_dataset_1d/*.sdf", separate_times=True)
73
92
 
74
93
  This is better for memory consumption, at the cost of perhaps slightly less
75
94
  friendly comparisons between variables on different time coordinates.
@@ -6,17 +6,17 @@ Key Functionality
6
6
 
7
7
  .. ipython:: python
8
8
 
9
+ import xarray as xr
10
+ import sdf_xarray as sdfxr
9
11
  import matplotlib.pyplot as plt
10
12
  from IPython.display import display, HTML
11
- import xarray as xr
12
- from sdf_xarray import SDFFile, SDFPreprocess
13
13
 
14
14
  Loading SDF Files
15
15
  -----------------
16
16
  There are several ways to load SDF files:
17
17
 
18
18
  - To load a single file, use :func:`xarray.open_dataset`.
19
- - To load multiple files, use :func:`xarray.open_mfdataset` or :func:`sdf_xarray.open_mfdataset`.
19
+ - To load multiple files, use :func:`sdf_xarray.open_mfdataset` or :func:`xarray.open_mfdataset`.
20
20
  - To access the raw contents of a single SDF file, use :func:`sdf_xarray.sdf_interface.SDFFile`.
21
21
 
22
22
  .. note::
@@ -34,28 +34,48 @@ Loading a Single Raw SDF File
34
34
 
35
35
  .. ipython:: python
36
36
 
37
- with SDFFile("tutorial_dataset_1d/0010.sdf") as sdf_file:
37
+ with sdfxr.SDFFile("tutorial_dataset_1d/0010.sdf") as sdf_file:
38
38
  print(sdf_file.variables)
39
39
 
40
40
  Loading all SDF Files for a Simulation
41
41
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42
42
 
43
- When loading in all the files we have do some processing of the data
43
+ Multiple files can be loaded using one of two methods. The first of which
44
+ is by using the :func:`sdf_xarray.open_mfdataset`
45
+
46
+ .. ipython:: python
47
+
48
+ sdfxr.open_mfdataset("tutorial_dataset_1d/*.sdf")
49
+
50
+ Alternatively files can be loaded using :func:`xarray.open_mfdataset`
51
+ however when loading in all the files we have do some processing of the data
44
52
  so that we can correctly align it along the time dimension; This is
45
53
  done via the ``preprocess`` parameter.
46
54
 
47
55
  .. ipython:: python
48
56
 
49
- xr.open_mfdataset("tutorial_dataset_1d/*.sdf", preprocess=SDFPreprocess())
57
+ xr.open_mfdataset(
58
+ "tutorial_dataset_1d/*.sdf",
59
+ join="outer",
60
+ compat="no_conflicts",
61
+ preprocess=sdfxr.SDFPreprocess())
50
62
 
51
63
  Reading particle data
52
64
  ~~~~~~~~~~~~~~~~~~~~~
53
65
 
54
66
  .. warning::
55
- It is **not recommended** to use :func:`xarray.open_mfdataset` or :func:`open_mfdataset` to load particle data from multiple SDF outputs. The number of particles often varies between outputs, which can lead to inconsistent array shapes that these functions cannot handle. Instead, consider loading each file individually and then concatenating them manually.
67
+ It is **not recommended** to use :func:`xarray.open_mfdataset` or
68
+ :func:`sdf_xarray.open_mfdataset` to load particle data from multiple
69
+ SDF outputs. The number of particles often varies between outputs,
70
+ which can lead to inconsistent array shapes that these functions
71
+ cannot handle. Instead, consider loading each file individually and
72
+ then concatenating them manually.
56
73
 
57
74
  .. note::
58
- When loading multiple probes from a single SDF file, you **must** use the `probe_names` parameter to assign a unique name to each. For example, use `probe_names=["Front_Electron_Probe", "Back_Electron_Probe"]`. Failing to do so will result in dimension name conflicts.
75
+ When loading multiple probes from a single SDF file, you **must** use the
76
+ ``probe_names`` parameter to assign a unique name to each. For example,
77
+ use ``probe_names=["Front_Electron_Probe", "Back_Electron_Probe"]``.
78
+ Failing to do so will result in dimension name conflicts.
59
79
 
60
80
  By default, particle data isn't kept as it takes up a lot of space.
61
81
  Pass ``keep_particles=True`` as a keyword argument to
@@ -85,7 +105,7 @@ looking at when you call ``.values``
85
105
 
86
106
  .. ipython:: python
87
107
 
88
- ds = xr.open_mfdataset("tutorial_dataset_1d/*.sdf", preprocess=SDFPreprocess())
108
+ ds = sdfxr.open_mfdataset("tutorial_dataset_1d/*.sdf")
89
109
 
90
110
  ds["Electric_Field_Ex"]
91
111
 
@@ -0,0 +1,65 @@
1
+ begin:control
2
+ nx = 64
3
+ ny = 64
4
+
5
+ # Final time of simulation
6
+ t_end = 5 * femto
7
+
8
+ # Size of domain
9
+ x_min = 0
10
+ x_max = 6 * micron
11
+
12
+ y_min = 0
13
+ y_max = 6 * micron
14
+
15
+ stdout_frequency = 1
16
+ nparticles = nx * ny * 50
17
+ end:control
18
+
19
+ begin:constant
20
+ n_elec = 1000
21
+ L_target_x = 2 * micron
22
+ L_target_y = 4 * micron
23
+
24
+ x_center = (x_min + x_max) / 2
25
+ y_center = (y_min + y_max) / 2
26
+
27
+ density_profile = if( (abs(x - x_center) lt L_target_x/2), if( (abs(y - y_center) lt L_target_y/2), 1, 0), 0)
28
+ end:constant
29
+
30
+ begin:boundaries
31
+ bc_x_min = periodic
32
+ bc_x_max = periodic
33
+ bc_y_min = periodic
34
+ bc_y_max = periodic
35
+ end:boundaries
36
+
37
+
38
+ begin:species
39
+ name = Electron
40
+ frac = 0.5
41
+ number_density = n_elec * density_profile
42
+ identify:electron
43
+ end:species
44
+
45
+ begin:species
46
+ name = Ion
47
+ frac = 0.5
48
+ number_density = n_elec * density_profile
49
+ identify:proton
50
+ end:species
51
+
52
+ begin:output_global
53
+ force_last_to_be_restartable = F
54
+ end:output_global
55
+
56
+ begin:output
57
+ name = normal
58
+
59
+ dt_snapshot = 1 * femto
60
+
61
+ grid = always
62
+ number_density = always + species
63
+
64
+ end:output
65
+
@@ -0,0 +1,228 @@
1
+ .. _sec-unit-conversion:
2
+
3
+ ===============
4
+ Unit Conversion
5
+ ===============
6
+
7
+ The ``sdf-xarray`` package automatically extracts the units for each
8
+ coordinate/variable/constant from an SDF file and stores them as an :class:`xarray.Dataset`
9
+ attribute called ``"units"``. Sometimes we want to convert our data from one format to
10
+ another, e.g. converting the grid coordinates from meters to microns, time from seconds
11
+ to femto-seconds or particle energy from Joules to electron-volts.
12
+
13
+ .. ipython:: python
14
+
15
+ from sdf_xarray import open_mfdataset
16
+ import matplotlib.pyplot as plt
17
+ plt.rcParams.update({
18
+ "axes.labelsize": 16,
19
+ "xtick.labelsize": 14,
20
+ "ytick.labelsize": 14,
21
+ "axes.titlesize": 16
22
+ })
23
+
24
+
25
+ =====================
26
+ Rescaling Coordinates
27
+ =====================
28
+
29
+ For simple scaling and unit relabeling of coordinates (e.g., converting meters to microns),
30
+ the most straightforward approach is to use the ``rescale_coords()`` method
31
+ via the custom ``xarray.Dataset.epoch`` dataset accessor.
32
+
33
+ This method scales the coordinate values by a given multiplier and updates the ``"units"``
34
+ attribute in one step.
35
+
36
+ Rescaling Grid Coordinates
37
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
38
+
39
+ We can use the ``xarray.Dataset.epoch.rescale_coords()`` method to convert X, Y, and Z
40
+ coordinates from meters (m) to microns (µm) by applying a multiplier of ``1e6``.
41
+
42
+ .. ipython:: python
43
+
44
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))
45
+
46
+ with open_mfdataset("tutorial_dataset_2d/*.sdf") as ds:
47
+ ds_in_microns = ds.epoch.rescale_coords(
48
+ multiplier=1e6,
49
+ unit_label="µm",
50
+ coord_names=["X_Grid_mid", "Y_Grid_mid"]
51
+ )
52
+ derived_number_density = ds["Derived_Number_Density_Electron"].isel(time=0).compute()
53
+ derived_number_density_microns = ds_in_microns["Derived_Number_Density_Electron"].isel(time=0).compute()
54
+
55
+ derived_number_density.plot(ax=ax1, x="X_Grid_mid", y="Y_Grid_mid")
56
+ ax1.set_title("Original X Coordinate (m)")
57
+
58
+ derived_number_density_microns.plot(ax=ax2, x="X_Grid_mid", y="Y_Grid_mid")
59
+ ax2.set_title("Rescaled X Coordinate (µm)")
60
+
61
+ @savefig coordinate_conversion.png width=9in
62
+ fig.tight_layout()
63
+
64
+
65
+ Rescaling Time Coordinate
66
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
67
+
68
+ We can also use the ``xarray.Dataset.epoch.rescale_coords()`` method to convert the time
69
+ coordinate from seconds (s) to femto-seconds (fs) by applying a multiplier of ``1e15``.
70
+
71
+ .. ipython:: python
72
+
73
+ with open_mfdataset("tutorial_dataset_2d/*.sdf") as ds:
74
+ ds_time_in_femto = ds.epoch.rescale_coords(
75
+ multiplier=1e15,
76
+ unit_label="fs",
77
+ coord_names="time"
78
+ )
79
+
80
+ print(f"[Original] units: {ds['time'].attrs['units']}, values: {ds['time'].values}")
81
+ print(f"[Rescaled] units: {ds_time_in_femto['time'].attrs['units']}, values: {ds_time_in_femto['time'].values}")
82
+
83
+
84
+ ================================
85
+ Unit Conversion with pint-xarray
86
+ ================================
87
+
88
+ While this is sufficient for most use cases, we can enhance this functionality
89
+ using the `pint <https://pint.readthedocs.io/en/stable/getting/index.html>`_ library.
90
+ Pint allows us to specify the units of a given array and convert them
91
+ to another, which is incredibly handy. We can take this a step further,
92
+ however, and utilize the `pint-xarray
93
+ <https://pint-xarray.readthedocs.io/en/latest/>`_ library. This library
94
+ allows us to infer units directly from an `xarray.Dataset.attrs` while
95
+ retaining all the information about the `xarray.Dataset`. This works
96
+ very similarly to taking a NumPy array and multiplying it by a constant or
97
+ another array, which returns a new array; however, this library will also
98
+ retain the unit logic (specifically the ``"units"`` information).
99
+
100
+ .. note::
101
+ Unit conversion is not supported on coordinates in ``pint-xarray`` which is due to an
102
+ underlying issue with how ``xarray`` implements indexes.
103
+
104
+ Installation
105
+ ~~~~~~~~~~~~
106
+
107
+ To install the pint libraries you can simply run the following optional
108
+ dependency pip command which will install both the ``pint`` and ``pint-xarray``
109
+ libraries. You can install these optional dependencies via pip:
110
+
111
+ .. code:: console
112
+
113
+ $ pip install "sdf_xarray[pint]"
114
+
115
+ .. note::
116
+ Once you install ``pint-xarray`` it is automatically picked up and loaded
117
+ by the code so you should have access to the ``xarray.Dataset.pint`` accessor.
118
+
119
+ Quantifying Arrays
120
+ ~~~~~~~~~~~~~~~~~~
121
+
122
+ When using ``pint-xarray``, the library attempts to infer units from the
123
+ ``"units"`` attribute on each `xarray.DataArray`. Alternatively, you can
124
+ also specify the units yourself by passing a string into the
125
+ ``xarray.Dataset.DataArray.pint.quantify()`` function call. Once the type is inferred
126
+ the original `xarray.DataArray` will be converted to a `pint.Quantity`
127
+ and the ``"units"`` attribute will
128
+ be removed.
129
+
130
+ In the following example we will extract the time-resolved total particle
131
+ energy of electrons which is measured in Joules and convert it to electron
132
+ volts.
133
+
134
+ .. ipython:: python
135
+
136
+ with open_mfdataset("tutorial_dataset_1d/*.sdf") as ds:
137
+ total_particle_energy = ds["Total_Particle_Energy_Electron"]
138
+
139
+ total_particle_energy
140
+
141
+ total_particle_energy = ds["Total_Particle_Energy_Electron"].pint.quantify()
142
+
143
+ total_particle_energy
144
+
145
+
146
+ Now that this dataset has been converted a `pint.Quantity`, we can check
147
+ it's units and dimensionality
148
+
149
+ .. ipython:: python
150
+
151
+ total_particle_energy.pint.units
152
+ total_particle_energy.pint.dimensionality
153
+
154
+
155
+ Converting Units
156
+ ~~~~~~~~~~~~~~~~
157
+
158
+ We can now convert it to electron volts utilising the `pint.Quantity.to`
159
+ function
160
+
161
+ .. ipython:: python
162
+
163
+ total_particle_energy_ev = total_particle_energy.pint.to("eV")
164
+
165
+ Unit Propagation
166
+ ~~~~~~~~~~~~~~~~
167
+
168
+ Suppose instead of converting to ``"eV"``, we want to convert to ``"W"``
169
+ (watts). To do this, we divide the total particle energy by time. However,
170
+ since coordinates in `xarray.Dataset` cannot be directly converted to
171
+ `pint.Quantity`, we must first extract the coordinate values manually
172
+ and create a new Pint quantity for time.
173
+
174
+ Once both arrays are quantified, Pint will automatically handle the unit
175
+ propagation when we perform arithmetic operations like division.
176
+
177
+ .. note::
178
+ Pint does not automatically simplify ``"J/s"`` to ``"W"``, so we use
179
+ `pint.Quantity.to` to convert the unit string. Since these units are
180
+ the same it will not change the underlying data, only the units. This is
181
+ only a small formatting choice and is not required.
182
+
183
+ .. ipython:: python
184
+
185
+ import pint
186
+ time_values = total_particle_energy.coords["time"].data
187
+ time = pint.Quantity(time_values, "s")
188
+ total_particle_energy_w = total_particle_energy / time
189
+ total_particle_energy_w.pint.units
190
+ total_particle_energy_w = total_particle_energy_w.pint.to("W")
191
+ total_particle_energy_w.pint.units
192
+
193
+ Dequantifying and Restoring Units
194
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
195
+
196
+ .. note::
197
+ If this function is not called prior to plotting then the ``units`` will be
198
+ inferred from the `pint.Quantity` array which will return the long
199
+ name of the units. i.e. instead of returning ``"eV"`` it will return
200
+ ``"electron_volt"``.
201
+
202
+ The ``xarray.Dataset.DataArray.pint.dequantify`` function converts the data from
203
+ `pint.Quantity` back to the original `xarray.DataArray` and adds
204
+ the ``"units"`` attribute back in. It also has an optional ``format`` parameter
205
+ that allows you to specify the formatting type of ``"units"`` attribute. We
206
+ have used the ``format="~P"`` option as it shortens the unit to its
207
+ "short pretty" format (``"eV"``). For more options, see the `Pint formatting
208
+ documentation <https://pint.readthedocs.io/en/stable/user/formatting.html>`_.
209
+
210
+ .. ipython:: python
211
+
212
+ total_particle_energy_ev = total_particle_energy_ev.pint.dequantify(format="~P")
213
+ total_particle_energy_w = total_particle_energy_w.pint.dequantify(format="~P")
214
+ total_particle_energy_ev
215
+
216
+ To confirm the conversion has worked correctly, we can plot the original and
217
+ converted `xarray.Dataset` side by side:
218
+
219
+ .. ipython:: python
220
+
221
+ fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16,8))
222
+ ds["Total_Particle_Energy_Electron"].plot(ax=ax1)
223
+ total_particle_energy_ev.plot(ax=ax2)
224
+ total_particle_energy_w.plot(ax=ax3)
225
+ ax4.set_visible(False)
226
+ fig.suptitle("Comparison of conversion from Joules to electron volts and watts", fontsize="18")
227
+ @savefig unit_conversion.png width=9in
228
+ fig.tight_layout()
@@ -1,13 +1,17 @@
1
+ import contextlib
1
2
  import os
2
3
  import re
3
4
  from collections import Counter, defaultdict
4
5
  from collections.abc import Callable, Iterable
6
+ from importlib.metadata import version
5
7
  from itertools import product
8
+ from os import PathLike as os_PathLike
6
9
  from pathlib import Path
7
10
  from typing import ClassVar
8
11
 
9
12
  import numpy as np
10
13
  import xarray as xr
14
+ from packaging.version import Version
11
15
  from xarray.backends import AbstractDataStore, BackendArray, BackendEntrypoint
12
16
  from xarray.backends.file_manager import CachingFileManager
13
17
  from xarray.backends.locks import ensure_lock
@@ -15,12 +19,24 @@ from xarray.core import indexing
15
19
  from xarray.core.utils import close_on_error, try_read_magic_number_from_path
16
20
  from xarray.core.variable import Variable
17
21
 
18
- # NOTE: Do not delete this line, otherwise the "epoch" accessor will not be
19
- # imported when the user imports sdf_xarray
22
+ # NOTE: Do not delete these lines, otherwise the "epoch" dataset and dataarray
23
+ # accessors will not be imported when the user imports sdf_xarray
24
+ import sdf_xarray.dataset_accessor
20
25
  import sdf_xarray.plotting # noqa: F401
21
26
 
27
+ # NOTE: This attempts to initialise with the "pint" accessor if the user
28
+ # has installed the package
29
+ with contextlib.suppress(ImportError):
30
+ import pint_xarray # noqa: F401
31
+
22
32
  from .sdf_interface import Constant, SDFFile # type: ignore # noqa: PGH003
23
33
 
34
+ # TODO Remove this once the new kwarg options are fully implemented
35
+ if Version(version("xarray")) >= Version("2025.8.0"):
36
+ xr.set_options(use_new_combine_kwarg_defaults=True)
37
+
38
+ PathLike = str | os_PathLike
39
+
24
40
 
25
41
  def _rename_with_underscore(name: str) -> str:
26
42
  """A lot of the variable names have spaces, forward slashes and dashes in them, which
@@ -51,14 +67,32 @@ def _process_latex_name(variable_name: str) -> str:
51
67
  return variable_name
52
68
 
53
69
 
70
+ def _resolve_glob(path_glob: PathLike | Iterable[PathLike]):
71
+ """
72
+ Normalise input path_glob into a sorted list of absolute, resolved Path objects.
73
+ """
74
+
75
+ try:
76
+ p = Path(path_glob)
77
+ paths = list(p.parent.glob(p.name)) if p.name == "*.sdf" else list(p)
78
+ except TypeError:
79
+ paths = list({Path(p) for p in path_glob})
80
+
81
+ paths = sorted(p.resolve() for p in paths)
82
+ if not paths:
83
+ raise FileNotFoundError(f"No files matched pattern or input: {path_glob!r}")
84
+ return paths
85
+
86
+
54
87
  def combine_datasets(path_glob: Iterable | str, **kwargs) -> xr.Dataset:
55
88
  """Combine all datasets using a single time dimension"""
56
89
 
57
90
  return xr.open_mfdataset(
58
91
  path_glob,
59
- data_vars="minimal",
60
- coords="minimal",
61
- compat="override",
92
+ data_vars="all",
93
+ coords="different",
94
+ compat="no_conflicts",
95
+ join="outer",
62
96
  preprocess=SDFPreprocess(),
63
97
  **kwargs,
64
98
  )
@@ -103,19 +137,13 @@ def open_mfdataset(
103
137
  List of EPOCH probe names
104
138
  """
105
139
 
106
- # TODO: This is not very robust, look at how xarray.open_mfdataset does it
107
- if isinstance(path_glob, str):
108
- path_glob = Path().glob(path_glob)
109
-
110
- # Coerce to list because we might need to use the sequence multiple times
111
- path_glob = sorted(list(path_glob)) # noqa: C414
112
-
140
+ path_glob = _resolve_glob(path_glob)
113
141
  if not separate_times:
114
142
  return combine_datasets(
115
143
  path_glob, keep_particles=keep_particles, probe_names=probe_names
116
144
  )
117
145
 
118
- time_dims, var_times_map = make_time_dims(path_glob)
146
+ _, var_times_map = make_time_dims(path_glob)
119
147
  all_dfs = [
120
148
  xr.open_dataset(f, keep_particles=keep_particles, probe_names=probe_names)
121
149
  for f in path_glob
@@ -136,7 +164,12 @@ def open_mfdataset(
136
164
  )
137
165
 
138
166
  return xr.combine_by_coords(
139
- all_dfs, data_vars="minimal", combine_attrs="drop_conflicts"
167
+ all_dfs,
168
+ data_vars="all",
169
+ coords="different",
170
+ combine_attrs="drop_conflicts",
171
+ join="outer",
172
+ compat="no_conflicts",
140
173
  )
141
174
 
142
175
 
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.2.6'
32
- __version_tuple__ = version_tuple = (0, 2, 6)
31
+ __version__ = version = '0.3.1'
32
+ __version_tuple__ = version_tuple = (0, 3, 1)
33
33
 
34
- __commit_id__ = commit_id = 'g67411803b'
34
+ __commit_id__ = commit_id = 'gce5426d4a'