osiris-utils 1.1.4__py3-none-any.whl → 1.1.6__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.
- osiris_utils/__init__.py +8 -2
- osiris_utils/data/data.py +316 -42
- osiris_utils/data/diagnostic.py +691 -233
- osiris_utils/data/simulation.py +30 -17
- osiris_utils/postprocessing/derivative.py +29 -49
- osiris_utils/postprocessing/fft.py +8 -14
- osiris_utils/postprocessing/field_centering.py +168 -0
- osiris_utils/postprocessing/heatflux_correction.py +193 -0
- osiris_utils/postprocessing/mft.py +14 -28
- osiris_utils/postprocessing/pressure_correction.py +171 -0
- osiris_utils/utils.py +140 -1
- {osiris_utils-1.1.4.dist-info → osiris_utils-1.1.6.dist-info}/METADATA +1 -1
- osiris_utils-1.1.6.dist-info/RECORD +25 -0
- osiris_utils-1.1.4.dist-info/RECORD +0 -22
- {osiris_utils-1.1.4.dist-info → osiris_utils-1.1.6.dist-info}/WHEEL +0 -0
- {osiris_utils-1.1.4.dist-info → osiris_utils-1.1.6.dist-info}/licenses/LICENSE.txt +0 -0
- {osiris_utils-1.1.4.dist-info → osiris_utils-1.1.6.dist-info}/top_level.txt +0 -0
osiris_utils/data/simulation.py
CHANGED
|
@@ -31,15 +31,7 @@ class Simulation:
|
|
|
31
31
|
__getitem__(key)
|
|
32
32
|
Get a diagnostic.
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
-------
|
|
36
|
-
>>> sim = Simulation('electrons', 'path/to/simulation')
|
|
37
|
-
>>> diag = sim['e1']
|
|
38
|
-
>>> diag.load_all()
|
|
39
|
-
|
|
40
|
-
>>> sim = Simulation('electrons', 'path/to/simulation')
|
|
41
|
-
>>> diag = sim['e1']
|
|
42
|
-
>>> diag[<index>]
|
|
34
|
+
|
|
43
35
|
'''
|
|
44
36
|
def __init__(self, input_deck_path):
|
|
45
37
|
folder_path = os.path.dirname(input_deck_path)
|
|
@@ -135,6 +127,10 @@ class Simulation:
|
|
|
135
127
|
@property
|
|
136
128
|
def species(self):
|
|
137
129
|
return self._species
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def loaded_diagnostics(self):
|
|
133
|
+
return self._diagnostics
|
|
138
134
|
|
|
139
135
|
# This is to handle species related diagnostics
|
|
140
136
|
class Species_Handler:
|
|
@@ -180,13 +176,7 @@ class Species_Handler:
|
|
|
180
176
|
-------
|
|
181
177
|
str
|
|
182
178
|
The name (key) used to store the diagnostic
|
|
183
|
-
|
|
184
|
-
Example
|
|
185
|
-
-------
|
|
186
|
-
>>> sim = Simulation('path/to/simulation', 'input_deck.txt')
|
|
187
|
-
>>> nT = sim['electrons']['n'] * sim['electrons']['T11']
|
|
188
|
-
>>> sim.add_diagnostic(nT, 'nT')
|
|
189
|
-
>>> sim['nT'] # Access the custom diagnostic
|
|
179
|
+
|
|
190
180
|
"""
|
|
191
181
|
# Generate a name if none provided
|
|
192
182
|
if name is None:
|
|
@@ -200,4 +190,27 @@ class Species_Handler:
|
|
|
200
190
|
if isinstance(diagnostic, Diagnostic):
|
|
201
191
|
self._diagnostics[name] = diagnostic
|
|
202
192
|
else:
|
|
203
|
-
raise ValueError("Only Diagnostic objects are supported for now")
|
|
193
|
+
raise ValueError("Only Diagnostic objects are supported for now")
|
|
194
|
+
|
|
195
|
+
def delete_diagnostic(self, key):
|
|
196
|
+
"""
|
|
197
|
+
Delete a diagnostic.
|
|
198
|
+
"""
|
|
199
|
+
if key in self._diagnostics:
|
|
200
|
+
del self._diagnostics[key]
|
|
201
|
+
else:
|
|
202
|
+
print(f"Diagnostic {key} not found in species {self._species_name}")
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
def delete_all_diagnostics(self):
|
|
206
|
+
"""
|
|
207
|
+
Delete all diagnostics.
|
|
208
|
+
"""
|
|
209
|
+
self._diagnostics = {}
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def species(self):
|
|
213
|
+
return self._species_name
|
|
214
|
+
@property
|
|
215
|
+
def loaded_diagnostics(self):
|
|
216
|
+
return self._diagnostics
|
|
@@ -12,7 +12,7 @@ class Derivative_Simulation(PostProcess):
|
|
|
12
12
|
----------
|
|
13
13
|
simulation : Simulation
|
|
14
14
|
The simulation object.
|
|
15
|
-
|
|
15
|
+
deriv_type : str
|
|
16
16
|
The type of derivative to compute. Options are:
|
|
17
17
|
- 't' for time derivative.
|
|
18
18
|
- 'x1' for first spatial derivative.
|
|
@@ -24,19 +24,14 @@ class Derivative_Simulation(PostProcess):
|
|
|
24
24
|
axis : int or tuple
|
|
25
25
|
The axis to compute the derivative. Only used for 'xx', 'xt' and 'tx' types.
|
|
26
26
|
|
|
27
|
-
Example
|
|
28
|
-
-------
|
|
29
|
-
>>> sim = Simulation('electrons', 'path/to/simulation')
|
|
30
|
-
>>> derivative = Derivative(sim, 'x1')
|
|
31
|
-
>>> deriv_e1_wrt_x1 = derivative['e1']
|
|
32
27
|
"""
|
|
33
28
|
|
|
34
|
-
def __init__(self, simulation,
|
|
35
|
-
super().__init__(f"Derivative({
|
|
29
|
+
def __init__(self, simulation, deriv_type, axis=None):
|
|
30
|
+
super().__init__(f"Derivative({deriv_type})")
|
|
36
31
|
if not isinstance(simulation, Simulation):
|
|
37
32
|
raise ValueError("Simulation must be a Simulation object.")
|
|
38
33
|
self._simulation = simulation
|
|
39
|
-
self.
|
|
34
|
+
self._deriv_type = deriv_type
|
|
40
35
|
self._axis = axis
|
|
41
36
|
self._derivatives_computed = {}
|
|
42
37
|
self._species_handler = {}
|
|
@@ -44,12 +39,12 @@ class Derivative_Simulation(PostProcess):
|
|
|
44
39
|
def __getitem__(self, key):
|
|
45
40
|
if key in self._simulation._species:
|
|
46
41
|
if key not in self._species_handler:
|
|
47
|
-
self._species_handler[key] = Derivative_Species_Handler(self._simulation[key], self.
|
|
42
|
+
self._species_handler[key] = Derivative_Species_Handler(self._simulation[key], self._deriv_type, self._axis)
|
|
48
43
|
return self._species_handler[key]
|
|
49
44
|
|
|
50
45
|
if key not in self._derivatives_computed:
|
|
51
46
|
self._derivatives_computed[key] = Derivative_Diagnostic(diagnostic=self._simulation[key],
|
|
52
|
-
|
|
47
|
+
deriv_type=self._deriv_type, axis=self._axis)
|
|
53
48
|
return self._derivatives_computed[key]
|
|
54
49
|
|
|
55
50
|
def delete_all(self):
|
|
@@ -63,7 +58,7 @@ class Derivative_Simulation(PostProcess):
|
|
|
63
58
|
|
|
64
59
|
def process(self, diagnostic):
|
|
65
60
|
"""Apply derivative to a diagnostic"""
|
|
66
|
-
return Derivative_Diagnostic(diagnostic, self.
|
|
61
|
+
return Derivative_Diagnostic(diagnostic, self._deriv_type, self._axis)
|
|
67
62
|
|
|
68
63
|
|
|
69
64
|
class Derivative_Diagnostic(Diagnostic):
|
|
@@ -75,7 +70,7 @@ class Derivative_Diagnostic(Diagnostic):
|
|
|
75
70
|
----------
|
|
76
71
|
diagnostic : Diagnostic
|
|
77
72
|
The diagnostic object.
|
|
78
|
-
|
|
73
|
+
deriv_type : str
|
|
79
74
|
The type of derivative to compute. Options are: 't', 'x1', 'x2', 'x3', 'xx', 'xt' and 'tx'.
|
|
80
75
|
axis : int or tuple
|
|
81
76
|
The axis to compute the derivative. Only used for 'xx', 'xt' and 'tx' types
|
|
@@ -87,45 +82,30 @@ class Derivative_Diagnostic(Diagnostic):
|
|
|
87
82
|
__getitem__(index)
|
|
88
83
|
Get data at a specific index.
|
|
89
84
|
|
|
90
|
-
Example
|
|
91
|
-
-------
|
|
92
|
-
>>> sim = Simulation('electrons', 'path/to/simulation')
|
|
93
|
-
>>> diag = sim['e1']
|
|
94
|
-
>>> derivative = Derivative_Diagnostic(diag, 'x1')
|
|
95
85
|
"""
|
|
96
86
|
|
|
97
|
-
def __init__(self, diagnostic,
|
|
87
|
+
def __init__(self, diagnostic, deriv_type, axis=None):
|
|
98
88
|
# Initialize using parent's __init__ with the same species
|
|
99
89
|
if hasattr(diagnostic, '_species'):
|
|
100
90
|
super().__init__(simulation_folder=diagnostic._simulation_folder if hasattr(diagnostic, '_simulation_folder') else None,
|
|
101
91
|
species=diagnostic._species)
|
|
102
92
|
else:
|
|
103
93
|
super().__init__(None)
|
|
94
|
+
|
|
95
|
+
self.postprocess_name = f"DERIV"
|
|
104
96
|
|
|
105
97
|
# self._name = f"D[{diagnostic._name}, {type}]"
|
|
106
98
|
self._diag = diagnostic
|
|
107
|
-
self.
|
|
99
|
+
self._deriv_type = deriv_type
|
|
108
100
|
self._axis = axis if axis is not None else diagnostic._axis
|
|
109
101
|
self._data = None
|
|
110
102
|
self._all_loaded = False
|
|
111
103
|
|
|
112
104
|
# Copy all relevant attributes from diagnostic
|
|
113
|
-
for attr in ['_dt', '_dx', '_ndump', '_axis', '_nx', '_x', '_grid', '_dim', '_maxiter']:
|
|
105
|
+
for attr in ['_dt', '_dx', '_ndump', '_axis', '_nx', '_x', '_grid', '_dim', '_maxiter', '_type']:
|
|
114
106
|
if hasattr(diagnostic, attr):
|
|
115
107
|
setattr(self, attr, getattr(diagnostic, attr))
|
|
116
108
|
|
|
117
|
-
def load_metadata(self):
|
|
118
|
-
"""Copy metadata from original diagnostic to ensure consistency"""
|
|
119
|
-
self._dt = self._diag._dt
|
|
120
|
-
self._dx = self._diag._dx
|
|
121
|
-
self._ndump = self._diag._ndump
|
|
122
|
-
self._axis = self._diag._axis
|
|
123
|
-
self._nx = self._diag._nx
|
|
124
|
-
self._x = self._diag._x
|
|
125
|
-
self._grid = self._diag._grid
|
|
126
|
-
self._dim = self._diag._dim
|
|
127
|
-
self._maxiter = self._diag._maxiter
|
|
128
|
-
|
|
129
109
|
def load_all(self):
|
|
130
110
|
"""Load all data and compute the derivative"""
|
|
131
111
|
if self._data is not None:
|
|
@@ -135,34 +115,34 @@ class Derivative_Diagnostic(Diagnostic):
|
|
|
135
115
|
if not hasattr(self._diag, '_data') or self._diag._data is None:
|
|
136
116
|
self._diag.load_all()
|
|
137
117
|
|
|
138
|
-
if self.
|
|
118
|
+
if self._deriv_type == "t":
|
|
139
119
|
result = np.gradient(self._diag._data, self._diag._dt * self._diag._ndump, axis=0, edge_order=2)
|
|
140
120
|
|
|
141
|
-
elif self.
|
|
121
|
+
elif self._deriv_type == "x1":
|
|
142
122
|
if self._dim == 1:
|
|
143
123
|
result = np.gradient(self._diag._data, self._diag._dx, axis=1, edge_order=2)
|
|
144
124
|
else:
|
|
145
125
|
result = np.gradient(self._diag._data, self._diag._dx[0], axis=1, edge_order=2)
|
|
146
126
|
|
|
147
|
-
elif self.
|
|
148
|
-
result = np.gradient(self._diag._data, self._diag._dx[
|
|
127
|
+
elif self._deriv_type == "x2":
|
|
128
|
+
result = np.gradient(self._diag._data, self._diag._dx[1], axis=2, edge_order=2)
|
|
149
129
|
|
|
150
|
-
elif self.
|
|
151
|
-
result = np.gradient(self._diag._data, self._diag._dx[
|
|
130
|
+
elif self._deriv_type == "x3":
|
|
131
|
+
result = np.gradient(self._diag._data, self._diag._dx[2], axis=3, edge_order=2)
|
|
152
132
|
|
|
153
|
-
elif self.
|
|
133
|
+
elif self._deriv_type == "xx":
|
|
154
134
|
if len(self._axis) != 2:
|
|
155
135
|
raise ValueError("Axis must be a tuple with two elements.")
|
|
156
136
|
result = np.gradient(np.gradient(self._diag._data, self._diag._dx[self._axis[0]-1], axis=self._axis[0], edge_order=2),
|
|
157
137
|
self._diag._dx[self._axis[1]-1], axis=self._axis[1], edge_order=2)
|
|
158
138
|
|
|
159
|
-
elif self.
|
|
139
|
+
elif self._deriv_type == "xt":
|
|
160
140
|
if not isinstance(self._axis, int):
|
|
161
141
|
raise ValueError("Axis must be an integer.")
|
|
162
142
|
result = np.gradient(np.gradient(self._diag._data, self._diag._dt, axis=0, edge_order=2),
|
|
163
143
|
self._diag._dx[self._axis-1], axis=self._axis[0], edge_order=2)
|
|
164
144
|
|
|
165
|
-
elif self.
|
|
145
|
+
elif self._deriv_type == "tx":
|
|
166
146
|
if not isinstance(self._axis, int):
|
|
167
147
|
raise ValueError("Axis must be an integer.")
|
|
168
148
|
result = np.gradient(np.gradient(self._diag._data, self._diag._dx[self._axis-1], axis=self._axis, edge_order=2),
|
|
@@ -177,19 +157,19 @@ class Derivative_Diagnostic(Diagnostic):
|
|
|
177
157
|
|
|
178
158
|
def _data_generator(self, index):
|
|
179
159
|
"""Generate data for a specific index on-demand"""
|
|
180
|
-
if self.
|
|
160
|
+
if self._deriv_type == "x1":
|
|
181
161
|
if self._dim == 1:
|
|
182
162
|
yield np.gradient(self._diag[index], self._diag._dx, axis=0, edge_order=2)
|
|
183
163
|
else:
|
|
184
164
|
yield np.gradient(self._diag[index], self._diag._dx[0], axis=0, edge_order=2)
|
|
185
165
|
|
|
186
|
-
elif self.
|
|
166
|
+
elif self._deriv_type == "x2":
|
|
187
167
|
yield np.gradient(self._diag[index], self._diag._dx[1], axis=1, edge_order=2)
|
|
188
168
|
|
|
189
|
-
elif self.
|
|
169
|
+
elif self._deriv_type == "x3":
|
|
190
170
|
yield np.gradient(self._diag[index], self._diag._dx[2], axis=2, edge_order=2)
|
|
191
171
|
|
|
192
|
-
elif self.
|
|
172
|
+
elif self._deriv_type == "t":
|
|
193
173
|
if index == 0:
|
|
194
174
|
yield (-3 * self._diag[index] + 4 * self._diag[index + 1] - self._diag[index + 2]) / (2 * self._diag._dt * self._diag._ndump)
|
|
195
175
|
elif index == self._diag._maxiter - 1:
|
|
@@ -230,14 +210,14 @@ class Derivative_Species_Handler:
|
|
|
230
210
|
axis : int or tuple
|
|
231
211
|
The axis to compute the derivative. Only used for 'xx', 'xt' and 'tx' types.
|
|
232
212
|
"""
|
|
233
|
-
def __init__(self, species_handler,
|
|
213
|
+
def __init__(self, species_handler, deriv_type, axis=None):
|
|
234
214
|
self._species_handler = species_handler
|
|
235
|
-
self.
|
|
215
|
+
self._deriv_type = deriv_type
|
|
236
216
|
self._axis = axis
|
|
237
217
|
self._derivatives_computed = {}
|
|
238
218
|
|
|
239
219
|
def __getitem__(self, key):
|
|
240
220
|
if key not in self._derivatives_computed:
|
|
241
221
|
diag = self._species_handler[key]
|
|
242
|
-
self._derivatives_computed[key] = Derivative_Diagnostic(diag, self.
|
|
222
|
+
self._derivatives_computed[key] = Derivative_Diagnostic(diag, self._deriv_type, self._axis)
|
|
243
223
|
return self._derivatives_computed[key]
|
|
@@ -19,11 +19,6 @@ class FastFourierTransform_Simulation(PostProcess):
|
|
|
19
19
|
axis : int
|
|
20
20
|
The axis to compute the FFT.
|
|
21
21
|
|
|
22
|
-
Example
|
|
23
|
-
-------
|
|
24
|
-
>>> sim = Simulation('electrons', 'path/to/simulation')
|
|
25
|
-
>>> fft = FastFourierTransform(sim, 1)
|
|
26
|
-
>>> fft_e1 = fft['e1']
|
|
27
22
|
"""
|
|
28
23
|
def __init__(self, simulation, fft_axis):
|
|
29
24
|
super().__init__("FFT")
|
|
@@ -78,12 +73,7 @@ class FFT_Diagnostic(Diagnostic):
|
|
|
78
73
|
Get the angular frequency array for the FFT.
|
|
79
74
|
__getitem__(index)
|
|
80
75
|
Get data at a specific index.
|
|
81
|
-
|
|
82
|
-
Example
|
|
83
|
-
-------
|
|
84
|
-
>>> sim = Simulation('electrons', 'path/to/simulation')
|
|
85
|
-
>>> diag = sim['e1']
|
|
86
|
-
>>> fft = FFT_Diagnostic(diag, 1)
|
|
76
|
+
|
|
87
77
|
"""
|
|
88
78
|
def __init__(self, diagnostic, fft_axis):
|
|
89
79
|
if hasattr(diagnostic, '_species'):
|
|
@@ -92,6 +82,8 @@ class FFT_Diagnostic(Diagnostic):
|
|
|
92
82
|
else:
|
|
93
83
|
super().__init__(None)
|
|
94
84
|
|
|
85
|
+
self.postprocess_name = f"FFT"
|
|
86
|
+
|
|
95
87
|
self._name = f"FFT[{diagnostic._name}, {fft_axis}]"
|
|
96
88
|
self._diag = diagnostic
|
|
97
89
|
self._fft_axis = fft_axis
|
|
@@ -99,7 +91,7 @@ class FFT_Diagnostic(Diagnostic):
|
|
|
99
91
|
self._all_loaded = False
|
|
100
92
|
|
|
101
93
|
# Copy all relevant attributes from diagnostic
|
|
102
|
-
for attr in ['_dt', '_dx', '_ndump', '_axis', '_nx', '_x', '_grid', '_dim', '_maxiter']:
|
|
94
|
+
for attr in ['_dt', '_dx', '_ndump', '_axis', '_nx', '_x', '_grid', '_dim', '_maxiter', '_type']:
|
|
103
95
|
if hasattr(diagnostic, attr):
|
|
104
96
|
setattr(self, attr, getattr(diagnostic, attr))
|
|
105
97
|
|
|
@@ -110,7 +102,7 @@ class FFT_Diagnostic(Diagnostic):
|
|
|
110
102
|
|
|
111
103
|
def load_all(self):
|
|
112
104
|
if self._data is not None:
|
|
113
|
-
print("Using cached
|
|
105
|
+
print("Using cached data.")
|
|
114
106
|
return self._data
|
|
115
107
|
|
|
116
108
|
if not hasattr(self._diag, '_data') or self._diag._data is None:
|
|
@@ -119,7 +111,9 @@ class FFT_Diagnostic(Diagnostic):
|
|
|
119
111
|
|
|
120
112
|
# Apply appropriate windows based on which axes we're transforming
|
|
121
113
|
if isinstance(self._fft_axis, (list, tuple)):
|
|
122
|
-
|
|
114
|
+
if self._diag._data is None:
|
|
115
|
+
raise ValueError(f"Unable to load data for diagnostic {self._diag._name}. The data is None even after loading.")
|
|
116
|
+
|
|
123
117
|
result = self._diag._data.copy()
|
|
124
118
|
|
|
125
119
|
for axis in self._fft_axis:
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from ..utils import *
|
|
2
|
+
from ..data.simulation import Simulation
|
|
3
|
+
from .postprocess import PostProcess
|
|
4
|
+
from ..data.diagnostic import Diagnostic
|
|
5
|
+
|
|
6
|
+
OSIRIS_FLD = ["e1", "e2", "e3", "b1", "b2", "b3"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FieldCentering_Simulation(PostProcess):
|
|
10
|
+
"""
|
|
11
|
+
Class to handle the field centering on data. Works as a wrapper for the FieldCentering_Diagnostic class.
|
|
12
|
+
Inherits from PostProcess to ensure all operation overloads work properly.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
simulation : Simulation
|
|
17
|
+
The simulation object.
|
|
18
|
+
field : str
|
|
19
|
+
The field to center.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, simulation: Simulation):
|
|
23
|
+
super().__init__(f"FieldCentering Simulation")
|
|
24
|
+
"""
|
|
25
|
+
Class to center the field in the simulation.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
sim : Simulation
|
|
30
|
+
The simulation object.
|
|
31
|
+
field : str
|
|
32
|
+
The field to center.
|
|
33
|
+
"""
|
|
34
|
+
if not isinstance(simulation, Simulation):
|
|
35
|
+
raise ValueError("Simulation must be a Simulation object.")
|
|
36
|
+
self._simulation = simulation
|
|
37
|
+
|
|
38
|
+
self._field_centered = {}
|
|
39
|
+
# no need to create a species handler for field centering since fields are not species related
|
|
40
|
+
|
|
41
|
+
def __getitem__(self, key):
|
|
42
|
+
if key not in OSIRIS_FLD:
|
|
43
|
+
raise ValueError(f"Does it make sense to center {key} field? Only {OSIRIS_FLD} are supported.")
|
|
44
|
+
if key not in self._field_centered:
|
|
45
|
+
self._field_centered[key] = FieldCentering_Diagnostic(self._simulation[key])
|
|
46
|
+
return self._field_centered[key]
|
|
47
|
+
|
|
48
|
+
def delete_all(self):
|
|
49
|
+
self._field_centered = {}
|
|
50
|
+
|
|
51
|
+
def delete(self, key):
|
|
52
|
+
if key in self._field_centered:
|
|
53
|
+
del self._field_centered[key]
|
|
54
|
+
else:
|
|
55
|
+
print(f"Field {key} not found in simulation")
|
|
56
|
+
|
|
57
|
+
def process(self, diagnostic):
|
|
58
|
+
"""Apply field centering to a diagnostic"""
|
|
59
|
+
return FieldCentering_Diagnostic(diagnostic)
|
|
60
|
+
|
|
61
|
+
class FieldCentering_Diagnostic(Diagnostic):
|
|
62
|
+
def __init__(self, diagnostic):
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
Class to center the field in the simulation.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
diagnostic : Diagnostic
|
|
70
|
+
The diagnostic object.
|
|
71
|
+
"""
|
|
72
|
+
if hasattr(diagnostic, '_species'):
|
|
73
|
+
super().__init__(simulation_folder=diagnostic._simulation_folder if hasattr(diagnostic, '_simulation_folder') else None,
|
|
74
|
+
species=diagnostic._species)
|
|
75
|
+
else:
|
|
76
|
+
super().__init__(None)
|
|
77
|
+
|
|
78
|
+
self.postprocess_name = "FLD_CTR"
|
|
79
|
+
|
|
80
|
+
if diagnostic._name not in OSIRIS_FLD:
|
|
81
|
+
raise ValueError(f"Does it make sense to center {diagnostic._name} field? Only {OSIRIS_FLD} are supported.")
|
|
82
|
+
|
|
83
|
+
self._diag = diagnostic
|
|
84
|
+
|
|
85
|
+
for attr in ['_dt', '_dx', '_ndump', '_axis', '_nx', '_x', '_grid', '_dim', '_maxiter']:
|
|
86
|
+
if hasattr(diagnostic, attr):
|
|
87
|
+
setattr(self, attr, getattr(diagnostic, attr))
|
|
88
|
+
|
|
89
|
+
self._original_name = diagnostic._name
|
|
90
|
+
self._name = diagnostic._name + "_centered"
|
|
91
|
+
|
|
92
|
+
self._data = None
|
|
93
|
+
self._all_loaded = False
|
|
94
|
+
|
|
95
|
+
def load_all(self):
|
|
96
|
+
if self._data is not None:
|
|
97
|
+
return self._data
|
|
98
|
+
|
|
99
|
+
if not hasattr(self._diag, '_data') or self._diag._data is None:
|
|
100
|
+
self._diag.load_all()
|
|
101
|
+
|
|
102
|
+
if self._dim == 1:
|
|
103
|
+
if self._original_name.lower() in ['b2', 'b3', 'e1']:
|
|
104
|
+
result = 0.5 * (np.roll(self._diag.data, shift=1, axis=1) + self._diag.data)
|
|
105
|
+
elif self._original_name.lower() in ['b1', 'e2', 'e3']:
|
|
106
|
+
result = self._diag.data
|
|
107
|
+
|
|
108
|
+
elif self._dim == 2:
|
|
109
|
+
if self._original_name.lower() in ['e1', 'b2']:
|
|
110
|
+
result = 0.5 * (np.roll(self._diag.data, shift=1, axis=1) + self._diag.data)
|
|
111
|
+
elif self._original_name.lower() in ['e2', 'b1']:
|
|
112
|
+
result = 0.5 * (np.roll(self._diag.data, shift=1, axis=2) + self._diag.data)
|
|
113
|
+
elif self._original_name.lower() in ['b3']:
|
|
114
|
+
result = 0.5 * (np.roll((0.5 * (np.roll(self._diag.data, shift=1, axis=1) + self._diag.data)), shift=1, axis=2) + (0.5 * (np.roll(self._diag.data, shift=1, axis=1) + self._diag.data)))
|
|
115
|
+
elif self._original_name.lower() in ['e3']:
|
|
116
|
+
result = self._diag.data
|
|
117
|
+
|
|
118
|
+
elif self._dim == 3:
|
|
119
|
+
raise NotImplementedError("3D field centering is not implemented yet.")
|
|
120
|
+
|
|
121
|
+
else:
|
|
122
|
+
raise ValueError(f"Unknown dimension {self._dim}.")
|
|
123
|
+
|
|
124
|
+
self._data = result
|
|
125
|
+
self._all_loaded = True
|
|
126
|
+
return self._data
|
|
127
|
+
|
|
128
|
+
def __getitem__(self, index):
|
|
129
|
+
"""Get data at a specific index"""
|
|
130
|
+
if self._all_loaded and self._data is not None:
|
|
131
|
+
return self._data[index]
|
|
132
|
+
|
|
133
|
+
if isinstance(index, int):
|
|
134
|
+
return next(self._data_generator(index))
|
|
135
|
+
elif isinstance(index, slice):
|
|
136
|
+
start = 0 if index.start is None else index.start
|
|
137
|
+
step = 1 if index.step is None else index.step
|
|
138
|
+
stop = self._diag._maxiter if index.stop is None else index.stop
|
|
139
|
+
return np.array([next(self._data_generator(i)) for i in range(start, stop, step)])
|
|
140
|
+
else:
|
|
141
|
+
raise ValueError("Invalid index type. Use int or slice.")
|
|
142
|
+
|
|
143
|
+
def _data_generator(self, index):
|
|
144
|
+
if self._dim == 1:
|
|
145
|
+
if self._original_name.lower() in ['b2', 'b3', 'e1']:
|
|
146
|
+
yield 0.5 * (np.roll(self._diag[index], shift=1) + self._diag[index])
|
|
147
|
+
elif self._original_name.lower() in ['b1', 'e2', 'e3']: # it's already centered but self._data does not exist
|
|
148
|
+
yield self._diag[index]
|
|
149
|
+
else:
|
|
150
|
+
raise ValueError(f"Unknown field {self._original_name}.")
|
|
151
|
+
|
|
152
|
+
elif self._dim == 2:
|
|
153
|
+
if self._original_name in ['e1', 'b2']:
|
|
154
|
+
yield 0.5 * (np.roll(self._diag[index], shift=1, axis=0) + self._diag[index])
|
|
155
|
+
elif self._original_name in ['e2', 'b1']:
|
|
156
|
+
yield 0.5 * (np.roll(self._diag[index], shift=1, axis=1) + self._diag[index])
|
|
157
|
+
elif self._original_name in ['b3']:
|
|
158
|
+
yield 0.5 * (np.roll((0.5 * (np.roll(self._diag[index], shift=1, axis=0) + self._diag[index])), shift=1, axis=1) + (0.5 * (np.roll(self._diag[index], shift=1, axis=0) + self._diag[index])))
|
|
159
|
+
elif self._original_name in ['e3']:
|
|
160
|
+
yield self._diag[index]
|
|
161
|
+
else:
|
|
162
|
+
raise ValueError(f"Unknown field {self._original_name}.")
|
|
163
|
+
|
|
164
|
+
elif self._dim == 3:
|
|
165
|
+
raise NotImplementedError("3D field centering is not implemented yet.")
|
|
166
|
+
|
|
167
|
+
else:
|
|
168
|
+
raise ValueError(f"Unknown dimension {self._dim}.")
|