Skip to content

Commit

Permalink
changed reader policy
Browse files Browse the repository at this point in the history
the default behavior of the reader classes is now to raise exceptions
whenever not dumped data is requested. This gives more control in the
program to intentionally handle that as an exception or allow to show
comprehensive error messages.
  • Loading branch information
skuschel committed Dec 28, 2014
1 parent 7b98ed9 commit bab0391
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 35 deletions.
20 changes: 10 additions & 10 deletions postpic/analyzer/particles.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ class _SingleSpeciesAnalyzer(object):
def __init__(self, dumpreader, species):
self.species = species
self._dumpreader = dumpreader
self._mass = dumpreader.getSpecies(species, 'mass')
self._charge = dumpreader.getSpecies(species, 'charge')
if self._mass is None or self._charge is None:
try:
self._mass = dumpreader.getSpecies(species, 'mass')
self._charge = dumpreader.getSpecies(species, 'charge')
except(KeyError):
self._idfy = identifyspecies(species)
self._mass = self._idfy['mass'] # SI
self._charge = self._idfy['charge'] # SI
Expand All @@ -57,13 +58,12 @@ def __getitem__(self, key):
ret = self._cache[key]
else:
ret = self._dumpreader.getSpecies(self.species, key)
if ret is not None:
ret = np.float64(ret)
if not isinstance(ret, float) and self._compressboollist is not None:
ret = ret[self._compressboollist] # avoid executing this line too often.
self._cache[key] = ret
# if memomry is low, caching could be skipped entirely.
# See commit message for benchmark.
ret = np.float64(ret)
if not isinstance(ret, float) and self._compressboollist is not None:
ret = ret[self._compressboollist] # avoid executing this line too often.
self._cache[key] = ret
# if memomry is low, caching could be skipped entirely.
# See commit message for benchmark.
return ret

def compress(self, condition, name='unknown condition'):
Expand Down
29 changes: 27 additions & 2 deletions postpic/datareader/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
but in the easiest case this is the filepath pointing to a single file
containing every information about this simulation dump. With this information
the dumpreader must be able to read all data regarding this dump (which is a
lot: X, Y, Z, Px, Py, Py, weight for all particle species, electric and
magnetic fields on grid, the grid itself, mabe particle ids,...)
lot: X, Y, Z, Px, Py, Py, weight, mass, charge, ID,.. for all particle species,
electric and magnetic fields on grid, the grid itself, mabe particle ids,...)
The Simulationreader
--------------------
Expand Down Expand Up @@ -64,6 +64,20 @@ class Dumpreader_ifc(object):
the dump holding all data of that timestep
(for example .sdf file for EPOCH, .hdf5 for some other code).
The dumpreader should provide all necessary informations in a unified interface
but at the same time it should not restrict the user to these properties of there
dump only. The recommended implementation is shown here (EPOCH and VSim reader
work like this):
All (!) data, that is saved in a single dump should be accessible via the
self.__getitem__(key) method. Together with the self.keys() method, this will ensure,
that every dumpreader works as a dictionary and every dump attribute is accessible
via this dictionary.
All other functions, which provide a unified interface should just point to the
right key. If some attribute wasnt dumped a KeyError must be thrown. This allows
classes which are using the reader to just exit if a needed property wasnt dumped
or to catch the KeyError and proceed by actively ignoring it.
It is highly recommended to also override the __str__ function.
Args:
Expand Down Expand Up @@ -123,6 +137,17 @@ def listSpecies(self):

@abc.abstractmethod
def getSpecies(self, species, attrib):
'''
This function gives access to any of the particle properties in
.._const.attribidentify
This method can behave in the following ways:
1) Return a list of scalar properties for each particle of this species
2) Return a single float (i.e. `1.2`, NOT `[1.2]`) to show that
every particle of this species has the same scalar value to thisdimmax
property assigned. This might be quite often used for charge or mass
that are defined per species.
3) Raise a KeyError if the requested property or species wasn dumped.
'''
pass

@property
Expand Down
3 changes: 2 additions & 1 deletion postpic/datareader/dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ def getSpecies(self, species, attrib):
elif attribid == 9: # weights
ret = np.repeat(1, len(self._xdata))
else:
ret = None
raise KeyError('Attrib "' + str(attrib) + '" of species "' +
str(species) + '" not present')
return ret

def __str__(self):
Expand Down
5 changes: 1 addition & 4 deletions postpic/datareader/epochsdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,7 @@ def getSpecies(self, species, attrib):
4: lambda s: 'Particles/Py/' + s,
5: lambda s: 'Particles/Pz/' + s,
10: lambda s: 'Particles/ID/' + s}
try:
ret = np.float64(self[options[attribid](species)])
except(KeyError):
ret = None
ret = np.float64(self[options[attribid](species)])
return ret

def getderived(self):
Expand Down
25 changes: 10 additions & 15 deletions postpic/datareader/vsimhdf5.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,24 +119,19 @@ def getSpecies(self, species, attrib):
Returns one of the attributes out of (x,y,z,px,py,pz,weight,ID) of
this particle species.
Valid Scalar attributes are (mass, charge).
returning None means that this particle property wasnt dumped.
Note that this is different from returning an empty list!
'''
# x, y, z, px, py, pz same as in sdf. weigt and ID not included.
attrib = _const.attribidentify[attrib]
try:
attrib = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 9: 'numPtclsInMacro',
11: 'mass', 12: 'charge'}[attrib]
if isinstance(attrib, int):
ret = np.float64(self[species])[:, attrib]
# VSim dumps gamma*v = p/m0, so multiply by mass if px, pz or pz requested
if attrib > 2:
ret = ret * self.getSpecies(species, 'mass')
return ret
else:
return np.float64(self[species].attrs[attrib])
except(KeyError):
return None
attrib = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 9: 'numPtclsInMacro',
11: 'mass', 12: 'charge'}[attrib]
if isinstance(attrib, int):
ret = np.float64(self[species])[:, attrib]
# VSim dumps gamma*v = p/m0, so multiply by mass if px, pz or pz requested
if attrib > 2:
ret = ret * self.getSpecies(species, 'mass')
return ret
else:
return np.float64(self[species].attrs[attrib])

def getderived(self):
'''
Expand Down
6 changes: 3 additions & 3 deletions test/test_dumpreader.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ def test_dims(self):
self.assertEqual(self.sr3d[20].simdimensions(), 3)

def test_general(self):
self.assertEqual(self.dr1d.getSpecies('electron', 1), None)
self.assertEqual(self.dr1d.getSpecies('electron', 2), None)
self.assertEqual(self.dr2d.getSpecies('electron', 2), None)
self.assertRaises(KeyError, self.dr1d.getSpecies, 'electron', 1)
self.assertRaises(KeyError, self.dr1d.getSpecies, 'electron', 2)
self.assertRaises(KeyError, self.dr2d.getSpecies, 'electron', 2)
self.assertEqual(len(self.dr3d.getSpecies('electron', 1)), 10000)
self.assertEqual(len(self.sr3d), 11377)
pz = self.sr3d[7777].getSpecies('electron', 'pz')
Expand Down

0 comments on commit bab0391

Please sign in to comment.