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 +39 -0
- flekspy/flekspy.py +2 -0
- flekspy/idl/__init__.py +0 -0
- flekspy/idl/idl_format_data.py +564 -0
- flekspy/plot/__init__.py +0 -0
- flekspy/plot/streamplot.py +728 -0
- flekspy/tp/__init__.py +0 -0
- flekspy/tp/test_particles.py +441 -0
- flekspy/util/__init__.py +0 -0
- flekspy/util/data_container.py +627 -0
- flekspy/util/utilities.py +110 -0
- flekspy/yt/__init__.py +0 -0
- flekspy/yt/yt_interface.py +548 -0
- flekspy-0.1.0.dist-info/LICENSE.md +21 -0
- flekspy-0.1.0.dist-info/METADATA +58 -0
- flekspy-0.1.0.dist-info/RECORD +17 -0
- flekspy-0.1.0.dist-info/WHEEL +4 -0
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
flekspy/idl/__init__.py
ADDED
|
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
|
flekspy/plot/__init__.py
ADDED
|
File without changes
|