# Copyright (c) 2018, Matthias Frey, Paul Scherrer Institut, Villigen PSI, Switzerland
# All rights reserved
#
# Implemented as part of the PhD thesis
# "Precise Simulations of Multibunches in High Intensity Cyclotrons"
#
# This file is part of pyOPALTools.
#
# pyOPALTools 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.
# You should have received a copy of the GNU General Public License
# along with pyOPALTools. If not, see <https://www.gnu.org/licenses/>.
import re
import numpy as np
from .BaseParser import BaseParser
[docs]class HistogramParser(BaseParser):
"""Parses OPAL ``*.hist`` files
Parses ``*.hist`` files of OPAL that are written for probe
elements and ``*.hist`` files of measurements.
The first line in both files is a comment line starting with
'#'. The measurement header line should be::
# Histogram bin counts (radius ['unit'], intensity ['unit'])
where a simulation header is::
# Histogram bin counts (min, max, nbins, binsize) 'min' 'unit' 'max' 'unit' '#bins' 'binsize' 'unit'
Attributes
----------
_info : dict
Dictionary with histogram info:
'min'
List of minimum value and unit
'max'
List of maximum value and unit
'nbins'
List of number of bins and unit
'binsize'
List of size of bin and unit
'bincount'
List of bin counts and unit
"""
[docs] def __init__(self):
self.clear()
[docs] def parse(self, filename):
simulation_pattern = r'# Histogram bin counts \((\w+), (\w+), (\w+), (\w+)\) (.*) (\w+) (.*) (\w+) (\d+) (.*) (\w+)'
measurement_pattern = r'# Histogram bin counts \((\w+) \[(\w+)\], (\w+) \[(\w+)\]\)'
match = False
# measurement file
radius = None
runit = None
current = None
cunit = None
with open(filename) as f:
for line in f:
if '#' in line:
# 24. March
# https://stackoverflow.com/questions/2077897/substitute-multiple-whitespace-with-single-whitespace-in-python
line = ' '.join(line.split())
obj = re.match(simulation_pattern, line)
if obj:
j = 5
for i in range(1, 5):
key = obj.group(i)
if not key in self._info:
raise ValueError("Not able to read info '" + key + "' from header.")
if not key == 'nbins':
self._info[key].append( obj.group( j ) ) # value
self._info[key].append( obj.group( j + 1 ) ) # unit
j += 2
else:
# no unit
self._info[key].append( obj.group( j ) )
self._info[key].append( '' )
j += 1
match = True
obj = re.match(measurement_pattern, line)
if obj:
radius = obj.group(1)
runit = obj.group(2)
current = obj.group(3)
cunit = obj.group(4)
match = True
else:
break
if not match:
raise RuntimeError('Not proper file format.')
if not radius == None:
# measurement file
r, c = np.loadtxt(filename, skiprows=1, usecols=(0,1), unpack=True)
self._info['nbins'] = [len(r), '']
self._info['min'] = [min(r), runit]
self._info['max'] = [max(r), runit]
self._info['binsize'] = [
(self._info['max'][0] - self._info['min'][0]) / self._info['nbins'][0],
runit
]
self._info['bincount'].append( c )
else:
self._info['bincount'].append( np.genfromtxt(filename,
comments='#',
delimiter = ' ',
dtype=np.float64
)
)
self._info['bincount'].append('')
[docs] def clear(self):
"""Clear data.
"""
self._info = {
'min': [],
'max': [],
'nbins': [],
'binsize': [],
'bincount': []
}
[docs] def getDataOfVariable(self, var):
if not self.isVariable(var):
raise ValueError("No variable '" + var + "' in dataset.")
return self._info[var][0]
[docs] def getUnitOfVariable(self, var):
if not self.isVariable(var):
raise ValueError("No variable '" + var + "' in dataset.")
return self._info[var][1]
[docs] def isVariable(self, var):
return var in self._info
@property
def size(self):
return self._info['nbins'][0]
[docs] def getVariables(self):
return self._info.keys()