import h5py
import numpy as np
import astropy.units as u

from plasmapy.classes import GenericPlasma
from plasmapy.utils import OpenPMDError

import os


_UNITS = (u.meter,
          u.kilogram,
          u.second,
          u.ampere,
          u.Kelvin,
          u.mol,
          u.candela)


def _fetch_units(openPMD_dims):
    units = u.dimensionless_unscaled
    for factor, unit in zip(openPMD_dims, _UNITS):
        units *= (unit ** factor)
    units, *_ = units.compose()
    return units


class _ElectricField:
    def __init__(self, E):
        self.units = _fetch_units(E.attrs["unitDimension"])
        self.E = E

    @property
    def x(self):
        return np.array(self.E['x']) * self.units

    @property
    def y(self):
        return np.array(self.E['y']) * self.units

    @property
    def z(self):
        return np.array(self.E['z']) * self.units


class HDF5Reader(GenericPlasma):
    def __init__(self, hdf5, **kwargs):
        if not os.path.isfile(hdf5):
            raise FileNotFoundError(f"Could not find file: '{hdf5}'")

        h5 = h5py.File(hdf5)
        try:
            openPMD = h5.attrs["openPMDextension"]
        except KeyError:
            openPMD = False

        if not openPMD:
            raise OpenPMDError("Input HDF5 file does not go on with "
                               "standards defined by OpenPMD")

        self.subname = tuple(h5['data'])[0]
        self.h5 = h5

    @property
    def electric_field(self):
        path = 'data/' + self.subname + '/fields/E'
        if path in self.h5:
            return _ElectricField(self.h5[path])
        else:
            raise AttributeError

    @property
    def charge_density(self):
        path = 'data/' + self.subname + '/fields/rho'
        if path in self.h5:
            units = _fetch_units(self.h5[path].attrs["unitDimension"])
            return np.array(self.h5[path]) * units
        else:
            raise AttributeError

    @classmethod
    def is_datasource_for(cls, **kwargs):
        if not "hdf5" in kwargs:
            return False

        hdf5 = kwargs.get("hdf5")
        openPMD = kwargs.get("openPMD")

        if not "openPMD" in kwargs and os.path.isfile(hdf5):
            h5 = h5py.File(hdf5)
            try:
                openPMD = h5.attrs["openPMDextension"]
            except KeyError:
                openPMD = False

        return openPMD

@@ -147,16 +147,95 @@
 rt os%0A%0A%0A
+# This is the order what OpenPMD uses to store unit%0A# dimensions for a record.%0A
 _UNITS =
@@ -395,24 +395,107 @@
 nPMD_dims):%0A
+    %22%22%22%0A    Converts a collection of OpenPMD dimensions to astropy.units.%0A    %22%22%22%0A%0A
     units = 
@@ -685,430 +685,1559 @@
     
-def __init__(self, E):%0A        self.units = _fetch_units(E.attrs%5B%22unitDimension%22%5D)%0A        self.E = E%0A%0A    @property%0A    def x(self):%0A        return np.array(self.E%5B'x'%5D) * self.units%0A%0A    @property%0A    def y(self):%0A        return np.array(self.E%5B'y'%5D) * self.units%0A%0A    @property%0A    def z(self):%0A        return np.array(self.E%5B'z'%5D) * self.units%0A%0A%0Aclass HDF5Reader(GenericPlasma):%0A    def __init__(self, hdf5, **kwargs):
+%22%22%22%0A    A class for representing electric field obtained from HDF5 based on%0A    OpenPMD standards.%0A%0A    Attributes%0A    ----------%0A    x : %60astropy.units.Quantity%60%0A        Array of x-coordinates data within the electric field domain.%0A    y : %60astropy.units.Quantity%60%0A        Array of y-coordinates data within the electric field domain.%0A    z : %60astropy.units.Quantity%60%0A        Array of z-coordinates data within the electric field domain.%0A%0A    Parameters%0A    ----------%0A    E : %60h5py.hl.group.Group%60%0A        Path to 'fields/E' in an HDF5 file.%0A    %22%22%22%0A%0A    def __init__(self, E):%0A        self.units = _fetch_units(E.attrs%5B%22unitDimension%22%5D)%0A        self.E = E%0A%0A    @property%0A    def x(self):%0A        return np.array(self.E%5B'x'%5D) * self.units%0A%0A    @property%0A    def y(self):%0A        return np.array(self.E%5B'y'%5D) * self.units%0A%0A    @property%0A    def z(self):%0A        return np.array(self.E%5B'z'%5D) * self.units%0A%0A%0Aclass HDF5Reader(GenericPlasma):%0A    def __init__(self, hdf5, **kwargs):%0A        %22%22%22%0A        Core class for accessing various attributes on HDF5 files that%0A        are based on OpenPMD standards.%0A%0A        Attributes%0A        ----------%0A        electric_field : %60_ElectricField%60%0A            An instance of %60%60_ElectricField%60%60 whose cartesian%0A            coordinate attributes can be accessed by reading suitable%0A            coordinate axis.%0A        charge_density : %60astropy.units.Quantity%60%0A            An array containing charge density data.%0A%0A        Parameters%0A        ----------%0A        hdf5 : %60str%60%0A            Path to HDF5 file.%0A        %22%22%22%0A
 %0A   

Add docstrings for classes