Source code for eqtools.pfilereader

# This program is distributed under the terms of the GNU General Purpose License (GPL).
# Refer to http://www.gnu.org/licenses/gpl.txt
#
# This file is part of EqTools.
#
# EqTools is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# EqTools is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with EqTools.  If not, see <http://www.gnu.org/licenses/>.

"""
This module contains the :py:class:`PFileReader` class, a lightweight data
handler for p-file (radial profile) datasets.

Classes:
    PFileReader: 
        Data-storage class for p-file data.  Reads 
        data from ASCII p-file, storing as copy-safe object 
        attributes.
"""

import numpy as np
import csv
import re
from collections import namedtuple


[docs]class PFileReader(object): """Class to read ASCII p-file (profile data storage) into lightweight, user-friendly data structure. P-files store data blocks containing the following: a header with parameter name, parameter units, x-axis units, and number of data points, followed by values of axis x, parameter y, and derivative dy/dx. Each parameter block is read into a namedtuple storing ======== ============== 'name' parameter name 'npts' array size 'x' abscissa array 'y' data array 'dydx' data gradient 'xunits' abscissa units 'units' data units ======== ============== with each namedtuple stored as an attribute of the PFileReader instance. This gracefully handles variable formats of p-files (differing versions of p-files will have different parameters stored). Data blocks are accessed as attributes in a copy-safe manner. Creates instance of PFileReader. Args: pfile (String): Path to ASCII p-file to be loaded. Keyword Args: verbose (Boolean): Option to print message on object creation listing available data parameters. Defaults to True. Examples: Load p-file data located at `file_path`, while suppressing terminal output of stored parameters:: pfr = eqtools.PFileReader(file_path,verbose=False) Recover electron density data (for example):: ne_data = pfr.ne Recover abscissa and electron density data (for example):: ne = pfr.ne.y abscis = pfr.ne.x Available parameters in pfr may be listed via the overridden __str__ command. """ def __init__(self,pfile,verbose=True): self._pfile = pfile self._params = [] with open(pfile,'r') as readfile: dia = csv.excel() dia.skipinitialspace = True reader = csv.reader(readfile,dia,delimiter=' ') # define data structure as named tuple for storing parameter values data = namedtuple('DataStruct', ['name','npts','units','xunits','x','y','dydx']) # iterate through lines of file, checking for a header line; # at each header, read the next npts lines of data into # appropriate arrays. # continue until no headerline is found (throws StopIteration). # Populate list of params with available variables. while True: try: headerline = next(reader) except StopIteration: break npts = int(headerline[0]) # size of abscissa, data arrays abscis = headerline[1] # string name of abscissa variable (e.g. 'psinorm') var = re.split('[\(\)]',headerline[2]) param = var[0] # string name of parameter (e.g. 'ne') units = var[1] # string name of units (e.g. '10^20/m^3') # read npts next lines, populate arrays x = [] val = [] gradval = [] for j in range(npts): dataline = next(reader) x.append(float(dataline[0])) val.append(float(dataline[1])) gradval.append(float(dataline[2])) x = np.array(x) val = np.array(val) gradval = np.array(gradval) # collate into storage structure vars(self)['_'+param] = data(name=param, npts=npts, units=units, xunits=abscis, x=x, y=val, dydx=gradval) self._params.append(param) if verbose: print('P-file data loaded from '+self._pfile) print('Available parameters:') for par in self._params: un = vars(self)['_'+par].units xun = vars(self)['_'+par].xunits print(str(par).ljust(8)+str(xun).ljust(12)+str(un)) def __str__(self): """overrides default string method for useful output. """ mes = 'P-file data from '+self._pfile+' containing parameters:\n' for par in self._params: un = vars(self)['_'+par].units xun = vars(self)['_'+par].xunits mes += str(par).ljust(8)+str(xun).ljust(12)+str(un)+'\n' return mes def __getattribute__(self, name): """Copy-safe attribute retrieval method overriding default object.__getattribute__. Tries to retrieve attribute as-written (first check for default object attributes). If that fails, looks for pseudo-private attributes, marked by preceding underscore, to retrieve data blocks. If this fails, raise AttributeError. Args: name (String): Name (without leading underscore for data variables) of attribute. Raises: AttributeError: if no attribute can be found. """ try: return super(PFileReader,self).__getattribute__(name) except AttributeError: try: attr = super(PFileReader,self).__getattribute__('_'+name) if type(attr) is list: return attr[:] else: return attr except AttributeError: raise AttributeError('No attribute "%s" found' % name) def __setattr__(self, name, value): """Copy-safe attribute setting method overriding default `object.__setattr__`. Raises error if object already has attribute `_{name}` for input name, as such an attribute would interfere with automatic property generation in :py:meth:`__getattribute__`. Args: name (String): Attribute name. Raises: AttributeError: if attempting to create attribute with protected pseudo-private name. """ if hasattr(self, '_'+name): raise AttributeError("PFileReader object already has data attribute" " '_%(n)s', creating attribute '%(n)s' will" " conflict with automatic property generation." % {'n': name}) else: super(PFileReader, self).__setattr__(name, value)