osiris-utils 1.0.9__tar.gz → 1.1.2__tar.gz

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.
@@ -1,8 +1,8 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: osiris_utils
3
- Version: 1.0.9
3
+ Version: 1.1.2
4
4
  Summary: Utilities to manipulate and visualize OSIRIS framework output data
5
- Author: ['João Pedro Ferreira Biu', 'João Cândido']
5
+ Author: ['João Pedro Ferreira Biu', 'João Cândido', 'Diogo Carvalho']
6
6
  Author-email: ['joaopedrofbiu@tecnico.ulisboa.pt']
7
7
  License: MIT
8
8
  Project-URL: Issues Tracker, https://github.com/joaopedrobiu6/osiris_utils/issues
@@ -20,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Topic :: Scientific/Engineering
22
22
  Requires-Python: >=3.11
23
- Description-Content-Type: text/markdown
23
+ Description-Content-Type: text/x-rst
24
24
  License-File: LICENSE.txt
25
25
  Requires-Dist: numpy
26
26
  Requires-Dist: matplotlib
@@ -33,10 +33,39 @@ Requires-Dist: tqdm
33
33
  Dynamic: author
34
34
  Dynamic: author-email
35
35
  Dynamic: classifier
36
+ Dynamic: description
36
37
  Dynamic: description-content-type
37
38
  Dynamic: keywords
38
39
  Dynamic: license
40
+ Dynamic: license-file
39
41
  Dynamic: project-url
40
42
  Dynamic: requires-dist
41
43
  Dynamic: requires-python
42
44
  Dynamic: summary
45
+
46
+ OSIRIS_UTILS
47
+ ============
48
+ |Pypi|
49
+
50
+ This package contains a set of utilities to open and analyze OSIRIS output files, using Python. All the methods implemented are fully integrated with `NumPy`, and use `np.ndarray` as the main data structure.
51
+
52
+ How to install it?
53
+ ------------------
54
+
55
+ To install this package, you can use `pip`::
56
+
57
+ pip install osiris_utils
58
+
59
+ To install it from source, you can clone this repository and run (in the folder containing ``setup.py``)::
60
+
61
+ pip install -e .
62
+
63
+ Documentation
64
+ -------------
65
+
66
+ The documentation is available at https://osiris-utils.readthedocs.io or via this link: `osiris-utils.readthedocs.io <https://osiris-utils.readthedocs.io>`_.
67
+
68
+ .. |Pypi| image:: https://img.shields.io/pypi/v/osiris-utils
69
+ :target: https://pypi.org/project/osiris-utils/
70
+ :alt: Pypi
71
+
@@ -0,0 +1,15 @@
1
+ from .utils import (time_estimation, filesize_estimation, transverse_average, integrate, animate_2D,
2
+ save_data, read_data, courant2D)
3
+ from .gui.gui import LAVA_Qt, LAVA
4
+ from .data.data import OsirisGridFile, OsirisRawFile, OsirisData, OsirisHIST
5
+ from .data.simulation import Simulation
6
+ from .data.diagnostic import Diagnostic
7
+
8
+ from .postprocessing.postprocess import PostProcess
9
+ from .postprocessing.derivative import Derivative, Derivative_Diagnostic
10
+ from .postprocessing.fft import FFT_Diagnostic, FastFourierTransform
11
+
12
+ from .postprocessing.mean_field_theory_single import MFT_Single
13
+ from .postprocessing.mean_field_theory import MeanFieldTheory_Diagnostic
14
+
15
+ # true div not working because of rtruediv - division is not commutative
@@ -2,7 +2,6 @@ import numpy as np
2
2
  import h5py
3
3
  import matplotlib.pyplot as plt
4
4
  import matplotlib.animation as animation
5
- from .data_readers import *
6
5
  import scipy
7
6
  import pandas as pd
8
7
 
@@ -1,8 +1,8 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: osiris_utils
3
- Version: 1.0.9
3
+ Version: 1.1.2
4
4
  Summary: Utilities to manipulate and visualize OSIRIS framework output data
5
- Author: ['João Pedro Ferreira Biu', 'João Cândido']
5
+ Author: ['João Pedro Ferreira Biu', 'João Cândido', 'Diogo Carvalho']
6
6
  Author-email: ['joaopedrofbiu@tecnico.ulisboa.pt']
7
7
  License: MIT
8
8
  Project-URL: Issues Tracker, https://github.com/joaopedrobiu6/osiris_utils/issues
@@ -20,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Topic :: Scientific/Engineering
22
22
  Requires-Python: >=3.11
23
- Description-Content-Type: text/markdown
23
+ Description-Content-Type: text/x-rst
24
24
  License-File: LICENSE.txt
25
25
  Requires-Dist: numpy
26
26
  Requires-Dist: matplotlib
@@ -33,10 +33,39 @@ Requires-Dist: tqdm
33
33
  Dynamic: author
34
34
  Dynamic: author-email
35
35
  Dynamic: classifier
36
+ Dynamic: description
36
37
  Dynamic: description-content-type
37
38
  Dynamic: keywords
38
39
  Dynamic: license
40
+ Dynamic: license-file
39
41
  Dynamic: project-url
40
42
  Dynamic: requires-dist
41
43
  Dynamic: requires-python
42
44
  Dynamic: summary
45
+
46
+ OSIRIS_UTILS
47
+ ============
48
+ |Pypi|
49
+
50
+ This package contains a set of utilities to open and analyze OSIRIS output files, using Python. All the methods implemented are fully integrated with `NumPy`, and use `np.ndarray` as the main data structure.
51
+
52
+ How to install it?
53
+ ------------------
54
+
55
+ To install this package, you can use `pip`::
56
+
57
+ pip install osiris_utils
58
+
59
+ To install it from source, you can clone this repository and run (in the folder containing ``setup.py``)::
60
+
61
+ pip install -e .
62
+
63
+ Documentation
64
+ -------------
65
+
66
+ The documentation is available at https://osiris-utils.readthedocs.io or via this link: `osiris-utils.readthedocs.io <https://osiris-utils.readthedocs.io>`_.
67
+
68
+ .. |Pypi| image:: https://img.shields.io/pypi/v/osiris-utils
69
+ :target: https://pypi.org/project/osiris-utils/
70
+ :alt: Pypi
71
+
@@ -5,10 +5,6 @@ pyproject.toml
5
5
  requirements.txt
6
6
  setup.py
7
7
  osiris_utils/__init__.py
8
- osiris_utils/data.py
9
- osiris_utils/data_readers.py
10
- osiris_utils/gui.py
11
- osiris_utils/mean_field_theory.py
12
8
  osiris_utils/utils.py
13
9
  osiris_utils.egg-info/PKG-INFO
14
10
  osiris_utils.egg-info/SOURCES.txt
@@ -7,19 +7,19 @@ from setuptools import find_packages, setup
7
7
  here = os.path.abspath(os.path.dirname(__file__))
8
8
 
9
9
 
10
- # with open(os.path.join(here, 'README.md'), encoding='utf-8') as f:
11
- # long_description = f.read()
10
+ with open(os.path.join(here, 'README.rst'), encoding='utf-8') as f:
11
+ long_description = f.read()
12
12
 
13
13
  with open(os.path.join(here, 'requirements.txt'), encoding='utf-8') as f:
14
14
  requirements = f.read().splitlines()
15
15
 
16
16
  setup(
17
17
  name='osiris_utils',
18
- version='v1.0.9',
18
+ version='v1.1.2',
19
19
  description=('Utilities to manipulate and visualize OSIRIS framework output data'),
20
- # long_description=long_description,
21
- long_description_content_type='text/markdown',
22
- author=['João Pedro Ferreira Biu', 'João Cândido'],
20
+ long_description=long_description,
21
+ long_description_content_type='text/x-rst',
22
+ author=['João Pedro Ferreira Biu', 'João Cândido', 'Diogo Carvalho'],
23
23
  author_email=['joaopedrofbiu@tecnico.ulisboa.pt'],
24
24
  license='MIT',
25
25
  classifiers=[
@@ -1,6 +0,0 @@
1
- from .utils import (time_estimation, filesize_estimation, transverse_average, integrate, animate_2D,
2
- save_data, read_data, courant2D)
3
- from .data_readers import open1D, open2D, open3D, read_osiris_file
4
- from .gui import LAVA_Qt, LAVA
5
- from .data import OsirisGridFile, OsirisRawFile, OsirisData
6
- from .mean_field_theory import MeanFieldTheory
@@ -1,477 +0,0 @@
1
- import numpy as np
2
- import h5py
3
-
4
- class OsirisData():
5
- '''
6
- Parent class for Osiris data files.
7
-
8
- Input:
9
- ------
10
- - filename: the path to the HDF5 file
11
- Attributes:
12
- ----------
13
- - filename - the path to the HDF5 file
14
- str
15
- - file - the HDF5 file
16
- h5py.File
17
- - verbose - if True, the class will print messages
18
- bool
19
- - dt - the time step
20
- float
21
- - dim - the number of dimensions
22
- int
23
- - time - the time and its units
24
- list [time, units]
25
- list [float, str]
26
- - iter - the iteration number
27
- int
28
- - name - the name of the data
29
- str
30
- - type - the type of data
31
- str
32
- '''
33
- def __init__(self, filename):
34
- self._filename = filename
35
- self._file = None
36
-
37
- self._verbose = False
38
-
39
- self._open_file(self._filename)
40
- self._load_basic_attributes(self._file)
41
-
42
- def _load_basic_attributes(self, f: h5py.File) -> None:
43
- '''Load common attributes from HDF5 file'''
44
- self._dt = float(f['SIMULATION'].attrs['DT'][0])
45
- self._dim = int(f['SIMULATION'].attrs['NDIMS'][0])
46
- self._time = [float(f.attrs['TIME'][0]), f.attrs['TIME UNITS'][0].decode('utf-8')]
47
- self._iter = int(f.attrs['ITER'][0])
48
- self._name = f.attrs['NAME'][0].decode('utf-8')
49
- self._type = f.attrs['TYPE'][0].decode('utf-8')
50
-
51
- def verbose(self, verbose: bool = True):
52
- '''
53
- Set the verbosity of the class
54
-
55
- Parameters
56
- ----------
57
- verbose : bool, optional
58
- If True, the class will print messages, by default True when calling (False when not calling)
59
- '''
60
- self._verbose = verbose
61
-
62
- def _open_file(self, filename):
63
- '''
64
- Open the HDF5 file.
65
-
66
- Parameters
67
- ----------
68
- filename : str
69
- The path to the HDF5 file.
70
- '''
71
- if self._verbose: print(f'Opening file > {filename}')
72
- self._file = h5py.File(self._filename, 'r')
73
-
74
- def _close_file(self):
75
- '''
76
- Close the HDF5 file.
77
- '''
78
- if self._verbose: print('Closing file')
79
- if self._file:
80
- self._file.close()
81
-
82
- @property
83
- def dt(self):
84
- '''
85
- Returns
86
- -------
87
- float
88
- The time step
89
- '''
90
- return self._dt
91
- @property
92
- def dim(self):
93
- '''
94
- Returns
95
- -------
96
- int
97
- The number of dimensions
98
- '''
99
- return self._dim
100
- @property
101
- def time(self):
102
- '''
103
- Returns
104
- -------
105
- list
106
- The time and its units
107
- '''
108
- return self._time
109
- @property
110
- def iter(self):
111
- '''
112
- Returns
113
- -------
114
- int
115
- The iteration number
116
- '''
117
- return self._iter
118
- @property
119
- def name(self):
120
- '''
121
- Returns
122
- -------
123
- str
124
- The name of the data
125
- '''
126
- return self._name
127
- @property
128
- def type(self):
129
- '''
130
- Returns
131
- -------
132
- str
133
- The type of data
134
- '''
135
- return self._type
136
-
137
- class OsirisGridFile(OsirisData):
138
- '''
139
- Class to read the grid data from an OSIRIS HDF5 file.
140
-
141
- Input:
142
- -----
143
- - filename: the path to the HDF5 file
144
-
145
- Attributes:
146
- ----------
147
- - filename - the path to the HDF5 file
148
- str
149
- - file - the HDF5 file
150
- h5py.File
151
- - verbose - if True, the class will print messages
152
- bool
153
- - dt - the time step
154
- float
155
- - dim - the number of dimensions
156
- int
157
- - time - the time and its units
158
- list [time, units]
159
- list [float, str]
160
- - iter - the iteration number
161
- int
162
- - name - the name of the data
163
- str
164
- - type - the type of data
165
- str
166
- - grid - the grid data ((x1.min, x1.max), (x2.min, x2.max), (x3.min, x3.max))
167
- numpy.ndarray
168
- - nx - the number of grid points (nx1, nx2, nx3)
169
- numpy.ndarray
170
- - dx - the grid spacing (dx1, dx2, dx3)
171
- numpy.ndarray
172
- - axis - the axis data [(name_x1, units_x1, long_name_x1, type_x1), ...]
173
- list of dictionaries
174
- - data: the data (numpy array) with shape (nx1, nx2, nx3) (Transpose to use `plt.imshow`)
175
- numpy.ndarray
176
- - units - the units of the data
177
- str
178
- - label - the label of the data (LaTeX formatted)
179
-
180
- '''
181
- def __init__(self, filename):
182
- super().__init__(filename)
183
-
184
- variable_key = self._get_variable_key(self._file)
185
-
186
- self._units = self._file.attrs['UNITS'][0].decode('utf-8')
187
- self._label = self._file.attrs['LABEL'][0].decode('utf-8')
188
- self._FFTdata = None
189
-
190
- data = np.array(self._file[variable_key][:])
191
-
192
- axis = list(self._file['AXIS'].keys())
193
- if len(axis) == 1:
194
- self._grid = self._file['AXIS/' + axis[0]][()]
195
- self._nx = len(data)
196
- self._dx = (self.grid[1] - self.grid[0] ) / self.nx
197
- self._x = np.arange(self.grid[0], self.grid[1], self.dx)
198
- else:
199
- grid = []
200
- for ax in axis: grid.append(self._file['AXIS/' + ax][()])
201
- self._grid = np.array(grid)
202
- self._nx = self._file[variable_key][()].transpose().shape
203
- self._dx = (self.grid[:, 1] - self.grid[:, 0])/self.nx
204
- self._x = [np.arange(self.grid[i, 0], self.grid[i, 1], self.dx[i]) for i in range(self.dim)]
205
-
206
- self._axis = []
207
- for ax in axis:
208
- axis_data = {
209
- 'name': self._file['AXIS/'+ax].attrs['NAME'][0].decode('utf-8'),
210
- 'units': self._file['AXIS/'+ax].attrs['UNITS'][0].decode('utf-8'),
211
- 'long_name': self._file['AXIS/'+ax].attrs['LONG_NAME'][0].decode('utf-8'),
212
- 'type': self._file['AXIS/'+ax].attrs['TYPE'][0].decode('utf-8'),
213
- 'plot_label': rf'${self._file["AXIS/"+ax].attrs["LONG_NAME"][0].decode("utf-8")}$ $[{self._file["AXIS/"+ax].attrs["UNITS"][0].decode("utf-8")}]$',
214
- }
215
- self._axis.append(axis_data)
216
-
217
- self._data = np.ascontiguousarray(data.T)
218
-
219
- self._close_file()
220
-
221
- def _load_basic_attributes(self, f: h5py.File) -> None:
222
- '''Load common attributes from HDF5 file'''
223
- self._dt = float(f['SIMULATION'].attrs['DT'][0])
224
- self._dim = int(f['SIMULATION'].attrs['NDIMS'][0])
225
- self._time = [float(f.attrs['TIME'][0]), f.attrs['TIME UNITS'][0].decode('utf-8')]
226
- self._iter = int(f.attrs['ITER'][0])
227
- self._name = f.attrs['NAME'][0].decode('utf-8')
228
- self._type = f.attrs['TYPE'][0].decode('utf-8')
229
-
230
- def _get_variable_key(self, f: h5py.File) -> str:
231
- return next(k for k in f.keys() if k not in {'AXIS', 'SIMULATION'})
232
-
233
- # Getters
234
- @property
235
- def grid(self):
236
- '''
237
- Returns
238
- -------
239
- numpy.ndarray
240
- The grid data ((x1.min, x1.max), (x2.min, x2.max), (x3.min, x3.max
241
- '''
242
- return self._grid
243
- @property
244
- def nx(self):
245
- '''
246
- Returns
247
- -------
248
- numpy.ndarray
249
- The number of grid points (nx1, nx2, nx3)
250
- '''
251
- return self._nx
252
- @property
253
- def dx(self):
254
- '''
255
- Returns
256
- -------
257
- numpy.ndarray
258
- The grid spacing (dx1, dx2, dx3)
259
- '''
260
- return self._dx
261
- @property
262
- def x(self):
263
- '''
264
- Returns
265
- -------
266
- numpy.ndarray
267
- The grid points in each axis
268
- '''
269
- return self._x
270
- @property
271
- def axis(self):
272
- '''
273
- Returns
274
- -------
275
- list of dictionaries
276
- The axis data [(name_x1, units_x1, long_name_x1, type_x1), ...]
277
- '''
278
- return self._axis
279
- @property
280
- def data(self):
281
- '''
282
- Returns
283
- -------
284
- numpy.ndarray
285
- The data (numpy array) with shape (nx1, nx2, nx3) (Transpose to use `plt.imshow`)
286
- '''
287
- return self._data
288
- @property
289
- def units(self):
290
- '''
291
- Returns
292
- -------
293
- str
294
- The units of the data (LaTeX formatted)
295
- '''
296
- return self._units
297
- @property
298
- def label(self):
299
- '''
300
- Returns
301
- -------
302
- str
303
- The label of the data (LaTeX formatted)
304
- '''
305
- return self._label
306
- @property
307
- def FFTdata(self):
308
- '''
309
- Returns
310
- -------
311
- numpy.ndarray
312
- The FFT of the data
313
- '''
314
- if self._FFTdata is None:
315
- raise ValueError('The FFT of the data has not been computed yet. Compute it using the FFT method.')
316
- return self._FFTdata
317
- # Setters
318
- @data.setter
319
- def data(self, data):
320
- '''
321
- Set the data attribute
322
- '''
323
- self._data = data
324
-
325
- def __str__(self):
326
- # write me a template to print with the name, label, units, time, iter, grid, nx, dx, axis, dt, dim in a logical way
327
- return rf'{self.name}' + f'\n' + rf'Time: [{self.time[0]} {self.time[1]}], dt = {self.dt}' + f'\n' + f'Iteration: {self.iter}' + f'\n' + f'Grid: {self.grid}' + f'\n' + f'dx: {self.dx}' + f'\n' + f'Dimensions: {self.dim}D'
328
-
329
-
330
- def __array__(self):
331
- return np.asarray(self.data)
332
-
333
-
334
- def _yeeToCellCorner1d(self, boundary):
335
- '''
336
- Converts 1d EM fields from a staggered Yee mesh to a grid with field values centered on the corner of the cell (the corner of the cell [1] has coordinates [1])
337
- '''
338
-
339
- if self.name.lower() in ['b2', 'b3', 'e1']:
340
- if boundary == 'periodic': return 0.5 * (np.roll(self.data, shift=1) + self.data)
341
- else: return 0.5 * (self.data[1:] + self.data[:-1])
342
- elif self.name.lower() in ['b1', 'e2', 'e3']:
343
- if boundary == 'periodic': return self.data
344
- else: return self.data[1:]
345
- else:
346
- raise TypeError(f'This method expects magnetic or electric field grid data but received \'{self.name}\' instead')
347
-
348
-
349
- def _yeeToCellCorner2d(self, boundary):
350
- '''
351
- Converts 2d EM fields from a staggered Yee mesh to a grid with field values centered on the corner of the cell (the corner of the cell [1,1] has coordinates [1,1])
352
- '''
353
-
354
- if self.name.lower() in ['e1', 'b2']:
355
- if boundary == 'periodic': return 0.5 * (np.roll(self.data, shift=1, axis=0) + self.data)
356
- else: return 0.5 * (self.data[1:, 1:] + self.data[:-1, 1:])
357
- elif self.name.lower() in ['e2', 'b1']:
358
- if boundary == 'periodic': return 0.5 * (np.roll(self.data, shift=1, axis=1) + self.data)
359
- else: return 0.5 * (self.data[1:, 1:] + self.data[1:, :-1])
360
- elif self.name.lower() in ['b3']:
361
- if boundary == 'periodic':
362
- return 0.5 * (np.roll((0.5 * (np.roll(self.data, shift=1, axis=0) + self.data)), shift=1, axis=1) + (0.5 * (np.roll(self.data, shift=1, axis=0) + self.data)))
363
- else:
364
- return 0.25 * (self.data[1:, 1:] + self.data[:-1, 1:] + self.data[1:, :-1] + self.data[:-1, :-1])
365
- elif self.name.lower() in ['e3']:
366
- if boundary == 'periodic': return self.data
367
- else: return self.data[1:, 1:]
368
- else:
369
- raise TypeError(f'This method expects magnetic or electric field grid data but received \'{self.name}\' instead')
370
-
371
-
372
- def _yeeToCellCorner3d(self, boundary):
373
- '''
374
- Converts 3d EM fields from a staggered Yee mesh to a grid with field values centered on the corner of the cell (the corner of the cell [1,1,1] has coordinates [1,1,1])
375
- '''
376
- if boundary == 'periodic':
377
- raise ValueError('Centering field from 3D simulations considering periodic boundary conditions is not implemented yet')
378
- if self.name.lower() == 'b1':
379
- return 0.25 * (self.data[1:, 1:, 1:] + self.data[1:, :-1, 1:] + self.data[1:, 1:, :-1] + self.data[1:, :-1, :-1])
380
- elif self.name.lower() == 'b2':
381
- return 0.25 * (self.data[1:, 1:, 1:] + self.data[:-1, 1:, 1:] + self.data[1:, 1:, :-1] + self.data[:-1, 1:, :-1])
382
- elif self.name.lower() == 'b3':
383
- return 0.25 * (self.data[1:, 1:, 1:] + self.data[:-1, 1:, 1:] + self.data[1:, :-1, 1:] + self.data[:-1, :-1, 1:])
384
- elif self.name.lower() == 'e1':
385
- return 0.5 * (self.data[1:, 1:, 1:] + self.data[:-1, 1:, 1:])
386
- elif self.name.lower() == 'e2':
387
- return 0.5 * (self.data[1:, 1:, 1:] + self.data[1:, :-1, 1:])
388
- elif self.name.lower() == 'e3':
389
- return 0.5 * (self.data[1:, 1:, 1:] + self.data[1:, 1:, :-1])
390
- else:
391
- raise TypeError(f'This method expects magnetic or electric field grid data but received \'{self.name}\' instead')
392
-
393
- def yeeToCellCorner(self, boundary=None):
394
- ''''
395
- Converts EM fields from a staggered Yee mesh to a grid with field values centered on the corner of the cell.'
396
- Can be used for 1D, 2D and 3D simulations.'
397
- Creates a new attribute `data_centered` with the centered data.'
398
- '''
399
-
400
- cases = {'b1', 'b2', 'b3', 'e1', 'e2', 'e3'}
401
- if self.name not in cases:
402
- raise TypeError(f'This method expects magnetic or electric field grid data but received \'{self.name}\' instead')
403
-
404
- if self.dim == 1:
405
- self.data_centered = self._yeeToCellCorner1d(boundary)
406
- return self.data_centered
407
- elif self.dim == 2:
408
- self.data_centered = self._yeeToCellCorner2d(boundary)
409
- return self.data_centered
410
- elif self.dim == 3:
411
- self.data_centered = self._yeeToCellCorner3d(boundary)
412
- return self.data_centered
413
- else:
414
- raise ValueError(f'Dimension {self.dim} is not supported')
415
-
416
- def FFT(self, axis=(0, )):
417
- '''
418
- Computes the Fast Fourier Transform of the data along the specified axis and shifts the zero frequency to the center.
419
- Transforms the data to the frequency domain. A(x, y, z) -> A(kx, ky, kz)
420
- '''
421
- datafft = np.fft.fftn(self.data, axes=axis)
422
- self._FFTdata = np.fft.fftshift(datafft, axes=axis)
423
-
424
-
425
-
426
- class OsirisRawFile(OsirisData):
427
- '''
428
- Class to read the raw data from an OSIRIS HDF5 file.
429
-
430
- Input:
431
- - filename: the path to the HDF5 file
432
-
433
- Attributes:
434
- - axis - a dictionary where each key is a dataset name, and each value is another dictionary containing
435
- name (str): The name of the quantity (e.g., r'x1', r'ene').
436
- units (str): The units associated with that dataset in LaTeX (e.g., r'c/\\omega_p', r'm_e c^2').
437
- long_name (str): The name of the quantity in LaTeX (e.g., r'x_1', r'En2').
438
- dictionary of dictionaries
439
- - data - a dictionary where each key is a dataset name, and each value is the data
440
- dictionary of np.arrays
441
- - dim - the number of dimensions
442
- int
443
- - dt - the time step
444
- float
445
- - grid - maximum and minimum coordinates of the box, for each axis
446
- numpy.ndarray(dim,2)
447
- - iter - the iteration number
448
- int
449
- - name - the name of the species
450
- str
451
- - time - the time and its units
452
- list [time, units]
453
- list [float, str]
454
- - type - type of data (particles in the case of raw files)
455
- str
456
-
457
- '''
458
-
459
- def __init__(self, filename):
460
- super().__init__(filename)
461
-
462
- self.grid = np.array([self._file['SIMULATION'].attrs['XMIN'], self._file['SIMULATION'].attrs['XMAX']]).T
463
-
464
- self.data = {}
465
- self.axis = {}
466
- for key in self._file.keys():
467
- if key == 'SIMULATION': continue
468
-
469
- self.data[key] = np.array(self._file[key][()])
470
-
471
- idx = np.where(self._file.attrs['QUANTS'] == str(key).encode('utf-8'))
472
- axis_data = {
473
- 'name': self._file.attrs['QUANTS'][idx][0].decode('utf-8'),
474
- 'units': self._file.attrs['UNITS'][idx][0].decode('utf-8'),
475
- 'long_name': self._file.attrs['LABELS'][idx][0].decode('utf-8'),
476
- }
477
- self.axis[key] = axis_data
@@ -1,119 +0,0 @@
1
- '''
2
- This file contains methods to create datasets to train the neural network.
3
- '''
4
-
5
- import numpy as np
6
- from .utils import *
7
- import os
8
- import h5py
9
-
10
- def read_osiris_file(filename, pressure = False):
11
- f = h5py.File(filename, 'r+')
12
- atr = f.attrs
13
- k = [key for key in f.keys()]
14
- if 'SIMULATION' in k:
15
- attrs1 = atr
16
- attrs2 = f['SIMULATION'].attrs
17
- attrs = {}
18
- for i in range(len(attrs1)):
19
- attrs[[key for key in attrs1][i]] = [value for value in attrs1.values()][i]
20
- for i in range(len(attrs2)):
21
- attrs[[key for key in attrs2][i]] = [value for value in attrs2.values()][i]
22
- ax = f.get([key for key in f.keys()][0])
23
- leanx = len(ax)
24
- axis = []
25
- for i in range(leanx):
26
- axis.append(ax.get([key for key in ax.keys()][i]))
27
- if 'SIMULATION' in k and pressure == False:
28
- data = f.get([key for key in f.keys()][2])
29
- data.attrs['UNITS'] = attrs1['UNITS']
30
- data.attrs['LONG_NAME'] = attrs1['LABEL']
31
- elif 'SIMULATION' in k and pressure == True:
32
- data = f.get([key for key in f.keys()][1])
33
- data.attrs['UNITS'] = attrs1['UNITS']
34
- data.attrs['LONG_NAME'] = attrs1['LABEL']
35
- else:
36
- data = f.get([key for key in f.keys()][1])
37
-
38
- return attrs, axis, data
39
-
40
- def open1D(filename, pressure = False):
41
- '''
42
- Open a 1D OSIRIS file and return the x axis and the data array.
43
-
44
- Parameters
45
- ----------
46
- filename : str
47
- The path to the file.
48
-
49
- Returns
50
- -------
51
- x : numpy.ndarray
52
- The x axis.
53
- data_array : numpy.ndarray
54
- The data array.
55
- '''
56
- attrs, axes, data = read_osiris_file(filename, pressure)
57
- datash = data.shape
58
- ax1 = axes[0]
59
- x = np.linspace(ax1[0], ax1[1], datash[0])
60
- data_array = data[:]
61
- return x, data_array, [attrs, axes, data]
62
-
63
- def open2D(filename, pressure = False):
64
- '''
65
- Open a 2D OSIRIS file and return the x and y axes and the data array.
66
-
67
- Parameters
68
- ----------
69
- filename : str
70
- The path to the file.
71
-
72
- Returns
73
- -------
74
- x : numpy.ndarray
75
- The x axis.
76
- y : numpy.ndarray
77
- The y axis.
78
- data_array : numpy.ndarray
79
- The data array.
80
- '''
81
- attrs, axes, data = read_osiris_file(filename, pressure)
82
- datash = data.shape
83
- ax1 = axes[0]
84
- ax2 = axes[1]
85
- x = np.linspace(ax1[0], ax1[1], datash[-1])
86
- y = np.linspace(ax2[0], ax2[1], datash[-2])
87
- data_array = data[:]
88
- return x, y, data_array, [attrs, axes, data]
89
-
90
- def open3D(filename):
91
- '''
92
- Open a 3D OSIRIS file and return the x, y and z axes and the data array.
93
-
94
- Parameters
95
- ----------
96
- filename : str
97
- The path to the file.
98
-
99
- Returns
100
- -------
101
- x : numpy.ndarray
102
- The x axis.
103
- y : numpy.ndarray
104
- The y axis.
105
- z : numpy.ndarray
106
- The z axis.
107
- data_array : numpy.ndarray
108
- The data array.
109
- '''
110
- attrs, axes, data = read_osiris_file(filename)
111
- datash = data.shape
112
- ax1 = axes[0]
113
- ax2 = axes[1]
114
- ax3 = axes[2]
115
- x = np.linspace(ax1[0], ax1[1], datash[-1])
116
- y = np.linspace(ax2[0], ax2[1], datash[-2])
117
- z = np.linspace(ax3[0], ax3[1], datash[-3])
118
- data_array = data[:]
119
- return x, y, z, data_array, [attrs, axes, data]
@@ -1,266 +0,0 @@
1
- import sys
2
- import os
3
- from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QPushButton,
4
- QFileDialog, QMessageBox, QComboBox, QHBoxLayout,
5
- QVBoxLayout, QLabel, QLineEdit, QFrame, QDoubleSpinBox)
6
- from PySide6.QtCore import Qt
7
- from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
8
- import matplotlib.pyplot as plt
9
- from matplotlib.colors import LogNorm
10
- import numpy as np
11
- from .data import OsirisGridFile # Update import as needed
12
- from .utils import integrate, transverse_average # Update import as needed
13
-
14
- class LAVA_Qt(QMainWindow):
15
- def __init__(self):
16
- super().__init__()
17
- self.setWindowTitle('LAVA (LabAstro Visualization Assistant) - OSIRIS Data Grid Viewer')
18
- self.setGeometry(100, 100, 1000, 600)
19
-
20
- # Initialize data
21
- self.data_info = None
22
- self.dims = 0
23
- self.current_ax = None
24
- self.current_folder = None
25
-
26
- # Main widget and layout
27
- self.main_widget = QWidget()
28
- self.setCentralWidget(self.main_widget)
29
- self.main_layout = QVBoxLayout(self.main_widget)
30
-
31
- # Create UI elements
32
- self.create_controls()
33
- self.create_labels_section()
34
- self.create_plot_area()
35
-
36
- def create_controls(self):
37
- # Control buttons frame
38
- control_frame = QWidget()
39
- control_layout = QHBoxLayout(control_frame)
40
-
41
- # Buttons
42
- self.browse_btn = QPushButton('Browse Folder')
43
- self.browse_btn.clicked.connect(self.load_folder)
44
- self.save_btn = QPushButton('Save Plot')
45
- self.save_btn.clicked.connect(self.save_plot)
46
-
47
- # File selector
48
- self.file_selector = QComboBox()
49
- self.file_selector.setPlaceholderText('Select file...')
50
- self.file_selector.currentIndexChanged.connect(self.file_selection_changed)
51
- self.file_selector.view().setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
52
- self.file_selector.setSizeAdjustPolicy(QComboBox.AdjustToContents)
53
-
54
- # Plot type combo box
55
- self.plot_combo = QComboBox()
56
- self.plot_combo.addItem('Select Plot Type')
57
- self.plot_combo.currentTextChanged.connect(self.plot_data)
58
-
59
- control_layout.addWidget(self.browse_btn)
60
- control_layout.addWidget(self.save_btn)
61
- control_layout.addWidget(QLabel('Files:'))
62
- control_layout.addWidget(self.file_selector)
63
- control_layout.addWidget(QLabel('Plot Type:'))
64
- control_layout.addWidget(self.plot_combo)
65
- self.main_layout.addWidget(control_frame)
66
-
67
- def create_labels_section(self):
68
- # Labels frame
69
- labels_frame = QWidget()
70
- labels_layout = QHBoxLayout(labels_frame)
71
-
72
- # Title and labels
73
- self.title_edit = QLineEdit()
74
- self.xlabel_edit = QLineEdit()
75
- self.ylabel_edit = QLineEdit()
76
-
77
- # Connect text changes
78
- self.title_edit.textChanged.connect(self.update_plot_labels)
79
- self.xlabel_edit.textChanged.connect(self.update_plot_labels)
80
- self.ylabel_edit.textChanged.connect(self.update_plot_labels)
81
-
82
-
83
- labels_layout.addWidget(QLabel('Title:'))
84
- labels_layout.addWidget(self.title_edit)
85
- labels_layout.addWidget(QLabel('X Label:'))
86
- labels_layout.addWidget(self.xlabel_edit)
87
- labels_layout.addWidget(QLabel('Y Label:'))
88
- labels_layout.addWidget(self.ylabel_edit)
89
-
90
- # define the size of the labels frame
91
- self.main_layout.addWidget(labels_frame)
92
-
93
-
94
- def load_folder(self):
95
- folder_dialog = QFileDialog()
96
- folderpath = folder_dialog.getExistingDirectory(
97
- self, 'Select Folder with HDF5 Files'
98
- )
99
-
100
- if not folderpath:
101
- return
102
-
103
- try:
104
- self.current_folder = folderpath
105
- self.file_selector.clear()
106
-
107
- # Find all .h5 files
108
- h5_files = [f for f in os.listdir(folderpath) if f.endswith('.h5')]
109
- # all the files end with xxxxxx.h5 so we can use this to order them by the number
110
- def sort_key(filename):
111
- try:
112
- # Split filename into parts and get the numeric portion
113
- base = os.path.splitext(filename)[0] # Remove .h5
114
- numeric_part = base.split('-')[-1] # Get last part after -
115
- return int(numeric_part)
116
- except (IndexError, ValueError):
117
- return 0 # Fallback for malformed filenames
118
-
119
- h5_files.sort(key=sort_key)
120
-
121
- if not h5_files:
122
- raise ValueError('No HDF5 files found in selected folder')
123
-
124
- self.file_selector.addItems(h5_files)
125
- self.file_selector.setCurrentIndex(0)
126
-
127
- except Exception as e:
128
- QMessageBox.critical(self, 'Error', str(e))
129
-
130
- def file_selection_changed(self, index):
131
- '''Handle file selection change in the combo box'''
132
- if index >= 0 and self.current_folder:
133
- filename = self.file_selector.itemText(index)
134
- self.process_file(filename)
135
-
136
- def process_file(self, filename):
137
- try:
138
- filepath = os.path.join(self.current_folder, filename)
139
- gridfile = OsirisGridFile(filepath)
140
- self.dims = len(gridfile.axis)
141
- self.type = gridfile.type
142
-
143
- if self.type == 'grid':
144
- if self.dims == 1:
145
- x = np.arange(gridfile.grid[0], gridfile.grid[1], gridfile.dx)
146
- self.xlabel_edit.setText(r'$%s$ [$%s$]' % (gridfile.axis[0]['long_name'], gridfile.axis[0]['units']))
147
- self.ylabel_edit.setText(r'$%s$ [$%s$]' % (gridfile.label, gridfile.units))
148
- self.data_info = (x, gridfile.data)
149
- elif self.dims == 2:
150
- x = np.arange(gridfile.grid[0][0], gridfile.grid[0][1], gridfile.dx[0])
151
- y = np.arange(gridfile.grid[1][0], gridfile.grid[1][1], gridfile.dx[1])
152
- self.xlabel_edit.setText(r'$%s$ [$%s$]' % (gridfile.axis[0]['long_name'], gridfile.axis[0]['units']))
153
- self.ylabel_edit.setText(r'$%s$ [$%s$]' % (gridfile.axis[1]['long_name'], gridfile.axis[1]['units']))
154
- self.data_info = (x, y, gridfile.data)
155
- elif self.dims == 3:
156
- raise ValueError('3D not supported yet')
157
- else:
158
- raise ValueError('Unsupported dimensionality')
159
-
160
- self.title_edit.setText(r'$%s$ [$%s$]' %( gridfile.label, gridfile.units))
161
- self.update_plot_menu()
162
- self.plot_data()
163
-
164
- else:
165
- QMessageBox.information(self, 'Info', f'{self.type} data not supported yet')
166
-
167
- except Exception as e:
168
- QMessageBox.critical(self, 'Error', str(e))
169
-
170
- def create_plot_area(self):
171
- # Matplotlib figure and canvas
172
- self.figure = plt.figure(figsize=(8, 6))
173
- self.canvas = FigureCanvas(self.figure)
174
- self.main_layout.addWidget(self.canvas)
175
-
176
- def update_plot_labels(self):
177
- if self.current_ax:
178
- self.current_ax.set_xlabel(self.xlabel_edit.text())
179
- self.current_ax.set_ylabel(self.ylabel_edit.text())
180
- self.figure.suptitle(self.title_edit.text())
181
- self.canvas.draw()
182
-
183
- def plot_data(self):
184
- self.figure.clear()
185
- if self.dims == 1:
186
- self.plot_1d()
187
- elif self.dims == 2:
188
- self.plot_2d()
189
- self.update_plot_labels()
190
- self.canvas.draw()
191
-
192
- def plot_1d(self):
193
- x, data = self.data_info
194
- self.current_ax = self.figure.add_subplot(111)
195
- plot_type = self.plot_combo.currentText()
196
-
197
- if 'Line' in plot_type:
198
- self.current_ax.plot(x, data)
199
- elif 'Scatter' in plot_type:
200
- self.current_ax.scatter(x, data)
201
-
202
- self.current_ax.set_xlabel(self.xlabel_edit.text())
203
- self.current_ax.set_ylabel(self.ylabel_edit.text())
204
- self.figure.suptitle(self.title_edit.text())
205
-
206
- def plot_2d(self):
207
- x, y, data = self.data_info
208
- self.current_ax = self.figure.add_subplot(111)
209
- plot_type = self.plot_combo.currentText()
210
-
211
- if 'Quantity' in plot_type:
212
- img = self.current_ax.imshow(data.T, extent=(x[0], x[-1], y[0], y[-1]), origin='lower', aspect='auto')
213
- self.figure.colorbar(img)
214
- elif 'Integral' in plot_type:
215
- avg = integrate(transverse_average(data), x[-1]/len(x))
216
- self.current_ax.plot(x, avg)
217
- elif 'Transverse' in plot_type:
218
- avg = transverse_average(data)
219
- self.current_ax.plot(x, avg)
220
- elif 'Phase' in plot_type:
221
- img = self.current_ax.imshow(np.abs(-data.T), extent=(x[0], x[-1], y[0], y[-1]), origin='lower', aspect='auto', norm=LogNorm())
222
- self.figure.colorbar(img)
223
-
224
- self.current_ax.set_xlabel(self.xlabel_edit.text())
225
- self.current_ax.set_ylabel(self.ylabel_edit.text())
226
- self.figure.suptitle(self.title_edit.text())
227
-
228
- def update_plot_menu(self):
229
-
230
- # Save current plot type before clearing
231
- current_plot_type = self.plot_combo.currentText()
232
- self.plot_combo.clear()
233
-
234
- # Determine items based on dimensions
235
- if self.dims == 1:
236
- items = ['Line Plot', 'Scatter Plot']
237
- elif self.dims == 2:
238
- items = ['Quantity Plot', 'T. Average Integral', 'Transverse Average', 'Phase Space']
239
- else:
240
- items = []
241
-
242
- self.plot_combo.addItems(items)
243
-
244
- # Restore previous selection if possible
245
- if current_plot_type in items:
246
- self.plot_combo.setCurrentText(current_plot_type)
247
- else:
248
- self.plot_combo.setCurrentIndex(0 if items else -1)
249
-
250
- def save_plot(self):
251
- file_dialog = QFileDialog()
252
- filepath, _ = file_dialog.getSaveFileName(
253
- self, 'Save Plot', '', 'PNG Files (*.png);;PDF Files (*.pdf)'
254
- )
255
-
256
- if filepath:
257
- self.figure.savefig(filepath, dpi=800, bbox_inches='tight')
258
-
259
- def LAVA():
260
- app = QApplication(sys.argv)
261
- window = LAVA_Qt()
262
- window.show()
263
- sys.exit(app.exec())
264
-
265
- if __name__ == '__main__':
266
- LAVA()
@@ -1,52 +0,0 @@
1
- import numpy as np
2
- from .data import OsirisGridFile
3
-
4
-
5
- class MeanFieldTheory(OsirisGridFile):
6
- '''
7
- Class to handle the mean field theory on data. Inherits from OsirisGridFile.
8
-
9
- Parameters
10
- ----------
11
- source : str or OsirisGridFile
12
- The filename or an OsirisGridFile object.
13
- axis : int
14
- The axis to average over.
15
- '''
16
- def __init__(self, source, axis=1):
17
- if isinstance(source, OsirisGridFile):
18
- self.__dict__.update(source.__dict__)
19
- else:
20
- super().__init__(source)
21
- self._compute_mean_field(axis=axis)
22
-
23
- def _compute_mean_field(self, axis=1):
24
- self._average = np.expand_dims(np.mean(self.data, axis=axis), axis=axis)
25
- self._fluctuations = self.data - self._average
26
-
27
- def __array__(self):
28
- return self.data
29
-
30
- @property
31
- def average(self):
32
- return self._average
33
-
34
- @property
35
- def delta(self):
36
- return self._fluctuations
37
-
38
- def __str__(self):
39
- return super().__str__() + f'\nAverage: {self.average.shape}\nDelta: {self.delta.shape}'
40
-
41
- def derivative(self, field, axis=0):
42
- '''
43
- Compute the derivative of the average or the fluctuations.
44
-
45
- Parameters
46
- ----------
47
- field : MeanFieldTheory.average or MeanFieldTheory.delta
48
- The field to compute the derivative.
49
- axis : int
50
- The axis to compute the derivative.
51
- '''
52
- return np.gradient(field, self.dx[axis], axis=0)
File without changes
File without changes
File without changes
File without changes