sdf-xarray 0.2.2__cp312-cp312-win_amd64.whl → 0.2.4__cp312-cp312-win_amd64.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.

Potentially problematic release.


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

lib/SDFC_14.4.7/sdfc.lib CHANGED
Binary file
sdf_xarray/__init__.py CHANGED
@@ -310,6 +310,8 @@ class SDFDataStore(AbstractDataStore):
310
310
  # Had some problems with these variables, so just ignore them for now
311
311
  if "cpu" in key.lower():
312
312
  continue
313
+ if "boundary" in key.lower():
314
+ continue
313
315
  if "output file" in key.lower():
314
316
  continue
315
317
 
sdf_xarray/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.2.2'
21
- __version_tuple__ = version_tuple = (0, 2, 2)
20
+ __version__ = version = '0.2.4'
21
+ __version_tuple__ = version_tuple = (0, 2, 4)
sdf_xarray/plotting.py CHANGED
@@ -10,60 +10,82 @@ if TYPE_CHECKING:
10
10
  from matplotlib.animation import FuncAnimation
11
11
 
12
12
 
13
- def get_frame_title(data: xr.DataArray, frame: int, display_sdf_name: bool) -> str:
13
+ def get_frame_title(
14
+ data: xr.DataArray,
15
+ frame: int,
16
+ display_sdf_name: bool = False,
17
+ title_custom: str | None = None,
18
+ ) -> str:
14
19
  """Generate the title for a frame"""
15
- sdf_name = f", {frame:04d}.sdf" if display_sdf_name else ""
20
+ # Adds custom text to the start of the title, if specified
21
+ title_custom = "" if title_custom is None else f"{title_custom}, "
22
+ # Adds the time and associated units to the title
16
23
  time = data["time"][frame].to_numpy()
17
- return f"t = {time:.2e}s{sdf_name}"
18
24
 
25
+ time_units = data["time"].attrs.get("units", False)
26
+ time_units_formatted = f" [{time_units}]" if time_units else ""
27
+ title_time = f"time = {time:.2e}{time_units_formatted}"
19
28
 
20
- def calculate_window_velocity_and_edges(
21
- data: xr.DataArray, x_axis_coord: str
22
- ) -> tuple[float, tuple[float, float], np.ndarray]:
23
- """Calculate the moving window's velocity and initial edges.
29
+ # Adds sdf name to the title, if specifed
30
+ title_sdf = f", {frame:04d}.sdf" if display_sdf_name else ""
31
+ return f"{title_custom}{title_time}{title_sdf}"
24
32
 
25
- 1. Finds a lineout of the target atribute in the x coordinate of the first frame
26
- 2. Removes the NaN values to isolate the simulation window
27
- 3. Produces the index size of the window, indexed at zero
28
- 4. Uses distance moved and final time of the simulation to calculate velocity and initial xlims
29
- """
30
- time_since_start = data["time"].values - data["time"].values[0]
31
- initial_window_edge = (0, 0)
32
- target_lineout = data.values[0, :, 0]
33
- target_lineout_window = target_lineout[~np.isnan(target_lineout)]
34
- x_grid = data[x_axis_coord].values
35
- window_size_index = target_lineout_window.size - 1
36
33
 
37
- velocity_window = (x_grid[-1] - x_grid[window_size_index]) / time_since_start[-1]
38
- initial_window_edge = (x_grid[0], x_grid[window_size_index])
39
- return velocity_window, initial_window_edge, time_since_start
34
+ def calculate_window_boundaries(
35
+ data: xr.DataArray, xlim: tuple[float, float] | False = False
36
+ ) -> np.ndarray:
37
+ """Calculate the bounderies a moving window frame. If the user specifies xlim, this will
38
+ be used as the initial bounderies and the window will move along acordingly.
39
+ """
40
+ x_grid = data["X_Grid_mid"].values
41
+ x_half_cell = (x_grid[1] - x_grid[0]) / 2
42
+ N_frames = data["time"].size
40
43
 
44
+ # Find the window bounderies by finding the first and last non-NaN values in the 0th lineout
45
+ # along the x-axis.
46
+ window_boundaries = np.zeros((N_frames, 2))
47
+ for i in range(N_frames):
48
+ # Check if data is 1D
49
+ if data.ndim == 2:
50
+ target_lineout = data[i].values
51
+ # Check if data is 2D
52
+ if data.ndim == 3:
53
+ target_lineout = data[i, :, 0].values
54
+ x_grid_non_nan = x_grid[~np.isnan(target_lineout)]
55
+ window_boundaries[i, 0] = x_grid_non_nan[0] - x_half_cell
56
+ window_boundaries[i, 1] = x_grid_non_nan[-1] + x_half_cell
41
57
 
42
- def compute_global_limits(data: xr.DataArray) -> tuple[float, float]:
43
- """Remove all NaN values from the target data to calculate the 1st and 99th percentiles,
44
- excluding extreme outliers.
45
- """
46
- values_no_nan = data.values[~np.isnan(data.values)]
47
- global_min = np.percentile(values_no_nan, 1)
48
- global_max = np.percentile(values_no_nan, 99)
49
- return global_min, global_max
58
+ # User's choice for initial window edge supercides the one calculated
59
+ if xlim:
60
+ window_boundaries = window_boundaries + xlim - window_boundaries[0]
61
+ return window_boundaries
50
62
 
51
63
 
52
- def is_1d(data: xr.DataArray) -> bool:
53
- """Check if the data is 1D."""
54
- return len(data.shape) == 2
64
+ def compute_global_limits(
65
+ data: xr.DataArray,
66
+ min_percentile: float = 0,
67
+ max_percentile: float = 100,
68
+ ) -> tuple[float, float]:
69
+ """Remove all NaN values from the target data to calculate the global minimum and maximum of the data.
70
+ User defined percentiles can remove extreme outliers.
71
+ """
55
72
 
73
+ # Removes NaN values, needed for moving windows
74
+ values_no_nan = data.values[~np.isnan(data.values)]
56
75
 
57
- def is_2d(data: xr.DataArray) -> bool:
58
- """Check if the data is 2D or 3D."""
59
- return len(data.shape) == 3
76
+ # Finds the global minimum and maximum of the plot, based on the percentile of the data
77
+ global_min = np.percentile(values_no_nan, min_percentile)
78
+ global_max = np.percentile(values_no_nan, max_percentile)
79
+ return global_min, global_max
60
80
 
61
81
 
62
- def generate_animation(
82
+ def animate(
63
83
  data: xr.DataArray,
84
+ fps: float = 10,
85
+ min_percentile: float = 0,
86
+ max_percentile: float = 100,
87
+ title: str | None = None,
64
88
  display_sdf_name: bool = False,
65
- fps: int = 10,
66
- move_window: bool = False,
67
89
  ax: plt.Axes | None = None,
68
90
  **kwargs,
69
91
  ) -> FuncAnimation:
@@ -71,17 +93,18 @@ def generate_animation(
71
93
 
72
94
  Parameters
73
95
  ---------
74
- dataset
75
- The dataset containing the simulation data
76
- target_attribute
77
- The attribute to plot for each timestep
78
- display_sdf_name
79
- Display the sdf file name in the animation title
96
+ data
97
+ The dataarray containing the target data
80
98
  fps
81
99
  Frames per second for the animation (default: 10)
82
- move_window
83
- If the simulation has a moving window, the animation will move along
84
- with it (default: False)
100
+ min_percentile
101
+ Minimum percentile of the data (default: 0)
102
+ max_percentile
103
+ Maximum percentile of the data (default: 100)
104
+ title
105
+ Custom title to add to the plot.
106
+ display_sdf_name
107
+ Display the sdf file name in the animation title
85
108
  ax
86
109
  Matplotlib axes on which to plot.
87
110
  kwargs
@@ -89,18 +112,28 @@ def generate_animation(
89
112
 
90
113
  Examples
91
114
  --------
92
- >>> generate_animation(dataset["Derived_Number_Density_Electron"])
115
+ >>> dataset["Derived_Number_Density_Electron"].epoch.animate()
93
116
  """
94
117
  import matplotlib.pyplot as plt
95
118
  from matplotlib.animation import FuncAnimation
96
119
 
120
+ kwargs_original = kwargs.copy()
121
+
97
122
  if ax is None:
98
123
  _, ax = plt.subplots()
99
124
 
100
125
  N_frames = data["time"].size
101
- global_min, global_max = compute_global_limits(data)
126
+ global_min, global_max = compute_global_limits(data, min_percentile, max_percentile)
127
+
128
+ # Initialise plot and set y-limits for 1D data
129
+ if data.ndim == 2:
130
+ kwargs.setdefault("x", "X_Grid_mid")
131
+ plot = data.isel(time=0).plot(ax=ax, **kwargs)
132
+ ax.set_title(get_frame_title(data, 0, display_sdf_name, title))
133
+ ax.set_ylim(global_min, global_max)
102
134
 
103
- if is_2d(data):
135
+ # Initilise plot and set colour bar for 2D data
136
+ if data.ndim == 3:
104
137
  kwargs["norm"] = plt.Normalize(vmin=global_min, vmax=global_max)
105
138
  kwargs["add_colorbar"] = False
106
139
  # Set default x and y coordinates for 2D data if not provided
@@ -109,44 +142,32 @@ def generate_animation(
109
142
 
110
143
  # Initialize the plot with the first timestep
111
144
  plot = data.isel(time=0).plot(ax=ax, **kwargs)
112
- ax.set_title(get_frame_title(data, 0, display_sdf_name))
145
+ ax.set_title(get_frame_title(data, 0, display_sdf_name, title))
113
146
 
114
147
  # Add colorbar
115
- long_name = data.attrs.get("long_name")
116
- units = data.attrs.get("units")
117
- plt.colorbar(plot, ax=ax, label=f"{long_name} [${units}$]")
118
-
119
- # Initialise plo and set y-limits for 1D data
120
- if is_1d(data):
121
- plot = data.isel(time=0).plot(ax=ax, **kwargs)
122
- ax.set_title(get_frame_title(data, 0, display_sdf_name))
123
- ax.set_ylim(global_min, global_max)
148
+ if kwargs_original.get("add_colorbar", True):
149
+ long_name = data.attrs.get("long_name")
150
+ units = data.attrs.get("units")
151
+ plt.colorbar(plot, ax=ax, label=f"{long_name} [${units}$]")
124
152
 
153
+ # check if there is a moving window by finding NaNs in the data
154
+ move_window = np.isnan(np.sum(data.values))
125
155
  if move_window:
126
- window_velocity, window_initial_edge, time_since_start = (
127
- calculate_window_velocity_and_edges(data, kwargs["x"])
128
- )
129
-
130
- # User's choice for initial window edge supercides the one calculated
131
- if "xlim" in kwargs:
132
- window_initial_edge = kwargs["xlim"]
156
+ window_boundaries = calculate_window_boundaries(data, kwargs.get("xlim", False))
133
157
 
134
158
  def update(frame):
135
159
  # Set the xlim for each frame in the case of a moving window
136
160
  if move_window:
137
- kwargs["xlim"] = (
138
- window_initial_edge[0] + window_velocity * time_since_start[frame],
139
- window_initial_edge[1] * 0.99
140
- + window_velocity * time_since_start[frame],
141
- )
161
+ kwargs["xlim"] = window_boundaries[frame]
142
162
 
143
163
  # Update plot for the new frame
144
164
  ax.clear()
165
+
145
166
  data.isel(time=frame).plot(ax=ax, **kwargs)
146
- ax.set_title(get_frame_title(data, frame, display_sdf_name))
167
+ ax.set_title(get_frame_title(data, frame, display_sdf_name, title))
147
168
 
148
- # # Update y-limits for 1D data
149
- if is_1d(data):
169
+ # Update y-limits for 1D data
170
+ if data.ndim == 2:
150
171
  ax.set_ylim(global_min, global_max)
151
172
 
152
173
  return FuncAnimation(
@@ -181,4 +202,4 @@ class EpochAccessor:
181
202
  >>> ani = ds["Electric_Field_Ey"].epoch.animate()
182
203
  >>> ani.save("myfile.mp4")
183
204
  """
184
- return generate_animation(self._obj, *args, **kwargs)
205
+ return animate(self._obj, *args, **kwargs)
@@ -39,7 +39,7 @@ cdef class Block:
39
39
 
40
40
  @dataclasses.dataclass
41
41
  cdef class Variable(Block):
42
- units: tuple[str] | None
42
+ units: str | None
43
43
  mult: float | None
44
44
  grid: str | None
45
45
  grid_mid: str | None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: sdf-xarray
3
- Version: 0.2.2
3
+ Version: 0.2.4
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: Copyright 2024, Peter Hill, Joel Adams, epochpic team
@@ -32,11 +32,19 @@ License: Copyright 2024, Peter Hill, Joel Adams, epochpic team
32
32
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33
33
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
34
 
35
+ Classifier: Programming Language :: Python
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.10
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Programming Language :: Python :: 3.13
41
+ Classifier: Intended Audience :: Science/Research
42
+ Classifier: Topic :: Scientific/Engineering
43
+ Classifier: Operating System :: OS Independent
35
44
  Requires-Python: >=3.10
36
45
  Requires-Dist: numpy>=2.0.0
37
46
  Requires-Dist: xarray>=2024.1.0
38
47
  Requires-Dist: dask>=2024.7.1
39
- Requires-Dist: cython>=3.0
40
48
  Provides-Extra: docs
41
49
  Requires-Dist: sphinx>=5.3; extra == "docs"
42
50
  Requires-Dist: sphinx_autodoc_typehints>=1.19; extra == "docs"
@@ -67,9 +75,14 @@ Description-Content-Type: text/markdown
67
75
 
68
76
  # sdf-xarray
69
77
 
70
- ![PyPI](https://img.shields.io/pypi/v/sdf-xarray?color=blue)
78
+ ![Dynamic TOML Badge](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fepochpic%2Fsdf-xarray%2Frefs%2Fheads%2Fmain%2Fpyproject.toml&query=%24.project.requires-python&label=python&logo=python)
79
+ [![Available on PyPI](https://img.shields.io/pypi/v/sdf-xarray?color=blue&logo=pypi)](https://pypi.org/project/sdf-xarray/)
80
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.15351323.svg)](https://doi.org/10.5281/zenodo.15351323)
71
81
  ![Build/Publish](https://github.com/epochpic/sdf-xarray/actions/workflows/build_publish.yml/badge.svg)
72
82
  ![Tests](https://github.com/epochpic/sdf-xarray/actions/workflows/tests.yml/badge.svg)
83
+ [![Read the Docs](https://img.shields.io/readthedocs/sdf-xarray?logo=readthedocs&link=https%3A%2F%2Fsdf-xarray.readthedocs.io%2F)](https://sdf-xarray.readthedocs.io)
84
+ [![Formatted with black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black)
85
+
73
86
 
74
87
  sdf-xarray provides a backend for [xarray](https://xarray.dev) to read SDF files as created by
75
88
  [EPOCH](https://epochpic.github.io) using the [SDF-C](https://github.com/epochpic/SDF_C) library.
@@ -6,19 +6,19 @@ include/SDFC_14.4.7/sdf_list_type.h,sha256=Quu8v0-SEsQuJpGtEZnm09tAyXqWNitx0sXl5
6
6
  include/SDFC_14.4.7/sdf_vector_type.h,sha256=dbKjhzRRsvhzrnTwVjtVlvnuisEnRMKY-vvdm94ok_Q,1595
7
7
  include/SDFC_14.4.7/stack_allocator.h,sha256=L7U9vmGiVSw3VQLIv9EzTaVq7JbFxs9aNonKStTkUSg,1335
8
8
  include/SDFC_14.4.7/uthash.h,sha256=rIyy_-ylY6S_7WaZCCC3VtvXaC9q37rFyA0f1U9xc4w,63030
9
- lib/SDFC_14.4.7/sdfc.lib,sha256=aQ_GV-hDgqP7AbGSjuEpkj7Z5zpUd-C5_567JJeZ1yk,350410
9
+ lib/SDFC_14.4.7/sdfc.lib,sha256=DhpyA_nvLlofl1uM9klwcs3syjPQzAzBvhGIn7ioeho,350410
10
10
  lib/SDFC_14.4.7/SDFCConfig.cmake,sha256=IOA1eusC-KvUK4LNTEiOAmEdaPH1ZvNvbYPgiG1oZio,802
11
11
  lib/SDFC_14.4.7/SDFCConfigVersion.cmake,sha256=pN7Qqyf04s3izw7PYQ0XK6imvmhaVegSdR_nEl3Ok_o,2830
12
12
  lib/SDFC_14.4.7/SDFCTargets-release.cmake,sha256=G4zdx5PyjePigeD_a6rmZAxbk7L8Nf0klUnV78Lm2fI,828
13
13
  lib/SDFC_14.4.7/SDFCTargets.cmake,sha256=OVt1Gm8n7Ew4fiTmA9yHoef3vIIGwsXUZfqeG9p9Bys,4152
14
- sdf_xarray/__init__.py,sha256=fJz9_MEcXjGAuZusNY6S3CDZmCJOg_Gjpzl1R-hAfmw,17573
15
- sdf_xarray/_version.py,sha256=UC90NvqLUDpj_FE8q0wCmEOBTFqTc2wo52DnLF1e7lU,532
14
+ sdf_xarray/__init__.py,sha256=bPzOJwcKQAsx-mryOyxOCXHijA4nr5ayZWPRrMNNuMU,17642
15
+ sdf_xarray/_version.py,sha256=4HOJxLsEz0nPHMraGaR2KjWuypKklVh7NijIwVz3nSg,532
16
16
  sdf_xarray/csdf.pxd,sha256=ADPjAuHsodAvdOz96Z_XlFF7VL3KmVaXcTifWDP3rK0,4205
17
- sdf_xarray/plotting.py,sha256=pCKZ01sAT0VLEhnWrX6LN5OSJnzcIkRA3joN7f62giM,6238
18
- sdf_xarray/sdf_interface.cp312-win_amd64.pyd,sha256=X3q3Kpj9SyxEKI53c8V3gZ5ZPQmi1HZqTGjsq5d0xKg,379392
19
- sdf_xarray/sdf_interface.pyx,sha256=3XRFlrC1e_HkJrU8-i3fMz8DlyUxZgt9wTx_QkGE_TQ,11982
20
- sdf_xarray-0.2.2.dist-info/METADATA,sha256=z5fvCcdbezFWBQWZJXHAlzUf32R4_kKHjYsshvoue6I,8032
21
- sdf_xarray-0.2.2.dist-info/WHEEL,sha256=NwgjyrmVpVzZ-osOl_ZcT-QXHLFroWlAkN7IC9J-0Pg,106
22
- sdf_xarray-0.2.2.dist-info/entry_points.txt,sha256=gP7BIQpXNg6vIf7S7p-Rw_EJZTC1X50BsVTkK7dA7g0,57
23
- sdf_xarray-0.2.2.dist-info/licenses/LICENCE,sha256=aHWuyELjtzIL1jTXFHTbI3tr9vyVyhnw3I9_QYPdEX8,1515
24
- sdf_xarray-0.2.2.dist-info/RECORD,,
17
+ sdf_xarray/plotting.py,sha256=ze1paC1Uw42WOWspdqkyNsUviDt-Z7AwQlPyO7JB90o,7007
18
+ sdf_xarray/sdf_interface.cp312-win_amd64.pyd,sha256=dHhckvhBNazLGLM_R2geBlAoWZFcll1UGrKC5VW8h3Y,359424
19
+ sdf_xarray/sdf_interface.pyx,sha256=PFC6upg14OZBqiGInLgBoxztIIKBk-HOh3WC9Ro4YUw,11975
20
+ sdf_xarray-0.2.4.dist-info/METADATA,sha256=l0Gwyl2O5oSNQJ8yLOCcABSSd5VlCz9MAwo4GgHxcuQ,9129
21
+ sdf_xarray-0.2.4.dist-info/WHEEL,sha256=k3AOLK754NVb09U5k7PdROOgoHUP1Q4mZk2PKi14SoM,106
22
+ sdf_xarray-0.2.4.dist-info/entry_points.txt,sha256=gP7BIQpXNg6vIf7S7p-Rw_EJZTC1X50BsVTkK7dA7g0,57
23
+ sdf_xarray-0.2.4.dist-info/licenses/LICENCE,sha256=aHWuyELjtzIL1jTXFHTbI3tr9vyVyhnw3I9_QYPdEX8,1515
24
+ sdf_xarray-0.2.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: scikit-build-core 0.11.1
2
+ Generator: scikit-build-core 0.11.2
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp312-cp312-win_amd64
5
5