#!/usr/bin/env python
# encoding: utf-8
"""
NMR.py
This file defines generic classes for NMR Spectroscopy
Used to be inside NPKData
"""
from __future__ import print_function, division
import math
import unittest
import numpy as np
from . import NPKData
from .NPKData import _NPKData, Axis, Unit, copyaxes
from .NPKError import NPKError
[docs]class NMRAxis(Axis):
"""
hold information for one NMR axis
used internally
"""
def __init__(self,size = 64, specwidth = 2000.0*math.pi, offset = 0.0, frequency = 400.0, itype = 0, currentunit = "points"):
"""
all parameters from Axis, plus
specwidth spectral width, in Hz
offset position in Hz of the rightmost point
frequency carrier frequency, in MHz
zerotime position (in points) on the time zero
"""
super(NMRAxis, self).__init__(size = size, itype = itype)
self.specwidth = specwidth # spectral width, in Hz
self.offset = offset # position in Hz of the rightmost point
self.frequency = frequency # carrier frequency, in MHz
self.zerotime = 0.0 # position (in points) on the time zero
self.P0 = 0.0 # phase parameters
self.P1 = 0.0
self.NMR = "NMR"
self.kind = "NMR"
self.units["ppm"] = Unit(name="ppm", converter=self.itop, bconverter=self.ptoi, reverse=True)
self.units["Hz"]= Unit(name="Hz", converter=self.itoh, bconverter=self.htoi, reverse=True)
self.units["sec"]= Unit(name="Hz", converter=self.itos, bconverter=self.stoi) # for FID
for i in ("specwidth", "offset", "frequency", "NMR"): # updates storable attributes
self.attributes.insert(0, i)
self.currentunit = currentunit
[docs] def report(self):
"high level reporting"
if self.itype == 0:
return "NMR axis at %f MHz, %d real points, from %f ppm (%f Hz) to %f ppm (%f Hz)"% \
(self.frequency, self.size, self.itop(self.size-1), self.itoh(self.size-1), self.itop(0), self.itoh(0))
else:
return "NMR axis at %f MHz, %d complex pairs, from %f ppm (%f Hz) to %f ppm (%f Hz)"% \
(self.frequency, self.cpxsize, self.itop(self.size-1), self.itoh(self.size-1), self.itop(0), self.itoh(0))
#-------------------------------------------------------------------------------
def __extract(self, zoom):
"""
redefines the axis parameters so that the new axis is extracted for the points [start:end]
zoom is given in current unit - does not modify the Data, only the axis definition
"""
start, end = self.getslice(zoom)
self.specwidth = (self.specwidth * (end - start)) /self.size
self.offset = self.offset + self.specwidth * (self.size - end)/self.size
self.size = end-start
return (start, end)
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
[docs] def itos(self,value):
"""
returns time value (s) from point value
"""
return 0.5*(value-2*self.zerotime)/self.specwidth
[docs] def stoi(self,value):
"""
returns point value (i) from time value (s)
"""
return 2.0*value*self.specwidth + 2*self.zerotime
#-------------------------------------------------------------------------------
[docs] def itop(self,value):
"""
returns ppm value (p) from point value (i)
"""
ppm_value = self.itoh(value) / self.frequency
return ppm_value
#-------------------------------------------------------------------------------
[docs] def htop(self,value):
"""
returns ppm value (p) from Hz value (h)
"""
ppm_value = value / self.frequency
return ppm_value
#-------------------------------------------------------------------------------
[docs] def htoi(self,value):
"""
returns point value (i) from Hz value (h)
"""
pt_value = (self.size -1)*(self.offset - value)/self.specwidth + self.size-1
return pt_value
#-------------------------------------------------------------------------------
[docs] def ptoh(self,value):
"""
returns Hz value (h) from ppm value (p)
"""
Hz_value = value * self.frequency
return Hz_value
#-------------------------------------------------------------------------------
[docs] def ptoi(self,value):
"""
returns point value (i) from ppm value (p)
"""
pt_value = self.htoi((value*self.frequency))
return pt_value
#-------------------------------------------------------------------------------
[docs] def itoh(self,value):
"""
returns Hz value (h) from point value (i)
"""
# N points define N-1 intervals ! hence the -1 in itoh() and htoi()
hz_value = (self.size-value-1)*self.specwidth / (self.size-1) + self.offset
return hz_value
[docs] def freq_axis(self):
"""return axis containing Hz values, can be used for display"""
return self.itoh(self.points_axis())
Hz_axis = freq_axis # two names for this function
[docs] def ppm_axis(self):
"""return axis containing ppm values, can be used for display"""
return self.itop( self.points_axis() )
########################################################################
[docs]class NMRData(_NPKData):
"""
a working data used by the NPK package
The data is a numpy array, found in self.buffer can also be accessed directly d[i], d[i,j], ...
1D 2D and 3D are handled, 3 axes are defined : axis1 axis2 axis3
axes are defined as in NMR
in 1D, every is in axis1
in 2D, the fastest varying dimension is in axis2, the slowest in axis1
in 3D, the fastest varying dimension is in axis3, the slowest in axis1
see axis_index
typical properties and methods are :
utilities:
.display()
.check()
properties
.itype
.dim .size1, .size2, .size3 ...
moving data :
.row(i) .col(i) .set_row(i) .set_col(i)
.copy()
.load() .save()
processing :
.fft() .rfft() .modulus() .apod_xxx() sg() transpose() ...
arithmetics :
.fill() .mult .add()
also direct arithmetics : f = 2*d+e
all methods return self, so computation can be piped
etc...
"""
def __init__(self, dim = 1, shape = None, buffer = None, name = None, debug = 0):
"""
data initialisation,
four alternative posibilities :
- name : file-name is given, the file is read and loaded
- buffer : numpy buffer is given - used as is, not copied !
- shape eg : (si1,si2) is given
- dim is given (default is dim=1)
the first found takes over the others which are not used
"""
from .File.GifaFile import GifaFile
self.debug = debug
self.frequency = 400.0
self._absmax = 0.0
self.noise = 0.0
self.name = None
self.level = None
if buffer is not None:
dim = len(buffer.shape)
if name is not None:
Go = GifaFile(name,"r")
Go.load()
Go.close()
B = Go.get_data()
del(Go)
self.buffer = B.buffer
copyaxes(B,self)
self.name = name
elif shape is not None:
self.buffer = np.zeros(shape)
self.axis1 = NMRAxis()
if (len(shape) == 2):
self.axis1 = NMRAxis()
self.axis2 = NMRAxis()
elif (len(shape) == 3):
self.axis1 = NMRAxis()
self.axis2 = NMRAxis()
self.axis3 = NMRAxis()
else:
if dim == 1:
self.buffer = np.zeros((64,))
self.axis1 = NMRAxis()
elif dim == 2:
self.buffer = np.zeros((64,64))
self.axis1 = NMRAxis()
self.axis2 = NMRAxis()
elif dim == 3:
self.buffer = np.zeros((64,64,64))
self.axis1 = NMRAxis()
self.axis2 = NMRAxis()
self.axis3 = NMRAxis()
else:
raise NPKError("invalid dimension")
if buffer is not None:
self.set_buffer(buffer)
self.adapt_size()
self.check()
########################################################################
#-------------------------------------------------------------------------------
[docs] def copy(self):
"""return a copy of itself"""
c = NPKData._NPKData.copy(self)
c.frequency = self.frequency
return c
[docs] def itop(self,axis,value):
todo = self.test_axis(axis)
if self.dim == 1:
return self.axis1.itop(value)
elif self.dim == 2:
return self.axes(todo).itop(value)
[docs] def htop(self,axis,value):
todo = self.test_axis(axis)
if self.dim == 1:
return self.axis1.htop(value)
elif self.dim == 2:
return self.axes(todo).htop(value)
[docs] def htoi(self,axis,value):
todo = self.test_axis(axis)
if self.dim == 1:
return self.axis1.htoi(value)
elif self.dim == 2:
return self.axes(todo).htoi(value)
[docs] def ptoh(self,axis,value):
todo = self.test_axis(axis)
if self.dim == 1:
return self.axis1.ptoh(value)
elif self.dim == 2:
return self.axes(todo).ptoh(value)
[docs] def ptoi(self,axis,value):
todo = self.test_axis(axis)
if self.dim == 1:
return self.axis1.ptoi(value)
elif self.dim == 2:
return self.axes(todo).ptoi(value)
[docs] def itoh(self,axis,value):
todo = self.test_axis(axis)
if self.dim == 1:
print("going for F1 , on ", value)
return self.axis1.itoh(value)
elif self.dim == 2:
return self.axes(todo).itoh(value)
[docs]class NMRDataTests(unittest.TestCase):
[docs] def test_load(self):
" - Testing load methods"
from .Tests import filename
name1D = filename("proj.gs1")
E = NMRData(name=name1D)
self.assertAlmostEqual(E[0], 1869.4309082)
self.assertAlmostEqual(E.get_buffer().max(), 603306.75)
[docs] def test_unitval(self):
"testing unit conversion functions"
F = NMRAxis(size=1000, specwidth=12345.0, frequency = 400.0, offset=321)
self.assertAlmostEqual(F.itoh(0), F.specwidth+F.offset)
self.assertAlmostEqual(F.itoh(F.size-1), F.offset) # last point is size-1 !!!
for f,g in ((F.htoi,F.itoh), (F.ptoi,F.itop), (F.htop,F.ptoh)):
self.assertAlmostEqual( g(f(4321)), 4321)
for u in ("points", "Hz", "ppm"):
F.currentunit = u
self.assertAlmostEqual( F.ctoi( F.itoc(4321)), 4321)
if __name__ == '__main__':
# minitest
d = NMRData(dim=1, debug=1)
print(d.report())
d = NMRData(shape=(33,33),debug=1)
print(d.report())
d = NMRData(buffer=np.ones((12,24,48)),debug=1)
print(d.report())
print('Hello from NMR')