PageRenderTime 649ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/postpic/datareader/openPMDh5.py

https://gitlab.com/c-3_po/postpic
Python | 200 lines | 100 code | 38 blank | 62 comment | 10 complexity | 3f8d7e37a8333bf3b1ae43ac0dfbe1d6 MD5 | raw file
  1. #
  2. # This file is part of postpic.
  3. #
  4. # postpic is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # postpic is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with postpic. If not, see <http://www.gnu.org/licenses/>.
  16. #
  17. # Copyright Stephan Kuschel 2016
  18. '''
  19. .. _openPMD: https://github.com/openPMD/openPMD-standard
  20. Support for hdf5 files following the openPMD_ Standard.
  21. Dependecies:
  22. - h5py: read hdf5 files with python
  23. Written by Stephan Kuschel 2016
  24. '''
  25. from __future__ import absolute_import, division, print_function, unicode_literals
  26. from . import Dumpreader_ifc
  27. from . import Simulationreader_ifc
  28. import numpy as np
  29. import re
  30. from .. import helper
  31. __all__ = ['OpenPMDreader', 'FileSeries']
  32. class OpenPMDreader(Dumpreader_ifc):
  33. '''
  34. The Reader implementation for Data written in the hdf5 file
  35. format following openPMD_ naming conventions.
  36. Args:
  37. h5file : String
  38. A String containing the relative Path to the .h5 file.
  39. '''
  40. def __init__(self, h5file, **kwargs):
  41. super(self.__class__, self).__init__(h5file, **kwargs)
  42. import os.path
  43. import h5py
  44. if not os.path.isfile(h5file):
  45. raise IOError('File "' + str(h5file) + '" doesnt exist.')
  46. self._h5 = h5py.File(h5file, 'r')
  47. self._iteration = int(list(self._h5['data'].keys())[0])
  48. self._data = self._h5['/data/{:d}/'.format(self._iteration)]
  49. self.attrs = self._data.attrs
  50. def __del__(self):
  51. del self._data
  52. # --- Level 0 methods ---
  53. def keys(self):
  54. return list(self._data.keys())
  55. def __getitem__(self, key):
  56. return self._data[key]
  57. # --- Level 1 methods ---
  58. def data(self, key):
  59. '''
  60. should work with any key, that contains data, thus on every hdf5.Dataset,
  61. but not on hdf5.Group. Will extract the data, convert it to SI and return it
  62. as a numpy array. Constant records will be detected and converted to
  63. a numpy array containing a single value only.
  64. '''
  65. record = self[key]
  66. if "value" in record.attrs:
  67. # constant data (a single int or float)
  68. ret = np.float64(record.attrs['value']) * record.attrs['unitSI']
  69. else:
  70. # array data
  71. ret = np.float64(record.value) * record.attrs['unitSI']
  72. return ret
  73. def gridoffset(self, key, axis):
  74. axid = helper.axesidentify[axis]
  75. attrs = self[key].parent.attrs
  76. return attrs['gridGlobalOffset'][axid] * attrs['gridUnitSI']
  77. def gridspacing(self, key, axis):
  78. axid = helper.axesidentify[axis]
  79. attrs = self[key].parent.attrs
  80. return attrs['gridSpacing'][axid] * attrs['gridUnitSI']
  81. def gridpoints(self, key, axis):
  82. axid = helper.axesidentify[axis]
  83. return self[key].shape[axid]
  84. # --- Level 2 methods ---
  85. def timestep(self):
  86. return self._iteration
  87. def time(self):
  88. return np.float64(self.attrs['time'] * self.attrs['timeUnitSI'])
  89. def _keyE(self, component, **kwargs):
  90. axsuffix = {0: 'x', 1: 'y', 2: 'z'}[helper.axesidentify[component]]
  91. return 'fields/E/' + axsuffix
  92. def _keyB(self, component, **kwargs):
  93. axsuffix = {0: 'x', 1: 'y', 2: 'z'}[helper.axesidentify[component]]
  94. return 'fields/B/' + axsuffix
  95. def simgridkeys(self):
  96. return ['fields/E/x', 'fields/E/y', 'fields/E/z',
  97. 'fields/B/x', 'fields/B/y', 'fields/B/z']
  98. def listSpecies(self):
  99. ret = list(self['particles'].keys())
  100. return ret
  101. def getSpecies(self, species, attrib):
  102. """
  103. Returns one of the attributes out of (x,y,z,px,py,pz,weight,ID,mass,charge) of
  104. this particle species.
  105. """
  106. attribid = helper.attribidentify[attrib]
  107. options = {9: lambda s: self.data('particles/' + s + '/weighting'),
  108. 0: lambda s: self.data('particles/' + s + '/position/x') +
  109. self.data('particles/' + s + '/positionOffset/x'),
  110. 1: lambda s: self.data('particles/' + s + '/position/y') +
  111. self.data('particles/' + s + '/positionOffset/y'),
  112. 2: lambda s: self.data('particles/' + s + '/position/z') +
  113. self.data('particles/' + s + '/positionOffset/z'),
  114. 3: lambda s: self.data('particles/' + s + '/momentum/x'),
  115. 4: lambda s: self.data('particles/' + s + '/momentum/y'),
  116. 5: lambda s: self.data('particles/' + s + '/momentum/z'),
  117. 10: lambda s: self.data('particles/' + s + '/id'),
  118. 11: lambda s: self.data('particles/' + s + '/mass'),
  119. 12: lambda s: self.data('particles/' + s + '/charge')}
  120. try:
  121. ret = np.float64(options[attribid](species))
  122. except(IndexError):
  123. raise KeyError
  124. return ret
  125. def getderived(self):
  126. '''
  127. return all other fields dumped, except E and B.
  128. '''
  129. ret = []
  130. self['fields'].visit(ret.append)
  131. ret = ['fields/' + r for r in ret if not (r.startswith('E') or r.startswith('B'))]
  132. ret = [r for r in ret if hasattr(self[r], 'value')]
  133. ret.sort()
  134. return ret
  135. def __str__(self):
  136. return '<OpenPMDh5reader at "' + str(self.dumpidentifier) + '">'
  137. class FileSeries(Simulationreader_ifc):
  138. '''
  139. Reads a time series of dumps from a given directory.
  140. The simidentifier is expanded using glob in order to
  141. find matching files.
  142. '''
  143. def __init__(self, simidentifier, dumpreadercls=OpenPMDreader, **kwargs):
  144. super(self.__class__, self).__init__(simidentifier, **kwargs)
  145. self.dumpreadercls = dumpreadercls
  146. import glob
  147. self._dumpfiles = glob.glob(simidentifier)
  148. self._dumpfiles.sort()
  149. def _getDumpreader(self, n):
  150. '''
  151. Do not use this method. It will be called by __getitem__.
  152. Use __getitem__ instead.
  153. '''
  154. return self.dumpreadercls(self._dumpfiles[n])
  155. def __len__(self):
  156. return len(self._dumpfiles)
  157. def __str__(self):
  158. return '<FileSeries at "' + self.simidentifier + '">'