-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathparams_reader.py
More file actions
230 lines (203 loc) · 8.96 KB
/
params_reader.py
File metadata and controls
230 lines (203 loc) · 8.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
"""
This file is part of the openPMD-viewer.
It defines a function that can read standard parameters from an openPMD file.
Copyright 2015-2016, openPMD-viewer contributors
Authors: Remi Lehe, Axel Huebl
License: 3-Clause-BSD-LBNL
"""
import h5py
import numpy as np
from .utilities import is_scalar_record, get_shape, join_infile_path
def read_openPMD_params(filename, iteration, extract_parameters=True):
"""
Extract the time and some openPMD parameters from a file
Parameter
---------
filename: string
The path to the file from which parameters should be extracted
iteration : int
The iteration at which to obtain the data
extract_parameters: bool, optional
Whether to extract all parameters or only the time
(Function execution is faster when extract_parameters is False)
Returns
-------
A tuple with:
- A float corresponding to the time of this iteration in SI units
- A dictionary containing several parameters, such as the geometry, etc.
When extract_parameters is False, the second argument returned is None.
"""
# Open the file, and do a version check
f = h5py.File(filename, 'r')
version = f.attrs['openPMD'].decode()
if version[:2] != '1.':
raise ValueError(
"File %s is not supported: Invalid openPMD version: "
"%s)" % (filename, version))
# Find the base path object, and extract the time
bpath = f['/data/{0}'.format( iteration )]
t = bpath.attrs["time"] * bpath.attrs["timeUnitSI"]
# If the user did not request more parameters, close file and exit
if not extract_parameters:
f.close()
return(t, None)
# Otherwise, extract the rest of the parameters
params = {}
# Find out supported openPMD extensions claimed by this file
# note: a file might implement multiple extensions
known_extensions = {'ED-PIC': np.uint32(1)}
bitmask_all_extensions = f.attrs['openPMDextension']
params['extensions'] = []
for extension, bitmask in known_extensions.items():
# This uses a bitmask to identify activated extensions
# efficiently in static programming languages via
# a single attribute and a binary AND (&) operation.
# Standard: https://git.io/vwnMw
# Bitmasks: https://en.wikipedia.org/wiki/Mask_%28computing%29
if bitmask_all_extensions & bitmask == bitmask:
params['extensions'].append(extension)
# Find out whether fields are present and extract their metadata
fields_available = False
if ('meshesPath' in f.attrs): # Check for openPMD 1.1 files
meshes_path = f.attrs['meshesPath'].decode().strip('/')
if meshes_path in bpath.keys(): # Check for openPMD 1.0 files
fields_available = True
if fields_available:
params['avail_fields'] = []
params['fields_metadata'] = {}
# Loop through the available fields
for field_name in bpath[meshes_path].keys():
field = bpath[join_infile_path(meshes_path, field_name)]
metadata = {}
metadata['geometry'] = field.attrs['geometry'].decode()
metadata['axis_labels'] = [ coord.decode() for coord in
field.attrs['axisLabels'] ]
# Swap the order of the labels if the code that wrote the HDF5 file
# was Fortran order (i.e. reverse order with respect to Python)
if field.attrs['dataOrder'].decode() == 'F':
metadata['axis_labels'] = metadata['axis_labels'][::-1]
# Check whether the field is a vector or a scalar
if is_scalar_record(field):
metadata['type'] = 'scalar'
components = []
else:
metadata['type'] = 'vector'
components = list(field.keys())
# Register available components
metadata['avail_components'] = components
# Check the number of modes
if metadata['geometry'] == "thetaMode":
if is_scalar_record(field):
Nm, _, _ = get_shape(field)
else:
coord = list(field.keys())[0]
Nm, _, _ = get_shape(field[coord])
metadata['avail_circ_modes'] = ['all'] + \
[str(m) for m in range(int(Nm / 2) + 1)]
# Check if this a 1d, 2d or 3d Cartesian
elif metadata['geometry'] == "cartesian":
dim = len(metadata['axis_labels'])
if dim == 1:
metadata['geometry'] = "1dcartesian"
elif dim == 2:
metadata['geometry'] = "2dcartesian"
elif dim == 3:
metadata['geometry'] = "3dcartesian"
metadata['avail_circ_modes'] = []
params['avail_fields'].append( field_name )
params['fields_metadata'][field_name] = metadata
else:
params['avail_fields'] = None
# Find out whether particles are present, and if yes of which species
particles_available = False
if ('particlesPath' in f.attrs): # Check for openPMD 1.1 files
particle_path = f.attrs['particlesPath'].decode().strip('/')
if particle_path in bpath.keys(): # Check for openPMD 1.0 files
# Check that there is at least one species
if len(bpath[particle_path].keys()) > 0:
particles_available = True
if particles_available:
# Particles are present ; extract the species
params['avail_species'] = []
for species_name in bpath[particle_path].keys():
params['avail_species'].append(species_name)
# dictionary with list of record components for each species
record_components = {}
# Go through all species
for species_name in iter(params['avail_species']):
species = bpath[join_infile_path(particle_path, species_name)]
record_components[species_name] = []
# Go through all the particle records of this species
for record_name in species.keys():
# Skip the particlePatches, which are not used here.
if record_name == 'particlePatches':
continue
record = species[record_name]
if is_scalar_record(record):
# Add the name of the scalar record
record_components[species_name]. \
append(record_name)
else:
# Add each component of the vector record
for coord in record.keys():
record_components[species_name]. \
append(join_infile_path(record_name, coord))
# Simplify the name of some standard openPMD records
record_components[species_name] = \
simplify_record(record_components[species_name])
params['avail_record_components'] = record_components
# deprecated
first_species_name = next(iter(params['avail_species']))
params['avail_ptcl_quantities'] = \
record_components[first_species_name]
else:
# Particles are absent
params['avail_species'] = None
params['avail_record_components'] = None
# deprecated
params['avail_ptcl_quantities'] = None
# Close the file and return the parameters
f.close()
return(t, params)
def simplify_record(record_comps):
"""
Replace the names of some standard record by shorter names
Parameter
---------
record_comps: a list of strings
A list of available particle record components
Returns
-------
A list with shorter names, where applicable
"""
# Replace the names of the positions
if ('position/x' in record_comps) and ('positionOffset/x' in record_comps):
record_comps.remove('position/x')
record_comps.remove('positionOffset/x')
record_comps.append('x')
if ('position/y' in record_comps) and ('positionOffset/y' in record_comps):
record_comps.remove('position/y')
record_comps.remove('positionOffset/y')
record_comps.append('y')
if ('position/z' in record_comps) and ('positionOffset/z' in record_comps):
record_comps.remove('position/z')
record_comps.remove('positionOffset/z')
record_comps.append('z')
# Replace the names of the momenta
if 'momentum/x' in record_comps:
record_comps.remove('momentum/x')
record_comps.append('ux')
record_comps.append('px')
if 'momentum/y' in record_comps:
record_comps.remove('momentum/y')
record_comps.append('uy')
record_comps.append('py')
if 'momentum/z' in record_comps:
record_comps.remove('momentum/z')
record_comps.append('uz')
record_comps.append('pz')
# Replace the name for 'weights'
if 'weighting' in record_comps:
record_comps.remove('weighting')
record_comps.append('w')
return(record_comps)