flekspy 0.1.0__py3-none-any.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.
flekspy/__init__.py ADDED
@@ -0,0 +1,39 @@
1
+ """
2
+ flekspy Public API.
3
+ """
4
+
5
+ import glob
6
+ import os
7
+ import errno
8
+ from flekspy.idl.idl_format_data import IDLData
9
+ from flekspy.yt.yt_interface import FLEKSData
10
+ from flekspy.tp.test_particles import FLEKSTP
11
+
12
+
13
+ def load(filename: str, iDomain=0, iSpecies=0, readFieldData=False):
14
+ """Load FLEKS data.
15
+
16
+ Args:
17
+ filename (str): Input file name.
18
+ iDomain (int, optional): Test particle domain index. Defaults to 0.
19
+ iSpecies (int, optional): Test particle species index. Defaults to 0.
20
+ readFieldData (bool, optional): Whether or not to read field data for test particles. Defaults to False.
21
+
22
+ Returns:
23
+ FLEKS data: IDLData, FLEKSData, or FLEKSTP
24
+ """
25
+ files = glob.glob(filename)
26
+ if len(files) == 0:
27
+ raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), files)
28
+ filename = files[0]
29
+
30
+ basename = os.path.basename(os.path.normpath(filename))
31
+
32
+ if basename == "test_particles":
33
+ return FLEKSTP(filename, iDomain=iDomain, iSpecies=iSpecies)
34
+ elif basename.find(".") != -1 and basename.split(".")[-1] in ["out", "outs"]:
35
+ return IDLData(filename)
36
+ elif basename[-6:] == "_amrex":
37
+ return FLEKSData(filename, readFieldData)
38
+ else:
39
+ raise Exception("Error: unknown file format!")
flekspy/flekspy.py ADDED
@@ -0,0 +1,2 @@
1
+ if __name__ == "__main__":
2
+ print("Running from CLI...")
File without changes
@@ -0,0 +1,564 @@
1
+ import glob
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ import struct
5
+ import math
6
+ import flekspy.util.utilities as utilities
7
+ import yt
8
+ import flekspy.util.data_container as data_container
9
+
10
+ x_ = 0
11
+ y_ = 1
12
+ z_ = 2
13
+
14
+
15
+ class selector:
16
+ def __getitem__(self, keys) -> list:
17
+ self.indices = list(keys)
18
+ if len(self.indices) < 3:
19
+ self.indices += [0] * (3 - len(self.indices))
20
+ return self.indices
21
+
22
+
23
+ class dataframe:
24
+ def __init__(self):
25
+ self.array = None
26
+ self.name = None
27
+ # Selector for the spatial indices
28
+ self.cut = selector()
29
+ self.cut[:, :, :]
30
+
31
+ def setData(self, dataIn, nameIn):
32
+ assert nameIn.size == dataIn.shape[0]
33
+ assert dataIn.ndim <= 4
34
+ shape = list(dataIn.shape) + [1] * (4 - dataIn.ndim)
35
+ self.array = np.reshape(dataIn, shape)
36
+ self.name = tuple(nameIn)
37
+
38
+ def fixDataSize(self):
39
+ self.name = tuple(self.name)
40
+ assert len(self.name) == self.array.shape[0]
41
+ assert self.array.ndim <= 4
42
+ shape = list(self.array.shape) + [1] * (4 - self.array.ndim)
43
+ self.array = np.reshape(self.array, shape)
44
+
45
+ def __getitem__(self, keys):
46
+ """Example:
47
+ d["varname", 3:, 1:4:2, 3]
48
+ """
49
+ if type(keys) is str:
50
+ # If the spatial indices are not specified, use self.cut
51
+ keys = [keys] + self.cut.indices
52
+ else:
53
+ keys = list(keys) + [slice(None, None, None)] * (4 - len(keys))
54
+ ivar = self.name.index(keys[0])
55
+ return np.squeeze(self.array[ivar, keys[1], keys[2], keys[3]])
56
+
57
+
58
+ class IDLData(object):
59
+ r"""
60
+ A class used to handle the `*.out` format SWMF data.
61
+
62
+ Example:
63
+ >>> ds = IDLData("3d.out")
64
+ >>> dc2d = ds.get_slice("y", 1)
65
+ >>> dc3d = ds.get_domain()
66
+ """
67
+
68
+ def __init__(self, filename="none"):
69
+ fileList = glob.glob(filename)
70
+ nfiles = len(fileList)
71
+ assert nfiles > 0, "Error: can not find file!"
72
+ if nfiles > 1:
73
+ fileList.sort()
74
+ print("nfiles = ", nfiles)
75
+ ifile = input("ifile= ? start from 1 ")
76
+ while ifile > nfiles or ifile < 1:
77
+ print("ifile = ", ifile, " is a bad value. Input again")
78
+ ifile = input("ifile= ? start from 1 ")
79
+ else:
80
+ ifile = 1
81
+
82
+ self.filename = fileList[ifile - 1]
83
+ self.isOuts = self.filename[-4:] == "outs"
84
+ self.data = dataframe()
85
+ self.nInstance = None if self.isOuts else 1
86
+ self.npict = 1
87
+ self.fileformat = None
88
+ self.variables = None
89
+ self.unit = None
90
+ self.iter = None
91
+ self.time = None
92
+ self.ndim = None
93
+ self.gencoord = None
94
+ self.grid = None
95
+
96
+ self.read_data()
97
+
98
+ def __post_process_param__(self):
99
+
100
+ planet_radius = 1.0
101
+
102
+ # Not always correct.
103
+ for var, val in zip(self.param_name, self.para):
104
+ if var == "xSI":
105
+ planet_radius = float(100 * val)
106
+
107
+ self.registry = yt.units.unit_registry.UnitRegistry()
108
+ self.registry.add("Planet_Radius", planet_radius, yt.units.dimensions.length)
109
+
110
+ def __repr__(self):
111
+ str = (
112
+ f"filename : {self.filename}\n"
113
+ f"variables : {self.variables}\n"
114
+ f"unit : {self.unit}\n"
115
+ f"nInstance : {self.nInstance}\n"
116
+ f"npict : {self.npict}\n"
117
+ f"time : {self.time}\n"
118
+ f"nIter : {self.iter}\n"
119
+ f"ndim : {self.ndim}\n"
120
+ f"gencoord : {self.gencoord}\n"
121
+ f"grid : {self.grid}\n"
122
+ )
123
+ return str
124
+
125
+ def get_domain(self):
126
+ r"""
127
+ This methods returns a dataContainer3D object that contains all the 3D data.
128
+ """
129
+ dataSets = {}
130
+ for varname in self.data.name:
131
+ idx = self.data.name.index(varname)
132
+ unit = utilities.get_unit(varname, self.unit)
133
+ dataSets[varname] = yt.YTArray(
134
+ np.squeeze(self.data.array[idx, :, :, :]), unit, registry=self.registry
135
+ )
136
+
137
+ labels = ["", "", ""]
138
+ axes = [None, None, None]
139
+ for idim in range(self.ndim):
140
+ name = self.variables[idim]
141
+ idx = self.data.name.index(name)
142
+ labels[idim] = name.upper()
143
+ unit = utilities.get_unit("X", self.unit)
144
+ name = name.upper()
145
+ if name in ["X", "Y", "Z"]:
146
+ axes[idim] = yt.YTArray(
147
+ self.data.array[idx, :, :, :], unit, registry=self.registry
148
+ )
149
+
150
+ if self.gencoord:
151
+ dc = data_container.dataContainer2D(
152
+ dataSets,
153
+ np.squeeze(axes[0]),
154
+ np.squeeze(axes[1]),
155
+ labels[0],
156
+ labels[1],
157
+ step=self.iter,
158
+ time=self.time,
159
+ filename=self.filename,
160
+ gencoord=True,
161
+ )
162
+ elif self.ndim == 1:
163
+ dc = data_container.dataContainer1D(
164
+ dataSets,
165
+ np.squeeze(axes[0]),
166
+ labels[0],
167
+ step=self.iter,
168
+ time=self.time,
169
+ filename=self.filename,
170
+ )
171
+ elif self.ndim == 2:
172
+ dc = data_container.dataContainer2D(
173
+ dataSets,
174
+ np.squeeze(axes[0])[:, 0],
175
+ np.squeeze(axes[1])[0, :],
176
+ labels[0],
177
+ labels[1],
178
+ step=self.iter,
179
+ time=self.time,
180
+ filename=self.filename,
181
+ )
182
+ else:
183
+ dc = data_container.dataContainer3D(
184
+ dataSets,
185
+ axes[0][:, 0, 0],
186
+ axes[1][0, :, 0],
187
+ axes[2][0, 0, :],
188
+ step=self.iter,
189
+ time=self.time,
190
+ filename=self.filename,
191
+ )
192
+
193
+ return dc
194
+
195
+ def save_data(self, saveName, saveFormat="ascii"):
196
+ # Currently only support ascii output.
197
+ self.save_ascii_instance(saveName)
198
+
199
+ def save_ascii_instance(self, saveName):
200
+ with open(saveName, "w") as f:
201
+ f.write(self.unit + "\n")
202
+ f.write(
203
+ "{:d}\t{:e}\t{:d}\t{:d}\t{:d}\n".format(
204
+ self.iter, self.time, self.ndim, self.nparam, self.nvar
205
+ )
206
+ )
207
+ [f.write("{:d}\t".format(i)) for i in self.grid]
208
+ f.write("\n")
209
+ if self.nparam > 0:
210
+ [f.write("{:e}\t".format(i)) for i in self.para]
211
+ f.write("\n")
212
+ [f.write(i + " ") for i in self.variables]
213
+ f.write("\n")
214
+
215
+ nk = self.grid[2] if self.ndim > 2 else 1
216
+ nj = self.grid[1] if self.ndim > 1 else 1
217
+ ni = self.grid[0]
218
+ for kk in range(nk):
219
+ for jj in range(nj):
220
+ for ii in range(ni):
221
+ [
222
+ f.write("{:e}\t".format(i))
223
+ for i in self.data.array[:, ii, jj, kk]
224
+ ]
225
+ f.write("\n")
226
+
227
+ def read_data(self):
228
+ if self.fileformat is None:
229
+ with open(self.filename, "rb") as f:
230
+ EndChar = "<" # Endian marker (default: little.)
231
+ RecLenRaw = f.read(4)
232
+ RecLen = (struct.unpack(EndChar + "l", RecLenRaw))[0]
233
+ if RecLen != 79 and RecLen != 500:
234
+ self.fileformat = "ascii"
235
+ else:
236
+ self.fileformat = "binary"
237
+
238
+ if self.fileformat == "ascii":
239
+ self.read_ascii()
240
+ elif self.fileformat == "binary":
241
+ self.read_binary()
242
+ else:
243
+ print("Unknown format = ", self.fileformat)
244
+
245
+ ndim = self.ndim
246
+ nvar = self.nvar
247
+ self.data.name = tuple(self.variables)[0 : ndim + nvar]
248
+ self.data.fixDataSize()
249
+ self.param_name = self.variables[ndim + nvar :]
250
+ self.__post_process_param__()
251
+
252
+ def read_ascii(self):
253
+ if self.nInstance is None:
254
+ # Count the number of instances.
255
+ with open(self.filename, "r") as f:
256
+ for i, l in enumerate(f):
257
+ pass
258
+ nLineFile = i + 1
259
+
260
+ with open(self.filename, "r") as f:
261
+ self.nInstanceLength = self.read_ascii_instance(f)
262
+
263
+ self.nInstance = round(nLineFile / self.nInstanceLength)
264
+
265
+ nLineSkip = (self.npict) * self.nInstanceLength if self.isOuts else 0
266
+ with open(self.filename, "r") as f:
267
+ if nLineSkip > 0:
268
+ for i, line in enumerate(f):
269
+ if i == nLineSkip - 1:
270
+ break
271
+ self.read_ascii_instance(f)
272
+
273
+ def read_ascii_instance(self, infile):
274
+ nline = 0
275
+ # Read the top header line
276
+ headline = infile.readline().strip()
277
+ nline += 1
278
+ self.unit = headline.split()[0]
279
+
280
+ # Read & convert iters, time, etc. from next line
281
+ parts = infile.readline().split()
282
+ nline += 1
283
+ self.iter = int(parts[0])
284
+ self.time = float(parts[1])
285
+ self.ndim = int(parts[2])
286
+ self.gencoord = self.ndim < 0
287
+ self.ndim = abs(self.ndim)
288
+ self.nparam = int(parts[3])
289
+ self.nvar = int(parts[4])
290
+
291
+ # Read & convert grid dimensions
292
+ grid = [int(x) for x in infile.readline().split()]
293
+ nline += 1
294
+ self.grid = np.array(grid)
295
+ self.npoints = abs(self.grid.prod())
296
+
297
+ time = self.time
298
+ npts = self.npoints
299
+ ndim = self.grid.size
300
+ nvar = self.nvar
301
+ npar = self.nparam
302
+
303
+ # Read parameters stored in file
304
+ self.para = np.zeros(npar)
305
+ if npar > 0:
306
+ self.para[:] = infile.readline().split()
307
+ nline += 1
308
+
309
+ # Read variable names
310
+ names = infile.readline().split()
311
+ nline += 1
312
+
313
+ # Save grid names (e.g. "x" or "r") and save associated params
314
+ self.dims = names[0:ndim]
315
+ self.variables = np.array(names)
316
+
317
+ # Create string representation of time
318
+ self.strtime = "%4.4ih%2.2im%06.3fs" % (
319
+ np.floor(time / 3600.0),
320
+ np.floor(time % 3600.0 / 60.0),
321
+ time % 60.0,
322
+ )
323
+
324
+ nRow = ndim + nvar
325
+ nCol = self.npoints
326
+ self.data.array = np.zeros((nRow, nCol))
327
+
328
+ for i, line in enumerate(infile.readlines()):
329
+ parts = line.split()
330
+
331
+ if i >= self.npoints:
332
+ break
333
+
334
+ for j, p in enumerate(parts):
335
+ self.data.array[j][i] = float(p)
336
+
337
+ nline += self.npoints
338
+ shapeNew = np.append([nRow], self.grid)
339
+ self.data.array = np.reshape(self.data.array, shapeNew, order="F")
340
+
341
+ return nline
342
+
343
+ def read_binary(self):
344
+ if self.nInstance is None:
345
+ with open(self.filename, "rb") as f:
346
+ self.read_binary_instance(f)
347
+ self.nInstanceLength = f.tell()
348
+ f.seek(0, 2)
349
+ endPos = f.tell()
350
+ self.nInstance = round(endPos / self.nInstanceLength)
351
+
352
+ with open(self.filename, "rb") as f:
353
+ if self.isOuts:
354
+ f.seek((self.npict) * self.nInstanceLength, 0)
355
+ self.read_binary_instance(f)
356
+
357
+ def read_binary_instance(self, infile):
358
+ # On the first try, we may fail because of wrong-endianess.
359
+ # If that is the case, swap that endian and try again.
360
+ EndChar = "<" # Endian marker (default: little)
361
+ self.endian = "little"
362
+ RecLenRaw = infile.read(4)
363
+
364
+ RecLen = (struct.unpack(EndChar + "l", RecLenRaw))[0]
365
+ if (RecLen > 10000) or (RecLen < 0):
366
+ EndChar = ">"
367
+ self.endian = "big"
368
+ RecLen = (struct.unpack(EndChar + "l", RecLenRaw))[0]
369
+
370
+ headline = (
371
+ struct.unpack("{0}{1}s".format(EndChar, RecLen), infile.read(RecLen))
372
+ )[0].strip()
373
+ if str is not bytes:
374
+ headline = headline.decode()
375
+ self.unit = headline.split()[0]
376
+
377
+ (OldLen, RecLen) = struct.unpack(EndChar + "2l", infile.read(8))
378
+ pformat = "f"
379
+ # Parse rest of header; detect double-precision file
380
+ if RecLen > 20:
381
+ pformat = "d"
382
+ (self.iter, self.time, self.ndim, self.nparam, self.nvar) = struct.unpack(
383
+ "{0}l{1}3l".format(EndChar, pformat), infile.read(RecLen)
384
+ )
385
+ self.gencoord = self.ndim < 0
386
+ self.ndim = abs(self.ndim)
387
+ # Get gridsize
388
+ (OldLen, RecLen) = struct.unpack(EndChar + "2l", infile.read(8))
389
+
390
+ self.grid = np.array(
391
+ struct.unpack(
392
+ "{0}{1}l".format(EndChar, abs(self.ndim)), infile.read(RecLen)
393
+ )
394
+ )
395
+ self.npoints = abs(self.grid.prod())
396
+
397
+ time = self.time
398
+ npts = self.npoints
399
+ ndim = self.grid.size
400
+ nvar = self.nvar
401
+ npar = self.nparam
402
+
403
+ # Read parameters stored in file
404
+ self.para = np.zeros(npar)
405
+ if npar > 0:
406
+ (OldLen, RecLen) = struct.unpack(EndChar + "2l", infile.read(8))
407
+ self.para[:] = struct.unpack(
408
+ "{0}{1}{2}".format(EndChar, npar, pformat), infile.read(RecLen)
409
+ )
410
+
411
+ (OldLen, RecLen) = struct.unpack(EndChar + "2l", infile.read(8))
412
+ names = (struct.unpack("{0}{1}s".format(EndChar, RecLen), infile.read(RecLen)))[
413
+ 0
414
+ ]
415
+ if str is not bytes:
416
+ names = names.decode()
417
+
418
+ names.strip()
419
+ names = names.split()
420
+
421
+ # Save grid names (e.g. "x" or "r") and save associated params
422
+ self.dims = names[0:ndim]
423
+ self.variables = np.array(names)
424
+
425
+ self.strtime = "{0:04d}h{1:02d}m{2:06.3f}s".format(
426
+ int(time // 3600), int(time % 3600 // 60), time % 60
427
+ )
428
+
429
+ nRow = ndim + nvar
430
+ nCol = self.npoints
431
+ self.data.array = np.zeros((nRow, nCol), dtype=np.float32)
432
+
433
+ # Get the grid points...
434
+ (OldLen, RecLen) = struct.unpack(EndChar + "2l", infile.read(8))
435
+ for i in range(0, ndim):
436
+ # Read the data into a temporary grid
437
+ tempgrid = np.array(
438
+ struct.unpack(
439
+ "{0}{1}{2}".format(EndChar, npts, pformat),
440
+ infile.read(int(RecLen // ndim)),
441
+ )
442
+ )
443
+ self.data.array[i, :] = tempgrid
444
+
445
+ # Get the actual data and sort
446
+ for i in range(ndim, nvar + ndim):
447
+ (OldLen, RecLen) = struct.unpack(EndChar + "2l", infile.read(8))
448
+ tmp = np.array(
449
+ struct.unpack(
450
+ "{0}{1}{2}".format(EndChar, npts, pformat), infile.read(RecLen)
451
+ )
452
+ )
453
+ self.data.array[i, :] = tmp
454
+ # Consume the last record length
455
+ infile.read(4)
456
+
457
+ shapeNew = np.append([nRow], self.grid)
458
+ self.data.array = np.reshape(self.data.array, shapeNew, order="F")
459
+
460
+ def plot(self, iv1name: str, iv2name: str, *dvname):
461
+ """
462
+ dvname: dependent variables name
463
+ iv1name: the first independent variable name
464
+ iv2name: the second independent variable name
465
+ """
466
+ x = self.data[iv1name]
467
+ y = self.data[iv2name]
468
+
469
+ nvar = len(dvname)
470
+ nRow = int(round(np.sqrt(nvar)))
471
+ nCol = math.ceil(nvar / nRow)
472
+ print("nvar = ", nvar, " nRow = ", nRow, " nCol = ", nCol)
473
+
474
+ f, axes = plt.subplots(nRow, nCol)
475
+ axes = np.array(axes) # in case nRow = nCol = 1
476
+ aspect = (y.max() - y.min()) / (x.max() - x.min())
477
+ axes = axes.reshape(-1)
478
+
479
+ for isub, ax in zip(range(nvar), axes):
480
+ w = self.data[dvname[isub]]
481
+ cs = ax.contourf(x, y, w, levels=100, cmap="turbo")
482
+ cb = f.colorbar(cs, ax=ax, shrink=aspect * 0.6)
483
+ cb.ax.set_yticks([w.min(), w.max()])
484
+ ax.set_aspect(aspect)
485
+ ax.set_xlabel(iv1name)
486
+ ax.set_ylabel(iv2name)
487
+ ax.set_title(dvname[isub])
488
+
489
+ # Delete axes for empty subplots
490
+ for ax in axes[nvar : nRow * nCol]:
491
+ f.delaxes(ax)
492
+
493
+ def extract_data(self, sat: np.ndarray) -> np.ndarray:
494
+ """Extract data at given 2D points.
495
+
496
+ Args:
497
+ sat (np.ndarray): 2D point locations.
498
+
499
+ Returns:
500
+ np.ndarray: 2D array of variables at each point.
501
+ """
502
+ satData = None
503
+ if sat.ndim == 2 and sat.shape[1] >= self.ndim:
504
+ nVar = self.nvar + self.ndim
505
+ nPoint = sat.shape[0]
506
+ satData = np.zeros((nPoint, nVar))
507
+ for i in range(nPoint):
508
+ satData[i, :] = self.get_data(sat[i, :])
509
+ return satData
510
+
511
+ def get_data(self, loc: np.ndarray) -> np.ndarray:
512
+ """Extract data at a given 2D point using bilinear interpolation.
513
+
514
+ Args:
515
+ loc (np.ndarray): 2D point location.
516
+
517
+ Returns:
518
+ np.ndarray: 1D array of saved variables at the survey point.
519
+ """
520
+ i1, j1, k1 = 0, 0, 0
521
+ while self.data["x"][i1, 0, 0] < loc[x_]:
522
+ i1 = i1 + 1
523
+ while self.data["y"][0, j1, 0] < loc[y_]:
524
+ j1 = j1 + 1
525
+ while self.data["z"][0, 0, k1] < loc[z_]:
526
+ k1 = k1 + 1
527
+
528
+ i0 = i1 - 1
529
+ j0 = j1 - 1
530
+ k0 = k1 - 1
531
+
532
+ wx0 = (self.data["x"][i1, 0, 0] - loc[x_]) / (
533
+ self.data["x"][i1, 0, 0] - self.data["x"][i0, 0, 0]
534
+ )
535
+ wy0 = (self.data["y"][0, j1, 0] - loc[y_]) / (
536
+ self.data["y"][0, j1, 0] - self.data["y"][0, j0, 0]
537
+ )
538
+ wz0 = (self.data["z"][0, 0, k1] - loc[z_]) / (
539
+ self.data["z"][0, 0, k1] - self.data["z"][0, 0, k0]
540
+ )
541
+
542
+ wx1 = 1.0 - wx0
543
+ wy1 = 1.0 - wy0
544
+ wz1 = 1.0 - wz0
545
+
546
+ w = np.array(
547
+ [
548
+ [
549
+ [wx0 * wy0 * wz0, wx0 * wy0 * wz1],
550
+ [wx0 * wy1 * wz0, wx0 * wy1 * wz1],
551
+ ],
552
+ [
553
+ [wx1 * wy0 * wz0, wx1 * wy0 * wz1],
554
+ [wx1 * wy1 * wz0, wx1 * wy1 * wz1],
555
+ ],
556
+ ]
557
+ )
558
+
559
+ res = np.sum(
560
+ w * self.data.array[:, i0 : i0 + 2, j0 : j0 + 2, k0 : k0 + 2],
561
+ axis=(1, 2, 3),
562
+ )
563
+
564
+ return res
File without changes