sdf-xarray 0.2.3__cp313-cp313-win_amd64.whl → 0.2.4__cp313-cp313-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/_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.3'
21
- __version_tuple__ = version_tuple = (0, 2, 3)
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.3
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
@@ -45,7 +45,6 @@ Requires-Python: >=3.10
45
45
  Requires-Dist: numpy>=2.0.0
46
46
  Requires-Dist: xarray>=2024.1.0
47
47
  Requires-Dist: dask>=2024.7.1
48
- Requires-Dist: cython>=3.0
49
48
  Provides-Extra: docs
50
49
  Requires-Dist: sphinx>=5.3; extra == "docs"
51
50
  Requires-Dist: sphinx_autodoc_typehints>=1.19; extra == "docs"
@@ -78,6 +77,7 @@ Description-Content-Type: text/markdown
78
77
 
79
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)
80
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)
81
81
  ![Build/Publish](https://github.com/epochpic/sdf-xarray/actions/workflows/build_publish.yml/badge.svg)
82
82
  ![Tests](https://github.com/epochpic/sdf-xarray/actions/workflows/tests.yml/badge.svg)
83
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)
@@ -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=8OW2nRnLaTs74FF0utW5k5GYWxLTw0zylpcV19OY8W4,350410
9
+ lib/SDFC_14.4.7/sdfc.lib,sha256=YxwOVNVfavyYxQmoH_aoLi3VGSkJPZa0Pr6KRjephd4,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
14
  sdf_xarray/__init__.py,sha256=bPzOJwcKQAsx-mryOyxOCXHijA4nr5ayZWPRrMNNuMU,17642
15
- sdf_xarray/_version.py,sha256=MDT1IkZtH-Xmev6HiMN7i_ymWczLZC-vKGfO8zcq6q0,532
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.cp313-win_amd64.pyd,sha256=Cfn4s74qggrSCiWuOAeJXlWqT77NALvu-HPjAIITk0A,378368
19
- sdf_xarray/sdf_interface.pyx,sha256=3XRFlrC1e_HkJrU8-i3fMz8DlyUxZgt9wTx_QkGE_TQ,11982
20
- sdf_xarray-0.2.3.dist-info/METADATA,sha256=lqXX5Gbv6Owmrsit-iN9e4-OoJMLZDV2huiixewB-ik,9048
21
- sdf_xarray-0.2.3.dist-info/WHEEL,sha256=sy0UFHejwb8HKZF4TZKxRLz0uqFoYZA7Xu3tB1pL5yA,106
22
- sdf_xarray-0.2.3.dist-info/entry_points.txt,sha256=gP7BIQpXNg6vIf7S7p-Rw_EJZTC1X50BsVTkK7dA7g0,57
23
- sdf_xarray-0.2.3.dist-info/licenses/LICENCE,sha256=aHWuyELjtzIL1jTXFHTbI3tr9vyVyhnw3I9_QYPdEX8,1515
24
- sdf_xarray-0.2.3.dist-info/RECORD,,
17
+ sdf_xarray/plotting.py,sha256=ze1paC1Uw42WOWspdqkyNsUviDt-Z7AwQlPyO7JB90o,7007
18
+ sdf_xarray/sdf_interface.cp313-win_amd64.pyd,sha256=imqVtqkBGrAc5gH9M8dHnNCjMl5c5w5oG4VNSojK7rM,357888
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=sy0UFHejwb8HKZF4TZKxRLz0uqFoYZA7Xu3tB1pL5yA,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,,