storage and xray modules live now on their own

This commit is contained in:
marco cammarata 2017-04-26 09:04:01 +02:00
parent 422c30e9b6
commit 5d14890e82
14 changed files with 0 additions and 2516 deletions

View File

@ -1,298 +0,0 @@
""" npy/npz/hdf5 file based storage;
this modules adds the possibility to dump and load objects in files and
a more convenient was of accessing the data via the .attributedict thanks
to the DataStorage class """
import numpy as np
import os
import h5py
import collections
import logging
log = logging.getLogger(__name__)
_array_cache = {}
def unwrapArray(a,recursive=True,readH5pyDataset=True):
""" This function takes an object (like a dictionary) and recursively
unwraps it solving issues like:
* the fact that many objects are packaged as 0d array
This funciton has also some specific hack for handling h5py limits:
* handle the None python object
* numpy unicode ...
"""
# is h5py dataset convert to array
if isinstance(a,h5py.Dataset) and readH5pyDataset: a = a[...]
if isinstance(a,h5py.Dataset) and a.shape == (): a = a[...]
if isinstance(a,h5py.Group) and "IS_LIST_OF_ARRAYS" in a.attrs:
items = list(a.keys())
items.sort()
a = np.asarray( [a[item][...] for item in items] )
if isinstance(a,np.ndarray) and a.ndim == 0 : a = a.item()
if isinstance(a,np.ndarray) and a.dtype.char == "S": a = a.astype(str)
if recursive:
if "items" in dir(a): # dict, h5py groups, npz file
a = dict(a); # convert to dict, otherwise can't asssign values
for key,value in a.items(): a[key] = unwrapArray(value)
elif isinstance(a,(list,tuple)):
a = [unwrapArray(element) for element in a]
else:
pass
if isinstance(a,dict): a = DataStorage(a)
# restore None that cannot be saved in h5py
if isinstance(a,str) and a == "NONE_PYTHON_OBJECT": a = None
# h5py can't save numpy unicode
if isinstance(a,np.ndarray) and a.dtype.char == "S": a = a.astype(str)
return a
def dictToH5Group(d,group,link_copy=True):
""" helper function that transform (recursive) a dictionary into an
hdf group by creating subgroups
link_copy = True, tries to save space in the hdf file by creating an internal link.
the current implementation uses memory though ...
"""
global _array_cache
for key,value in d.items():
TOTRY = True
if isinstance(value,(list,tuple)): value = np.asarray(value)
if isinstance(value,dict):
group.create_group(key)
dictToH5Group(value,group[key],link_copy=link_copy)
elif value is None:
group[key] = "NONE_PYTHON_OBJECT"
elif isinstance(value,np.ndarray):
# take care of unicode (h5py can't handle numpy unicode arrays)
if value.dtype.char == "U": value = np.asarray([vv.encode('ascii') for vv in value])
# check if it is list of array
elif isinstance(value,np.ndarray) and value.ndim == 1 and isinstance(value[0],np.ndarray):
group.create_group(key)
group[key].attrs["IS_LIST_OF_ARRAYS"] = True
for index,array in enumerate(value): dictToH5Group( { "index%010d"%index : array},group[key],link_copy=link_copy );
TOTRY = False; # don't even try to save as generic call group[key]=value
if link_copy:
found_address = None
for address,(file_handle,array) in _array_cache.items():
if np.array_equal(array,value) and group.file == file_handle:
log.info("Found array in cache, asked for %s/%s, found as %s"%(group.name,key,address))
found_address = address
break
if found_address is not None:
value = group.file[found_address]
try:
if TOTRY:
group[key] = value
if link_copy:
log.info("Addind array %s to cache"%(group[key].name))
_array_cache[ group[key].name ] = (group.file,value)
except Exception as e:
log.warning("Can't save %s, error was %s"%(key,e))
# try saving everything else that is not dict or array
else:
try:
group[key] = value
except Exception as e:
log.error("Can't save %s, error was %s"%(key,e))
def dictToH5(h5,d,link_copy=False):
""" Save a dictionary into an hdf5 file
TODO: add capability of saving list of array
h5py is not capable of handling dictionaries natively"""
h5 = h5py.File(h5,mode="w")
# group = h5.create_group("/")
dictToH5Group(d,h5["/"],link_copy=link_copy)
h5.close()
def h5ToDict(h5,readH5pyDataset=True):
""" Read a hdf5 file into a dictionary """
with h5py.File(h5,"r") as h:
ret = unwrapArray(h,recursive=True,readH5pyDataset=readH5pyDataset)
return ret
def npzToDict(npzFile):
with np.load(npzFile) as npz: d = dict(npz)
d = unwrapArray(d,recursive=True)
return d
def npyToDict(npyFile):
d = unwrapArray( np.load(npyFile).item() ,recursive=True)
return d
def dictToNpz(npzFile,d): np.savez(npzFile,**d)
def dictToNpy(npyFile,d): np.save(npyFile,d)
def objToDict(o,recursive=True):
""" convert a DictWrap to a dictionary (useful for saving); it should work for other objects too
TODO: this function does not catch a list of DataStorage instances like
objToDict( ( DataStorage(), DataStorage() ) )
is not converted !!
"""
if "items" not in dir(o): return o
d = dict()
for k,v in o.items():
try:
d[k] = objToDict( v )
except Exception as e:
log.info("In objToDict, could not convert key %s to dict, error was"%\
(k,e))
d[k] = v
return d
def read(fname):
extension = os.path.splitext(fname)[1]
log.info("Reading storage file %s"%fname)
if extension == ".npz":
return DataStorage(npzToDict(fname))
elif extension == ".npy":
return DataStorage(npyToDict(fname))
elif extension == ".h5":
return DataStorage(h5ToDict(fname))
else:
raise ValueError("Extension must be h5, npy or npz, it was %s"%extension)
def save(fname,d,link_copy=True):
""" link_copy is used by hdf5 saving only, it allows to creat link of identical arrays (saving space) """
# make sure the object is dict (recursively) this allows reading it
# without the DataStorage module
d = objToDict(d,recursive=True)
d['filename'] = fname
extension = os.path.splitext(fname)[1]
log.info("Saving storage file %s"%fname)
try:
if extension == ".npz":
return dictToNpz(fname,d)
elif extension == ".h5":
return dictToH5(fname,d,link_copy=link_copy)
elif extension == ".npy":
return dictToNpy(fname,d)
else:
raise ValueError("Extension must be h5, npy or npz, it was %s"%extension)
except Exception as e:
log.exception("Could not save %s"%fname)
class DataStorage(dict):
""" Storage for dict like object.
recursive : bool
recursively convert dict-like objects to DataStorage
It can save data to file (format npy,npz or h5)
To initialize it:
data = DataStorage( a=(1,2,3),b="add",filename='store.npz' )
# recursively by default
# data.a will be a DataStorage instance
data = DataStorage( a=dict( b = 1)) );
# data.a will be a dictionary
data = DataStorage( a=dict( b = 1),recursive=False )
# reads from file if it exists
data = DataStorage( 'mysaveddata.npz' ) ;
DOES NOT READ FROM FILE (even if it exists)!!
data = DataStorage( filename = 'mysaveddata.npz' );
create empty storage (with default filename)
data = DataStorage()
"""
def __init__(self,*args,filename='data_storage.npz',recursive=True,**kwargs):
# self.filename = kwargs.pop('filename',"data_storage.npz")
self.filename = filename
self._recursive = recursive
# interpret kwargs as dict if there are
if len(kwargs) != 0:
fileOrDict = dict(kwargs)
elif len(kwargs)==0 and len(args)>0:
fileOrDict = args[0]
else:
fileOrDict = dict()
d = dict(); # data dictionary
if isinstance(fileOrDict,dict):
d = fileOrDict
elif isinstance(fileOrDict,str):
if os.path.isfile(fileOrDict):
d = read(fileOrDict)
else:
self.filename=fileOrDict
d = dict()
else:
raise ValueError("Invalid DataStorage definition")
if recursive:
for k in d.keys():
if not isinstance(d[k],DataStorage) and isinstance(d[k],dict):
d[k] = DataStorage(d[k])
# allow accessing with .data, .delays, etc.
for k,v in d.items(): setattr(self,k,v)
# allow accessing as proper dict
self.update( **dict(d) )
def __setitem__(self, key, value):
#print("__setitem__")
setattr(self,key,value)
super().__setitem__(key, value)
def __setattr__(self, key, value):
""" allows to add fields with data.test=4 """
# check if attr exists is essential (or it fails when defining an instance)
if hasattr(self,"_recursive") and self._recursive and \
isinstance(value,(dict,collections.OrderedDict)): value = DataStorage(value)
super().__setitem__(key, value)
super().__setattr__(key,value)
def __delitem__(self, key):
delattr(self,key)
super().__delitem__(key)
def __str__(self):
keys = list(self.keys())
keys.sort()
return "DataStorage obj containing: %s" % ",".join(keys)
def __repr__(self):
keys = list(self.keys())
keys.sort()
if len(keys) == 0: return "Empty DataStorage"
nchars = max(map(len,keys))
fmt = "%%%ds %%s" % (nchars)
s = ["DataStorage obj containing (sorted): ",]
for k in keys:
if k[0] == "_": continue
obj = self[k]
if ( (isinstance(obj,np.ndarray) and obj.ndim == 1) or isinstance(obj,(list,tuple))) and all( [isinstance(v,np.ndarray) for v in obj]):
value_str = "list of arrays, shapes " + ",".join([str(v.shape) for v in obj[:5]]) + " ..."
elif isinstance(obj,np.ndarray):
value_str = "array, size %s, type %s"% ("x".join(map(str,obj.shape)),obj.dtype)
elif isinstance(obj,DataStorage):
value_str = str(obj)[:50]
elif isinstance(obj,(str,DataStorage)):
value_str = obj[:50]
elif self[k] is None:
value_str = "None"
else:
value_str = str(self[k])
if len(str(obj))>50: value_str += " ..."
s.append( fmt % (k,value_str) )
return "\n".join(s)
def keys(self):
keys = list(super().keys())
keys = [k for k in keys if k != 'filename' ]
keys = [k for k in keys if k[0] != '_' ]
return keys
def save(self,fname=None,link_copy=False):
""" link_copy: only works in hfd5 format
save space by creating link when identical arrays are found,
it slows down the saving (3 or 4 folds) but saves A LOT of space
when saving different dataset together (since it does not duplicate
internal pyfai matrices
"""
if fname is None: fname = self.filename
assert fname is not None
save(fname,self,link_copy=link_copy)

View File

@ -1,24 +0,0 @@
# xray
Different utilities to work with time resolved solution/powder scattering data
They should be extended to absorption soon
The idea is an hierarchical structure of modules
> user → beamline → workhorses
## Workhorses
azav.py: uses pyFAI to convert images into 1d curves
dataReduction.py: works on the normalization, referencing and averaging.
Both modules uses some common utilities in utils.py including file based storage for persistency and convenience (numpy or hdf5 based);
Indeed the data_storage class wraps around dictionaries allowing accessing with .attribute syntax.
In utils plotfuncitons are presents
## beamline
this step is needed to read the diagnostic information (time delay, monitors, etc). An example for current id9 data collection macros is given (id9.py). Essentially it means creating a dictionary where each key will be added to the main data dictionary (that has keys like q, data, files, etc.)
## user
at the user level, a rather simple macro like the one provided as an example (example_main_tiox.py) should be sufficient for most needs.

View File

@ -1,12 +0,0 @@
from .. import storage
from . import utils
from . import mask
from . import peaks
from . import cell
from . import filters
from . import id9
try:
from . import azav
except ImportError:
print("Can't import azav ...")
from . import dataReduction

View File

@ -1,465 +0,0 @@
from __future__ import print_function,division
import logging
log = logging.getLogger(__name__)
import numpy as np
np.seterr(all='ignore')
import os
import collections
import glob
import pathlib
from . import storage
from . import utils
from . import filters
import re
import fabio
import pyFAI
try:
import matplotlib.pyplot as plt
except ImportError:
log.warn("Can't import matplotlib !")
def _read(fname):
""" read data from file using fabio """
f = fabio.open(fname)
data = f.data
del f; # close file
return data
def read(fnames):
""" read data from file(s) using fabio """
if isinstance(fnames,str):
data = _read(fnames)
else:
# read one image to know img size
temp = _read(fnames[0])
shape = [len(fnames),]+list(temp.shape)
data = np.empty(shape)
data[0] = temp
for i in range(1,len(fnames)): data[i] = _read(fnames[i])
return data
def ai_as_dict(ai):
""" ai is a pyFAI azimuthal intagrator"""
methods = dir(ai)
methods = [m for m in methods if m.find("get_") == 0]
names = [m[4:] for m in methods]
values = [getattr(ai,m)() for m in methods]
ret = dict( zip(names,values) )
ret["detector"] = ai.detector.get_name()
return ret
def ai_as_str(ai):
""" ai is a pyFAI azimuthal intagrator"""
s=[ "# Detector : %s" % ai.detector.name,
"# Pixel [um] : %.2fx%.2f" % (ai.pixel1*1e6,ai.pixel2*1e6),
"# Distance [mm] : %.3f" % (ai.dist*1e3),
"# Center [mm] : %.3f,%.3f" % (ai.poni1*1e3,ai.poni2*1e3),
"# Center [px] : %.3f,%.3f" % (ai.poni1/ai.pixel1,ai.poni2/ai.pixel2),
"# Wavelength [A] : %.5f" % (ai.wavelength*1e10),
"# rot[1,2,3] [rad]: %.3f,%.3f,%.3f" % (ai.rot1,ai.rot2,ai.rot3) ]
return "\n".join(s)
def do1d(ai, imgs, mask = None, npt_radial = 600, method = 'csr',safe=True,dark=10., polCorr = 1,dezinger=None):
""" ai is a pyFAI azimuthal intagrator
it can be defined with pyFAI.load(ponifile)
dezinger: None or float (used as percentile of ai.separate)
mask: True are points to be masked out """
# force float to be sure of type casting for img
if isinstance(dark,int): dark = float(dark);
if imgs.ndim == 2: imgs = (imgs,)
out_i = np.empty( ( len(imgs), npt_radial) )
out_s = np.empty( ( len(imgs), npt_radial) )
for _i,img in enumerate(imgs):
if dezinger is not None and dezinger > 0:
_,img=ai.separate(img,npt_rad=npt_radial,npt_azim=512,unit='q_A^-1',
method=method,mask=mask,percentile=dezinger)
q,i, sig = ai.integrate1d(img-dark, npt_radial, mask= mask, safe = safe,\
unit="q_A^-1", method = method, error_model = "poisson",
polarization_factor = polCorr)
out_i[_i] = i
out_s[_i] = sig
return q,np.squeeze(out_i),np.squeeze(out_s)
def do2d(ai, imgs, mask = None, npt_radial = 600, npt_azim=360,method = 'csr',safe=True,dark=10., polCorr = 1):
""" ai is a pyFAI azimuthal intagrator
it can be defined with pyFAI.load(ponifile)
mask: True are points to be masked out """
# force float to be sure of type casting for img
if isinstance(dark,int): dark = float(dark);
if imgs.ndim == 2: imgs = (imgs,)
out = np.empty( ( len(imgs), npt_azim,npt_radial) )
for _i,img in enumerate(imgs):
i2d,q,azTheta = ai.integrate2d(img-dark, npt_radial, npt_azim=npt_azim,
mask= mask, safe = safe,unit="q_A^-1", method = method,
polarization_factor = polCorr )
out[_i] = i2d
return q,azTheta,np.squeeze(out)
def getAI(poni=None,folder=None,**kwargs):
""" get AzimuthalIntegrator instance:
if poni is a string, it is used as filename to read.
in this case if folder is given it is used (together with all its
subfolder) as search path (along with ./ and home folder)
kwargs if present can be used to define (or override) parameters from files,
dist,xcen,ycen,poni1,poni2,rot1,rot2,rot3,pixel1,pixel2,splineFile,
detector,wavelength
"""
if isinstance(poni,pyFAI.azimuthalIntegrator.AzimuthalIntegrator):
ai = poni
elif isinstance(poni,str):
# look is file exists in cwd
if os.path.isfile(poni):
fname = poni
# if file does not exist look for one with that name around
else:
# build search paths
folders = []
if folder is not None:
temp = os.path.abspath(folder)
path = pathlib.Path(temp)
folders = [ str(path), ]
for p in path.parents: folders.append(str(p))
folders.append( "./" )
folders.append( os.path.expanduser("~/") )
# look for file
for path in folders:
fname = path + "/" + poni
if os.path.isfile(fname):
log.info("Found poni file %s",fname)
break
else:
log.debug("Could not poni file %s",fname)
ai = pyFAI.load(fname)
else:
ai = pyFAI.azimuthalIntegrator.AzimuthalIntegrator()
for par,value in kwargs.items(): setattr(ai,par,value)
# provide xcen and ycen for convenience (note: xcen changes poni2
# and ycen changes poni1)
if 'xcen' in kwargs: ai.poni2 = kwargs['xcen'] * ai.pixel2
if 'ycen' in kwargs: ai.poni1 = kwargs['ycen'] * ai.pixel1
ai.reset(); # needed in case of overridden parameters
return ai
g_mask_str = re.compile("(\w)\s*(<|>)\s*(\d+)")
def _interpretMask(mask,shape=None):
"""
if mask is an existing filename, returns it
if mask is a string like [x|y] [<|>] int;
for example y>500 will dis-regard out for y>500
"""
maskout = None
if isinstance(mask,str) and os.path.isfile(mask):
maskout = read(mask).astype(np.bool)
elif isinstance(mask,str) and not os.path.isfile(mask):
err_msg = ValueError("The string '%s' could not be interpreted as simple\
mask; it should be something like x>10"%mask)
assert shape is not None
# interpret string
maskout = np.zeros(shape,dtype=bool)
match = g_mask_str.match(mask)
if match is None: raise err_msg
(axis,sign,lim) = match.groups()
if axis not in ("x","y"): raise err_msg
if sign not in (">","<"): raise err_msg
lim = int(lim)
idx = slice(lim,None) if sign == ">" else slice(None,lim)
if axis == 'y':
maskout[idx,:] = True
else:
maskout[:,idx] = True
elif isinstance(mask,np.ndarray):
maskout = mask.astype(np.bool)
elif mask is None:
assert shape is not None
maskout = np.zeros(shape,dtype=bool)
else:
maskout = None
raise ValueError("Could not interpret %s as mask input"%mask)
if shape is not None and maskout.shape != shape:
raise ValueError("The mask shape %s does not match the shape given as\
argument %s"%(maskout.shape,shape))
return maskout
def interpretMask(masks,shape=None):
"""
if masks is a list of masks, eachone can be:
* an existing filename
* a string like [x|y] [<|>] int;
"""
if not isinstance( masks, (list,tuple,np.ndarray) ):
masks = (masks,)
masks = [_interpretMask(mask,shape) for mask in masks]
# put them all together
mask = masks[0]
for m in masks[1:]:
mask = np.logical_or(mask,m)
return mask
def removeBackground(data,qlims=(0,10),max_iter=30,background_regions=[],force=False,
storageFile=None,save=True,**removeBkg):
""" similar function to the zray.utils one, this works on dataset created by
doFolder """
idx = utils.findSlice(data.orig.q,qlims)
# see if there are some to do ...
if force:
idx_start = 0
else:
idx_start = len(data.data)
if idx_start < len(data.orig.data):
_q,_data = utils.removeBackground(data.orig.q[idx],data.orig.data[:,idx],
max_iter=max_iter,background_regions=background_regions,**removeBkg)
data.q = _q
data.data = np.concatenate( (data.data,_data ) )
data.err = np.concatenate( (data.err ,data.err[idx_start,idx] ) )
if save: data.save(storageFile); # if None uses .filename
return data
def doFolder(folder,files='*.edf*',nQ = 1500,force=False,mask=None,dark=10,
norm='auto',save_pyfai=False,saveChi=True,poni='pyfai.poni',
storageFile='auto',save=True,logDict=None,dezinger=None,skip_first=0,last=None):
""" calc 1D curves from files in folder, returning a dictionary of stuff
nQ : number of Q-points (equispaced)
force : if True, redo from beginning even if previous data are found
if False, do only new files
mask : can be a list of [filenames|array of booleans|mask string]
pixels that are True are dis-regarded
saveChi: self-explanatory
dezinger: None or 0 to disable; good value is ~50. Needs good center and mask
logDict: dictionary(-like) structure. has to have 'file' key
save_pyfai: store all pyfai's internal arrays (~110 MB)
poni : could be:
an AzimuthalIntegrator instance
a filename that will be look for in
1 'folder' first
2 in ../folder
3 in ../../folder
....
n-1 in pwd
n in homefolder
a dictionary (use to bootstrap an AzimuthalIntegrator using
AzimuthalIntegrator(**poni)
"""
if storageFile == 'auto': storageFile = folder + "/" + "pyfai_1d.h5"
if os.path.isfile(storageFile) and not force:
saved = storage.DataStorage(storageFile)
log.info("Found %d images in storage file"%saved.data.shape[0])
else:
saved = None
files = utils.getFiles(folder,files)
if logDict is not None:
files = [f for f in files if utils.getBasename(f) in logDict['file'] ]
files = files[skip_first:last]
if saved is not None:
files = [f for f in files if f not in saved["files"]]
log.info("Will do azimuthal integration for %d files"%(len(files)))
files = np.asarray(files)
basenames = np.asarray( [ utils.getBasename(file) for file in files] )
if len(files) > 0:
# which poni file to use:
ai = getAI(poni,folder)
shape = read(files[0]).shape
mask = interpretMask(mask,shape)
data = np.empty( (len(files),nQ) )
err = np.empty( (len(files),nQ) )
for ifname,fname in enumerate(files):
img = read(fname)
q,i,e = do1d(ai,img,mask=mask,npt_radial=nQ,dark=dark,dezinger=dezinger)
data[ifname] = i
err[ifname] = e
if saveChi:
chi_fname = utils.removeExt(fname) + ".chi"
utils.saveTxt(chi_fname,q,np.vstack((i,e)),info=ai_as_str(ai),overwrite=True)
if saved is not None:
files = np.concatenate( (saved["files"] ,basenames ) )
data = np.concatenate( (saved["data"] ,data ) )
err = np.concatenate( (saved["err"] ,err ) )
theta_rad = utils.qToTheta(q,wavelength=ai.wavelength)
theta_deg = utils.qToTheta(q,wavelength=ai.wavelength,asDeg=True)
orig = dict(data=data.copy(),err=err.copy(),q=q.copy())
ret = dict(q=q,folder=folder,files=files,data=data,err=err,
orig = orig, theta_rad = theta_rad, theta_deg=theta_deg,
pyfai=ai_as_dict(ai),pyfai_info=ai_as_str(ai),mask=mask)
if not save_pyfai:
ret['pyfai']['chia'] = None
ret['pyfai']['dssa'] = None
ret['pyfai']['q'] = None
ret['pyfai']['ttha'] = None
ret = storage.DataStorage(ret)
# add info from logDict if provided
if logDict is not None:
ret['log']=logDict
# sometime saving is not necessary (if one has to do it after subtracting background
if storageFile is not None and save: ret.save(storageFile)
else:
ret = saved
return ret
def removeBackground(data,qlims=(0,10),max_iter=30,background_regions=[],
storageFile=None,save=True,**removeBkg):
""" similar function to the zray.utils one, this works on dataset created by
doFolder """
idx = utils.findSlice(data.q,qlims)
data.q,data.data = utils.removeBackground(data.q[idx],data.data[:,idx],
max_iter=max_iter,background_regions=background_regions,**removeBkg)
data.err = data.err[idx]
if save: data.save(storageFile); # if None uses .filename
return data
def _calc_R(x,y, xc, yc):
""" calculate the distance of each 2D points from the center (xc, yc) """
return np.sqrt((x-xc)**2 + (y-yc)**2)
def _chi2(c, x, y):
""" calculate the algebraic distance between the data points and the mean
circle centered at c=(xc, yc) """
Ri = _calc_R(x, y, *c)
return Ri - Ri.mean()
def leastsq_circle(x,y):
from scipy import optimize
# coordinates of the barycenter
center_estimate = np.nanmean(x), np.nanmean(y)
center, ier = optimize.leastsq(_chi2, center_estimate, args=(x,y))
xc, yc = center
Ri = _calc_R(x, y, *center)
R = Ri.mean()
residu = np.sum((Ri - R)**2)
return xc, yc, R
def find_center(img,psize=100e-6,dist=0.1,wavelength=0.8e-10,center=None,reference=None,**kwargs):
""" center is the initial centr (can be None)
reference is a reference position to be plot in 2D plots """
plt.ion()
kw = dict( pixel1 = psize, pixel2 = psize, dist = dist,wavelength=wavelength )
kw.update(kwargs)
ai = pyFAI.azimuthalIntegrator.AzimuthalIntegrator(**kw)
fig_img,ax_img = plt.subplots(1,1)
fig_pyfai,ax_pyfai = plt.subplots(1,1)
fig_pyfai = plt.figure(2)
temp= ax_img.imshow(img)
plt.sca(ax_img); # set figure to use for mouse interaction
temp.set_clim( *np.percentile(img,(2,95) ) )
ans = ""
print("Enter 'end' when done")
while ans != "end":
if center is None:
print("Click on beam center:")
plt.sca(ax_img); # set figure to use for mouse interaction
center = plt.ginput()[0]
print("Selected center:",center)
ai.set_poni1(center[1]*psize)
ai.set_poni2(center[0]*psize)
q,az,i = do2d(ai,img)
mesh = ax_pyfai.pcolormesh(q,az,i)
mesh.set_clim( *np.percentile(i,(2,95) ) )
ax_pyfai.set_title(str(center))
if reference is not None: ax_pyfai.axvline(reference)
plt.pause(0.01)
plt.draw()
plt.draw_all()
ans=input("Enter to continue with clinking or enter xc,yc values ")
if ans == '':
center = None
else:
try:
center = list(map(float,ans.split(",")))
except Exception as e:
center = None
if center == []: center = None
print("Final values: (in pixels) %.3f %.3f"%(center[0],center[1]))
return ai
def average(fileOrFolder,delays=slice(None),scale=1,norm=None,returnAll=False,plot=False,
showTrend=False):
data = storage.DataStorage(fileOrFolder)
if isinstance(delays,slice):
idx = np.arange(data.delays.shape[0])[delays]
elif isinstance(delays,(int,float)):
idx = data.delays == float(delays)
else:
idx = data.delays < 0
if idx.sum() == 0:
print("No data with the current filter")
return None
i = data.data[idx]
q = data.q
if isinstance(norm,(tuple,list)):
idx = ( q>norm[0] ) & (q<norm[1])
norm = np.nanmean(i[:,idx],axis=1)
i = i/norm[:,np.newaxis]
if isinstance(norm,np.ndarray):
i = i/norm[:,np.newaxis]
title = "%s %s" % (fileOrFolder,str(delays))
utils.plotdata(q,i*scale,showTrend=showTrend,plot=plot,title=title)
if returnAll:
return q,i.mean(axis=0)*scale,i
else:
return q,i.mean(axis=0)*scale
#### Utilities for chi files ####
def chiRead(fname,scale=1):
q,i = np.loadtxt(fname,unpack=True,usecols=(0,1))
return q,i*scale
def chiPlot(fname,useTheta=False,E=12.4):
q,i = chiRead(fname)
lam = 12.4/E
theta = 2*180/3.14*np.arcsin(q*lam/4/3.14)
x = theta if useTheta else q
plt.plot(x,i,label=fname)
def chiAverage(folder,basename="",scale=1,norm=None,returnAll=False,plot=False,showTrend=False,clim='auto'):
files = glob.glob("%s/%s*chi"%(folder,basename))
files.sort()
print(files)
if len(files) == 0:
print("No file found (basename %s)" % basename)
return None
q,_ = chiRead(files[0])
i = np.asarray( [ chiRead(f)[1] for f in files ] )
if isinstance(norm,(tuple,list)):
idx = ( q>norm[0] ) & (q<norm[1])
norm = np.nanmean(i[:,idx],axis=1)
i = i/norm[:,np.newaxis]
elif isinstance(norm,np.ndarray):
i = i/norm[:,np.newaxis]
title = "%s %s" % (folder,basename)
utils.plotdata(q,i,plot=plot,showTrend=showTrend,title=title,clim=clim)
if (showTrend and plot): plt.subplot(1,2,1)
if showTrend:
plt.pcolormesh(np.arange(i.shape[0]),q,i.T)
plt.xlabel("image number, 0 being older")
plt.ylabel(r"q ($\AA^{-1}$)")
if (showTrend and plot): plt.subplot(1,2,2)
if plot:
plt.plot(q,i.mean(axis=0)*scale)
if (plot or showTrend):
plt.title(folder+"/"+basename)
if returnAll:
return q,i.mean(axis=0)*scale,i
else:
return q,i.mean(axis=0)*scale

View File

@ -1,98 +0,0 @@
from __future__ import division,print_function
import collections
import itertools
import numpy as np
from numpy import sin,cos
class Triclinic(object):
def __init__(self,a=1,b=1,c=1,alpha=90,beta=90,gamma=90):
self.a = a
self.b = b
self.c = c
alpha = alpha*np.pi/180
beta = beta*np.pi/180
gamma = gamma*np.pi/180
self.alpha = alpha
self.beta = beta
self.gamma = gamma
self._s11 = b**2 * c**2 * sin(alpha)**2
self._s22 = a**2 * c**2 * sin(beta)**2
self._s33 = a**2 * b**2 * sin(gamma)**2
self._s12 = a*b*c**2*(cos(alpha) * cos(beta) - cos(gamma))
self._s23 = a**2*b*c*(cos(beta) * cos(gamma) - cos(alpha))
self._s13 = a*b**2*c*(cos(gamma) * cos(alpha) - cos(beta))
self.V = (a*b*c)*np.sqrt(1-cos(alpha)**2 - cos(beta)**2 - cos(gamma)**2 + 2*cos(alpha)*cos(beta)*cos(gamma))
def __call__(self,h,k,l): return self.q(h,k,l)
def d(self,h,k,l):
temp = self._s11*h**2 + \
self._s22*k**2 + \
self._s33*l**2 + \
2*self._s12*h*k+ \
2*self._s23*k*l+ \
2*self._s13*h*l
d = self.V/np.sqrt(temp)
return d
def Q(self,h,k,l):
return 2*np.pi/self.d(h,k,l)
def reflection_list(self,maxQ=3,lim=10):
ret=dict()
# prepare hkl
i = range(-lim,lim+1)
prod = itertools.product( i,i,i )
hkl = np.asarray( list( itertools.product( i,i,i ) ) )
h,k,l = hkl.T
q = self.Q(h,k,l)
idx = q<maxQ;
q = q[idx]
hkl = hkl[idx]
qunique = np.unique(q)
ret = []
for qi in qunique:
reflec = hkl[ q == qi ]
ret.append( (qi,tuple(np.abs(reflec)[0]),len(reflec),reflec) )
return qunique,ret
# for h in range(-lim,lim+1):
# for j in range(-lim,lim+1):
class Orthorombic(Triclinic):
def __init__(self,a=1,b=1,c=1):
Triclinic.__init__(self,a=a,b=b,c=c,alpha=90,beta=90,gamma=90)
class Monoclinic(object):
def __init__(self,a=1,b=1,c=1,beta=90.):
Triclinic.__init__(self,a=a,b=b,c=c,alpha=90,beta=beta,gamma=90)
def plotReflections(cell_instance,ax=None,line_kw=dict(),text_kw=dict()):
import matplotlib.pyplot as plt
from matplotlib import lines
import matplotlib.transforms as transforms
_,refl_info = cell_instance.reflection_list()
if ax is None: ax = plt.gca()
# the x coords of this transformation are data, and the
# y coord are axes
trans = transforms.blended_transform_factory(ax.transData, ax.transAxes)
txt_kw = dict( horizontalalignment='center', rotation=45)
txt_kw.update(**text_kw)
for reflection in refl_info[1:]:
q,hkl,n,_ = reflection
line = lines.Line2D( [q,q],[1,1.1],transform=trans,**line_kw)
line.set_clip_on(False)
ax.add_line(line)
ax.text(q,1.15,str(hkl),transform=trans,**txt_kw)
ti3o5_lambda = Triclinic(a = 9.83776, b = 3.78674, c = 9.97069, beta = 91.2567)
ti3o5_beta = Triclinic(a = 9.7382 , b = 3.8005 , c = 9.4333 , beta = 91.496)
#ti3o5_beta = Monoclinic(a = 9.7382 , b = 3.8005 , c = 9.4333 , beta = 91.496)
ti3o5_alpha = Triclinic(a = 9.8372, b = 3.7921, c = 9.9717)
ti3o5_alpha1 = Orthorombic(a = 9.8372, b = 3.7921, c = 9.9717)

View File

@ -1,261 +0,0 @@
from __future__ import print_function,division
import logging
log = logging.getLogger(__name__)
import numpy as np
np.seterr(all='ignore')
from . import utils
from . import storage
import os
def subtractReferences(i,idx_ref, useRatio = False):
""" given data in i (first index is shot num) and the indeces of the
references (idx_ref, array of integers) it interpolates the closest
reference data for each shot and subtracts it (or divides it, depending
on useRatio = [True|False];
Note: it works in place (i.e. it modifies i) """
iref=np.empty_like(i)
idx_ref = np.squeeze(idx_ref)
idx_ref = np.atleast_1d(idx_ref)
# sometime there is just one reference (e.g. sample scans)
if idx_ref.shape[0] == 1:
if useRatio:
return i/i[idx_ref]
else:
return i-i[idx_ref]
# references before first ref are "first ref"
iref[:idx_ref[0]] = i[idx_ref[0]]
# references after last ref are "last ref"
iref[idx_ref[-1]:] = i[idx_ref[-1]]
_ref = 0
for _i in range(idx_ref[0],idx_ref[-1]):
if _i in idx_ref: continue
idx_ref_before = idx_ref[_ref]
idx_ref_after = idx_ref[_ref+1]
ref_before = i[idx_ref_before]
ref_after = i[idx_ref_after]
weight_before = float(_i-idx_ref_before)/(idx_ref_after-idx_ref_before)
weight_after = 1-weight_before
# normal reference for an on chi, the weighted average
iref[_i] = weight_before*ref_before + weight_after*ref_after
if _i>=idx_ref_after-1: _ref += 1
log.debug("For image %d : %d-%d"%(_i,idx_ref_before,idx_ref_after))
# take care of the reference for the references ...
if len(idx_ref) > 2:
iref[idx_ref[0]] = i[idx_ref[1]]
iref[idx_ref[-1]] = i[idx_ref[-2]]
for _i in range(1,len(idx_ref)-1):
idx_ref_before = idx_ref[_i-1]
idx_ref_after = idx_ref[_i+1]
ref_before = i[idx_ref_before]
ref_after = i[idx_ref_after]
weight_before = float(idx_ref[_i]-idx_ref_before)/(idx_ref_after-idx_ref_before)
weight_after = 1-weight_before
# normal reference for an on chi, the weighted average
iref[idx_ref[_i]] = weight_before*ref_before + weight_after*ref_after
log.debug("For reference image %d : %d-%d"%(idx_ref[_i],idx_ref_before,idx_ref_after))
else:
#print(idx_ref)
#print(iref[idx_ref])
iref[idx_ref]=i[idx_ref[0]]
#print(iref[idx_ref])
if useRatio:
i /= iref
else:
i -= iref
return i
def averageScanPoints(scan,data,errAbs=None,isRef=None,lpower=None,useRatio=False,\
funcForAveraging=np.nanmean):
""" given scanpoints in 'scan' and corresponding data in 'data'
average all data corresponding the exactly the same scanpoint.
If the values in scan are coming from a readback, rounding might be
necessary.
No normalization is done inside this function
if isRef is provided must be a boolean array of the same shape as 'scan'
is there is at least one scanpoint marked as True, the data are
subtracted/divided by the interpolated reference
if lpower is provided the data is divided by it (how it is done depends
if one uses the ratio or not
funcForAveraging: is usually np.nanmean or np.nanmedian. it can be any
function that support axis=0 as keyword argument
"""
data = data.astype(np.float)
avData = np.nanmedian( data , axis = 0 )
if isRef is None: isRef = np.zeros( data.shape[0], dtype=bool )
assert data.shape[0] == isRef.shape[0]
# subtract reference only is there is at least one
if isRef.sum()>0:
# create a copy (subtractReferences works in place)
diff = subtractReferences(data.copy(),np.argwhere(isRef), useRatio=useRatio)
avNeg = funcForAveraging(data[isRef],axis=0)
else:
diff = data
avNeg = np.zeros_like(avData)
# normalize signal for laser intensity if provided
if lpower is not None:
lpower = utils.reshapeToBroadcast(lpower,data)
if useRatio is False:
diff /= lpower
else:
diff = (data-1)/lpower+1
scan_pos = np.unique(scan)
shape_out = [len(scan_pos),] + list(diff.shape[1:])
ret = np.empty(shape_out)
err = np.empty(shape_out)
data_abs = np.empty(shape_out)
diffsInScanPoint = []
chi2_0 = []
for i,t in enumerate(scan_pos):
shot_idx = (scan == t)
# select data for the scan point
diff_for_scan = diff[shot_idx]
#if errAbs is not None:
# noise = np.nanmean(errAbs[shot_idx],axis = 0)
#else:
noise = np.nanstd(diff_for_scan, axis = 0)
# if it is the reference take only every second ...
if np.all( shot_idx == isRef ):
diff_for_scan = diff_for_scan[::2]
diffsInScanPoint.append( diff_for_scan )
# calculate average
ret[i] = funcForAveraging(diff_for_scan,axis=0)
data_abs[i] = funcForAveraging(data[shot_idx],axis=0)
# calculate chi2 of different repetitions
chi2 = np.power( (diff_for_scan - ret[i])/noise,2)
# sum over all axis but first
for _ in range(diff_for_scan.ndim-1):
chi2 = np.nansum( chi2, axis=-1 )
# store chi2_0
chi2_0.append( chi2/ret[i].size )
# store error of mean
err[i] = noise/np.sqrt(shot_idx.sum())
ret = dict(scan=scan_pos,data=ret,dataUnmasked=ret.copy(),err=err,
errUnmasked=err.copy(),chi2_0=chi2_0,diffsInScanPoint=diffsInScanPoint,
dataAbsAvNeg = avNeg, dataAsAbs=ret+avNeg,errAbs=errAbs,
dataAbsAvAll=avData,dataAbsAvScanPoint=data_abs,dataAbs=data.copy())
ret = storage.DataStorage(ret)
return ret
def calcTimeResolvedSignal(scan,data,err=None,reference="min",monitor=None,q=None,
saveTxt=True,folder="./",**kw):
"""
reference: can be 'min', 'max', a float|integer or an array of booleans
q : is needed if monitor is a tuple|list
monitor : normalization vector (if it is interpreted a list it is
interpreted as q-range normalization)
saveTxt : will save txt outputfiles (diff_av_*) in folder
other keywords are passed to averageScanPoints
"""
if reference == "min":
isRef = (scan == scan.min())
elif reference == "max":
isRef = (scan == scan.max())
elif isinstance(reference,(float,int)):
isRef = (scan == reference)
else:
isRef = reference
# normalize if needed
if monitor is not None:
if isinstance(monitor,(tuple,list)):
assert q is not None
assert data.ndim == 2
idx = (q>= monitor[0]) & (q<= monitor[1])
monitor = np.nanmedian(data[:,idx],axis=1)
monitor = utils.reshapeToBroadcast(monitor,data)
data = data/monitor
if err is not None: err = err/monitor
ret = averageScanPoints(scan,data,errAbs=err,isRef=isRef,**kw)
if q is not None: ret["q"] = q
return ret
def saveTxt(folder,data,delayToStr=True,basename='auto',info="",**kw):
""" data must be a DataStorage instance """
# folder ends usually with sample/run so use the last two subfolders
# the abspath is needed in case we analyze the "./"
folder = os.path.abspath(folder);
if basename == 'auto':
basename = "_".join(folder.rstrip("/").split("/")[-2:]) + "_"
q = data.q if "q" in data else np.arange(data.data.shape[-1])
# save one file with all average diffs
fname = "%s/%sdiff_av_matrix.txt" % (folder,basename)
utils.saveTxt(fname,q,data.data,headerv=data.scan,**kw)
# save error bars in the matrix form
fname = "%s/%sdiff_av_matrix_err.txt" % (folder,basename)
utils.saveTxt(fname,q,data.err,headerv=data.scan,**kw)
for iscan,scan in enumerate(data.scan):
scan = utils.timeToStr(scan) if delayToStr else "%+10.5e" % scan
# try retreiving info on chi2
try:
chi2_0 = data.chi2_0[iscan]
info_delay = [ "# rep_num : chi2_0 , discarded by chi2masking ?", ]
for irep,value in enumerate(chi2_0):
info_delay.append( "# %d : %.3f" % (irep,value))
if 'chi2' in data.masks: info_delay[-1] += " %s"%str(data.masks['chi2'][iscan][irep])
info_delay = "\n".join(info_delay)
if info != '': info_delay = "%s\n%s" % (info,info_delay)
except AttributeError:
info_delay = info
# save one file per timedelay with average diff (and err)
fname = "%s/%sdiff_av_%s.txt" % (folder,basename,scan)
if 'mask' in data:
tosave = np.vstack( (data.data[iscan],data.err[iscan],
data.dataUnmasked[iscan],data.errUnmasked[iscan] ) )
columns = 'q diffmask errmask diffnomask errnomask'.split()
else:
tosave = np.vstack( (data.data[iscan],data.err[iscan] ) )
columns = 'q diff err'.split()
utils.saveTxt(fname,q,tosave,info=info_delay,columns=columns)
# save one file per timedelay with all diffs for given delay
fname = "%s/%sdiffs_%s.txt" % (folder,basename,scan)
utils.saveTxt(fname,q,data.diffsInScanPoint[iscan],info=info_delay,**kw)
def read_diff_av(folder,plot2D=False,save=None):
print("Never tested !!!")
basename = folder+"/"+"diff_av*"
files = glob.glob(basename)
files.sort()
if len(files) == 0:
print("No file found (basename %s)" % basename)
return None
temp = [os.path.basename(f[:-4]) for f in files]
delays = [f.split("_")[-1] for f in temp ]
diffav = collections.OrderedDict()
diffs = collections.OrderedDict()
for d,f in zip(delays,files):
data = np.loadtxt(f)
diffav[d]=data[:,1]
diffs[d] = np.loadtxt(folder+"/diffs_%s.dat"%d)[:,1:]
q =data[:,0]
t = np.asarray( [mc.strToTime(ti) for ti in delays] )
if plot2D:
idx = t>0
i = np.asarray( diffav.values() )
plt.pcolor(np.log10(t[idx]),q,i[idx].T)
plt.xlabel(r"$\log_{10}(t)$")
plt.ylabel(r"q ($\AA^{-1}$)")
it=np.asarray(diffav.values())
if save:
tosave = np.vstack( (q,it) )
header = np.hstack( (len(it),t) )
tosave = np.vstack( (header,tosave.T) )
np.savetxt(folder + "/all_diffs_av_matrix.txt",tosave)
return q,it,diffs,t

View File

@ -1,356 +0,0 @@
from __future__ import print_function,division
import os
import collections
# prepare logging
import logging
logfname = os.path.splitext(__file__)[0] + ".log"
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
datefmt='%y-%m-%d %H:%M:%S',
filename=logfname,
filemode='w')
import numpy as np
import pylab as plt
import mcutils as mc
import mcutils.xray as xray
from mcutils.xray import id9
#id9 = xray.id9
# use npz files (they can handle more stuff (list of arrays,unicode) than h5py)
g_default_ext = '.npz'
id9.default_extension = g_default_ext
g_default_mask = '../masks/fesalen1_run8_careful.edf'
g_default_folder = "../fesalen/fesalen1/run%d"
runsE = collections.OrderedDict()
runsT = collections.OrderedDict()
runsE[7] = 60; runsT[7] = 135
runsE[8] = 60; runsT[8] = 135
runsE[9] = 120; runsT[9] = 135
runsE[10] = 240; runsT[10] = 135
runsE[11] = 30; runsT[11] = 135
runsE[12] = 45; runsT[12] = 135
runsE[13] = 60; runsT[13] = 120
runsE[14] = 120; runsT[14] = 120
runsE[15] = 30; runsT[15] = 120
runs135=(11,12,8,9,10)
runs120=(15,13,14)
def prepareLog():
""" It allows printing to terminal on top of logfile """
# define a Handler which writes INFO messages or higher to the sys.stderr
console = logging.StreamHandler()
console.setLevel(logging.INFO)
# set a format which is simpler for console use
formatter = logging.Formatter('%(message)s')
# tell the handler to use this format
console.setFormatter(formatter)
# add the handler to the root logger (if needed)
if len(logging.getLogger('').handlers)==1:
logging.getLogger('').addHandler(console)
prepareLog()
def defineCalibrants(dmin=3):
c120 = xray.cell.Triclinic(a=10.297,b=10.716,c=13.955,alpha=72.22,beta=70.45,gamma=74.13)
p120,_ = c120.reflection_list()
c300 = xray.cell.Triclinic(a=10.485,b=11.015,c=14.280,alpha=74.61,beta=69.29,gamma=75.29)
p300,_ = c300.reflection_list()
return xray.storage.DataStorage( p120=p120, c120=c120,p300=p300,c300=c300)
def readCalculations(folder="../fesalen/cif",force=False,plot=False):
npz = folder + "/fesalen_calc.npz"
if not os.path.isfile(npz) or force:
ret = dict()
for T in ("120LS","200HS","300HS"):
# calculated with lambda = 1 Ang
theta,i = np.loadtxt( folder+"/fesalen_%s_fwhm0.1.tsv"%T,unpack=True )
theta = theta/180*np.pi
q = 4*np.pi/1*np.sin(theta/2)
key = "T%s"%T
ret[ key ] = dict( q = q, i = i )
ret = xray.storage.DataStorage(ret)
ret.save(folder + "/fesalen_calc.npz" )
ret = xray.storage.read(npz)
if plot:
T = list(ret.keys())
T.sort()
for i,t in enumerate(T):
plt.plot( ret[t].q,ret[t].i, label=t)#,color=plt.cm.Blues((0.3+0.7*i/2)) )
plt.grid()
plt.legend()
plt.xlabel(r"q ($\AA^{-1}$)")
return ret
def findCenter(cen=(484,479.5),dist=0.251):
files = xray.utils.getEdfFiles("../fesalen/fesalen1/run17/",nFiles=100)
img = xray.azav.read(files).mean(axis=0) - 10.
wavelength=12.398/18.*1e-10
# 1,0,0 peak are the one close to azimuth 0 and are at 0.6596Ang-1 at 120K
# and 0.65098 at 300K (run 17 is @ RT)
xray.azav.find_center(img,dist=dist,psize=0.0001770834,center=cen,
reference=0.65098,wavelength=wavelength)
def removeBaseline(folder,qlims=(0.5,2.5),max_iter=30,force=False):
if isinstance(folder,int): folder = g_default_folder%folder
fname = folder + "/pyfai_1d_nobaseline"+g_default_ext
if not os.path.isfile(fname) or force:
logging.info("Calling azav with default parameters")
data = azav(folder,withBaseline=True)
# first peak (around 0.6) is weak and requires special care ...
data.q,data.data = xray.utils.removeBackground(data.q,data.data,
xlims=qlims,max_iter=max_iter,background_regions=[ (0,0.58) ])
data.save(fname)
else:
data = xray.storage.read(fname)
return data
def azav(folder,nQ=3000,force=False,saveChi=True,mask=g_default_mask,
withBaseline=True):
if isinstance(folder,int): folder = g_default_folder%folder
if withBaseline:
data = id9.doFolder_azav(folder,nQ=nQ,force=force,mask=mask,
saveChi=saveChi)
else:
data=removeBaseline(folder)
return data
def datared(folder,monitor=(0.5,4),showPlot=True,errMask=5,chi2Mask=1.5,
qlims=(0.5,3),withBaseline=False,forceDatared=False,
select=slice(None),**kw):
if isinstance(folder,int): folder = g_default_folder%folder
data = azav(folder,withBaseline=withBaseline)
if withBaseline:
diffFile = folder + "/diffs" + g_default_ext
else:
diffFile = folder + "/diffs_nobaseline" + g_default_ext
if not os.path.isfile(diffFile) or forceDatared:
data,diffs = id9.doFolder_dataRed(data,monitor=monitor,
errMask=errMask,chi2Mask=chi2Mask,qlims=qlims,
outStorageFile=diffFile)
else:
diffs = xray.storage.read(diffFile)
if showPlot:
xray.utils.plotdiffs(diffs.q,diffs.data,t=diffs.scan,select=select,
absSignal=diffs.dataAbsAvNeg,absSignalScale=10)
plt.title(folder + " norm %s" % str(monitor))
plt.figure()
xray.utils.plotdiffs(diffs.q,diffs.dataAsAbs,t=diffs.scan,
select=select)
plt.title(folder + " norm %s" % str(monitor))
return data,diffs
def doall(folder,force=False,removeBaseline=True):
azav(folder,force=force)
return datared(folder)
def _getAmplitudeFromSVD(i,comp=0):
u,s,v = np.linalg.svd(i)
return u[:,comp]*s[comp]
def _getAmplitudeFromLinFit(i,ref=1):
amp = [mc.linFit(i[ref],ii) for ii in i]
return np.asarray(amp)
def _getAmplitudeFromAbs(i,e):
amp = np.abs(i).mean(axis=1)
baseline = np.median(e)
return amp-baseline
def _getAmplitudeFromPeak(i):
""" get max-min (to be less sensitive to baseline change """
amp = np.max(i,axis=1)-np.min(i,axis=1)
return amp
def anaAmplitue(run=12,plot=False,scaleWithE=False,withBaseline=False):
folder = g_default_folder % run
pars = dict( chi2Mask=1.5,errMask=3,monitor=(0.4,2.5),showPlot=False )
data,diffs=datared(folder,**pars,withBaseline=withBaseline)
ranges = ( (0.66,0.72), (0.74,0.8), (0.82,0.88) ) #, (0.96,1.02 ) )
nPlot = len(ranges)
if plot: fig,ax = plt.subplots(nPlot,1,sharex=True)
amp = collections.OrderedDict()
for num,r in enumerate(ranges):
idx = (diffs.q>r[0]) & (diffs.q<r[1])
#amplitude = _getAmplitudeFromLinFit( diffs.data[:,idx] )
amplitude = _getAmplitudeFromAbs( diffs.data[:,idx],diffs.err[:,idx] )
if scaleWithE: amplitude = amplitude/runsE[run]
#amplitude = _getAmplitudeFromPeak( diffs.data[:,idx] )
amp[r] = amplitude
if plot:
ax[num].plot(diffs.scan,amplitude,'-o')
ax[num].set_title("Range %s"%(str(r)))
return diffs.scan,amp
def _getPeakPos(x,y,e=1):
if y.ndim == 1: y = (y,)
ret = dict()
for yy,ee in zip(y,e):
res = xray.peaks.fitPeak(x,yy,err=ee)
#plt.figure(); res.plot_fit(); input("ok")
pars = res.params.keys()
for p in pars:
if not p in ret: ret[p] = dict( value = [], err = [] )
ret[p]['value'].append( res.params[p].value )
ret[p]['err'].append( res.params[p].stderr )
for p in pars:
ret[p]['value'] = np.asarray(ret[p]['value'])
ret[p]['err'] = np.asarray(ret[p]['err'])
return xray.storage.DataStorage(ret)
def anaPeakPos(run=12,plot=False,withBaseline=False):
folder = g_default_folder % run
pars = dict( chi2Mask=1.5,errMask=3,monitor=(0.4,2.5),showPlot=False )
data,diffs=datared(folder,**pars,withBaseline=withBaseline)
ranges = ( (0.66,0.72), (0.74,0.8), (0.82,0.88) ) #, (0.96,1.02 ) )
nPlot = len(ranges)
if plot: fig,ax = plt.subplots(nPlot,1,sharex=True)
pars = collections.OrderedDict()
for num,r in enumerate(ranges):
idx = (diffs.q>r[0]) & (diffs.q<r[1])
#amplitude = _getAmplitudeFromLinFit( diffs.data[:,idx] )
pars[r] = _getPeakPos( data.q[idx],diffs.dataAsAbs[:,idx],diffs.err[:,idx])
if plot:
ax[num].axhline( pars[r].center.value[0] )
ax[num].plot(diffs.scan,pars[r].center.value,'-o')
ax[num].set_title("Range %s"%(str(r)))
return diffs.scan,pars
#### PLOTS #####
def compareT():
fig,ax = plt.subplots(2,1,sharex=True)
plt.sca( ax[0] )
readCalculations(plot=True)
plt.sca( ax[1] )
t120 = removeBaseline("../fesalen/fesalen1/run16")
t300 = removeBaseline("../fesalen/fesalen1/run17")
plt.plot(t120.q,t120.data.mean(0),label="Data 120K")
plt.plot( [0,.01],[0,.01] ) # just to have a matching color
plt.plot(t300.q,t300.data.mean(0),label="Data 300K")
def showBaseline(N=2):
data_bs = azav("../fesalen/fesalen1/run17")
data = removeBaseline("../fesalen/fesalen1/run17")
idx1 = np.argwhere( data.q[0] == data_bs.q )[0][0]
idx2 = np.argwhere( data.q[-1] == data_bs.q )[0][0]
bs = data_bs.data[:,idx1:idx2+1]-data.data
fig,ax=plt.subplots(2,N,sharex=True,sharey='row',squeeze=False)
ax = ax.T
for i,a in enumerate(ax):
a[0].plot(data_bs.q,data_bs.data[i])
a[0].plot(data.q,bs[i])
a[1].plot(data.q,data.data[i])
def showBaselineStability(N=100):
# has to be done with cryo on
data_bs = azav("../fesalen/fesalen1/run15",withBaseline=True)
data = azav("../fesalen/fesalen1/run15",withBaseline=False)
fig,ax=plt.subplots(2,2,sharey=True)
i = np.arange(data.data.shape[0])
ax[0,0].pcolormesh(i,data_bs.q,data_bs.data.T)
ax[0,0].set_title("Without baseline subtraction")
ax[0,0].set_ylabel(r"q ($\AA^{-1}$)")
for x in range(N):
ax[0,1].plot(data_bs.data[x],data_bs.q,lw=0.5,color="0.5",alpha=0.1)
ax[1,0].pcolormesh(i,data.q,data.data.T)
ax[1,0].set_xlabel("Image number")
ax[1,0].set_title("With baseline subtraction")
ax[1,0].set_ylabel(r"q ($\AA^{-1}$)")
for x in range(N):
ax[1,1].plot(data.data[x],data.q,lw=0.5,color="0.5",alpha=0.1)
ax[0,1].set_xlim(0,400)
ax[1,1].set_xlim(0,180)
ax[1,1].set_xlabel("Intensity")
[a.set_xlim(0,400) for a in ax[:,0]]
plt.ylim(0.4,3)
def comparisonTRbaseline(folder="../fesalen/fesalen1/run9",select=slice(None)):
pars = dict( chi2Mask=1.5,errMask=3,monitor=(0.4,2.5),showPlot=False )
data,diffs_bs=datared(folder,**pars,withBaseline=True)
data,diffs=datared(folder,**pars,withBaseline=False)
fig,ax=plt.subplots(2,1,sharex=True)
plt.sca(ax[0])
xray.utils.plotdiffs(diffs.q,diffs.data,t=diffs.scan,
absSignal=diffs.dataAbsAvAll,absSignalScale=10,select=select)
plt.title(folder + " norm %s" % str(pars['monitor']))
plt.sca(ax[1])
xray.utils.plotdiffs(diffs_bs.q,diffs_bs.data,t=diffs_bs.scan,
absSignal=diffs_bs.dataAbsAvAll,absSignalScale=30,select=select)
def compareAmplitudes(runs=(11,12,8,9,10),scaleWithE=True,withBaseline=False):
runs=list(runs)
nRuns = len(runs)
fig,ax=plt.subplots(nRuns,3,sharex=True,sharey='col',figsize=[12,8])
subplots_kw = dict( hspace=0,wspace=0 )
plt.subplots_adjust( **subplots_kw )
plt.rcParams['font.size']=14
for run,arow in zip(runs,ax):
t,amp = anaAmplitue(run,plot=False,scaleWithE=scaleWithE,withBaseline=withBaseline)
for k,a in zip(amp.keys(),arow):
a.axhline(0,ls='--')
a.plot(t,amp[k],'o')
for r,a in zip(runs,ax[:,0]):
label = "run %d (%.0f K)\n%.0f uJ"%(r,runsT[r],runsE[r])
a.set_ylabel(label)
for interval,a in zip(amp.keys(),ax[0,:]): a.set_title("range %s"%(str(interval)))
def compareAmplitudes2(runs=(11,12,8,9,10),scaleWithE=False,withBaseline=False):
runs=list(runs)
nRuns = len(runs)
fig,ax=plt.subplots(1,3,sharex=True,figsize=[12,8])
subplots_kw = dict( hspace=0,wspace=0 )
plt.subplots_adjust( **subplots_kw )
plt.rcParams['font.size']=14
for a in ax: a.axhline(0,ls='--')
for run in runs:
t,amp = anaAmplitue(run,plot=False,scaleWithE=scaleWithE,withBaseline=withBaseline)
for k,a in zip(amp.keys(),ax):
label = "run %d %.0f uJ"%(run,runsE[run])
a.plot(t,amp[k],'-o',label=label)
# for r,a in zip(runs,ax[:,0]):
# a.set_ylabel(label)
for interval,a in zip(amp.keys(),ax): a.set_title("range %s"%(str(interval)))
def comparePosition2(runs=(11,12,8,9,10),withBaseline=False,useRatio=True,scaleWithE=False):
runs=list(runs)
nRuns = len(runs)
fig,ax=plt.subplots(1,3,sharex=True,figsize=[12,8],sharey=useRatio)
subplots_kw = dict( hspace=0,wspace=0 )
plt.subplots_adjust( **subplots_kw )
plt.rcParams['font.size']=14
if scaleWithE and not ratio:
logging.warn("scaleWithE requires useRatio, setting scaleWithE to False")
scaleWithE = Fale
for run in runs:
t,pars = anaPeakPos(run,plot=False,withBaseline=withBaseline)
for k,a in zip(pars.keys(),ax):
label = "run %d %.0f uJ"%(run,runsE[run])
if useRatio:
ratio = pars[k].center.value[1:]/pars[k].center.value[0]
err = pars[k].center.err[1:]/pars[k].center.value[0]
if scaleWithE: ratio = (ratio-1)/runsE[run]+1
line = a.errorbar(t[1:],ratio,err,fmt='-o',label=label)[0]
else:
line = a.plot(t[1:],pars[k].center.value[1:],'-o',label=label)[0]
a.axhline(pars[k].center.value[0],color=line.get_color())
for interval,a in zip(pars.keys(),ax): a.set_title("range %s"%(str(interval)))
if useRatio:
ax[0].set_ylabel("Peak Position / Peak Position(t=-5ns)")
else:
ax[0].set_ylabel("Peak Position ($\AA^{-1}$)")

View File

@ -1,68 +0,0 @@
from __future__ import print_function,division
import numpy as np
import pylab as plt
import os
import mcutils as mc
import mcutils.xray as xray
from mcutils.xray import id9
id9 = xray.id9
g_default_ext = '.npz'
# use npz files (they can handle more stuff (list of arrays,unicode) than h5py)
id9.default_extension = g_default_ext
#id9.default_extension = '.h5'
def readCalc():
fold = "../tiox/calculated_patterns/"
names = "alpha beta lam".split()
fnames = "alpha500K beta290K lambda".split()
calc = dict()
for name,fname in zip(names,fnames):
q,i=np.loadtxt(fold + "%s.xye.q"%fname,unpack=True)
calc[name] = dict( q=q, i=i )
return xray.storage.DataStorage(calc)
calc = readCalc()
def azav(folder,nQ=1500,force=False,saveChi=True,mask='y>470'):
return id9.doFolder_azav(folder,nQ=nQ,force=force,mask=mask,saveChi=saveChi)
def datared(folder,monitor=(1,5),forceDatared=False,showPlot=True,**kw):
azavFile = folder + "/pyfai_1d" + g_default_ext
diffFile = folder + "/diffs" + g_default_ext
if not os.path.isfile(diffFile) or forceDatared:
data,diffs = id9.doFolder_dataRed(folder,monitor=monitor,**kw)
else:
data = xray.storage.read(azavFile)
diffs = xray.storage.read(diffFile)
if showPlot:
xray.utils.plotdiffs(diffs.q,diffs.data,t=diffs.scan,
absSignal=diffs.dataAbsAvAll,absSignalScale=30)
plt.plot(calc.lam.q,calc.lam.i/1000+0.1,label='calc')
plt.title(folder + " norm %s" % str(monitor))
return data,diffs
def doall(folder,force=False):
azav(folder,force=force)
return datared(folder,forceDatared=force)
def anaAmplitue(run=6):
fname = "../tiox/tiox1/run%d/diffs.npz" % run
data = xray.storage.DataStorage(fname)
ranges = ( (1.75,1.85), (2.2,2.4), (3.25,3.4) )
nPlot = len(ranges)
fig,ax = plt.subplots(nPlot,1,sharex=True)
for r,a in zip(ranges,ax):
idx = (data.q>r[0]) & (data.q<r[1])
amplitude = np.abs(data.data[:,idx]).mean(axis=1)
a.plot(data.scan,amplitude,'-o')
a.set_title("Range %s"%(str(r)))
def plotCalc(scale=1):
fold = "../tiox/calculated_patterns/"
q,i=readtxt(fold + "alpha500K.xye.q")
plt.plot(q,i*scale,label="alpha")
q,i=readtxt(fold + "beta290K.xye.q")
plt.plot(q,i*scale,label="beta")
q,i=readtxt(fold + "lambda.xye.q")
plt.plot(q,i*scale,label="lambda")

View File

@ -1,85 +0,0 @@
"""
module that contains filters and outliers removal procedures
most of them return the data array and a dictionary with additional info
(parameters, statistics, etc)
"""
from __future__ import print_function,division
from . import utils
import logging
import statsmodels.robust
log = logging.getLogger(__name__) # __name__ is "foo.bar" here
import numpy as np
np.seterr(all='ignore')
def removeZingers(curves,errs=None,norm='auto',threshold=10,useDerivative=False):
""" curves will be normalized internally
if errs is None, calculate mad based noise
useDerivative for data with trends ..
"""
# normalize
if norm == 'auto':
norm = np.nanmean(curves,axis=1)
norm = utils.reshapeToBroadcast(norm,curves)
if useDerivative:
data = np.gradient(curves/norn,axis=0)
else:
data = curves/norm
median = np.median(data,axis=0)
# calculate or normalize error
if errs is None:
errs = statsmodels.robust.mad(data,axis=0)
else:
errs = errs/norm
diff = np.abs(data-median)/errs
idx = diff > threshold
log.debug("Removed %d zingers from %d curves"%(idx.sum(),len(curves)))
print("Removed %d zingers from %d curves"%(idx.sum(),len(curves)))
if idx.sum()>0:
curves[idx]=np.nan
#curves = np.ma.MaskedArray(data=curves,mask=idx)
return curves
def filterOutlier(curves,errs=None,norm=None,threshold=10):
# normalize
if norm == 'auto':
norm = np.nanmean(curves,axis=1)
norm = utils.reshapeToBroadcast(n,curves)
elif norm is None:
norm = 1
curves = curves/norm
if errs is None:
errs = statsmodels.robust.mad(curves,axis=0)
else:
errs = errs/norm
median = np.median(curves)
diff = np.abs(curves-median)/errs
chi2 = np.sum(diff**2)/len(curves)
idx = chi2 < threshold
return curves[idx]
def chi2Filter(diffs,threshold=10):
""" Contrary to removeZingers, this removes entire curves """
idx_mask = []
for iscan in range(len(diffs.diffsInScanPoint)):
idx = diffs.chi2_0[iscan] > threshold
# expand along other axis (q ...)
#idx = utils.reshapeToBroadcast(idx,data.diffsInScanPoint[iscan])
idx_mask.append(idx)
log.debug("Chi2 mask, scanpoint: %s, curves filtereout out %d/%d (%.2f%%)"%\
(data.scan[iscan],idx.sum(),len(idx),idx.sum()/len(idx)*100) )
print("Chi2 mask, scanpoint: %s, curves filtereout out %d/%d (%.2f%%)"%\
(data.scan[iscan],idx.sum(),len(idx),idx.sum()/len(idx)*100) )
if "masks" not in data: data['masks'] = dict()
if "masks_pars" not in data: data['masks_pars'] = dict()
data['masks']['chi2'] = idx_mask
data['masks_pars']['chi2_threshold'] = threshold
return data

View File

@ -1,212 +0,0 @@
import logging
log = logging.getLogger(__name__)
import os
import collections
import numpy as np
from . import azav
from . import dataReduction
from . import utils
from . import storage
from . import filters
default_extension = ".h5"
def _conv(x):
try:
x = float(x)
except:
x = np.nan
return x
def _readDiagnostic(fname,retry=3):
ntry = 0
while ntry<retry:
try:
data = np.genfromtxt(fname,usecols=(2,3),\
dtype=None,converters={3: lambda x: _conv(x)},
names = ['fname','delay'])
return data
except Exception as e:
log.warn("Could not read diagnostic file, retrying soon,error was %s"%e)
ntry += 1
# it should not arrive here
raise ValueError("Could not read diagnostic file after %d attempts"%retry)
def readDiagnostic(fname):
""" return an ordered dict dictionary of filename; for each key a rounded
value of delay is associated """
if os.path.isdir(fname): fname += "/diagnostics.log"
# try to read diagnostic couple of times
data = _readDiagnostic(fname,retry=4)
files = data['fname'].astype(str)
delays = data['delay']
# skip lines that cannot be interpreted as float (like done, etc)
idx_ok = np.isfinite( delays )
files = files[idx_ok]
files = np.asarray( [utils.getBasename(f) for f in files])
delays = delays[idx_ok]
delays = np.round(delays.astype(float),12)
return dict( file = files, scan = delays)
def _findDark(line):
_,value = line.split(":")
return float(value)
def _delayToNum(delay):
if delay.decode('ascii') == 'off':
delay = -10
else:
delay=utils.strToTime(delay)
return delay
def findLogFile(folder):
files = utils.getFiles(folder,basename='*.log')
files.remove(os.path.join(folder,"diagnostics.log"))
logfile = files[0]
if len(files)>1: log.warn("Found more than one *.log file that is not diagnostics.log: %s"%files)
return logfile
def readLogFile(fnameOrFolder,subtractDark=False,skip_first=0,asDataStorage=True,last=None):
""" read id9 style logfile """
if os.path.isdir(fnameOrFolder):
fname = findLogFile(fnameOrFolder)
else:
fname = fnameOrFolder
f = open(fname,"r")
lines = f.readlines()
f.close()
lines = [line.strip() for line in lines]
darks = {}
for line in lines:
if line.find("pd1 dark/sec")>=0: darks['pd1ic'] = _findDark(line)
if line.find("pd2 dark/sec")>=0: darks['pd2ic'] = _findDark(line)
if line.find("pd3 dark/sec")>=0: darks['pd3ic'] = _findDark(line)
if line.find("pd4 dark/sec")>=0: darks['pd4ic'] = _findDark(line)
for iline,line in enumerate(lines):
if line.lstrip()[0] != "#": break
data=np.genfromtxt(fname,skip_header=iline-1,names=True,comments="%",dtype=None,converters = {'delay': lambda s: _delayToNum(s)})
if subtractDark:
for diode in ['pd1ic','pd2ic','pd3ic','pd4ic']:
if diode in darks: data[diode]=data[diode]-darks[diode]*data['timeic']
data = data[skip_first:last]
if asDataStorage:
# rstrip("_") is used to clean up last _ that appera for some reason in file_
data = storage.DataStorage( dict((name.rstrip("_"),data[name]) for name in data.dtype.names ) )
data.file = data.file.astype(str)
return data
def doFolder_azav(folder,nQ=1500,files='*.edf*',force=False,mask=None,
saveChi=True,poni='pyfai.poni',storageFile='auto',dark=9.9,dezinger=None,
qlims=(0,10),removeBack=False,removeBack_kw=dict(),skip_first=0,
last=None):
""" very small wrapper around azav.doFolder, essentially just reading
the id9 logfile or diagnostics.log """
try:
loginfo = readLogFile(folder,skip_first=skip_first,last=last)
except Exception as e:
log.warn("Could not read log file, trying to read diagnostics.log")
loginfo = readDiagnostic(folder)
if storageFile == 'auto' : storageFile = folder + "/" + "pyfai_1d" + default_extension
data = azav.doFolder(folder,files=files,nQ=nQ,force=force,mask=mask,
saveChi=saveChi,poni=poni,storageFile=storageFile,logDict=loginfo,dark=dark,save=False,dezinger=dezinger)
#try:
# if removeBack is not None:
# _,data.data = azav.removeBackground(data,qlims=qlims,**removeBack_kw)
#except Exception as e:
# log.error("Could not remove background, error was %s"%(str(e)))
# idx = utils.findSlice(data.q,qlims)
# n = np.nanmean(data.data[:,idx],axis=1)
# data.norm_range = qlims
# data.norm = n
# n = utils.reshapeToBroadcast(n,data.data)
# data.data_norm = data.data/n
data.save(storageFile)
return data
def doFolder_dataRed(azavStorage,monitor=None,funcForAveraging=np.nanmean,
qlims=None,outStorageFile='auto',reference='min'):
""" azavStorage if a DataStorage instance or the filename to read
monitor : normalization vector that can be given as
1. numpy array
2. a list (interpreted as q-range normalization)
3. a string to look for as key in the log, e.g.
monitor="pd2ic" would reult in using
azavStorage.log.pd2ic
"""
if isinstance(azavStorage,storage.DataStorage):
data = azavStorage
folder = azavStorage.folder
elif os.path.isfile(azavStorage):
folder = os.path.dirname(azavStorage)
data = storage.DataStorage(azavStorage)
else:
# assume is just a folder name
folder = azavStorage
azavStorage = folder + "/pyfai_1d" + default_extension
data = storage.DataStorage(azavStorage)
#assert data.q.shape[0] == data.data.shape[1] == data.err.shape[1]
if qlims is not None:
idx = (data.q>qlims[0]) & (data.q<qlims[1])
data.data = data.data[:,idx]
data.err = data.err[:,idx]
data.q = data.q[idx]
if isinstance(monitor,str): monitor = data['log'][monitor]
# calculate differences
diffs = dataReduction.calcTimeResolvedSignal(data.log.delay,data.data,
err=data.err,q=data.q,reference=reference,monitor=monitor,
funcForAveraging=funcForAveraging)
# save txt and npz file
dataReduction.saveTxt(folder,diffs,info=data.pyfai_info)
if outStorageFile == 'auto':
outStorageFile = folder + "/diffs" + default_extension
diffs.save(outStorageFile)
return data,diffs
def doFolder(folder,azav_kw = dict(), datared_kw = dict(),online=True, retryMax=20,force=False):
import matplotlib.pyplot as plt
if folder == "./": folder = os.path.abspath(folder)
fig = plt.figure()
lastNum = None
keepGoing = True
lines = None
retryNum = 0
if online: print("Press Ctrl+C to stop")
while keepGoing and retryNum < retryMax:
try:
data = doFolder_azav(folder,**azav_kw)
# check if there are new data
if lastNum is None or lastNum<data.data.shape[0]:
data,diffs = doFolder_dataRed(data,**datared_kw)
if lines is None or len(lines) != diffs.data.shape[0]:
lines,_ = utils.plotdiffs(diffs,fig=fig,title=folder)
else:
utils.updateLines(lines,diffs.data)
plt.draw()
lastNum = data.data.shape[0]
retryNum = 0
else:
retryNum += 1
plt.pause(30)
if force: force=False; # does not make sense to have always True ...
except KeyboardInterrupt:
keepGoing = False
if not online: keepGoing = False
return data,diffs

View File

@ -1,210 +0,0 @@
from __future__ import print_function
import sys
if sys.version_info.major == 2: input=raw_input
import logging
log = logging.getLogger(__name__)
import os
import collections
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
maskComponent = collections.namedtuple('maskComponent',['operation','geometry','vertices'])
def _rectangleToMask(X,Y,vertices):
( (x1,y1), (x2,y2) ) = vertices
if x1>x2: x1,x2=x2,x1
if y1>y2: y1,y2=y2,y1
return (X>x1) & (X<x2) & ( Y>y1) & (Y<y2)
def _circleToMask(X,Y,vertices):
c,p = vertices
r = np.sqrt( (p[0]-c[0])**2 + (p[1]-c[1])**2 )
d = np.sqrt((X-c[0])**2+(Y-c[1])**2)
return d<r
def _polygonToMask(X,Y,vertices):
points = np.vstack((X.flatten(),Y.flatten())).T
path = Path(vertices)
grid = path.contains_points(points)
return grid.reshape(X.shape)
class MyMask(object):
def __init__(self,img=None):
self.comp = []
self.img = img
self.mask = None
self._cache = None
def _define_component(self,operation,geometry,*vertices):
#print("define_comp",type(vertices),vertices)
if geometry == 'circle' and len(vertices) == 3:
xcen,ycen,radius = vertices
vertices = ( (xcen,ycen), (xcen+radius,ycen) )
if geometry == 'rectangle' and len(vertices) == 4:
vertices = ( (vertices[0],vertices[1]),(vertices[2],vertices[3]) )
# make sure vertices tuples
if isinstance(vertices,list):
vertices = [ (v[0],v[1]) for v in vertices ]
vertices = tuple(vertices)
a = dict( vertices = None )
self.comp.append( maskComponent(operation=operation,vertices=vertices,geometry=geometry) )
def addCircle(self,*vertices): self._define_component( 'add', 'circle', *vertices )
def subtractCircle(self,*vertices): self._define_component( 'subtract', 'circle', *vertices )
def addRectangle(self,*vertices): self._define_component( 'add','rectangle', *vertices)
def subtractRectangle(self,*vertices): self._define_component( 'subtract','rectangle',*vertices)
def addPolygon(self,*vertices): self._define_component( 'add','polygon',*vertices)
def subtractPolygon(self,*vertices): self._define_component( 'subtract','polygon',*vertices)
def getMask(self,shape=None):
if shape is None and self.img is not None: shape = self.img.shape
if shape is None and self.img is None: shape = self._cache['shape']
if self._cache is None: self._cache = dict( shape = shape )
# reset cache if shape does not match
if shape != self._cache['shape']:
self._cache = dict( shape = shape )
X,Y = np.meshgrid ( range(shape[1]),range(shape[0]) )
for component in self.comp:
if component not in self._cache:
if component.geometry == 'circle':
mask = _circleToMask( X,Y,component.vertices )
elif component.geometry == 'rectangle':
mask = _rectangleToMask( X,Y,component.vertices )
elif component.geometry == 'polygon':
mask = _polygonToMask( X,Y,component.vertices )
else:
raise ValueError("Mask type %s not recongnized"%component.geometry)
self._cache[component] = mask
mask = np.zeros(shape,dtype=np.bool)
for comp in self.comp:
m = self._cache[ comp ]
if (comp.operation == "add"):
mask[m] = True
else:
mask[m] = False
self.mask = mask
return mask
def getMatplotlibMask(self,shape=None):
mask = self.getMask(shape=shape)
# convert
mpl_mask = np.zeros( (mask.shape[0],mask.shape[1],4) )
mpl_mask[:,:,:3] = 0.5; # gray color
mpl_mask[:,:,3] = mask/2; # give some transparency
return mpl_mask
def save(self,fname,inverted=False):
import fabio
mask = self.mask
if (inverted): mask = ~mask
i=fabio.edfimage.edfimage(mask.astype(np.uint8)); # edf does not support bool
i.save(fname)
def snap(point,shape,snapRange=20):
snapped = list(point)
if snapped[0] < snapRange: snapped[0] = 0
if snapped[0] > shape[1]-snapRange: snapped[0] = shape[1]
if snapped[1] < snapRange: snapped[1] = 0
if snapped[1] > shape[0]-snapRange: snapped[1] = shape[0]
return tuple(snapped)
def getPoints(N=1,shape=(100,100),snapRange=0):
if N<1: print('Right click cancels last point, middle click ends the polygon')
c = plt.ginput(N)
c = [ snap(point,shape,snapRange=snapRange) for point in c ]
if len(c) == 1: c = c[0]
return c
def makeMaskGui(img,snapRange=60):
""" snapRange controls border snapping (in pixels, use <= 0 to disable """
mask = MyMask(img)
ans='ok'
while (ans != 'done'):
plt.imshow(img)
plt.clim(np.percentile(img,(2,98)))
plt.imshow(mask.getMatplotlibMask())
plt.pause(0.01)
ans = input("What's next p/P/c/C/r/R/done? (capitals = subtract)")
if ans == "c":
print("Adding circle, click on center then another point to define radius")
vertices = getPoints(N=2,shape=img.shape,snapRange=snapRange)
mask.addCircle(*vertices)
if ans == "C":
print("Subtracting circle, click on center then another point to define radius")
vertices = getPoints(N=2,shape=img.shape,snapRange=snapRange)
mask.subtractCircle(*vertices)
if ans == "r":
print("Adding rectangle, click on one corner and then on the opposite one")
vertices = getPoints(N=2,shape=img.shape,snapRange=snapRange)
mask.addRectangle(*vertices)
if ans == "R":
print("Subtracting rectangle, click on one corner and then on the opposite one")
vertices = getPoints(N=2,shape=img.shape,snapRange=snapRange)
mask.subtractRectangle(*vertices)
if ans == 'p':
print("Adding polygon")
vertices = getPoints(N=-1,shape=img.shape,snapRange=snapRange)
mask.addPolygon(*vertices)
if ans == 'P':
print("Subtracting polygon")
vertices = getPoints(N=-1,shape=img.shape,snapRange=snapRange)
mask.subtractPolygon(*vertices)
plt.imshow(mask.getMatplotlibMask())
plt.pause(0.01)
fname = input("Enter a valid filename (ext .edf or .npy) if you want to save the mask (empty otherwise)")
try:
if fname != '':
ext = os.path.splitext(fname)[1]
if ext == '.edf':
mask.save(fname)
elif ext == '.npy':
np.save(fname,mask.getMask())
except Exception as e:
log.error("Error in saving mask")
log.error(e)
finally:
return mask
def maskBorder(width,shape):
mask = np.zeros(shape,dtype=bool)
mask[ :width , : ] = True
mask[ -width: , : ] = True
mask[ : , :width ] = True
mask[ : , -width: ] = True
return mask
def maskCenterLines(width,shape):
mask = np.zeros(shape,dtype=bool)
if isinstance(width,int): width = (width,width)
c0 = int(shape[0]/2)
c1 = int(shape[1]/2)
w0 = int(width[0]/2)
w1 = int(width[1]/2)
mask[ c0-w0:c0+w0 , : ] = True
mask[ : , c1-w1:c1+w1 ] = True
return mask
def test(shape=(1000,2000)):
mask = MyMask()
mask.addCircle(400,300,250)
mask.subtractCircle(400,300,150)
mask.addRectangle(350,250,1500,700)
plt.imshow( mask.getMask(shape) )
return mask
if __name__ == "__main__":
test()
plt.show()
ans=input("Enter to finish")

View File

@ -1,32 +0,0 @@
import lmfit
import numpy as np
import logging as log
pv = lmfit.models.PseudoVoigtModel()
def fitPeak(x,y,err=1,autorange=False):
if isinstance(err,np.ndarray):
if np.all(err==0):
err = 1
log.warn("Asked to fit peak but all errors are zero, forcing them to 1")
elif np.isfinite(err).sum() != len(err):
idx = np.isfinite(err)
x = x[idx]
y = y[idx]
err = err[idx]
log.warn("Asked to fit peak but some errorbars are infinite or nans,\
excluding those points")
if autorange:
# find fwhm
idx = np.ravel(np.argwhere( y<y.max()/2 ))
# find first crossing
p1 = idx[idx<np.argmax(y)][-1]
p2 = idx[idx>np.argmax(y)][0]
c = int( (p1+p2)/2 )
dp = int( np.abs(p1-p2) )
idx = slice(c-dp,c+dp)
x = x[idx]
y = y[idx]
pars = pv.guess(y,x=x)
ret = pv.fit(y,x=x,weights=1/err,params=pars)
return ret

View File

@ -1,346 +0,0 @@
from __future__ import print_function,division
import logging
log = logging.getLogger(__name__) # __name__ is "foo.bar" here
import numpy as np
np.seterr(all='ignore')
import os
import glob
import pathlib
import re
import numbers
from . import storage as storage
try:
import matplotlib.pyplot as plt
except ImportError:
log.warn("Can't import matplotlib !")
_time_regex = re.compile( "(-?\d+\.?\d*(?:ps|ns|us|ms)?)")
_timeInStr_regex = re.compile("_(-?\d+\.?\d*(?:ps|ns|us|ms)?)")
def getFiles(folder,basename="*.edf*",nFiles=None):
files = glob.glob(folder + "/" + basename)
files.sort()
if nFiles is not None: files = files[:nFiles]
return files
def getEdfFiles(folder,**kw):
return getFiles(folder,basename="*.edf*",**kw)
def getDelayFromString(string) :
match = _timeInStr_regex.search(string)
return match and match.group(1) or None
_time_regex = re.compile("(-?\d+\.?\d*)((?:s|fs|ms|ns|ps|us)?)")
def strToTime(delay) :
if isinstance(delay,bytes): delay = delay.decode('ascii')
_time2value = dict( fs = 1e-15, ps = 1e-12, ns = 1e-9, us = 1e-6, ms = 1e-3, s = 1)
match = _time_regex.search(delay)
if match:
n,t = float(match.group(1)),match.group(2)
value = _time2value.get(t,1)
return n*value
else:
return None
def timeToStr(delay,fmt="%+.0f"):
a_delay = abs(delay)
if a_delay >= 1:
ret = fmt % delay + "s"
elif 1e-3 <= a_delay < 1:
ret = fmt % (delay*1e3) + "ms"
elif 1e-6 <= a_delay < 1e-3:
ret = fmt % (delay*1e6) + "us"
elif 1e-9 <= a_delay < 1e-6:
ret = fmt % (delay*1e9) + "ns"
elif 1e-12 <= a_delay < 1e-9:
ret = fmt % (delay*1e12) + "ps"
elif 1e-15 <= a_delay < 1e-12:
ret = fmt % (delay*1e12) + "fs"
elif 1e-18 <= a_delay < 1e-15:
ret = fmt % (delay*1e12) + "as"
else:
ret = str(delay) +"s"
return ret
def removeExt(fname):
""" special remove extension meant to work with compressed files.edf and .edf.gz files """
if fname[-3:] == ".gz": fname = fname[:-3]
return os.path.splitext(fname)[0]
def getBasename(fname):
return os.path.basename(removeExt(fname));
def findSlice(array,lims):
start = np.ravel(np.argwhere(array>lims[0]))[0]
stop = np.ravel(np.argwhere(array<lims[1]))[-1]
return slice(int(start),int(stop))
def removeBackground(x,data,xlims=None,max_iter=100,background_regions=[],**kw):
from dualtree import dualtree
if data.ndim == 1: data = data[np.newaxis,:]
if xlims is not None:
idx = findSlice(x,xlims)
x = x[idx]
data = data[:,idx].copy()
else:
data = data.copy(); # create local copy
# has to be a list of lists ..
if background_regions != [] and isinstance(background_regions[0],numbers.Real):
background_regions = [background_regions,]
background_regions = [findSlice(x,brange) for brange in background_regions]
for i in range(len(data)):
data[i] = data[i] - dualtree.baseline(data[i],max_iter=max_iter,
background_regions=background_regions,**kw)
return x,np.squeeze(data)
def plotdata(*args,x=None,plot=True,showTrend=True,title=None,clim='auto',fig=None):
if isinstance(args[0],storage.DataStorage):
q = args[0].q; data=args[0].data;
if title is None: title = args[0].folder
else:
q,data = args
if not (plot or showTrend): return
if x is None: x = np.arange(data.shape[0])
if clim == 'auto': clim = np.nanpercentile(data,(1.5,98.5))
one_plot = showTrend or plot
two_plot = showTrend and plot
if one_plot and not two_plot:
if fig is None:
fig,ax = plt.subplots(1,1)
else:
fig.clear()
ax = fig.axes
if two_plot:
if fig is None:
fig,ax = plt.subplots(2,1,sharex=True)
else:
ax = fig.axes
ax = np.atleast_1d(ax)
if showTrend:
plt.sca(ax[1])
plt.pcolormesh(q,x,data)
plt.ylabel("image number, 0 being older")
plt.xlabel(r"q ($\AA^{-1}$)")
plt.clim( *clim )
if plot:
ax[0].plot(q,np.nanmean(data,axis=0))
if (plot or showTrend) and title is not None:
plt.title(title)
def volumeFraction(concentration=1,molWeight=17,density=1.347):
""" molWeight is kDa
concentration in mM
density g/ml
"""
concentration_mg_ml = concentration*molWeight
volume_fraction = concentration_mg_ml/density/1e3
return volume_fraction
def plotdiffs(*args,select=None,err=None,absSignal=None,absSignalScale=10,
showErr=False,cmap=plt.cm.jet,fig=None,title=None):
# this selection trick done in this way allows to keep the same colors when
# subselecting (because I do not change the size of diffs)
if isinstance(args[0],storage.DataStorage):
q = args[0].q; t = args[0].scan; err = args[0].err
diffs = args[0].data
diffs_abs = args[0].dataAbsAvScanPoint
else:
q,diffs,t = args
diffs_abs = None
if select is not None:
indices = range(*select.indices(t.shape[0]))
else:
indices = range(len(t))
if fig is None: fig = plt.gcf()
# fig.clear()
lines_diff = []
lines_abs = []
if absSignal is not None:
line = plt.plot(q,absSignal/absSignalScale,lw=3,
color='k',label="absSignal/%s"%str(absSignalScale))[0]
lines.append(line)
for linenum,idiff in enumerate(indices):
color = cmap(idiff/(len(diffs)-1))
label = timeToStr(t[idiff])
kw = dict( color = color, label = label )
if err is not None and showErr:
line = plt.errorbar(q,diffs[idiff],err[idiff],**kw)[0]
lines_diff.append(line)
else:
line = plt.plot(q,diffs[idiff],**kw)[0]
lines_diff.append(line)
if diffs_abs is not None:
line = plt.plot(q,diffs_abs[idiff],color=color)[0]
lines_abs.append(line)
if title is not None: fig.axes[0].set_title(title)
legend = plt.legend(loc=4)
plt.grid()
plt.xlabel(r"q ($\AA^{-1}$)")
# we will set up a dict mapping legend line to orig line, and enable
# picking on the legend line
lined = dict()
for legline, origline in zip(legend.get_lines(), lines_diff):
legline.set_picker(5) # 5 pts tolerance
lined[legline] = origline
def onpick(event):
# on the pick event, find the orig line corresponding to the
# legend proxy line, and toggle the visibility
legline = event.artist
origline = lined[legline]
vis = not origline.get_visible()
origline.set_visible(vis)
# Change the alpha on the line in the legend so we can see what lines
# have been toggled
if vis:
legline.set_alpha(1.0)
else:
legline.set_alpha(0.2)
fig = plt.gcf()
fig.canvas.draw()
fig.canvas.mpl_connect('pick_event', onpick)
return lines_diff,lines_abs
def updateLines(lines,data):
for l,d in zip(lines,data):
l.set_ydata(d)
#def getScan
def saveTxt(fname,q,data,headerv=None,info=None,overwrite=True,columns=''):
""" Write data to file 'fname' in text format.
Inputs:
q = x vector
data = one or 2D array (first axis is q)
info = dictionary (saved as '# key : value') or string
headerv = vector to be used as header or string
"""
if os.path.isfile(fname) and not overwrite:
log.warn("File %s exists, returning",fname)
return
if isinstance(info,dict):
header = [ "# %s : %s" %(k,str(v)) for (k,v) in info.items() ]
header = "\n".join(header); # skip first #, will be added by np
elif isinstance(info,str):
header = info
else:
header = ""
if isinstance(headerv,str): header += "\n%s" % headerv
if data.ndim == 1:
x = np.vstack( (q,data) )
elif data.ndim == 2:
x = np.vstack( (q,data) )
if headerv is not None:
headerv = np.concatenate(( (data.shape[1],),headerv))
x = np.hstack( (headerv[:,np.newaxis],x) )
if columns != '':
s = "#" + " ".join( [str(c).center(12) for c in columns] )
header = header + "\n" + s if header != '' else s
np.savetxt(fname,x.T,fmt="%+10.5e",header=header,comments='')
def reshapeToBroadcast(what,ref):
""" expand the 1d array 'what' to allow broadbasting to match
multidimentional array 'ref'. The two arrays have to same the same
dimensions along the first axis
"""
if what.shape == ref.shape: return what
assert what.shape[0] == ref.shape[0]
shape = [ref.shape[0],] + [1,]*(ref.ndim-1)
return what.reshape(shape)
def radToQ(theta,**kw):
""" theta is the scattering angle (theta_out - theta_in);
kw should be have either E or wavelength
it returns the scattering vector in the units of wavelength """
# Energy or wavelength should be in kw
assert "E" in kw or "wavelength" in kw
# but not both
assert not ("E" in kw and "wavelength" in kw)
if "E" in kw: kw["wavelength"] = 12.398/kw["E"]
return 4*np.pi/kw["wavelength"]*np.sin(theta)
def degToQ(theta,**kw):
theta = theta/180.*np.pi
return radToQ(theta,**kw)
degToQ.__doc__ = radToQ.__doc__
def qToTheta(q,asDeg=False,**kw):
""" Return scattering angle from q (given E or wavelength) """
# Energy or wavelength should be in kw
assert "E" in kw or "wavelength" in kw
# but not both
assert not ("E" in kw and "wavelength" in kw)
if "E" in kw: kw["wavelength"] = 12.398/kw["E"]
theta = np.arcsin(q*kw["wavelength"]/4/np.pi)
if asDeg: theta = np.rad2deg(theta)
return theta
def attenuation_length(compound, density=None, natural_density=None,energy=None, wavelength=None):
""" extend periodictable.xsf capabilities """
import periodictable.xsf
if energy is not None: wavelength = periodictable.xsf.xray_wavelength(energy)
assert wavelength is not None, "scattering calculation needs energy or wavelength"
if (np.isscalar(wavelength)): wavelength=np.array( [wavelength] )
n = periodictable.xsf.index_of_refraction(compound=compound,
density=density, natural_density=natural_density,
wavelength=wavelength)
attenuation_length = (wavelength*1e-10)/ (4*np.pi*np.imag(n))
return np.abs(attenuation_length)
def transmission(material='Si',thickness=100e-6, density=None, natural_density=None,energy=None, wavelength=None):
""" extend periodictable.xsf capabilities """
att_len = attenuation_length(material,density=density,
natural_density=natural_density,energy=energy,wavelength=wavelength)
return np.exp(-thickness/att_len)
def chargeToPhoton(chargeOrCurrent,material="Si",thickness=100e-6,energy=10,e_hole_pair=3.63):
"""
Function to convert charge (or current to number of photons (or number
of photons per second)
Parameters
----------
chargeOrCurrent: float or array
material : str
Used to calculate
"""
# calculate absortption
A = 1-transmission(material=material,energy=energy,thickness=thickness)
chargeOrCurrent = chargeOrCurrent/A
e_hole_pair_energy_keV = e_hole_pair*1e-3
n_charge_per_photon = energy/e_hole_pair_energy_keV
# convert to Q
charge_per_photon = n_charge_per_photon*1.60217662e-19
nphoton = chargeOrCurrent/charge_per_photon
if len(nphoton) == 1: nphoton = float(nphoton)
return nphoton
def logToScreen():
""" It allows printing to terminal on top of logfile """
# define a Handler which writes INFO messages or higher to the sys.stderr
console = logging.StreamHandler()
console.setLevel(logging.INFO)
# set a format which is simpler for console use
formatter = logging.Formatter('%(message)s')
# tell the handler to use this format
console.setFormatter(formatter)
# add the handler to the root logger (if needed)
if len(logging.getLogger('').handlers)==1:
logging.getLogger('').addHandler(console)

View File

@ -1,49 +0,0 @@
from __future__ import print_function,division
import numpy as np
import os
def xrayAttLenght(*args,**kw):
from periodictable import xsf
n = xsf.index_of_refraction(*args,**kw)
if not "wavelength" in kw:
wavelength = 12.398/np.asarray(kw["energy"])
else:
wavelength = np.asarray(kw["wavelength"])
attenuation_length = np.abs( (wavelength*1e-10)/ (4*np.pi*np.imag(n)) )
return attenuation_length
def xrayFluo(atom,density,energy=7.,length=30.,I0=1e10,\
det_radius=1.,det_dist=10.,det_material="Si",det_thick=300,verbose=True):
""" compound: anything periodictable would understand
density: in mM
length: sample length in um
energy: in keV, could be array
"""
import periodictable
from periodictable import xsf
wavelength = 12.398/energy
atom = periodictable.__dict__[atom]
# 1e3 -> from mM to M
# 1e3 -> from L to cm3
# so 1 mM = 1e-3M/L = 1e-6 M/cm3
density_g_cm3 = density*1e-6*atom.mass
n = xsf.index_of_refraction(atom,density=density_g_cm3,wavelength=wavelength)
attenuation_length = xrayAttLenght( atom,density=density_g_cm3,energy=energy )
# um-> m: 1e-6
fraction_absorbed = 1.-np.exp(-length*1e-6/attenuation_length)
if verbose:
print("Fraction of x-ray photons absorbed by the sample:",fraction_absorbed)
## detector ##
det_area = np.pi*det_radius**2
det_solid_angle = det_area/(4*np.pi*det_dist**2)
if verbose:
print("Detector fraction of solid angle:",det_solid_angle)
det_att_len = xrayAttLenght(det_material,wavelength=atom.K_alpha)
det_abs = 1-np.exp(-det_thick*1e-6/det_att_len)
if verbose:
print("Detector efficiency (assuming E fluo = E_K_alpha):",det_abs)
eff = fraction_absorbed*det_solid_angle*det_abs
if verbose:
print("Overall intensity (as ratio to incoming one):",eff)
return eff