sdf-xarray 0.4.0__cp314-cp314t-macosx_11_0_arm64.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.
- include/SDFC_14.4.7/sdf.h +804 -0
- include/SDFC_14.4.7/sdf_derived.h +34 -0
- include/SDFC_14.4.7/sdf_extension.h +36 -0
- include/SDFC_14.4.7/sdf_helper.h +46 -0
- include/SDFC_14.4.7/sdf_list_type.h +68 -0
- include/SDFC_14.4.7/sdf_vector_type.h +68 -0
- include/SDFC_14.4.7/stack_allocator.h +49 -0
- include/SDFC_14.4.7/uthash.h +963 -0
- lib/SDFC_14.4.7/SDFCConfig.cmake +18 -0
- lib/SDFC_14.4.7/SDFCConfigVersion.cmake +65 -0
- lib/SDFC_14.4.7/SDFCTargets-release.cmake +19 -0
- lib/SDFC_14.4.7/SDFCTargets.cmake +105 -0
- lib/SDFC_14.4.7/libsdfc.a +0 -0
- sdf_xarray/__init__.py +645 -0
- sdf_xarray/_version.py +34 -0
- sdf_xarray/csdf.pxd +127 -0
- sdf_xarray/dataset_accessor.py +71 -0
- sdf_xarray/download.py +87 -0
- sdf_xarray/plotting.py +293 -0
- sdf_xarray/sdf_interface.cpython-314t-darwin.so +0 -0
- sdf_xarray/sdf_interface.pyx +342 -0
- sdf_xarray-0.4.0.dist-info/METADATA +151 -0
- sdf_xarray-0.4.0.dist-info/RECORD +26 -0
- sdf_xarray-0.4.0.dist-info/WHEEL +6 -0
- sdf_xarray-0.4.0.dist-info/entry_points.txt +3 -0
- sdf_xarray-0.4.0.dist-info/licenses/LICENCE +28 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
cimport csdf
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
import re
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
from libc.string cimport memcpy
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
cimport numpy as cnp
|
|
12
|
+
|
|
13
|
+
cnp.import_array()
|
|
14
|
+
|
|
15
|
+
# Some systems don't have F128
|
|
16
|
+
_NUMPY_128 = getattr(np, "float128", None)
|
|
17
|
+
|
|
18
|
+
cdef list[cnp.dtype | None] _sdf_type_mapping = [
|
|
19
|
+
None,
|
|
20
|
+
cnp.dtype(np.int32),
|
|
21
|
+
cnp.dtype(np.int64),
|
|
22
|
+
cnp.dtype(np.float32),
|
|
23
|
+
cnp.dtype(np.float64),
|
|
24
|
+
_NUMPY_128,
|
|
25
|
+
cnp.dtype("S"),
|
|
26
|
+
cnp.dtype(np.bool_),
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclasses.dataclass
|
|
31
|
+
cdef class Block:
|
|
32
|
+
_id: str
|
|
33
|
+
name: str
|
|
34
|
+
dtype: np.dtype
|
|
35
|
+
shape: tuple[int]
|
|
36
|
+
is_point_data: bool
|
|
37
|
+
sdffile: SDFFile | None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclasses.dataclass
|
|
41
|
+
cdef class Variable(Block):
|
|
42
|
+
units: str | None
|
|
43
|
+
mult: float | None
|
|
44
|
+
grid: str | None
|
|
45
|
+
grid_mid: str | None
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def data(self) -> np.ndarray :
|
|
49
|
+
"""Read a variable from the file, returning numpy array
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
return self.sdffile.read(self)
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
cdef Variable from_block(str name, csdf.sdf_block_t* block, SDFFile sdffile):
|
|
56
|
+
return Variable(
|
|
57
|
+
_id=block.id.decode("UTF-8"),
|
|
58
|
+
name=name,
|
|
59
|
+
dtype=_sdf_type_mapping[block.datatype_out],
|
|
60
|
+
shape=tuple(block.dims[i] for i in range(block.ndims)),
|
|
61
|
+
units=block.units.decode("UTF-8") if block.units else None,
|
|
62
|
+
mult=block.mult if block.mult else None,
|
|
63
|
+
grid=block.mesh_id.decode("UTF-8") if block.mesh_id else None,
|
|
64
|
+
grid_mid=f"{block.mesh_id.decode('UTF-8')}_mid" if block.mesh_id else None,
|
|
65
|
+
is_point_data=block.blocktype == csdf.SDF_BLOCKTYPE_POINT_VARIABLE,
|
|
66
|
+
sdffile=sdffile,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclasses.dataclass
|
|
71
|
+
cdef class Mesh(Block):
|
|
72
|
+
units: tuple[str]
|
|
73
|
+
labels: tuple[str]
|
|
74
|
+
mults: tuple[float] | None
|
|
75
|
+
parent: Mesh | None = None
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def data(self) -> tuple[np.ndarray]:
|
|
79
|
+
"""Read a variable from the file, returning numpy array
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
return self.sdffile.read(self)
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
cdef Mesh from_block(str name, csdf.sdf_block_t* block, SDFFile sdffile):
|
|
86
|
+
return Mesh(
|
|
87
|
+
_id=block.id.decode("UTF-8"),
|
|
88
|
+
name=name,
|
|
89
|
+
dtype=_sdf_type_mapping[block.datatype_out],
|
|
90
|
+
shape=tuple(block.dims[i] for i in range(block.ndims)),
|
|
91
|
+
units=tuple(
|
|
92
|
+
block.dim_units[i].decode("UTF-8") for i in range(block.ndims)
|
|
93
|
+
),
|
|
94
|
+
labels=tuple(
|
|
95
|
+
block.dim_labels[i].decode("UTF-8") for i in range(block.ndims)
|
|
96
|
+
),
|
|
97
|
+
mults=(
|
|
98
|
+
tuple(block.dim_mults[i] for i in range(block.ndims))
|
|
99
|
+
if block.dim_mults
|
|
100
|
+
else None
|
|
101
|
+
),
|
|
102
|
+
is_point_data=block.blocktype == csdf.SDF_BLOCKTYPE_POINT_MESH,
|
|
103
|
+
sdffile=sdffile,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
_CONSTANT_UNITS_RE = re.compile(r"(?P<name>.*) \((?P<units>.*)\)$")
|
|
108
|
+
|
|
109
|
+
@dataclasses.dataclass
|
|
110
|
+
cdef class Constant:
|
|
111
|
+
_id: str
|
|
112
|
+
name: str
|
|
113
|
+
data: int | str | float | bool
|
|
114
|
+
units: str | None
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
cdef Constant from_block(str name, csdf.sdf_block_t* block):
|
|
118
|
+
data: int | str | float | double | bool
|
|
119
|
+
|
|
120
|
+
if block.datatype == csdf.SDF_DATATYPE_REAL4:
|
|
121
|
+
data = (<float*>block.const_value)[0]
|
|
122
|
+
elif block.datatype == csdf.SDF_DATATYPE_REAL8:
|
|
123
|
+
data = (<double*>block.const_value)[0]
|
|
124
|
+
if block.datatype == csdf.SDF_DATATYPE_INTEGER4:
|
|
125
|
+
data = (<csdf.int32_t*>block.const_value)[0]
|
|
126
|
+
if block.datatype == csdf.SDF_DATATYPE_INTEGER8:
|
|
127
|
+
data = (<csdf.int64_t*>block.const_value)[0]
|
|
128
|
+
if block.datatype == csdf.SDF_DATATYPE_LOGICAL:
|
|
129
|
+
data = (<bint*>block.const_value)[0]
|
|
130
|
+
|
|
131
|
+
# There's no metadata with e.g. units, but there's a
|
|
132
|
+
# convention to put one in brackets at the end of the name,
|
|
133
|
+
# if so, strip it off to give the name and units
|
|
134
|
+
units = None
|
|
135
|
+
if match := _CONSTANT_UNITS_RE.match(name):
|
|
136
|
+
name = match["name"]
|
|
137
|
+
units = match["units"]
|
|
138
|
+
|
|
139
|
+
return Constant(
|
|
140
|
+
_id=block.id.decode("UTF-8"), name=name, data=data, units=units
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def is_point_data(self) -> bool:
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
cdef class SDFFile:
|
|
149
|
+
"""Read an SDF file
|
|
150
|
+
|
|
151
|
+
Attributes
|
|
152
|
+
----------
|
|
153
|
+
header: dict
|
|
154
|
+
File metadata
|
|
155
|
+
run_info: dict
|
|
156
|
+
More metadata
|
|
157
|
+
variables: dict[str, Variable]
|
|
158
|
+
Mapping of variable name to metadata
|
|
159
|
+
grids: dict[str, Mesh]
|
|
160
|
+
Mapping of grid ID to metadata
|
|
161
|
+
|
|
162
|
+
"""
|
|
163
|
+
cdef csdf.sdf_file_t* _c_sdf_file
|
|
164
|
+
cdef public str filename
|
|
165
|
+
cdef public dict header, run_info
|
|
166
|
+
cdef public dict[str, Variable] variables
|
|
167
|
+
cdef public dict[str, Mesh] grids
|
|
168
|
+
|
|
169
|
+
def __cinit__(self, filename: str):
|
|
170
|
+
self._c_sdf_file = csdf.sdf_open(
|
|
171
|
+
filename.encode("UTF-8"), 0, csdf.SDF_READ, False
|
|
172
|
+
)
|
|
173
|
+
if self._c_sdf_file == NULL:
|
|
174
|
+
raise IOError(f"Failed to open SDF file '{filename}'")
|
|
175
|
+
|
|
176
|
+
csdf.sdf_stack_init(self._c_sdf_file)
|
|
177
|
+
csdf.sdf_read_blocklist_all(self._c_sdf_file)
|
|
178
|
+
|
|
179
|
+
self.header = {
|
|
180
|
+
"filename": filename,
|
|
181
|
+
"file_version": self._c_sdf_file.file_version,
|
|
182
|
+
"file_revision": self._c_sdf_file.file_revision,
|
|
183
|
+
"code_name": self._c_sdf_file.code_name.decode("UTF-8"),
|
|
184
|
+
"step": self._c_sdf_file.step,
|
|
185
|
+
"time": self._c_sdf_file.time,
|
|
186
|
+
"jobid1": self._c_sdf_file.jobid1,
|
|
187
|
+
"jobid2": self._c_sdf_file.jobid2,
|
|
188
|
+
"code_io_version": self._c_sdf_file.code_io_version,
|
|
189
|
+
"restart_flag": bool(self._c_sdf_file.restart_flag),
|
|
190
|
+
"other_domains": bool(self._c_sdf_file.other_domains),
|
|
191
|
+
"station_file": bool(self._c_sdf_file.station_file),
|
|
192
|
+
}
|
|
193
|
+
self._read_variable_metadata()
|
|
194
|
+
|
|
195
|
+
cdef _read_variable_metadata(self):
|
|
196
|
+
cdef csdf.sdf_block_t* block = self._c_sdf_file.blocklist
|
|
197
|
+
cdef csdf.run_info* run = NULL
|
|
198
|
+
|
|
199
|
+
self.variables = {}
|
|
200
|
+
self.grids = {}
|
|
201
|
+
|
|
202
|
+
for i in range(self._c_sdf_file.nblocks):
|
|
203
|
+
name = block.name.decode("UTF-8")
|
|
204
|
+
|
|
205
|
+
if block.blocktype == csdf.SDF_BLOCKTYPE_RUN_INFO:
|
|
206
|
+
run = <csdf.run_info*>block.data
|
|
207
|
+
self.run_info = {
|
|
208
|
+
"version": f"{run.version}.{run.revision}.{run.minor_rev}",
|
|
209
|
+
"commit_id": run.commit_id.decode("UTF-8"),
|
|
210
|
+
"sha1sum": run.sha1sum.decode("UTF-8"),
|
|
211
|
+
"compile_machine": run.compile_machine.decode("UTF-8"),
|
|
212
|
+
"compile_flags": run.compile_flags.decode("UTF-8"),
|
|
213
|
+
"defines": f"{run.defines}",
|
|
214
|
+
"compile_date": time.ctime(run.compile_date),
|
|
215
|
+
"run_date": time.ctime(run.run_date),
|
|
216
|
+
"io_date": time.ctime(run.io_date),
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
elif block.blocktype == csdf.SDF_BLOCKTYPE_CONSTANT:
|
|
220
|
+
# We modify the name to remove units, so convert it
|
|
221
|
+
# first so we can get the new name
|
|
222
|
+
constant = Constant.from_block(name, block)
|
|
223
|
+
self.variables[constant.name] = constant
|
|
224
|
+
|
|
225
|
+
elif block.blocktype in (
|
|
226
|
+
csdf.SDF_BLOCKTYPE_PLAIN_MESH,
|
|
227
|
+
csdf.SDF_BLOCKTYPE_POINT_MESH,
|
|
228
|
+
csdf.SDF_BLOCKTYPE_LAGRANGIAN_MESH
|
|
229
|
+
):
|
|
230
|
+
grid_id = block.id.decode("UTF-8")
|
|
231
|
+
self.grids[grid_id] = Mesh.from_block(name, block, self)
|
|
232
|
+
|
|
233
|
+
if block.blocktype != csdf.SDF_BLOCKTYPE_POINT_MESH:
|
|
234
|
+
# Make the corresponding grid at mid-points, except for
|
|
235
|
+
# particle grids
|
|
236
|
+
mid_grid_block = Mesh.from_block(f"{name}_mid", block, self)
|
|
237
|
+
mid_grid_block.shape = tuple(
|
|
238
|
+
dim - 1 for dim in mid_grid_block.shape if dim > 1
|
|
239
|
+
)
|
|
240
|
+
mid_grid_block.parent = self.grids[grid_id]
|
|
241
|
+
self.grids[f"{grid_id}_mid"] = mid_grid_block
|
|
242
|
+
|
|
243
|
+
elif block.blocktype in (
|
|
244
|
+
csdf.SDF_BLOCKTYPE_PLAIN_VARIABLE,
|
|
245
|
+
csdf.SDF_BLOCKTYPE_PLAIN_DERIVED,
|
|
246
|
+
csdf.SDF_BLOCKTYPE_POINT_VARIABLE,
|
|
247
|
+
csdf.SDF_BLOCKTYPE_POINT_DERIVED,
|
|
248
|
+
csdf.SDF_BLOCKTYPE_ARRAY,
|
|
249
|
+
):
|
|
250
|
+
# If the block doesn't have a datatype, that probably
|
|
251
|
+
# means its actually a grid dimension
|
|
252
|
+
if block.datatype_out != 0:
|
|
253
|
+
self.variables[name] = Variable.from_block(name, block, self)
|
|
254
|
+
|
|
255
|
+
block = block.next
|
|
256
|
+
|
|
257
|
+
cpdef read(self, var: Block):
|
|
258
|
+
"""Read a variable from the file, returning numpy array
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
if self._c_sdf_file is NULL:
|
|
262
|
+
raise RuntimeError(
|
|
263
|
+
f"Can't read '{var.name}', file '{self.filename}' is closed"
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
is_mesh: bool = isinstance(var, Mesh)
|
|
267
|
+
|
|
268
|
+
# Has a parent block, so we need to average node data to midpoint
|
|
269
|
+
if is_mesh and var.parent:
|
|
270
|
+
return self._read_mid_grid(var)
|
|
271
|
+
|
|
272
|
+
cdef csdf.sdf_block_t* block = csdf.sdf_find_block_by_name(
|
|
273
|
+
self._c_sdf_file, var.name.encode("utf-8")
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
if block is NULL:
|
|
277
|
+
raise RuntimeError(f"Could not read variable '{var.name}'")
|
|
278
|
+
|
|
279
|
+
self._c_sdf_file.current_block = block
|
|
280
|
+
csdf.sdf_helper_read_data(self._c_sdf_file, block)
|
|
281
|
+
|
|
282
|
+
if is_mesh:
|
|
283
|
+
# Meshes store the data for separate dimensions in block.grids
|
|
284
|
+
if not (block.grids is not NULL and block.grids[0] is not NULL):
|
|
285
|
+
raise RuntimeError(f"Could not read variable '{var.name}'")
|
|
286
|
+
|
|
287
|
+
data = []
|
|
288
|
+
for i, dim in enumerate(var.shape):
|
|
289
|
+
data.append(
|
|
290
|
+
self._make_array((dim,), var.dtype, block.grids[i])
|
|
291
|
+
)
|
|
292
|
+
return tuple(data)
|
|
293
|
+
|
|
294
|
+
# Normal variables
|
|
295
|
+
return self._make_array(var.shape, var.dtype, block.data)
|
|
296
|
+
|
|
297
|
+
cdef _read_mid_grid(self, mesh: Mesh):
|
|
298
|
+
"""Read a midpoint grid"""
|
|
299
|
+
|
|
300
|
+
data = []
|
|
301
|
+
for dim in mesh.parent.data:
|
|
302
|
+
if len(dim.shape) == 1:
|
|
303
|
+
mid_point = (dim[:-1] + dim[1:]) / 2
|
|
304
|
+
elif len(dim.shape) == 2:
|
|
305
|
+
mid_point = (
|
|
306
|
+
dim[:-1, :-1] + dim[1:, :-1] + dim[:-1, 1:] + dim[1:, 1:]
|
|
307
|
+
) / 4
|
|
308
|
+
else:
|
|
309
|
+
raise ValueError(
|
|
310
|
+
f"Unexpected number of dimensions reading mesh '{mesh.name}' "
|
|
311
|
+
f"(expected 1 or 2, got {len(dim.shape)})"
|
|
312
|
+
)
|
|
313
|
+
data.append(mid_point)
|
|
314
|
+
return tuple(data)
|
|
315
|
+
|
|
316
|
+
cdef cnp.ndarray _make_array(
|
|
317
|
+
self, tuple[int, ...] dims, cnp.dtype dtype, void* data
|
|
318
|
+
):
|
|
319
|
+
"""Helper function for making Numpy arrays from data allocated elsewhere"""
|
|
320
|
+
# This is not as efficient as it could be -- we should be able to steal
|
|
321
|
+
# the block's data, but I've not worked out to do that properly
|
|
322
|
+
# yet. This is correct at least, and means we don't need to worry about
|
|
323
|
+
# freeing the memory. Can't just use PyArray_NewFromDescr because this
|
|
324
|
+
# isn't available from Cython. Might be able to use one of the other
|
|
325
|
+
# low-level creation routines?
|
|
326
|
+
var_array = np.empty(dims, dtype=dtype, order="F")
|
|
327
|
+
memcpy(cnp.PyArray_DATA(var_array), data, var_array.nbytes)
|
|
328
|
+
|
|
329
|
+
return var_array
|
|
330
|
+
|
|
331
|
+
def close(self):
|
|
332
|
+
if self._c_sdf_file is NULL:
|
|
333
|
+
return
|
|
334
|
+
csdf.sdf_stack_destroy(self._c_sdf_file)
|
|
335
|
+
csdf.sdf_close(self._c_sdf_file)
|
|
336
|
+
self._c_sdf_file = NULL
|
|
337
|
+
|
|
338
|
+
def __enter__(self):
|
|
339
|
+
return self
|
|
340
|
+
|
|
341
|
+
def __exit__(self, exc_type, exc_value, exc_tb):
|
|
342
|
+
self.close()
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sdf-xarray
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Provides a backend for xarray to read SDF files as created by the EPOCH plasma PIC code.
|
|
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>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
8
|
+
Classifier: Intended Audience :: Science/Research
|
|
9
|
+
Classifier: Topic :: Scientific/Engineering
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Requires-Python: <3.15,>=3.10
|
|
19
|
+
Requires-Dist: numpy>=2.0.0
|
|
20
|
+
Requires-Dist: xarray>=2024.1.0
|
|
21
|
+
Requires-Dist: dask>=2024.7.1
|
|
22
|
+
Provides-Extra: jupyter
|
|
23
|
+
Requires-Dist: dask[diagnostics]; extra == "jupyter"
|
|
24
|
+
Requires-Dist: ipykernel>=6.29.5; extra == "jupyter"
|
|
25
|
+
Provides-Extra: pint
|
|
26
|
+
Requires-Dist: pint; extra == "pint"
|
|
27
|
+
Requires-Dist: pint-xarray; extra == "pint"
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# sdf-xarray
|
|
31
|
+
|
|
32
|
+

|
|
33
|
+
[](https://pypi.org/project/sdf-xarray/)
|
|
34
|
+
[](https://doi.org/10.5281/zenodo.15351323)
|
|
35
|
+

|
|
36
|
+

|
|
37
|
+
[](https://sdf-xarray.readthedocs.io)
|
|
38
|
+
[](https://github.com/python/black)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
sdf-xarray provides a backend for [xarray](https://xarray.dev) to read SDF files as created by
|
|
42
|
+
[EPOCH](https://epochpic.github.io) using the [SDF-C](https://github.com/epochpic/SDF_C) library.
|
|
43
|
+
Part of [BEAM](#broad-epoch-analysis-modules-beam) (Broad EPOCH Analysis Modules).
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
> [!IMPORTANT]
|
|
48
|
+
> To install this package make sure you are using one of the Python versions listed above.
|
|
49
|
+
|
|
50
|
+
Install from PyPI with:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install sdf-xarray
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
or download this code locally:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git clone --recursive https://github.com/epochpic/sdf-xarray.git
|
|
60
|
+
cd sdf-xarray
|
|
61
|
+
pip install .
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
We recommend switching to [uv](https://docs.astral.sh/uv/) to manage packages.
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
Below are some simple examples to get you started. Please read the full
|
|
69
|
+
documentation here <https://sdf-xarray.readthedocs.io>.
|
|
70
|
+
|
|
71
|
+
### Single file loading
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
import xarray as xr
|
|
75
|
+
|
|
76
|
+
df = xr.open_dataset("0010.sdf")
|
|
77
|
+
|
|
78
|
+
print(df["Electric_Field_Ex"])
|
|
79
|
+
|
|
80
|
+
# <xarray.DataArray 'Electric_Field_Ex' (X_x_px_deltaf_electron_beam: 16)> Size: 128B
|
|
81
|
+
# [16 values with dtype=float64]
|
|
82
|
+
# Coordinates:
|
|
83
|
+
# * X_x_px_deltaf_electron_beam (X_x_px_deltaf_electron_beam) float64 128B 1...
|
|
84
|
+
# Attributes:
|
|
85
|
+
# units: V/m
|
|
86
|
+
# full_name: "Electric Field/Ex"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Multi-file loading
|
|
90
|
+
|
|
91
|
+
You can open all the SDF files for a given simulation by calling the `open_mfdataset`
|
|
92
|
+
function from `sdf_xarray`. This will additionally add a time dimension using the `"time"`
|
|
93
|
+
value stored in each files attributes.
|
|
94
|
+
|
|
95
|
+
> [!IMPORTANT]
|
|
96
|
+
> If your simulation has multiple `output` blocks so that not all variables are
|
|
97
|
+
> output at every time step, then at the timesteps where those variables are not
|
|
98
|
+
> present they will have have a value of nan. To clean your dataset by removing
|
|
99
|
+
> these nan values we suggest using the `xarray.DataArray.dropna` function or
|
|
100
|
+
> loading sparse data along separate time dimensions using `separate_times=True`.
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from sdf_xarray import open_mfdataset
|
|
104
|
+
|
|
105
|
+
ds = open_mfdataset("*.sdf")
|
|
106
|
+
print(ds)
|
|
107
|
+
|
|
108
|
+
# Dimensions:
|
|
109
|
+
# time: 301, X_Grid_mid: 128, ...
|
|
110
|
+
# Coordinates: (9) ...
|
|
111
|
+
# Data variables: (18) ...
|
|
112
|
+
# Indexes: (9) ...
|
|
113
|
+
# Attributes: (22) ...
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Citing
|
|
117
|
+
|
|
118
|
+
If sdf-xarray contributes to a project that leads to publication, please acknowledge this by citing sdf-xarray. This can be done by clicking the "cite this repository" button located near the top right of this page.
|
|
119
|
+
|
|
120
|
+
## Contributing
|
|
121
|
+
|
|
122
|
+
We welcome contributions to the BEAM ecosystem! Whether it's reporting issues, suggesting features, or submitting pull requests, your input helps improve these tools for the community.
|
|
123
|
+
|
|
124
|
+
### How to Contribute
|
|
125
|
+
|
|
126
|
+
There are many ways to get involved:
|
|
127
|
+
- **Report bugs**: Found something not working as expected? Open an issue with as much detail as possible.
|
|
128
|
+
- **Request a feature**: Got an idea for a new feature or enhancement? Open a feature request on [GitHub Issues](https://github.com/epochpic/sdf-xarray/issues)!
|
|
129
|
+
- **Improve the documentation**: We aim to keep our docs clear and helpful—if something's missing or unclear, feel free to suggest edits.
|
|
130
|
+
- **Submit code changes**: Bug fixes, refactoring, or new features are welcome.
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
All code is automatically linted, formatted, and tested via GitHub Actions.
|
|
134
|
+
|
|
135
|
+
To run checks locally before opening a pull request, see [CONTRIBUTING.md](CONTRIBUTING.md) or [readthedocs documentation](https://sdf-xarray.readthedocs.io/en/latest/contributing.html)
|
|
136
|
+
|
|
137
|
+
## Broad EPOCH Analysis Modules (BEAM)
|
|
138
|
+
|
|
139
|
+

|
|
140
|
+
|
|
141
|
+
**BEAM** is a collection of independent yet complementary open-source tools for analysing EPOCH simulations, designed to be modular so researchers can adopt only the components they require without being constrained by a rigid framework. In line with the **FAIR principles — Findable**, **Accessible**, **Interoperable**, and **Reusable** — each package is openly published with clear documentation and versioning (Findable), distributed via public repositories (Accessible), designed to follow common standards for data structures and interfaces (Interoperable), and includes licensing and metadata to support long-term use and adaptation (Reusable). The packages are as follows:
|
|
142
|
+
|
|
143
|
+
- [sdf-xarray](https://github.com/epochpic/sdf-xarray): Reading and processing SDF files and converting them to [xarray](https://docs.xarray.dev/en/stable/).
|
|
144
|
+
- [epydeck](https://github.com/epochpic/epydeck): Input deck reader and writer.
|
|
145
|
+
- [epyscan](https://github.com/epochpic/epyscan): Create campaigns over a given parameter space using various sampling methods.
|
|
146
|
+
|
|
147
|
+
## PlasmaFAIR
|
|
148
|
+
|
|
149
|
+

|
|
150
|
+
|
|
151
|
+
Originally developed by [PlasmaFAIR](https://plasmafair.github.io), EPSRC Grant EP/V051822/1
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
include/SDFC_14.4.7/sdf_derived.h,sha256=VqRsCmjmSRIOGqZSiPMsvmNaFBoNzXhrlKeyWGYIzm8,636
|
|
2
|
+
include/SDFC_14.4.7/sdf_helper.h,sha256=UD291Xf8ZUeh34iv-qLN-u8f5vPQz0joTFxIQWUIVYA,1020
|
|
3
|
+
include/SDFC_14.4.7/sdf_vector_type.h,sha256=5tcQcF7zDm7kTkLCzm12G5Z3ARVXvw7-XpKQ6WWej6U,1527
|
|
4
|
+
include/SDFC_14.4.7/stack_allocator.h,sha256=dwR5vISInhS-qtN2HF3_8CVlnHJgWKMZxEn9RiMGjuM,1286
|
|
5
|
+
include/SDFC_14.4.7/sdf_extension.h,sha256=my_ze10hSGfWEQEaUWh8TxFuVsiAl-g6aDn6qB2QQUY,1136
|
|
6
|
+
include/SDFC_14.4.7/uthash.h,sha256=BN_4JfPHloW0kI3VRhvCqP8D2VcvD0HpSLgo4MBz6Sk,62067
|
|
7
|
+
include/SDFC_14.4.7/sdf.h,sha256=8RWP2yH8xn_sCcbqZPT443LBaPy_3SHjOxA6IL4xLzE,24996
|
|
8
|
+
include/SDFC_14.4.7/sdf_list_type.h,sha256=WEq5Qmtv00-A7lmLadxC7RbFy46yLYRgg0LynCGk4PE,1350
|
|
9
|
+
lib/SDFC_14.4.7/libsdfc.a,sha256=B_y_GclgdE0xdSv7uUxlENCg1xoKQ_7O20UMdNwyi94,601592
|
|
10
|
+
lib/SDFC_14.4.7/SDFCTargets-release.cmake,sha256=Xq-QJnH563tMRWlM5o2wdMSm9EiSKXru4U4hlR-eXlo,811
|
|
11
|
+
lib/SDFC_14.4.7/SDFCTargets.cmake,sha256=clkZz74QIZf_Fi28OhBWw6bM4D2ljPzytSapo0xz2r0,4046
|
|
12
|
+
lib/SDFC_14.4.7/SDFCConfig.cmake,sha256=QQOXXHTYSEzXXckN8CCi9vuhMmirf2MrOA8b3tY4m2A,784
|
|
13
|
+
lib/SDFC_14.4.7/SDFCConfigVersion.cmake,sha256=2dR0eWIHiMKSdOwWBD9kI6EA-1mvVZsn61d50hSazkE,2765
|
|
14
|
+
sdf_xarray/_version.py,sha256=l7h0kVANk_zkMPjiVLsVjseleQV1Jro7H3JphPhrob0,712
|
|
15
|
+
sdf_xarray/plotting.py,sha256=HHLZISdFaiafTlRrTRshwT4FdRn5Z6VWIyU8vAwm_J0,9257
|
|
16
|
+
sdf_xarray/download.py,sha256=5rY5TPXxTZr4B_0OQHgur2Knerg4OwSD4E5dboFAycg,3036
|
|
17
|
+
sdf_xarray/dataset_accessor.py,sha256=ziVMAunCxSvIsJuETt8dSYAVXQkBSX8mOmipxvNFW0s,2379
|
|
18
|
+
sdf_xarray/sdf_interface.pyx,sha256=6wnODbj5Gi4IinwL-EXTPfcxJGtN6Nm9BQ7R88xwkrs,11754
|
|
19
|
+
sdf_xarray/__init__.py,sha256=vHfy048FKTbrgyyQJ1QN6hzgxfH42oUuwnlvX5JHON0,23584
|
|
20
|
+
sdf_xarray/sdf_interface.cpython-314t-darwin.so,sha256=yjURK6RR0bfktkuUlQE8c3EOOjdeTNhrClTdtFVaBlg,419384
|
|
21
|
+
sdf_xarray/csdf.pxd,sha256=6VCmPTbvKOQt3O-r1R1Gj-GvQJ2pOUeSRnkxgMLBvy0,4078
|
|
22
|
+
sdf_xarray-0.4.0.dist-info/RECORD,,
|
|
23
|
+
sdf_xarray-0.4.0.dist-info/WHEEL,sha256=FQvoGV7U_hR0JELuWQHk3LhwiQiBfE7EgRQx8v3hXSQ,142
|
|
24
|
+
sdf_xarray-0.4.0.dist-info/entry_points.txt,sha256=gP7BIQpXNg6vIf7S7p-Rw_EJZTC1X50BsVTkK7dA7g0,57
|
|
25
|
+
sdf_xarray-0.4.0.dist-info/METADATA,sha256=9EoUJ22i_txDaZAX6ZIgnaQdogVv88M-L2VFIZDHofQ,6921
|
|
26
|
+
sdf_xarray-0.4.0.dist-info/licenses/LICENCE,sha256=0FfLVWQRQERtj0vUKjJk7-UrDp-t70Et9izWejGe4Os,1487
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Copyright 2024, Peter Hill, Joel Adams, epochpic team
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
|
4
|
+
modification, are permitted provided that the following conditions are
|
|
5
|
+
met:
|
|
6
|
+
|
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
notice, this list of conditions and the following disclaimer in the
|
|
12
|
+
documentation and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
15
|
+
contributors may be used to endorse or promote products derived from
|
|
16
|
+
this software without specific prior written permission.
|
|
17
|
+
|
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
19
|
+
“AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
20
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
21
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
22
|
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
23
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
24
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
25
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
26
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
27
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|