Browse Source

Initial commit

master
Trevor Irons 6 years ago
parent
commit
73ebe72ffa
13 changed files with 6303 additions and 0 deletions
  1. 93
    0
      GUI/MatplotlibWidget.py
  2. 76
    0
      GUI/borehole.py
  3. 1292
    0
      GUI/borehole2.py
  4. 1752
    0
      GUI/main.py
  5. 791
    0
      GUI/quad_corr.py
  6. 29
    0
      GUI/test.py
  7. 797
    0
      MRProc.py
  8. 712
    0
      decay.py
  9. 212
    0
      logbarrier.py
  10. 37
    0
      notch.py
  11. 67
    0
      plotHE.py
  12. 343
    0
      pwctimeWhite.py
  13. 102
    0
      smooth.py

+ 93
- 0
GUI/MatplotlibWidget.py View File

@@ -0,0 +1,93 @@
1
+####################################################
2
+# Adopted from pyqtgraph (MIT license)
3
+#from pyqtgraph.Qt import QtGui, QtCore, USE_PYSIDE
4
+USE_PYSIDE = True 
5
+
6
+import matplotlib
7
+matplotlib.use('Qt4Agg')
8
+#matplotlib.rcParams['backend.qt4']='PySide'
9
+
10
+if USE_PYSIDE == True: 
11
+    matplotlib.rcParams['backend.qt4']='PySide'
12
+    from PySide import QtCore, QtGui
13
+else:
14
+    from PyQt4 import QtCore, QtGui
15
+
16
+
17
+from matplotlib import rc                                                          #
18
+#matplotlib.rcParams['text.latex.preamble']=[r"\usepackage{timet,amsmath,amssymb}"] #
19
+#rc('font',**{'family':'serif','serif':['timet']})   
20
+rc('font',**{'size':8})                                                            #
21
+#rc('text', usetex=True)  
22
+
23
+from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
24
+from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
25
+from matplotlib.figure import Figure
26
+
27
+class MatplotlibWidget(QtGui.QWidget):
28
+    """
29
+    Implements a Matplotlib figure inside a QWidget.
30
+    Use getFigure() and redraw() to interact with matplotlib.
31
+    
32
+    Example::
33
+    
34
+        mw = MatplotlibWidget()
35
+        subplot = mw.getFigure().add_subplot(111)
36
+        subplot.plot(x,y)
37
+        mw.draw()
38
+    """
39
+    
40
+    def __init__(self, parent=None, size=(5.0, 4.0), dpi=100):
41
+        # thingy is the designer designated parent, layout manager, etc.
42
+        self.size = size
43
+        self.dpi = dpi
44
+ 
45
+        #QtGui.QWidget.__init__(self, thingy) 
46
+        #super(thingy, self).__init__( MatplotlibWidget )
47
+        #super(MatplotlibWidget, self).__init__( self, parent )
48
+        super(MatplotlibWidget, self).__init__(parent)
49
+
50
+        self.fig = Figure(size, dpi=dpi)
51
+        self.canvas = FigureCanvas(self.fig)
52
+        self.canvas.setParent(self)
53
+        self.toolbar = NavigationToolbar(self.canvas, self)
54
+        
55
+        self.vbox = QtGui.QVBoxLayout()
56
+        self.vbox.addWidget(self.toolbar)
57
+        self.vbox.addWidget(self.canvas)
58
+        
59
+        self.setLayout(self.vbox)
60
+
61
+    def Clear(self):
62
+
63
+        for ax in self.fig.axes:
64
+            ax.cla()
65
+            #ax.draw()
66
+            self.fig.delaxes(ax)
67
+        
68
+        self.canvas.draw()
69
+       
70
+        #print ("axes", self.fig.axes)
71
+ 
72
+#         del self.fig
73
+#         del self.canvas
74
+#         del self.toolbar
75
+#         del self.vbox
76
+# 
77
+#         self.fig = Figure((3,4), dpi=self.dpi)
78
+#         self.canvas = FigureCanvas(self.fig)
79
+#         self.canvas.setParent(self)
80
+#         self.toolbar = NavigationToolbar(self.canvas, self)
81
+#         
82
+#         self.vbox = QtGui.QVBoxLayout()
83
+#         self.vbox.addWidget(self.toolbar)
84
+#         self.vbox.addWidget(self.canvas)
85
+        
86
+        #self.setLayout(self.vbox)
87
+        
88
+
89
+    def getFigure(self):
90
+        return self.fig
91
+        
92
+    def draw(self):
93
+        self.canvas.draw()

+ 76
- 0
GUI/borehole.py View File

@@ -0,0 +1,76 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+# Form implementation generated from reading ui file 'borehole.ui'
4
+#
5
+# Created: Sat Jun 14 21:58:30 2014
6
+#      by: PyQt4 UI code generator 4.10.4
7
+#
8
+# WARNING! All changes made in this file will be lost!
9
+
10
+from PyQt4 import QtCore, QtGui
11
+
12
+try:
13
+    _fromUtf8 = QtCore.QString.fromUtf8
14
+except AttributeError:
15
+    def _fromUtf8(s):
16
+        return s
17
+
18
+try:
19
+    _encoding = QtGui.QApplication.UnicodeUTF8
20
+    def _translate(context, text, disambig):
21
+        return QtGui.QApplication.translate(context, text, disambig, _encoding)
22
+except AttributeError:
23
+    def _translate(context, text, disambig):
24
+        return QtGui.QApplication.translate(context, text, disambig)
25
+
26
+class Ui_MainWindow(object):
27
+    def setupUi(self, MainWindow):
28
+        MainWindow.setObjectName(_fromUtf8("MainWindow"))
29
+        MainWindow.resize(818, 682)
30
+        self.centralwidget = QtGui.QWidget(MainWindow)
31
+        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
32
+        MainWindow.setCentralWidget(self.centralwidget)
33
+        self.menubar = QtGui.QMenuBar(MainWindow)
34
+        self.menubar.setGeometry(QtCore.QRect(0, 0, 818, 19))
35
+        self.menubar.setObjectName(_fromUtf8("menubar"))
36
+        self.menuFile = QtGui.QMenu(self.menubar)
37
+        self.menuFile.setObjectName(_fromUtf8("menuFile"))
38
+        self.menuEdit = QtGui.QMenu(self.menubar)
39
+        self.menuEdit.setObjectName(_fromUtf8("menuEdit"))
40
+        self.menuHelp = QtGui.QMenu(self.menubar)
41
+        self.menuHelp.setObjectName(_fromUtf8("menuHelp"))
42
+        MainWindow.setMenuBar(self.menubar)
43
+        self.statusbar = QtGui.QStatusBar(MainWindow)
44
+        self.statusbar.setObjectName(_fromUtf8("statusbar"))
45
+        MainWindow.setStatusBar(self.statusbar)
46
+        self.actionLoad = QtGui.QAction(MainWindow)
47
+        self.actionLoad.setObjectName(_fromUtf8("actionLoad"))
48
+        self.action_Load_Dataset = QtGui.QAction(MainWindow)
49
+        self.action_Load_Dataset.setObjectName(_fromUtf8("action_Load_Dataset"))
50
+        self.action_Close = QtGui.QAction(MainWindow)
51
+        self.action_Close.setObjectName(_fromUtf8("action_Close"))
52
+        self.action_Quit = QtGui.QAction(MainWindow)
53
+        self.action_Quit.setObjectName(_fromUtf8("action_Quit"))
54
+        self.menuFile.addAction(self.actionLoad)
55
+        self.menuFile.addAction(self.action_Load_Dataset)
56
+        self.menuFile.addSeparator()
57
+        self.menuFile.addSeparator()
58
+        self.menuFile.addAction(self.action_Quit)
59
+        self.menubar.addAction(self.menuFile.menuAction())
60
+        self.menubar.addAction(self.menuEdit.menuAction())
61
+        self.menubar.addAction(self.menuHelp.menuAction())
62
+
63
+        self.retranslateUi(MainWindow)
64
+        QtCore.QObject.connect(self.action_Quit, QtCore.SIGNAL(_fromUtf8("activated()")), MainWindow.close)
65
+        QtCore.QMetaObject.connectSlotsByName(MainWindow)
66
+
67
+    def retranslateUi(self, MainWindow):
68
+        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
69
+        self.menuFile.setTitle(_translate("MainWindow", "File", None))
70
+        self.menuEdit.setTitle(_translate("MainWindow", "Edit", None))
71
+        self.menuHelp.setTitle(_translate("MainWindow", "Help", None))
72
+        self.actionLoad.setText(_translate("MainWindow", "&Open record", None))
73
+        self.action_Load_Dataset.setText(_translate("MainWindow", "&Load Dataset", None))
74
+        self.action_Close.setText(_translate("MainWindow", "&Close", None))
75
+        self.action_Quit.setText(_translate("MainWindow", "&Quit", None))
76
+

+ 1292
- 0
GUI/borehole2.py
File diff suppressed because it is too large
View File


+ 1752
- 0
GUI/main.py
File diff suppressed because it is too large
View File


+ 791
- 0
GUI/quad_corr.py View File

@@ -0,0 +1,791 @@
1
+# import matplotlib as mpl
2
+# mpl.use("pgf")
3
+# pgf_with_pdflatex = {
4
+#     "pgf.texsystem": "pdflatex",
5
+#     "pgf.preamble": [
6
+#          r"\usepackage{amsmath}",
7
+#          r"\usepackage{amssymb}",
8
+#          r"\usepackage{timet}",
9
+#          ]
10
+# }
11
+#          #r"\usepackage[utf8x]{inputenc}",
12
+#          #r"\usepackage[T1]{fontenc}",
13
+#          #r"\usepackage{cmbright}",
14
+# mpl.rcParams.update(pgf_with_pdflatex)
15
+# 
16
+# from matplotlib import rc
17
+# rc('font',**{'family':'serif','serif':['times']})
18
+# ## for Palatino and other serif fonts use:
19
+# #rc('font',**{'family':'serif','serif':['Palatino']})
20
+# #rc('text', usetex=True)
21
+
22
+#import matplotlib
23
+#from matplotlib import rc
24
+
25
+#matplotlib.rcParams['text.latex.preamble']=[r"\usepackage{timet,amsmath}"]
26
+#rc('font',**{'family':'serif','serif':['timet']})
27
+#rc('text', usetex=True)
28
+from GJIPlot import *
29
+
30
+import numpy as np
31
+import matplotlib.pyplot as plt
32
+from smooth import smooth
33
+#from invertColours import *
34
+from decay import *
35
+from scipy import signal
36
+from notch import * 
37
+import rotate # why is it called loss?
38
+
39
+BLACK = False
40
+labs = 16
41
+
42
+if BLACK:
43
+    labcol = 'white'
44
+else:
45
+    labcol = 'black'
46
+
47
+def Kw(a, phi2, s, rT2, wL): 
48
+    return  a*((np.sin(phi2)*s + ((1/rT2)*np.sin(phi2)) + wL*np.cos(phi2) ) / (wL**2+(s+1/rT2)**2 ))
49
+
50
+
51
+def quadDetect(T, vL, wL, dt, xn, DT):
52
+    # decimate
53
+    # blind decimation
54
+    # 1 instead of T 
55
+    irsamp = (T) * int(  (1./vL) / dt) # real 
56
+    iisamp =       int(  ((1./vL)/ dt) * ( .5*np.pi / (2.*np.pi) ) ) # imaginary
57
+   
58
+
59
+    dsamp =  int( DT / dt) # real 
60
+
61
+    iisamp += dsamp
62
+
63
+    ############################################################
64
+    # simple quadrature-detection via sampling
65
+    xr = xn[dsamp::irsamp]
66
+    xi = xn[iisamp::irsamp]
67
+    phase = np.angle( xr + 1j*xi )
68
+    abse = np.abs( xr + 1j*xi )
69
+
70
+    # times
71
+    ta = np.arange(0, TT, dt)
72
+    te = np.arange(DT, TT, TT/len(abse))
73
+
74
+    #############################################################
75
+    # hilbert transform
76
+    ht = signal.hilbert(xn) #, 100))
77
+    he = np.abs(ht)         #, 100))
78
+    he = ht*np.exp( -1j*wL*t )  # ht*e^{-jwLt}  # phase envelope
79
+
80
+    #hp = ((np.angle(ht[dsamp::irsamp]))) 
81
+    #[Q, I, tt] = rotate.quadrature(T, vL, wL, dt, xn, DT, t)
82
+    [E0,df,phi,T2] = quadratureDetect(np.imag(he), np.real(he), t)
83
+    D = rotate.RotateAmplitude(np.real(he), np.imag(he), phi, df, t)
84
+    he = D.imag 
85
+    #print ("df", df, "phi", phi)
86
+    """
87
+    plt.plot(xn)
88
+    plt.plot(np.real(he))
89
+    plt.plot(np.imag(he))
90
+    plt.plot(D.imag)
91
+    plt.plot(D.real)
92
+    print ("phi", phi)
93
+    plt.plot( np.unwrap(np.arctan(np.unwrap(np.angle( he ) + np.pi/2 ))) )
94
+#    plt.plot(np.imag(ht))
95
+#    plt.plot(xn)
96
+
97
+    # plot FFT
98
+
99
+    XN  = np.fft.rfft(xn)
100
+    plt.figure()
101
+    plt.plot(XN.real, label='orig')
102
+    #XNN = XN * np.exp(1j*phi-1.)
103
+    """
104
+
105
+    """
106
+    phi = np.arange(-np.pi, np.pi, .001)
107
+    best = 1e12
108
+    bestPhi = 0
109
+    for p in phi:
110
+        XNN = XN * np.exp(1j*p)
111
+        if ( (np.linalg.norm( XNN.real[XNN.real < 0] )) < best):
112
+            best = np.linalg.norm( XNN.real[XNN.real < 0])
113
+            bestPhi = p
114
+        #print (len( XNN.real[XNN.real < 0] ))
115
+        #best = min(best, -( np.sum(XNN.real[XNN.real < 0]) ))
116
+        #print (XNN[XNN.real < 0]) 
117
+        #plt.plot(XNN.real[XNN.real<0])
118
+    print ("best", best, bestPhi)
119
+    XNN = XN * np.exp(1j*bestPhi)
120
+    """
121
+   
122
+    """ 
123
+    plt.plot(XNN.real, label='phased')
124
+    plt.plot(XNN.imag, label='imag')
125
+    plt.legend()
126
+    plt.figure()
127
+    plt.plot(np.angle(XN))
128
+    plt.plot(np.angle(XNN))
129
+    plt.show()
130
+    exit()
131
+    """
132
+
133
+    #############################################################
134
+    # Resample ht, no gate integrate
135
+    """
136
+    htd = signal.decimate(he, 100, ftype='fir') 
137
+    td = signal.decimate(t, 100, ftype='fir')
138
+    #[htd, td] = signal.resample(he, 21, t) 
139
+    #toss first, and use every third 
140
+    htd = htd[1::5]
141
+    td = td[1::5]
142
+    """
143
+
144
+    #############################################################
145
+    # Resample ht and gate integrate to 20 gates per decate
146
+    GPD = 20 # Gates per decade
147
+    GI = 15  # initial 15 times are just used
148
+
149
+    # first just resample to 20 Gates per decade
150
+    #nrs = (np.log(t[-1]) - np.log10(t[0]))
151
+    tdd = np.logspace( np.log10(16*dt), np.log10(t[-1]), 80-GI, base=10, endpoint=True) 
152
+    tdl = tdd[0:-1]    # these are left edges
153
+    tdr = tdd[1::]    # these are left edges
154
+    td = (tdl+tdr) / 2.
155
+    
156
+    # Just use the first 20 datapoints
157
+    td = np.concatenate( ([t[0:GI] ,  td]) )
158
+
159
+    htd = np.zeros( len(td) )
160
+    #htd[0:15] = he[0:15]
161
+    htd[0:GI] = he[0:GI]
162
+
163
+    ii = 15
164
+    isum = np.zeros( len(td) )
165
+    Vars = np.ones( len(td) ) * .15**2
166
+    isum [0:15] = 1
167
+    #Vars [0:15] = .15**2  # variance 
168
+    #var = .15**2
169
+    for itd in range(GI, len(he)):
170
+        #print (itd)   
171
+        if ( t[itd] > tdr[ii-GI] ):
172
+            ii += 1
173
+        isum[ii] += 1
174
+        htd[ii] += he[ itd ]
175
+        Vars[ii] += .15**2
176
+    htd /= isum
177
+    #Vars = ( Vars) / isum
178
+    Stds = np.sqrt( Vars) / isum
179
+    Vars = Stds**2 
180
+    #plt.figure()
181
+    #plt.gca().errorbar(  td, htd, fmt='o', yerr=Vars )
182
+    #plt.gca().set_xscale('log', basex=10)
183
+    
184
+    #    htd = he[itd] # (int)(dt*itd) ]
185
+    #td = []
186
+    # OK, so tdd are the desired time gates, but we might not be able to satisfy all of them
187
+
188
+        
189
+
190
+    #htdt = signal.decimate(he, 10, ftype='fir') 
191
+    ##tdt = signal.decimate(t, 10, ftype='fir')
192
+    #tdt = t[::10]
193
+    #[htd, td] = signal.resample(he, 21, t) 
194
+    
195
+    ##toss first, and use every third 
196
+    #htd = [] # htdt[1::5]
197
+    #td = tdt[1::15]
198
+    #for ix in np.arange(1,len(htdt),15):
199
+    #    if ix>15 and ix<len(htdt)-15:
200
+    #        htd.append( np.mean(htdt[ix-7:ix+8]) ) # stupid could do recursive but who gives a shit
201
+    #    else:
202
+    #        htd.append( htdt[ix] ) # stupid could do recursive but who gives a shit
203
+    
204
+    ###############################################
205
+    # Plots
206
+    #plt.plot(ta, xn)
207
+    #plt.plot(te, abse, '-.', linewidth=2, markersize=10)
208
+    #plt.plot(ta, he, '.', markersize=10 )
209
+    #plt.plot(td, htd, color='green', linewidth=2)
210
+    # Phase Plots
211
+    #ax2 = plt.twinx()
212
+    #ax2.plot(te, hp, '.', markersize=10, color='green' )
213
+    #ax2.plot(te, phase, '-.', linewidth=2, markersize=10, color='green')
214
+    #plt.show()
215
+    #exit()
216
+
217
+    #####################################################################
218
+    # regress raw signal
219
+    
220
+    #[peaks, times, ind] = peakPicker(xn, wL, dt)
221
+    #[a0,b0,rt20] =  regressCurve(peaks,  times) #,sigma2=1,intercept=True):
222
+    #plt.figure()
223
+    #plt.plot(Vars)
224
+    #plt.show()
225
+    #exit()
226
+
227
+    
228
+    dsamp = 1 # int( DT / dt) # real 
229
+    # regress analytic signal
230
+    [b0,rt20] =  regressCurve(he,  t, intercept=False) #, sigma2=(1./Stds)/np.max(1/Stds)) #,sigma2=1,intercept=True):
231
+    #[a0,b0,rt20] =  regressCurve(he,  t, intercept=True, sigma2=(1./Vars)/np.max(1/Vars)) #,sigma2=1,intercept=True):
232
+    #[b0,rt20] =  regressCurve(he[dsamp::],  t[dsamp::], intercept=False) #,sigma2=1,intercept=True):
233
+    #[a0,b0,rt20] =  regressCurve(he,  t) #,sigma2=1,intercept=True):
234
+   
235
+    # regress downsampled 
236
+    #[a,b,rt2] =  regressCurve(abse,  t[dsamp::irsamp], intercept=True) #,sigma2=1,intercept=True):
237
+    [b,rt2] =  regressCurve(htd,  td, intercept=False,  sigma2=(1./Stds)/np.max(1/Stds) ) #,sigma2=1,intercept=True):
238
+    
239
+    return irsamp, iisamp, htd, b0, rt20, ta, b, rt2, phi, td, he, Stds
240
+    #return irsamp, iisamp, abse, a0, b0, rt20, times, a, b, rt2, phase
241
+
242
+if __name__ == "__main__":
243
+    
244
+    dt = 1e-4
245
+    TT = 1.5
246
+    t = np.arange(0, TT, dt)
247
+    vL = 2000.
248
+    wL = 2.*np.pi*vL
249
+    wL2 = 2.*np.pi*(vL+10 ) # 3 Hz off 
250
+    zeta = -np.pi/2 # .0 #np.pi/2.21
251
+    t2 = .050
252
+
253
+    # TODO, show boxplots of error of regressions parameters, and randomly sample T2 and Vn0 for realistic parameters. 
254
+    # do a lot of random realizations.   
255
+
256
+    N = 5000
257
+    T2 =  [.03, .075, .125, .2, .250] # <=== Final 
258
+    #T2 =  [.3]
259
+    NT2 = len(T2)
260
+    RALL = np.zeros((N, NT2))
261
+    RDEC = np.zeros((N, NT2))
262
+    BALL = np.zeros((N, NT2))
263
+    BDEC = np.zeros((N, NT2))
264
+    RF = np.zeros((N, NT2))
265
+    AF = np.zeros((N, NT2))
266
+    RM = np.zeros((N, NT2))
267
+    AM = np.zeros((N, NT2))
268
+    RFC = np.zeros((N, NT2))
269
+    AFC = np.zeros((N, NT2))
270
+    
271
+    sT2 = [str(int(1e3*i)) for i in T2]
272
+    it2 = 0 
273
+    for t2 in T2:
274
+        print(("simulating", t2))
275
+
276
+
277
+        xe = np.exp(-t/t2)    
278
+ 
279
+        for it in range(N):
280
+        
281
+            zeta = (np.random.rand() - .5) #np.random.rand()
282
+            #print ("zeta", zeta)
283
+            # TODO, right here do distribution of phases  
284
+            xs = np.exp(-t/t2) * np.sin(wL*t + numpy.pi*zeta)  #\
285
+              # + np.exp(-t/t2) * np.sin(wL*t  ) 
286
+            
287
+            # pretty low, highly-correlated noise
288
+            #xn =  ( (smooth(np.random.normal(0,.4,len(xs)) + \
289
+            #    np.random.random_integers(-1,1,len(xs))*0.5*np.random.lognormal(0, .25, len(xs)) + \
290
+            #    np.random.random_integers(-1,1,len(xs))*.0002*np.random.weibull(.25, len(xs)), 60)))
291
+            #xn += np.random.normal( 0, .02,len(xs) ) 
292
+            #print(np.std(xn))
293
+
294
+            xc = np.random.normal(0,.01,len(xs))  + (np.sign(xs) *  (smooth(np.random.normal(0,.25,len(xs)+160) + \
295
+                np.random.random_integers(-1,1,len(xs)+160)*0.15*np.random.lognormal(0, .25, len(xs)+160) + \
296
+                np.random.random_integers(-1,1,len(xs)+160)*.0002*np.random.weibull(.25, len(xs)+160), 80))[80:-80] )           
297
+            
298
+            #print(np.std(xc))
299
+            
300
+            xn = xs + xc 
301
+
302
+
303
+            #xn += xs            
304
+
305
+            #nn = np.random.normal( 0, .15,len(xs) ) 
306
+            #xn = xs + nn # np.random.normal(0,.15,len(xs)) 
307
+            
308
+            #xn = xs + (np.sign(xs) *  np.absolute(smooth(np.random.normal(0,.5,len(xs))) )) #+ \
309
+                #np.random.random_integers(-1,1,len(xs))*0.6*np.random.lognormal(0, .35, len(xs)) + \
310
+                #np.random.random_integers(-1,1,len(xs))*.004*np.random.weibull(.25, len(xs)), 60)))
311
+
312
+
313
+            ###################################################################
314
+            # Bandpass filter, concatenate the two and run both directions
315
+            #xraw = xn
316
+            #plt.plot(xn)
317
+            
318
+            #xf = notch( xn[::-1], .925, 2000., dt )            
319
+            #xn = (xn[::-1]-xf)[::-1]
320
+            
321
+            #xf = notch( xn[::-1], .9, 2000., dt )            
322
+            #xn = (xn[::-1]-xf)[::-1]
323
+
324
+            """ 
325
+            print("sigma xf", np.std(xf))
326
+            print("sigma nn", np.std(nn))
327
+            print("sigma xn", np.std(xn[-100::]))
328
+            #plt.plot(xraw)
329
+            plt.plot(xn)
330
+            plt.plot(xs)
331
+            #plt.plot(xf)
332
+            #plt.plot(xn[::-1]-xf)
333
+            plt.show()
334
+            exit()
335
+            """
336
+
337
+            # quadrature detection downsampling
338
+            T = 50    # sampling period, grab every T'th oscilation
339
+            DT = 0. #.002 #85  # dead time ms
340
+            [irsamp, iisamp, abse, b0, rt20, times, b, rt2, phase, tdec, he, Stds] = quadDetect(T, vL, wL, dt, xn, DT)
341
+            RALL[it, it2] = 1e2 * ((rt20-t2)/t2)
342
+            BALL[it, it2] = 1e2*(b0 -1.)
343
+            BDEC[it, it2] = 1e2*(b - 1.)
344
+            RDEC[it, it2] = 1e2 * ((rt2-t2)/t2)
345
+
346
+            # Fourier domain fitting
347
+            dsamp = int( DT / dt) # real 
348
+            #X = np.fft.rfft(xn[dsamp::]) #* dt
349
+            #V = np.fft.fftfreq(len(xn[dsamp::]), dt)
350
+            X = np.fft.rfft(xn) #* dt
351
+            V = np.fft.fftfreq(len(xn), dt)
352
+
353
+            #V[len(X)-1] *= -1.
354
+            w = V * 2.*np.pi
355
+ 
356
+            # Just do a window
357
+            nf = 50 # 150 =  200 Hz window
358
+            ws = np.abs(w[0:len(X)])
359
+            vs = ws/(2.*np.pi)
360
+            iL =  vL / vs[1]
361
+
362
+            # Do complex fit 
363
+            [af, bf] = regressModulus(ws[iL-nf:iL+nf] , wL, dt*X[iL-nf:iL+nf] )
364
+            AM[it, it2] =  1e2*(af - 1.) # backwards extrapolate
365
+            RM[it, it2] = 1e2 * ((bf-t2)/t2)
366
+
367
+            try:
368
+                [af, bf, nb] = regressSpecComplex(ws[iL-nf:iL+nf], wL, dt*X[iL-nf:iL+nf], known=False)
369
+            except:
370
+                print("munged FD regression")
371
+                af = 0
372
+                bf=10.
373
+                nb = 0
374
+    
375
+            cr = Kw(af, nb, 1j*ws[iL-nf:iL+nf], bf, wL) 
376
+            
377
+            AFC[it, it2] =  1e2*(af - 1.) # backwards extrapolate
378
+            afe = af #  (af * np.exp(DT/bf)) #, af # backwards extrapolate
379
+            RFC[it, it2] = 1e2 * ((bf-t2)/t2)
380
+            """
381
+            # rotate phase
382
+            phi = np.arange(0, 2.*np.pi, .001)
383
+            best = 1e12
384
+            bestPhi = 0
385
+            for p in phi:
386
+                XNN = X * np.exp(1j*p)
387
+                if ( (np.linalg.norm( XNN.real[XNN.real < 0] )) < best):
388
+                    best = np.linalg.norm( XNN.real[XNN.real < 0])
389
+                    bestPhi = p
390
+            nb2 = bestPhi
391
+            nb3 = nb
392
+            """
393
+            #print ("nb", nb, "zeta", zeta*pi, "nb2", nb2, np.pi/2-zeta*pi)
394
+            Xc = X[iL-nf:iL+nf]
395
+            
396
+            #X = (X * np.exp(1j*(np.pi/2-zeta*pi) )).real # Correct
397
+            X = (X * np.exp(1j*(np.pi/2-nb) )).real
398
+            X = X[iL-nf:iL+nf]
399
+            #print (phase, bestPhi, phase-bestPhi) 
400
+            #plt.plot(ws, np.abs(X))
401
+            #plt.show()
402
+            #exit()           
403
+
404
+            #[a, b] = regressSpec(w[0:len(X)], wL, dt*X)
405
+            #[af, bf, nb] = regressSpecComplex(ws[iL-nf:iL+nf], wL, dt*X[iL-nf:iL+nf])
406
+            #[af, bf, nb] = regressSpec(ws[iL-nf:iL+nf], wL, dt*X[iL-nf:iL+nf])
407
+            [af, bf, nb] = regressSpec(ws[iL-nf:iL+nf], wL, dt*X)
408
+            #AF[it, it2] =  af  - 1.
409
+            #AF[it, it2] =  1e2*(af * np.exp(DT/bf)  - 1.) # backwards extrapolate
410
+            AF[it, it2] =  1e2*(af - 1.) # backwards extrapolate
411
+            afe = af #  (af * np.exp(DT/bf)) #, af # backwards extrapolate
412
+            RF[it, it2] = 1e2 * ((bf-t2)/t2)
413
+            
414
+#             print af, bf, nb
415
+# 
416
+#             plt.figure()
417
+#             plt.plot(xn)           
418
+# 
419
+#             plt.figure() 
420
+#             plt.plot(V[0:len(X)], dt * np.abs(X), label='mod')
421
+#             xp = af* np.abs( wL / (wL**2 + (-1j*ws[iL-nf:iL+nf]  + 1./bf)**2 ) )
422
+#             xpc =  np.abs( wL / (wL**2 + (-1j*ws[iL-nf:iL+nf]  + 1./t2)**2 ) )
423
+#             #plt.plot(vs[iL-50:iL+50], dt*np.real(X[iL-50:iL+50]))
424
+#             #plt.plot(vs[iL-50:iL+50], dt*np.imag(X[iL-50:iL+50]))
425
+#             plt.plot(vs[iL-nf:iL+nf], xp, label='predicted')
426
+#             plt.plot(vs[iL-nf:iL+nf], xpc, label='actual')
427
+#             plt.legend()
428
+
429
+        it2 += 1
430
+
431
+    # a little plot
432
+    if BLACK:
433
+        quadfig = plt.figure(0, facecolor='black', figsize=[pc2in(20), pc2in(32)])
434
+    else: 
435
+        quadfig = plt.figure(0, figsize=[ pc2in(20), pc2in(32) ])
436
+    qa = quadfig.add_axes([.1, .6, .8,  .375])
437
+
438
+    if BLACK:
439
+        qa.plot(1e3*t, xn, label = 'noisy signal', color='yellow')
440
+        invertColours(qa)
441
+    else:
442
+        qa.plot(1e3*t, xn, color='blue', alpha=.25)
443
+        #qa.plot(t, xn, label = 'noisy signal', color='blue')
444
+
445
+    #fa.plot(X, V)
446
+
447
+
448
+    #plt.plot(t[::irsamp], xn[::irsamp], '.-', marker = 'b', markersize=18, label='real envelope')
449
+    #plt.plot(t[iisamp::irsamp], xn[iisamp::irsamp], '.-', marker = 'imaginary envelope', markersize=18, label='imaginary envelope')
450
+    deSpine(plt.gca())
451
+
452
+    if BLACK:
453
+        l2 = plt.plot(1e3*times, he, color='red', label='CA')
454
+        l1 = plt.plot(1e3*tdec, abse, '.', color='green', markersize=12, label='time gates')
455
+        leg = plt.legend( frameon=False, scatterpoints=1, labelspacing=0.2, numpoints=1 )
456
+        invertLegend(leg)
457
+    else:
458
+        l2 = qa.plot(1e3*times, he, color='red') #, label='CA')
459
+        #l1 = qa.plot(1e3*tdec, abse, 'o', color='green', markersize=4, label='time gates')
460
+
461
+        #leg = plt.legend( frameon=True, scatterpoints=1, numpoints=1, labelspacing=0.25 )
462
+        #rect = leg.get_frame()
463
+        #rect.set_facecolor(light_grey)
464
+        #rect.set_linewidth(0.0)
465
+        #rect.set_alpha(0.5)
466
+
467
+    #qa.set_xlabel("time [s]")
468
+    #qa.set_title("time-domain")
469
+    
470
+    # regress curve using all times
471
+    re0 = b0 * np.exp(-times/rt20)
472
+
473
+    # regress curve based on decimated data
474
+    irsamp = 1
475
+    re = b * np.exp(-t[::irsamp]/rt2)   
476
+        
477
+    qa.plot(1e3*t, xe, label = 'true envelope', linewidth=2, color='blue')
478
+    #qa.plot(1e3*t[::irsamp],re, linewidth=2, label='gated regression', color='green')
479
+    qa.plot(1e3*t[::irsamp],re, linewidth=1, color='green')
480
+    qa.plot(1e3*times,re0, '--',linewidth=1, label='CA regression', color='black')
481
+    l1 = qa.errorbar( 1e3*tdec, abse, yerr=Stds, fmt='o', color='green', markersize=3, label='time gates (1 $\sigma$ EB)')
482
+    qa.set_xscale('log', basex=10)
483
+        
484
+ 
485
+    leg = qa.legend(frameon=True, scatterpoints=1, numpoints=1, labelspacing=0.2)
486
+    fixLeg(leg)
487
+
488
+
489
+#       Frequency-domain axis, did not bring much information. So removed. 
490
+    #ff = plt.figure()
491
+    #fa = quadfig.add_axes([.1, .05,  .8, .25])
492
+    fa = quadfig.add_axes([.1, .1, .8, .375])
493
+ 
494
+    #ax2 = plt.gca().twinx()
495
+    #l2 = ax2.plot(t[iisamp::irsamp], phase, '.-', marker = 'imaginary envelope', markersize=18, label='phase')
496
+    #plt.legend([l1,l2])
497
+    #invertColours(qa)
498
+    
499
+    #fa.plot(V[0:len(X)], dt * np.abs(X), label='mod')
500
+
501
+    #fmla = robjects.Formula('XX ~ a*(.5/rT2)  / ((1./rT2)^2 + (w-wL)^2 )')
502
+    #xp = af * np.abs( wL / (wL**2 + (-1j*ws[iL-nf:iL+nf]  + 1./bf)**2 ) )
503
+    #xpc =     np.abs( wL / (wL**2 + (-1j*ws[iL-nf:iL+nf]  + 1./t2)**2 ) )
504
+
505
+
506
+
507
+    xp = af * ( (.5/bf) / (  ((1./bf)**2 + (ws[iL-nf:iL+nf]-wL)**2 ) ) )
508
+    xpc =     ( (.5/t2) / (  ((1./t2)**2 + (ws[iL-nf:iL+nf]-wL)**2 ) ) )
509
+    #print ("af", af) 
510
+    #plt.plot(vs[iL-50:iL+50], dt*np.real(X[iL-50:iL+50]))
511
+    #plt.plot(vs[iL-50:iL+50], dt*np.imag(X[iL-50:iL+50]))
512
+    #fa.plot(np.abs(V[0:len(X)]), dt * np.abs(X), color='red', linewidth=2, label='FT modulus')
513
+    fa.plot(vs[iL-nf:iL+nf], dt*X, color='red', linewidth=2, label='phased')
514
+    fa.plot(vs[iL-nf:iL+nf], xp, '--',linewidth=1, label='regressions', color='black')
515
+    #fa.plot(vs[iL-nf:iL+nf], dt*Xc.real, color='red', '--', linewidth=2, label='real')
516
+    fa.plot(vs[iL-nf:iL+nf], dt*Xc.real, '-', color='blue', linewidth=2, label=r'$\mathrm{Re}$ FFT ')
517
+    fa.plot(vs[iL-nf:iL+nf], dt*Xc.imag, '--', color='blue', dashes=(1.5,.75), linewidth=2, label=r'$\mathrm{Im}$ FFT')
518
+    
519
+    fa.plot(vs[iL-nf:iL+nf], cr.real, '--', color='black', linewidth=1) #, label=r'reg')
520
+    fa.plot(vs[iL-nf:iL+nf], cr.imag, '--', color='black', dashes=(1.5,.75), linewidth=1) #, label=r'reg')
521
+    #ax.plot(dead_freqs[0:ni], np.imag(WSINDP2[0:ni]), "--", dashes=(1.5,.75),  color='blue', linewidth=1, label=r"$\mathrm{Im} ( \mathrm{DP} )$")
522
+    #fa.plot(vs[iL-nf:iL+nf], xpc, linewidth=2, label='actual', color='blue')
523
+
524
+    leg = fa.legend(frameon=True, scatterpoints=1, numpoints=1, labelspacing=0.2)
525
+    fixLeg(leg)
526
+
527
+    fa.set_xlabel("frequency [Hz]")
528
+    #fa.set_title("frequency-domain")
529
+    fa.set_xlim([1950,2050])
530
+    #leg = plt.legend()
531
+
532
+    #rect = leg.get_frame()
533
+    #rect.set_facecolor(light_grey)
534
+    #rect.set_linewidth(0.0)
535
+    #rect.set_alpha(0.5)
536
+    
537
+    #ra = quadfig.add_axes([.1, .1,  .8, .25])
538
+    #vs = .0125
539
+    #ra = quadfig.add_axes([.1, .1-vs, .8, .375])
540
+    #ra.set_title("decay envelope regressions")
541
+
542
+    #plt.savefig('quadri.png',dpi=200, facecolor='black', edgecolor='black')
543
+
544
+    # real imaginary plot
545
+    #rfig = plt.figure(facecolor='black')
546
+    #ra = rfig.add_axes([.1,.1,.8,.8])
547
+    #ra.plot(t, xn, label = 'noisy signal', color='yellow')
548
+    #ra.plot(t[::irsamp], xn[::irsamp], '.-', marker = 's', markersize=10, label='real signal')
549
+    #ra.plot(t[iisamp::irsamp], xn[iisamp::irsamp], '.-', marker = 'v', markersize=10, label='imaginary envelope')
550
+    #ra.plot(tdec, abse, '.-', color='green', markersize=18, label='decimated modulus')
551
+    #ra.plot(t[iisamp::irsamp], phase, '.-', color='green', markersize=18, label='decimated phase')
552
+    #leg = plt.legend()
553
+    #invertLegend(leg)
554
+    #invertColours(ra)
555
+    #plt.savefig("quad2.png", dpi=200, facecolor='black', edgecolor='black')
556
+
557
+
558
+
559
+    plt.figure(0) 
560
+    deSpine(plt.gca())
561
+    if BLACK:
562
+        ra.plot(t[iisamp::irsamp],re, linewidth=2, label='decimated regression', color='green')
563
+        ra.plot(times,re0, linewidth=2, label='all data regression', color='cyan')
564
+        ra.plot(t, xe, label = 'true', linewidth=4, color='magenta')
565
+        ra.set_xlabel("time", fontsize=16, color='white')
566
+        leg = plt.legend( labelspacing=0.2, scatterpoints=1,frameon=False )
567
+        fixLeg(leg)
568
+        invertLegend(leg)
569
+        plt.savefig('noisytime.pdf',dpi=600, facecolor='black', edgecolor='black')
570
+    else:
571
+
572
+        #ra.plot(1e3*times, afe*np.exp(-times/bf), '-', color='purple', linewidth=2, label='FD modulus')
573
+        qa.set_xlabel("time [ms]") #, fontsize=16)
574
+        
575
+
576
+        #rect = leg.get_frame()
577
+        #rect.set_facecolor(light_grey)
578
+        #rect.set_linewidth(0.0)
579
+        #rect.set_alpha(0.5)
580
+        qa.set_ylim([-1.25,3.25])
581
+        plt.savefig('noisytime_corr.pdf',dpi=600)
582
+        plt.savefig('noisytime_corr.eps',dpi=600)
583
+
584
+    #plt.show()
585
+    #exit()
586
+    
587
+    #plt.savefig('quadreg.png',dpi=200, facecolor='black', edgecolor='black')
588
+
589
+    # Just show envelopes
590
+    #efig = plt.figure(facecolor='black')
591
+    #ea = efig.add_axes([.1,.1,.8,.8])
592
+    #ea.plot(t[iisamp::irsamp],re, linewidth=2, label='decimated regression', color='yellow')
593
+    #ea.plot(times,re0, linewidth=2, label='all data regression', color='cyan')
594
+    #ea.plot(t, xe, label = 'clean envelope', linewidth=2, color='magenta')
595
+    #leg = plt.legend()
596
+    #invertLegend(leg)
597
+    #invertColours(ea)
598
+    #plt.savefig("envelopes.png", dpi=200, edgecolor='black', facecolor='black')    
599
+
600
+    # boxplot
601
+    if BLACK:
602
+        boxfig = plt.figure( facecolor='black', edgecolor='black')
603
+    else:
604
+        boxfig = plt.figure( figsize=[pc2in(20), pc2in(32)] )
605
+    ##############
606
+    #  b2    r2  #
607
+    #  b1    r1  #
608
+    #  b0    r0  #
609
+    ##############
610
+    vs =  0 #-3*.0125
611
+    vs2 = 0 #-2*.0125
612
+
613
+    # Time domain    
614
+    b1 = boxfig.add_axes([.15,.075, .35, .15]) # bottom we want labels 
615
+    r1 = boxfig.add_axes([ .6,.075, .35, .15])
616
+    
617
+    b0 = boxfig.add_axes([.15,.25, .35, .15])
618
+    r0 = boxfig.add_axes([ .6,.25, .35, .15])
619
+    
620
+    # Frequency domain
621
+    b2 = boxfig.add_axes([.15,.425, .35, .15])
622
+    r2 = boxfig.add_axes([ .6,.425, .35, .15])
623
+    
624
+    b4 = boxfig.add_axes([.15,.6, .35, .15])
625
+    r4 = boxfig.add_axes([ .6,.6, .35, .15])
626
+    
627
+    b3 = boxfig.add_axes([.15,.775,  .35, .15])
628
+    r3 = boxfig.add_axes([ .6,.775,  .35, .15])
629
+    
630
+    for ax in [b1,r1,b0,r0,b2,r2,b3,r3,b4,r4]:
631
+        deSpine(ax)
632
+    
633
+    B0 = b0.boxplot( BALL , sym='', bootstrap=5000)# , notch=True )
634
+    plt.setp(b0, 'xticklabels', [])
635
+    
636
+    R0 = r0.boxplot( RALL , sym='', bootstrap=5000)# , notch=True )
637
+    plt.setp(r0, 'xticklabels', []) 
638
+    
639
+    B1 = b1.boxplot( BDEC , sym='', bootstrap=5000)# , notch=True )
640
+    plt.setp(b1, 'xticklabels', sT2) 
641
+    
642
+    R1 = r1.boxplot( RDEC , sym='', bootstrap=5000)# , notch=True )
643
+    plt.setp(r1, 'xticklabels', sT2) 
644
+    
645
+    B2 = b2.boxplot( AF , sym='', bootstrap=5000)# , notch=True )
646
+    R2 = r2.boxplot( RF , sym='', bootstrap=5000)# , notch=True )
647
+    plt.setp(r2, 'xticklabels', []) 
648
+    plt.setp(b2, 'xticklabels', []) 
649
+    
650
+    B3 = b3.boxplot( AFC , sym='', bootstrap=5000)# , notch=True )
651
+    R3 = r3.boxplot( RFC , sym='', bootstrap=5000)# , notch=True )
652
+    plt.setp(r3, 'xticklabels', []) 
653
+    plt.setp(b3, 'xticklabels', []) 
654
+    
655
+    B4 = b4.boxplot( AM , sym='', bootstrap=5000)# , notch=True )
656
+    R4 = r4.boxplot( RM , sym='', bootstrap=5000)# , notch=True )
657
+    plt.setp(r4, 'xticklabels', []) 
658
+    plt.setp(b4, 'xticklabels', []) 
659
+    
660
+    b1.set_xlabel(r"true $T_2^*$ [ms]", color=labcol) #, fontsize=12) 
661
+    r1.set_xlabel(r"true $T_2^*$ [ms]", color=labcol) #, fontsize=12) 
662
+    
663
+    b2.set_ylabel(r"phased FD", color=labcol) #, fontsize=12) 
664
+    b3.set_ylabel(r"complex FD", color=labcol) #, fontsize=12) 
665
+    b4.set_ylabel(r"modulus FD", color=labcol) #, fontsize=12) 
666
+    b0.set_ylabel(r"CA", color=labcol) #, fontsize=12) 
667
+    b1.set_ylabel(r"gated CA", color=labcol) #, fontsize=12) 
668
+    
669
+    #r0.set_ylabel(r"parameter estimate error") 
670
+    #b1.set_xlabel(r"$T_2^*$ decay parameter [ms]") 
671
+
672
+    if BLACK:
673
+        for box in [B0, R0, B1, R1]: 
674
+            box['boxes'][0].set_color('w')
675
+            box['boxes'][0].set_linewidth(2)
676
+            box['medians'][0].set_color('y')
677
+            box['medians'][0].set_linewidth(2)
678
+            box['fliers'][0].set_color('w')
679
+            box['fliers'][1].set_color('w')
680
+            box['whiskers'][0].set_color('w')
681
+            box['whiskers'][1].set_color('w')
682
+#     else:
683
+    #it2 = 0
684
+    #for ax in B0, r0, b1, r1: 
685
+        #for line in ax.xaxis.get_ticklabels():
686
+        #        print  line.get_text()
687
+        #        line.set_text(str(T2[it2]))
688
+        #        print  line.get_text()
689
+        #setp(gca(), 'xticklabels', ['first', 'second', 'third']) 
690
+                #line.set_fontsize(16)   
691
+     #   it2 += 1 
692
+#             box['boxes'][0].set_color('red')
693
+#             box['boxes'][0].set_linewidth(3)
694
+#             box['medians'][0].set_color('y')
695
+#             box['medians'][0].set_linewidth(3)
696
+#             box['fliers'][0].set_color('w')
697
+#             box['fliers'][1].set_color('w')
698
+#             box['whiskers'][0].set_color('w')
699
+#             box['whiskers'][1].set_color('w')
700
+        
701
+
702
+    #r0.set_title(r"analytic signal $T_2^*$", color=labcol)
703
+    #b0.set_title(r"analytic signal $V_N^{(0)}$", color=labcol)
704
+    r3.set_title(r"$\tilde{T}_2^*$ relative error [\%]", color=labcol) #, fontsize=12)
705
+    b3.set_title(r"$\tilde{V}_N^{(0)}$ absolute error [\%]", color=labcol) #, fontsize=12)
706
+
707
+    if BLACK:
708
+        invertColours(b0)
709
+        invertColours(r0)
710
+        invertColours(b1)
711
+        invertColours(r1)
712
+        #plt.savefig("boxplot.png", dpi=200, facecolor='black', edgecolor='black')
713
+    #else:
714
+    #plt.figure()
715
+    #plt.boxplot( BDEC, bootstrap=1000 )
716
+    #plt.title("dec")    
717
+        #plt.savefig("boxplot.png", dpi=200)
718
+
719
+    bylow = min(b0.get_ylim()[0], b1.get_ylim()[0])
720
+    bylow = min(bylow, b2.get_ylim()[0])
721
+    bylow = min(bylow, b3.get_ylim()[0])
722
+    bylow = min(bylow, b4.get_ylim()[0])
723
+
724
+    byhi = max(b0.get_ylim()[1], b1.get_ylim()[1])
725
+    byhi = max(byhi, b2.get_ylim()[1])
726
+    byhi = max(byhi, b3.get_ylim()[1])
727
+    byhi = max(byhi, b4.get_ylim()[1])
728
+    
729
+    rylow = min(r0.get_ylim()[0], r1.get_ylim()[0])
730
+    rylow = min(rylow, r2.get_ylim()[0])
731
+    rylow = min(rylow, r3.get_ylim()[0])
732
+    rylow = min(rylow, r4.get_ylim()[0])
733
+
734
+    ryhi = max(r0.get_ylim()[1], r1.get_ylim()[1])
735
+    ryhi = max(ryhi, r2.get_ylim()[1])
736
+    ryhi = max(ryhi, r3.get_ylim()[1])
737
+    ryhi = max(ryhi, r4.get_ylim()[1])
738
+ 
739
+    b0.set_ylim((bylow, byhi))
740
+    b1.set_ylim((bylow, byhi))
741
+    b2.set_ylim((bylow, byhi))
742
+    b3.set_ylim((bylow, byhi))
743
+    b4.set_ylim((bylow, byhi))
744
+    
745
+    r0.set_ylim((rylow, ryhi))
746
+    r1.set_ylim((rylow, ryhi))
747
+    r2.set_ylim((rylow, ryhi))
748
+    r3.set_ylim((rylow, ryhi))
749
+    r4.set_ylim((rylow, ryhi))
750
+
751
+    
752
+    if BLACK:
753
+        #plt.savefig("boxplotlim2.pdf", dpi=600, facecolor='black', edgecolor='black')
754
+        plt.savefig("boxplotlim_2.pdf", dpi=600, facecolor='black', edgecolor='black')
755
+        plt.savefig("boxplotlim_2.eps", dpi=600, facecolor='black', edgecolor='black')
756
+    else:
757
+        #plt.savefig("boxplotlim2.pdf", dpi=600)
758
+        plt.savefig("boxplotlim_2_corr.pdf", dpi=600)
759
+        plt.savefig("boxplotlim_2_corr.eps", dpi=600)
760
+        #plt.savefig("boxplotlim2.tex", dpi=600)
761
+   
762
+    plt.show()
763
+    exit()
764
+
765
+#########################################################
766
+#########################################################
767
+
768
+# # fft figure
769
+# plt.figure()
770
+# XS = np.fft.rfft(xs)
771
+# freqs = np.fft.fftfreq(len(xs), dt)
772
+# freqs[len(XS)-1] *= -1 # nyquist returned for negative freq
773
+# freqs = freqs[0:len(XS)]
774
+# 
775
+# plt.plot(freqs[0:len(XS)], XS.real)
776
+# plt.plot(freqs[0:len(XS)], XS.imag)
777
+# 
778
+# # truncate by T X
779
+# plt.figure()
780
+# dv = freqs[1] - freqs[0]
781
+# iLw = vL / dv
782
+# tr = int(len(freqs) / 35.)
783
+# plt.plot(freqs[iLw-tr:iLw + tr], XS.real[iLw-tr:iLw+tr], '-x')
784
+# plt.plot(freqs[iLw-tr:iLw + tr], XS.imag[iLw-tr:iLw+tr], '-x')
785
+# plt.twinx()
786
+# plt.plot(freqs[iLw-tr:iLw + tr], np.angle(XS)[iLw-tr:iLw+tr], '-x')
787
+# 
788
+# print len(XS[iLw-tr:iLw+tr])
789
+# print len(t[iisamp::irsamp])
790
+# 
791
+# plt.show()

+ 29
- 0
GUI/test.py View File

@@ -0,0 +1,29 @@
1
+#!/usr/bin/env python
2
+import sys
3
+import matplotlib
4
+matplotlib.use('Qt4Agg')
5
+matplotlib.rcParams['backend.qt4']='PySide'
6
+import pylab
7
+
8
+from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
9
+from matplotlib.figure import Figure
10
+
11
+from PySide import QtCore, QtGui
12
+
13
+if __name__ == '__main__':
14
+    app = QtGui.QApplication(sys.argv)
15
+
16
+    # generate the plot
17
+    fig = Figure(figsize=(600,600), dpi=72, facecolor=(1,1,1), edgecolor=(0,0,0))
18
+    ax = fig.add_subplot(111)
19
+    ax.plot([0,1])
20
+    # generate the canvas to display the plot
21
+    canvas = FigureCanvas(fig)
22
+
23
+    win = QtGui.QMainWindow()
24
+    # add the plot canvas to a window
25
+    win.setCentralWidget(canvas)
26
+
27
+    win.show()
28
+
29
+    sys.exit(app.exec_())

+ 797
- 0
MRProc.py View File

@@ -0,0 +1,797 @@
1
+from __future__ import division # in case this is called from python2 
2
+from __future__ import print_function
3
+
4
+import numpy as np
5
+import matplotlib.pyplot as plt
6
+import sys, os
7
+import notch 
8
+from scipy import signal
9
+from scipy.interpolate import splprep, splev, spline # for smoothing spline FITPACK 
10
+from scipy.interpolate import UnivariateSpline
11
+from decay import *
12
+import scipy.stats as stats
13
+from pwctimeWhite import * 
14
+# for signal slot communication with GUI 
15
+#from PyQt4.QtCore import QObject, SIGNAL
16
+from PySide.QtCore import QObject, SIGNAL
17
+from pandas.io.parsers import read_csv #<<- faster than Numpy but not working right now
18
+from timeit import timeit 
19
+
20
+
21
+class MRProc(QObject):
22
+    """ Class to process read and invert ORS borehole data
23
+    """ 
24
+    def __init__(self):
25
+        QObject.__init__(self)
26
+        self.burst = False           # For Schlumberger or VC burst data 
27
+
28
+    #def __init__(self, filename):
29
+    #    # what do we need to know that isn't in new files?       
30
+    #    fileName, fileExtension = os.path.splitext(filename)
31
+    #    if fileExtension == ".ors":
32
+    #        self.loadORSFile(filename)
33
+        #self.DistRes = { } 
34
+        #self.DistRes = { 'env':np.array(0), 'mod':np.array(0), 'Time':np.array(0) } #[a0,b0,rt20] }
35
+        
36
+    def loadORSFile_OLD(self, filename):
37
+        """ Loads ORS files with minimal header info
38
+        """
39
+        f = open(filename)
40
+        header = f.readline().split() #np.loadtxt(filename, comment="#", )
41
+        ih = 1
42
+        while (header[0] == "#"):
43
+            ih += 1
44
+            header = f.readline().split() #np.loadtxt(filename, comment="#", )
45
+        self.NR = eval(header[0])    # number of records in an echo
46
+        self.NE = eval(header[1])    # number of echoes
47
+        self.DT = eval(header[2])    # in s
48
+        self.TAUE = eval(header[3])  # in s
49
+        self.RL = self.DT * self.NR  # in s
50
+
51
+        # pandas is about ~16 times faster than numpy for import (7 sec vs. .5 sec on tested example)  
52
+        # Parse in datafile, TODO update for newer format and remove hardcoded 3 in pandas
53
+        #DATA = np.loadtxt(filename, comments="#", skiprows=ih)
54
+        DATA = read_csv(filename, comment="#", sep='\t', skiprows=3, engine='c', header=None, names=('time','inphase','quadrature')).as_matrix() 
55
+
56
+        self.NS = int(np.shape(DATA)[0] / (self.NE * self.NR))
57
+
58
+        # reshape DATA
59
+        #self.DATA = np.reshape( DATA[:,1] + 1j*DATA[:,2], ( self.NS, self.NE, self.NR) )
60
+        self.DATA = np.reshape( self.A2D(DATA[:,1]) + 1j*self.A2D(DATA[:,2]), ( self.NS, self.NE, self.NR) )
61
+        self.WTIME = np.reshape( DATA[:,0], ( self.NS, self.NE, self.NR) )
62
+        self.TIMES = np.arange(self.DT, self.RL + 1e-8, self.DT)           # 1 ms record, 1 us dwell 
63
+
64
+        self.emit(SIGNAL("plotRAW()"))   
65
+        self.emit(SIGNAL("enableDSP()"))   
66
+        self.emit(SIGNAL("doneStatus()"))  
67
+
68
+    def evaluate(self, value):
69
+        if len(value) == 1:
70
+            if value[0][-1].isdigit(): 
71
+                return eval(value[0])
72
+            else:
73
+                if value[0][-1] == "u":   # micro
74
+                    return eval(value[0][0:-1]) * 1e-6
75
+                elif value[0][-1] == "M": # mega
76
+                    return eval(value[0][0:-1]) * 1e6
77
+                elif value[0][-1] == "s": # second
78
+                    return eval(value[0][0:-1]) 
79
+                else:
80
+                    print("DOOM!")
81
+                    exit(1)
82
+        else:
83
+            # TODO
84
+            # logic here is a little lacking, but current tool does not do anything 
85
+            # sneaky with array values, could break in the future though 
86
+            vals = []
87
+            for val in value:
88
+                vals.append( eval(val) )
89
+            return vals
90
+
91
+    def loadORSFile(self, fname):
92
+        """ Loads ORS files with extensive header info
93
+        """
94
+        parameters = False
95
+        data = False
96
+        pars = {}
97
+        with open(fname) as f:
98
+            iline = 0
99
+            for line in f:
100
+                if len(line.split()) == 0:
101
+                    pass # empty do nothing
102
+                else:
103
+                    if line.split()[0] == "[ps]":
104
+                        parameters = False
105
+                        # these entries specify the chip programming 
106
+                    if parameters:
107
+                        # proc header info
108
+                        split,comment = line.split("#")
109
+                        var, val = split.split("=")
110
+                        val = val.split(";")[0].split()
111
+                        pars[var.strip()] = self.evaluate(val)
112
+                    if line.split()[0] == "[parameters]":
113
+                        parameters = True
114
+                    if data:
115
+                        if line[0] == "#":
116
+                            pass
117
+                        else:
118
+                            header = line.split()
119
+                            iline += 1
120
+                            break
121
+                    if line.split()[0] == "[data]":
122
+                        data = True
123
+                iline += 1
124
+
125
+        self.NR = eval(header[0])    # number of records in an echo
126
+        self.NE = eval(header[1])    # number of echoes
127
+        self.DT = eval(header[2])    # in s
128
+        self.TAUE = eval(header[3])  # in s
129
+        self.RL = self.DT * self.NR  # in s        
130
+        
131
+        # Pandas is much faster than numpy
132
+        DATA = read_csv(fname, comment="#", sep='\t', skiprows=iline, engine='c', header=None, names=('time','inphase','quadrature')).as_matrix() 
133
+        self.NS = int(np.shape(DATA)[0] / (self.NE * self.NR))
134
+
135
+        # reshape DATA
136
+        #self.DATA = np.reshape( DATA[:,1] + 1j*DATA[:,2], ( self.NS, self.NE, self.NR) )
137
+        self.DATA = np.reshape( self.A2D(DATA[:,1], pars) + 1j*self.A2D(DATA[:,2], pars), ( self.NS, self.NE, self.NR) )
138
+        self.WTIME = np.reshape( DATA[:,0], ( self.NS, self.NE, self.NR) )
139
+        self.TIMES = np.arange(self.DT, self.RL + 1e-8, self.DT)           # 1 ms record, 1 us dwell 
140
+
141
+        self.emit(SIGNAL("plotRAW()"))   
142
+        self.emit(SIGNAL("enableDSP()"))   
143
+        self.emit(SIGNAL("doneStatus()"))  
144
+    
145
+    # analogue to digital conversion, 'don't quote me on this -- kevin'
146
+    def A2D(self, i, pars):
147
+            """ Actual voltage is not known. This routine will need to be expaned to account for 
148
+                calibration datasets. Or perhaps the inversion and fitting routines instead.
149
+                TODO, if prestacked data, we need to account for this. 
150
+            """ 
151
+            #return  (i/(.5 * (2.**16)))
152
+            #cal = 7.85 # TODO query for this
153
+            cal = 5.51 # 1/2 full
154
+            return i / ( pars['n'] * pars['cal'] )
155
+
156
+    def loadJavelinLog(self, filename):
157
+        """Loads processed Javelin data"""
158
+        #print("Javelin time")
159
+        import scipy.io as sio
160
+        Data =  sio.loadmat(filename)
161
+        self.T2T = Data['time'][:,0] 
162
+        self.TAUE =  self.T2T[3] - self.T2T[2]
163
+        # Better sigma estimate needed
164
+        self.sigma = 2.5 
165
+        self.T2N = np.random.normal( 0, self.sigma, len(self.T2T) )    
166
+        self.NS = 1
167
+        self.T2D = self.T2N + 1j*Data['se_vector_wc'][:,56]       # depth 0 
168
+        # What about each depth? How is modelling handled.
169
+
170
+    def loadVCMFile(self, filename):
171
+        #print("LOADING VCM file")
172
+        Data = np.loadtxt(filename, comments = "#")
173
+        self.T2T = Data[:,0]    
174
+        self.sigma = eval(open( filename, 'r' ).readline().split()[1])       
175
+        self.T2N = np.random.normal( 0, self.sigma, len(self.T2T) )    
176
+        self.T2D = self.T2N + 1j*Data[:,1]
177
+        self.NS = 1
178
+        self.TAUE =  self.T2T[3] - self.T2T[2]
179
+        
180
+        #self.emit(SIGNAL("plotLOG10ENV()"))   
181
+        #self.emit(SIGNAL("enableDSP()"))   
182
+        #self.emit(SIGNAL("enableINV()"))   
183
+        #self.emit(SIGNAL("doneStatus()"))  
184
+ 
185
+    def loadTextFile(self, filename, NS, NE, NR):
186
+        """ Loads older format files without header info """
187
+        print ("OLD TEXT FORMAT DETECTED, not SUPPORTED RIGHT NOW")
188
+        exit()
189
+        pass
190
+
191
+    def batchThread(self, pars, tag, bload=True, bwindow=True, benvelope=True, bphase=True, bgate=True):
192
+        
193
+        #print("batdacheing", load, window, envelope, phase, gate)
194
+        fileName, fileExtension = os.path.splitext( str(tag) )
195
+        if bload:
196
+            #print ("loading", tag)
197
+            if fileExtension == ".ors":
198
+                self.loadORSFile(str(tag)) 
199
+            elif fileExtension == ".vcm":
200
+                self.loadVCMFile(str(tag)) 
201
+        
202
+        if bwindow:
203
+            #print("windowing", pars['ftype'], pars['winWidth'])
204
+            self.WindowAndStack( pars['ftype'] , pars['winTrimLeft'], pars['winTrimRight'] )
205
+
206
+        if benvelope:
207
+            #print("enveloping", pars['offset'])
208
+            self.T2EnvelopeDetect( pars['offset'] ) 
209
+        
210
+        if bphase:
211
+            self.CorrectedAmplitude( 0,0 ) 
212
+        
213
+        if bgate:
214
+            #print("gating", pars['gpd'])
215
+            self.gateIntegrate( pars['gpd'], pars['stackEfficiency'] ) 
216
+
217
+        self.emit(SIGNAL("doneStatus()"))  
218
+        
219
+
220
+    def WindowAndStack(self, ftype="Blackman-Harris", triml=0, trimr=0):
221
+        # TODO, consider lowpass filter and interpolate to higher DT in order to minimize window amplitude effects
222
+        #       what would cost of such an operation be? 
223
+        # TODO, should record be overwritten? I don't think so. I think we should make a deep copy here and then you can always fall back.
224
+        
225
+        # Trim ends to centre echo, this many are REMOVED        
226
+        #triml = 18  
227
+        #trimr = 15   
228
+        self.TIMES = self.TIMES[triml:-(trimr)]
229
+        self.NR -= triml + trimr
230
+        if ftype == "Blackman-Harris":
231
+            window = signal.blackmanharris(self.NR)
232
+        elif ftype == "Blackman":
233
+            window = signal.blackman(self.NR)
234
+        elif ftype == "Hann":
235
+            window = signal.hann(self.NR)
236
+        elif ftype == "sinc":
237
+            window = np.ones( (self.NR) )
238
+
239
+        self.DATAP = self.DATA[:,:,triml:-trimr].copy()
240
+        for istk in range(0,self.NS):
241
+            for ie in range(0,self.NE,1):
242
+                self.DATAP[istk, ie,:] *= window #  signal.blackmanharris(self.NR)
243
+                #pass
244
+                #self.DATA[istk, ie, :] *= signal.hann(self.NR)
245
+                #self.DATA[istk, ie, :] *= signal.blackman(self.NR)
246
+                #DATA2[istk, ie, :] *= signal.blackmanharris(self.NR)
247
+            self.emit(SIGNAL("updateProgress(int)"), (int)(1e2*istk/self.NS))   
248
+    
249
+        self.DATASTACK = np.average(self.DATAP, axis=0)
250
+        if (self.NS > 1):
251
+            # for noise calculation, destructively average phase-cycled echoes
252
+            self.DATAP[1::2,:,:] *= -1.
253
+            self.NOISE = np.average(self.DATAP, axis=0)
254
+            self.NOISE -= np.mean(self.NOISE) # The noise can be biased? Don't understand why!!
255
+            #self.DATA[0::2,:,:] *= -1. 
256
+        self.emit(SIGNAL("plotWIN()"))   
257
+        self.emit(SIGNAL("enableDSP()"))   
258
+        self.emit(SIGNAL("doneStatus()"))  
259
+
260
+    def ResampleEnvelopeData(self, Q, Mask=0, GatesPD=0):
261
+        """ Mirror the data and then apply window function and resample in Fourier domain at twice the number. 
262
+            Then take half. Should be better.
263
+            GatesPD is Gates Per Decade 
264
+        """
265
+        
266
+        tt = np.concatenate( [self.T2T[Mask::], self.T2T[Mask::][-1]+self.T2T[Mask::] ] )
267
+        xx = np.concatenate( [self.T2D[Mask::][::-1] , self.T2D[Mask::] ] )
268
+        
269
+        xx,tx = signal.resample( xx, 2*Q, t=tt)#, window='hanning')
270
+        self.T2D = xx[0:Q][::-1]
271
+        #self.T2D = self.T2D[::-1]
272
+        
273
+        xx = np.concatenate( [self.T2N[::-1] , self.T2N] )
274
+        xx,tx = signal.resample( xx, 2*Q, t=tt)#, window='hanning')
275
+        self.T2N = xx[0:Q][::-1]
276
+
277
+        # don't reverse times dummy!
278
+        self.T2T = tx[0:Q] #[::-1] ## <-- No! bad Trevor
279
+        self.NE = len(self.T2D)
280
+
281
+    def computeSTD(self):
282
+        if self.NS >= 2:
283
+            self.sigma = np.std( np.imag(self.T2N) )
284
+        else:
285
+            #N = np.real(self.T2D)
286
+            s0 = np.std( np.real(self.T2D) )
287
+            #sr = ( np.std( (np.exp(-1j*self.zeta)*N).real ))  
288
+            #si = ( np.std( (np.exp(-1j*self.zeta)*N).imag ))  
289
+            print("s0", s0)
290
+            self.sigma = s0 # np.array((.15)) #s0 #+ (sr+si-s0)/2.   
291
+            #self.phasegain =  (s0 + (sr+si-s0)/2) / s0
292
+            #print("ratio",  s0,  (s0+(sr+si-s0)/2.)/s0  )
293
+            #nr = (np.exp(-1j*self.zeta)*N).real
294
+            #ni = (np.exp(-1j*self.zeta)*N).imag
295
+            #print ("phasegain", self.phasegain)
296
+            #self.T2D.real *= self.phasegain 
297
+            #print("SIGMA CALCULATION HERE", s0, self.sigma)
298
+            
299
+    def computeSTDBurst(self):
300
+        if self.NS >= 2:
301
+            self.sigmaBurst = np.std( np.imag(self.T2Nb) )
302
+        else:
303
+            N = np.real(self.T2Db)
304
+            s0 = np.std( np.real(self.T2Db) )
305
+            sr = ( np.std( (np.exp(-1j*self.zeta)*N).real) )  
306
+            si = ( np.std( (np.exp(-1j*self.zeta)*N).imag) )  
307
+            self.sigmaBurst = s0 #+ (sr+si-s0)/2.   
308
+            #self.T2Db.real *= self.sigmaBurst/s0 #nr #(nr+ni)/2.          
309
+        #self.sigmaBurst = np.std( np.real(self.T2Db) ) 
310
+    
311
+    def gateIntegrateBurst(self, gpd, stackEfficiency):
312
+
313
+        self.computeSTDBurst()
314
+
315
+        # calculate total number of decades
316
+        nd = np.log10(self.T2Tb[-1]/self.T2Tb[0])   #np.log10(self.T2T[-1]) -  np.log10(self.T2T[-1])
317
+        tdd = np.logspace( np.log10(self.T2Tb[0]), np.log10(self.T2Tb[-1]), (int)(gpd*nd)+1, base=10, endpoint=True) 
318
+        tdl = tdd[0:-1]     # these are left edges
319
+        tdr = tdd[1::]      # these are left edges
320
+        td = (tdl+tdr) / 2. # window centres
321
+
322
+        htd = np.zeros( len(td), dtype=complex )
323
+        htn = np.zeros( len(td), dtype=complex )
324
+
325
+        isum = np.zeros( len(td), dtype=int )
326
+        
327
+        Vars = np.ones( len(td) ) * self.sigmaBurst**2 #* .15**2
328
+
329
+        ii = 0
330
+        for itd in range(len(self.T2Tb)):
331
+            if ( self.T2Tb[itd] > tdr[ii] ):
332
+                if (ii < len(td)-1):
333
+                    ii += 1
334
+                else:
335
+                    break
336
+            isum[ii] += 1
337
+            htd[ii]  += self.T2Db[ itd ]
338
+            #htn[ii] += self.T2N[ itd ]
339
+            Vars[ii] += self.sigmaBurst**2
340
+            #self.emit(SIGNAL("updateProgress(int)"), (int)(1e2*itd/len(self.T2T)))  
341
+        
342
+        # The 1.5 should be 2 in the theory, the 1.5 compensates for correlated noise, etc. Bascically 
343
+        #       we don't see noise reduce like ideal averaging. 
344
+        self.sigmaBurst = np.sqrt( Vars  * (1/isum)**stackEfficiency ) # Var (aX) = a^2 Var(x)
345
+
346
+        # Bootstrap
347
+        bootstrap = False
348
+        if bootstrap:
349
+            Means, isumbs = self.bootstrapWindows(np.real(self.T2Db), 300, isum)
350
+            ts,sm = self.smooth( isumbs, np.std(Means, axis=1) )
351
+            for it in range(ii):
352
+                self.sigmaBurst[it] = sm[isum[it]]
353
+
354
+        # RESET times where isum == 1
355
+        ii = 0
356
+        while (isum[ii] == 1):
357
+            td[ii] = self.T2Tb[ii]
358
+            ii += 1
359
+
360
+        htd /= isum
361
+        htn /= isum
362
+
363
+        self.T2Tb = td
364
+        self.T2Db = htd
365
+
366
+ 
367
+    def gateIntegrate(self, gpd, stackEfficiency):
368
+        """ 
369
+            Gate integrate the signal to gpd, gates per decade
370
+        """
371
+        self.computeSTD()
372
+        self.gpd = gpd
373
+
374
+        if self.burst:
375
+            self.gateIntegrateBurst(gpd, stackEfficiency)
376
+
377
+        # calculate total number of decades
378
+        nd = np.log10(self.T2T[-1]/self.T2T[0])   #np.log10(self.T2T[-1]) -  np.log10(self.T2T[-1])
379
+        tdd = np.logspace( np.log10(self.T2T[0]), np.log10(self.T2T[-1]), (int)(gpd*nd)+1, base=10, endpoint=True) 
380
+        tdl = tdd[0:-1]     # these are left edges
381
+        tdr = tdd[1::]      # these are left edges
382
+        td = (tdl+tdr) / 2. # window centres
383
+
384
+        htd = np.zeros( len(td), dtype=complex )
385
+        htn = np.zeros( len(td), dtype=complex )
386
+        isum = np.zeros( len(td), dtype=int )
387
+        Vars = np.zeros( len(td) ) 
388
+
389
+        ii = 0
390
+        for itd in range(len(self.T2T)):
391
+            if ( self.T2T[itd] > tdr[ii] ):
392
+                if (ii < len(td)-1):
393
+                    ii += 1
394
+                else:
395
+                    break
396
+            isum[ii] += 1
397
+            htd[ii]  += self.T2D[ itd ]
398
+            
399
+            #htn[ii] += self.T2N[ itd ]
400
+            Vars[ii] += self.sigma**2
401
+            self.emit(SIGNAL("updateProgress(int)"), (int)(1e2*itd/len(self.T2T)))  
402
+        
403
+        # Theoretical gates 
404
+        # The 1.5 should be 2 in the theory, the 1.5 compensates for correlated noise, etc. Bascically 
405
+        #       we don't see noise reduce like ideal averaging. 
406
+        self.sigma = np.sqrt(Vars  * (1./isum)**stackEfficiency ) # Var (aX) = a^2 Var(x)
407
+
408
+        # Bootstrap
409
+        bootstrap = False # False
410
+        bs = open("bootstrap.dat", "a")
411
+        if bootstrap:
412
+            nboot=10000
413
+            #Means, isumbs = self.bootstrapWindows(np.real(self.T2D), 100, isum[isum!=1])
414
+            Means, isumbs = self.bootstrapWindows(np.real(self.T2D), nboot, np.arange(1,isum[-1]+1))
415
+            #ts,sm = self.smooth(isumbs, np.std(Means - np.tile(np.mean(Means,axis=1),(nboot,1)).T  , axis=1, ddof=2)) # unbiased
416
+            # TODO use MAD instead of STD??
417
+            ts,sm = self.smooth(isumbs, np.std(Means, axis=1, ddof=1)) # unbiased
418
+            #scale = (1. + (nboot*isum)/(np.ones(ii+1)*len(self.T2D)))**.125
419
+            #print (scale) 
420
+            for item in (sm[isum[isum!=1]-1]-self.sigma[isum!=1]): #/self.sigma[isum!=1]:
421
+                bs.write( str(item) + " " )
422
+            bs.write("\n")
423
+            #ts,sm = self.smooth( isumbs, np.std(Means, axis=1))
424
+            #bias = np.average(Means,axis=1)
425
+            for it in range(len(self.sigma)):
426
+                self.sigma[it] = sm[isum[it]-1]
427
+            print('sigma boot', self.sigma)
428
+        # RESET times where isum == 1
429
+        ii = 0
430
+        while (isum[ii] == 1):
431
+            td[ii] = self.T2T[ii]
432
+            ii += 1
433
+
434
+        htd /= isum
435
+        htn /= isum
436
+
437
+        self.T2T = td
438
+        self.T2D = htd
439
+
440
+        self.emit(SIGNAL("plotLOG10ENV()"))   
441
+        self.emit(SIGNAL("enableDSP()"))   
442
+        self.emit(SIGNAL("enableINV()"))   
443
+        self.emit(SIGNAL("doneStatus()"))  
444
+
445
+    def bootstrapWindows(self, N, nboot, isum):
446
+        """ Bootstraps noise as a function of gate width
447
+        """
448
+        nc = np.shape(N)[0]
449
+
450
+        Means = {}
451
+        Means = np.zeros( (len(isum), nboot)  )
452
+        for ii, nwin in enumerate(isum):  
453
+            for iboot in range(nboot):
454
+                cs = np.random.randint(0,nc-nwin)
455
+                #Means[ii,iboot] = np.mean( N[cs:cs+nwin] )
456
+                Means[ii,iboot] = np.mean( np.random.normal(0, self.sigma[0], nwin))
457
+        return Means, np.array(isum)
458
+
459
+    def smooth(self, x, y):
460
+        w = np.ones(len(x))
461
+        w[0] = 100. # force first time gate
462
+        xs = np.arange(1, x[-1]+1, .1) # resample
463
+        #s = UnivariateSpline( np.log(x), np.log(y), s=2.5, w=w)
464
+        s = UnivariateSpline( np.log(x), np.log(y), s=4.5, w=w)
465
+        #s = UnivariateSpline( x, y, s=2.5, w=w)
466
+        ys = s(np.log(xs))
467
+        #ys = s(xs)
468
+
469
+        # resample 
470
+        ys = ys[0::10]    
471
+        xs = xs[0::10]    
472
+   
473
+        return xs,np.exp(ys)
474
+
475
+    def T2EnvelopeDetect(self, offset=0):
476
+
477
+        self.NRS = (int)(2**12)  # Number of zeros to pad with, total record length
478
+        # Zero pad
479
+        DATASTACKZPF = np.append( self.DATASTACK, np.zeros( (self.NE, self.NRS - self.NR) ), axis=1  )
480
+        if (self.NS > 1):
481
+            NOISEZPF = np.append( self.NOISE, np.zeros( (self.NE, self.NRS - self.NR) ), axis=1  )
482
+    
483
+        # TODO check for proper split
484
+        SPLIT = self.NR//2 + offset  # 20 # 15 #2 
485
+
486
+        # containers for array
487
+        self.T2D = np.zeros( (self.NE), dtype='complex' ) 
488
+        self.T2N = np.zeros( (self.NE), dtype='complex' ) 
489
+        self.T2T = np.zeros( (self.NE) ) 
490
+        
491
+        # frequency-domain processing
492
+        for ie in range(0,self.NE):
493
+
494
+            COLOUR = np.array([ie/self.NE, 0., 1.-ie/self.NE]) # for plots
495
+            
496
+            ###############################################################
497
+            # fold data need to reverse some of these
498
+            #FOLD = ((DATASTACK[ie,0:NR/2] + DATASTACK[ie,:][::-1][0:NR/2])/2.)[::-1] 
499
+            #plt.figure(223)
500
+            #plt.plot( self.TAUE*(float)(ie) + self.TIMES, np.imag(self.DATASTACK[ie]),  color=COLOUR)
501
+            #plt.plot( self.TAUE*(float)(ie) + self.TIMES, np.imag(self.DATA[0,ie]),  color='green')
502
+            #plt.plot( self.TAUE*(float)(ie) + self.TIMES, np.imag(self.DATA[1,ie]),  color='black')
503
+            #plt.plot( self.TAUE*(float)(ie) + self.TIMES, np.imag(self.DATA[2,ie]),  color='cyan')
504
+            #plt.plot( self.TAUE*(float)(ie) + self.TIMES, np.imag(self.DATA[3,ie]),  color='purple')
505
+            #plt.plot( TAUE*(1e-6)*(float)(ie) + TIMES[SPLIT], np.imag(DATASTACK[ie])[SPLIT], 'o', markersize=6, color=COLOUR)
506
+            #plt.plot( TAUE*DT*ie + TIMES, np.real(DATASTACK[ie]),  color=COLOUR[::-1])
507
+
508
+            ##################################################################
509
+            # do frequency domain workflow 
510
+            # fold to DC
511
+            ND = np.append( DATASTACKZPF[ie,:][SPLIT::], DATASTACKZPF[ie,:][0:SPLIT] )
512
+            #X = 1e-2*((self.RL/2.)/self.NR) * np.fft.fft(ND, n=self.NRS) # Why 1e-2?
513
+            X = 1e4*(( (self.DT*self.NRS) /2.)/self.NRS) * np.fft.fft(ND, n=self.NRS) # Why 1e-2?
514
+            #X = (self.DT) * np.fft.fft(ND, n=self.NRS) # Why 1e-2?
515
+
516
+            if (self.NS > 1):
517
+                NND = np.append( NOISEZPF[ie,:][SPLIT::], NOISEZPF[ie,:][0:SPLIT] )
518
+                #XN = 1e-2*((self.RL/2.)/self.NR) * np.fft.fft(NND, n=self.NRS) # Why 1e-2?
519
+                XN = 1e4*(( (self.DT*self.NRS) /2.)/self.NRS) * np.fft.fft(NND, n=self.NRS) # Why 1e-2?
520
+                self.T2N[ie] = XN[0] #complex( np.real(X[0]),  np.imag(X[0]) )
521
+    
522
+            ##################################################################
523
+            # Save T2 records
524
+            self.T2D[ie] = X[0] #complex( np.real(X[0]),  np.imag(X[0]) )
525
+            self.T2T[ie] = (1.+ie)*(self.TAUE)
526
+            self.emit(SIGNAL("updateProgress(int)"), (int)(1e2*ie/self.NE))   
527
+       
528
+        self.computeSTD() 
529
+        
530
+        self.emit(SIGNAL("plotENV()"))   
531
+        self.emit(SIGNAL("enableDSP()"))   
532
+        self.emit(SIGNAL("enableINV()"))   
533
+        self.emit(SIGNAL("doneStatus()"))  
534
+
535
+    def CorrectedAmplitude(self, joe=0, frank=0):
536
+        """ Phase corrects the record, joe and Frank are empty (ignored) and are workarounds for threading.
537
+        """
538
+        #[E0,df,phi,T2] = quadratureDetect(Q, I, tt)
539
+       
540
+        # mask first few echoes, just for detection 
541
+        NM = 2 
542
+        #[conv,E0,df,phi,T2] = quadratureDetect(np.imag(self.T2D[NM::]), np.real(self.T2D[NM::]), self.T2T[NM::], False, True)
543
+        #[conv,E0,df,phi,T2] = quadratureDetect(np.imag(self.T2D[NM::]), np.real(self.T2D[NM::]), self.T2T[NM::], False, False, True)
544
+        #                                                                              #CorrectFreq=False, BiExp=False, CorrectDC=False)
545
+        [conv,E0,df,phi,T2] = quadratureDetect2(np.imag(self.T2D[NM::]), np.real(self.T2D[NM::]), self.T2T[NM::])
546
+        self.zeta1 = phi
547
+
548
+        #if conv: # failed convergence
549
+        #    [conv,E0,df,phi,T2] = quadratureDetect(np.imag(self.T2D[NM::]), np.real(self.T2D[NM::]), self.T2T[NM::], False, False, False)
550
+        self.T2D = self.RotateAmplitude(np.real(self.T2D), np.imag(self.T2D), phi, df, self.T2T) 
551
+        self.computeSTD() 
552
+        #print(str(conv) + "\t" + str(phi)) #, file = "phase.dat")
553
+       
554
+        if self.burst:
555
+            #[conv,E0,df,phi,T2] = quadratureDetect(np.imag(self.T2Db), np.real(self.T2Db), self.T2Tb, False, False, False)
556
+            [conv,E0,df,phi,T2] = quadratureDetect2(np.imag(self.T2Db), np.real(self.T2Db), self.T2Tb) #, False, False, False)
557
+            #if conv: # failed convergence
558
+            #    [conv,E0,df,phi,T2] = quadratureDetect(np.imag(self.T2Db), np.real(self.T2Db), self.T2Tb, False, False, False)
559
+            env = E0*np.exp(-np.array(self.T2Tb)/T2)
560
+            self.T2Db = self.RotateAmplitude(np.real(self.T2Db), np.imag(self.T2Db), phi, df, self.T2Tb)
561
+            self.computeSTDBurst() 
562
+        
563
+        self.emit(SIGNAL("plotENV()"))   
564
+        self.emit(SIGNAL("enableDSP()"))   
565
+        self.emit(SIGNAL("enableINV()"))   
566
+        self.emit(SIGNAL("doneStatus()"))  
567
+
568
+    def RotateAmplitude(self, X, Y, zeta, df, t):
569
+        self.zeta = zeta 
570
+        self.df = df 
571
+        V = X + 1j*Y
572
+        return np.abs(V) * np.exp(1j * (np.angle(V) - zeta - 2.*np.pi*df*t)) 
573
+
574
+    def MonoFit(self, mask=2, intercept="False"):
575
+        component='imag'
576
+        #print "MonoRes", component, mask, intercept
577
+        a0 = 0
578
+        if intercept=="True":
579
+            if component == "imag":
580
+                [a0,b0,rt20] =  regressCurve(np.imag(self.T2D[mask::]), self.T2T[mask::], intercept=True)  
581
+            if component == "real":
582
+                [a0,b0,rt20] =  regressCurve(np.real(self.T2D[mask::]), self.T2T[mask::], intercept=True)  
583
+            rfit =  r"$\tilde{T}_2$= %4d [ms]"%(1e3*rt20)
584
+
585
+        else:
586
+            if component == "imag":
587
+                [b0,rt20] =  regressCurve(np.imag(self.T2D[mask::]), self.T2T[mask::], intercept=False) #, intercept=True) # 
588
+            if component == "real":
589
+                [b0,rt20] =  regressCurve(np.real(self.T2D[mask::]), self.T2T[mask::], intercept=False) #, intercept=True) # 
590
+            rfit =  r"$\tilde{T}_2$= %4d [ms]"%(1e3*rt20)
591
+        
592
+
593
+        extrapolate = False #False #True #False 
594
+        if extrapolate:
595
+            Textr = 3
596
+            nede = np.log10(Textr/self.T2T[-1]) #np.log10(self.T2T[-1]) -  np.log10(self.T2T[-1])
597
+            tex = np.logspace( np.log10(self.T2T[-1]), np.log10(Textr), (int)(self.gpd*nede)+1, base=10, endpoint=True) 
598
+            dex = 1j*  (a0 + b0*np.exp(-np.array(tex)/rt20))
599
+            self.T2T = np.concatenate( (self.T2T, tex) )            
600
+            self.T2D = np.concatenate( (self.T2D, dex) )            
601
+            self.sigma = np.concatenate( (self.sigma, self.sigma[-1]*np.ones(len(dex)) ) )            
602
+            
603
+        #env = b0*np.exp(-np.array(self.T2T)/rt20)
604
+        #self.MonoRes = { 'env':env, 'rfit':rfit, 'b0':b0, 'rt20' :rt20 } #[a0,b0,rt20] }
605
+        env = a0 + b0*np.exp(-np.array(self.T2T)/rt20)
606
+        self.MonoRes = { 'env':env, 'rfit':rfit, 'a0':a0, 'b0':b0, 'rt20' :rt20, 't':self.T2T } #[a0,b0,rt20] }
607
+
608
+
609
+        self.emit(SIGNAL("plotMONO()"))   
610
+        self.emit(SIGNAL("enableDSP()"))   
611
+        self.emit(SIGNAL("enableINV()"))   
612
+        self.emit(SIGNAL("doneStatus()"))  
613
+
614
+    def BiFit(self, mask=2, intercept="False"):
615
+        component='imag'
616
+        #print "MonoRes", component, mask, intercept
617
+        a0 = 0
618
+        if intercept=="True":
619
+            if component == "imag":
620
+                [a0,b0,rt20,bb0,rrt20] =  regressCurve2(np.imag(self.T2D[mask::]), self.T2T[mask::], sigma2=self.sigma[mask::], intercept=True)  
621
+            if component == "real":
622
+                [a0,b0,rt20,bb0,rrt20] =  regressCurve2(np.real(self.T2D[mask::]), self.T2T[mask::], sigma2=self.sigma[mask::], intercept=True)  
623
+            rfit =  r"$\tilde{T}_2$= %4d [ms]"%(1e3*rt20)
624
+
625
+        else:
626
+            if component == "imag":
627
+                [b0,rt20,bb0,rrt20] =  regressCurve2(np.imag(self.T2D[mask::]), self.T2T[mask::], sigma2=self.sigma[mask::], intercept=False) #, intercept=True) # 
628
+            if component == "real":
629
+                [b0,rt20,bb0,rrt20] =  regressCurve2(np.real(self.T2D[mask::]), self.T2T[mask::], sigma2=self.sigma[mask::], intercept=False) #, intercept=True) # 
630
+            rfit =  r"$\tilde{T}_2$= %4d [ms]"%(1e3*rt20)
631
+        
632
+
633
+        extrapolate = True # True #True 
634
+        if extrapolate:
635
+            Textr = 3
636
+            nede = np.log10(Textr/self.T2T[-1]) #np.log10(self.T2T[-1]) -  np.log10(self.T2T[-1])
637
+            tex = np.logspace( np.log10(self.T2T[-1]), np.log10(Textr), (int)(self.gpd*nede)+1, base=10, endpoint=True) 
638
+            dex = 1j*  (a0 + b0*np.exp(-np.array(tex)/rt20) + bb0*np.exp(-np.array(tex)/rrt20) )
639
+            self.T2T = np.concatenate( (self.T2T, tex) )            
640
+            self.T2D = np.concatenate( (self.T2D, dex) )            
641
+            self.sigma = np.concatenate( (self.sigma, self.sigma[-1]*np.ones(len(dex)) ) )            
642
+            
643
+        #env = b0*np.exp(-np.array(self.T2T)/rt20)
644
+        #self.MonoRes = { 'env':env, 'rfit':rfit, 'b0':b0, 'rt20' :rt20 } #[a0,b0,rt20] }
645
+        env = a0 + b0*np.exp(-np.array(self.T2T)/rt20) + bb0*np.exp(-np.array(self.T2T)/rrt20)
646
+
647
+        #print ("bi fits", a0, b0, rt20, bb0, rrt20)
648
+
649
+        self.BiRes = { 'env':env, 'rfit':rfit, 'a0':a0, 'b0':b0, 'rt20' :rt20, 'bb0':bb0, 'rrt20':rrt20, 't':self.T2T } #[a0,b0,rt20] }
650
+
651
+        self.emit(SIGNAL("plotBI()"))   
652
+        self.emit(SIGNAL("enableDSP()"))   
653
+        self.emit(SIGNAL("enableINV()"))   
654
+        self.emit(SIGNAL("doneStatus()")) 
655
+        
656
+ 
657
+    def DistFit(self, dc=0, mask=0, nT2=30, lowT2=.005, hiT2=3.25, spacing="Log_10", Smooth="Smallest", betaScale=1):
658
+        
659
+        Time = pwcTime()
660
+        Time.setT2(lowT2, hiT2, nT2, spacing) 
661
+        Time.setSampling( self.T2T[mask:] ) 
662
+        Time.generateGenv()
663
+
664
+        #self.burst = False
665
+        #print ("MRProc.py ~~ DistFit self.burst override for publication ", self.burst)
666
+        if self.burst:
667
+            # fit burst data first
668
+            Timeb = pwcTime()
669
+            #Timeb.setT2(lowT2, hiT2, nT2, spacing) 
670
+            Timeb.T2Bins = np.copy(Time.T2Bins)
671
+            #if len(Timeb.T2Bins) > 20:
672
+            #    Timeb.T2Bins = Timeb.T2Bins[0:20] # Subset
673
+            Timeb.setSampling( self.T2Tb ) 
674
+            Timeb.generateGenv()
675
+
676
+            # invert burst data, always use smooth to encourage fast time inversion
677
+            modelb  = logBarrier(Timeb.Genv, np.imag(self.T2Db)-dc, Timeb.T2Bins, MAXITER=500, sigma=betaScale*self.sigmaBurst, alpha=1e10, smooth=Smooth) 
678
+            modelb2 = np.zeros(nT2)
679
+            modelb2[0:len(modelb)] += modelb 
680
+            #model = modelb2 # TODO remove 
681
+ 
682
+            # invert regular data independently, sum inversions 
683
+            #model  = logBarrier(Time.Genv, np.imag(self.T2D[mask:])-dc, MAXITER=500, x_0=modelb, sigma=betaScale*self.sigma[mask:], alpha=1e10, smooth=Smooth)
684
+            #model += modelb 
685
+            
686
+            # Pass burt result as reference model
687
+            model  = logBarrier(Time.Genv, np.imag(self.T2D[mask:])-dc, Time.T2Bins, MAXITER=500, xr=modelb2, sigma=betaScale*self.sigma[mask:], alpha=1e10, smooth=Smooth)
688
+
689
+        else:
690
+            model  = logBarrier(Time.Genv, np.imag(self.T2D[mask:])-dc, Time.T2Bins, MAXITER=500, sigma=betaScale*self.sigma[mask:], alpha=1e10, smooth=Smooth) #, smooth=True) #
691
+
692
+        # forward model
693
+        env = np.dot(Time.Genv, model) 
694
+        # NOTE this is not thread safe or even thread aware. Need to use MPI style message passing 
695
+        self.DistRes = { 'env':env, 'mod':model, 'Time':Time } #[a0,b0,rt20] } no dice
696
+    
697
+    def DistFitMP(self, q, tag, dc=0, mask=0, nT2=30, lowT2=.005, hiT2=3.25, spacing="Log_10", Smooth="Smallest", betaScale=1):
698
+        """ Multi-processor version of DistFit using Queue. Note that Queue doesn't 
699
+            tolerate large messages. So instead we write them to file, using pickle! 
700
+            It's semi-absurd. But seems to function fast enough. 
701
+        """
702
+        import pickle 
703
+        self.DistFit(dc, mask, nT2, lowT2, hiT2, spacing, Smooth, betaScale)
704
+        self.DistRes['tag'] = tag
705
+        pickle.dump( self.DistRes, open( str(tag)+".p", "wb" ) )
706
+        q.put( tag )
707
+    
708
+
709
+if __name__ == "__main__":
710
+
711
+    fileName, fileExtension = os.path.splitext(sys.argv[1])
712
+    if fileExtension == ".ors":
713
+
714
+        ORS = MRProc()
715
+        ORS.loadORSFile(sys.argv[1])
716
+        ORS.WindowAndStack()
717
+        ORS.T2EnvelopeDetect()
718
+        
719
+        #ORS.ResampleEnvelopeData(200, Mask=2, GatesPD=20)  # resample size 
720
+        
721
+        #print (len(ORS.T2D))
722
+        #exit()
723
+        #mono, rfit, [a0,b0,mt2] = ORS.MonoFit()
724
+        mono, rfit, [b0,mt2] = ORS.MonoFit()
725
+        #ca, caEnv, CAS = ORS.CorrectedAmplitude()
726
+        mymask = 2
727
+        env, mod, Time = ORS.DistFit(0, mymask) #a0)
728
+
729
+        # Subtract DC term before 2D fit? Kind of kludgy but I don't see a way around it right now. 
730
+        plt.figure()
731
+        #plt.plot(ORS.T2T, np.imag(ca), label='imag ca')
732
+        #plt.plot(ORS.T2T, np.real(ca), label='real ca')
733
+        #if ORS.NS < 2:
734
+        #    plt.plot(ORS.T2T, np.imag(ORS.T2D), label='imag')
735
+        #    plt.plot(ORS.T2T, np.real(ORS.T2D), label='real')
736
+        #else:
737
+        plt.errorbar(ORS.T2T, np.imag(ORS.T2D), fmt='o', yerr=ORS.sigma, label="data imag")
738
+        plt.errorbar(ORS.T2T, np.real(ORS.T2D), fmt='o', yerr=np.std(ORS.T2D.real), label="data real")
739
+        if ORS.NS > 2:
740
+            plt.errorbar(ORS.T2T, np.imag(ORS.T2N), fmt='o', yerr=ORS.sigma, label="noise imag")
741
+        
742
+        plt.plot(ORS.T2T, mono, color='cyan', linewidth=3, label=rfit)
743
+        #plt.plot(ORS.T2T, caEnv, label="car")
744
+        plt.plot(ORS.T2T[mymask::], env, color='magenta', linewidth=3, label="dist")
745
+        #plt.plot(ORS.T2T, a0+env)
746
+        plt.legend()
747
+        plt.savefig(sys.argv[1].strip(".ors")+"_caenv.pdf", facecolor='white', edgecolor='white', dpi=200)
748
+
749
+
750
+        # Report RMS error of two results 
751
+        print("RMS error mono-exponential ", np.linalg.norm(np.imag(ORS.T2D) - mono) )
752
+        print("RMS error dist-fit         ", np.linalg.norm(np.imag(ORS.T2D[mymask::]) - env) )
753
+        print("RMS error real  channel    ", np.linalg.norm(np.real(ORS.T2D) ) )
754
+        if ORS.NS > 2:
755
+            print("RMS error noise channel    ", np.linalg.norm(np.imag(ORS.T2N) ) )
756
+
757
+        plt.figure()
758
+        plt.plot( ORS.T2T[mymask::],  np.imag(ORS.T2D[mymask::]) - env )
759
+        plt.title("residuals")
760
+
761
+        #plt.show()
762
+        #exit()
763
+
764
+        plt.figure() 
765
+        plt.plot(Time.T2Bins, mod, color='purple', linewidth=2, label="recovered")
766
+        #plt.plot(Time.T2Bins, guess, color='red', linewidth=2, label="guess")
767
+
768
+        axmine = plt.gca()
769
+        axmine.set_xlabel(r"$T_2$ [s]", color='black')
770
+        axmine.set_ylabel(r"$A_0$ [rku]", color='black')
771
+        axmine2 = plt.twinx()
772
+        axmine2.set_ylabel(r"$A_0$ [rku]", color='black')
773
+
774
+        theta = np.sum(mod)
775
+        LogMeanT2 = np.exp(np.sum( mod * np.log(Time.T2Bins) ) / theta ) 
776
+
777
+        #axmine2.plot(mt2, a0+b0, 'o', markersize=8, label="mono")
778
+        axmine2.plot(mt2, b0, 'o', markersize=8, label="mono")
779
+        #axmine2.plot(Time.T2Bins, guess, '-',color=colour[ic], linewidth=2, label="total")
780
+        axmine2.plot(LogMeanT2, theta, 's', markersize=8, label="$T_{2ML}$")
781
+        axmine2.set_ylim([0, axmine2.get_ylim()[1]])
782
+        leg = plt.legend()
783
+
784
+        plt.savefig(sys.argv[1].strip(".ors")+"_2DT2.pdf", facecolor='white', edgecolor='white', dpi=200)
785
+
786
+   
787
+        plt.figure(23)
788
+        stats.probplot( np.imag(ORS.T2D) - mono , dist="norm", plot=plt) #, label="mono resid")
789
+        stats.probplot( np.imag(ORS.T2D[mymask::]) - env , dist="norm", plot=plt) #, label="dist resid")
790
+        if ORS.NS > 2:
791
+            stats.probplot( np.imag(ORS.T2N), dist="norm", plot=plt) #, label="noise" )
792
+
793
+        plt.savefig(sys.argv[1].strip(".ors")+"_qq.pdf")
794
+ 
795
+        plt.show()
796
+        exit()
797
+  

+ 712
- 0
decay.py View File

@@ -0,0 +1,712 @@
1
+import numpy, array #,rpy2
2
+from matplotlib import pyplot as plt
3
+import numpy as np
4
+from scipy.optimize import least_squares
5
+from rpy2.robjects.packages import importr
6
+
7
+import rpy2.robjects as robjects
8
+import rpy2.robjects.numpy2ri
9
+
10
+#import notch
11
+from numpy.fft import fft, fftfreq
12
+
13
+# We know/can calculate frequency peak, use this to guess where picks will be.
14
+# maybe have a sliding window that reports peak values.
15
+def peakPicker(data, omega, dt):
16
+
17
+    # compute window based on omega and dt
18
+    # make sure you are not aliased, grab every other peak
19
+    window = (2*numpy.pi) / (omega*dt)
20
+
21
+    data = numpy.array(data) 
22
+    peaks = []
23
+    troughs = []
24
+    times = []
25
+    times2 = []
26
+    indices = []
27
+    ws = 0
28
+    we = window
29
+    ii = 0
30
+    for i in range((int)(len(data)/window)):
31
+        
32
+        # initially was just returning this I think avg is better
33
+        #times.append( (ws + numpy.abs(data[ws:we]).argmax()) * dt )
34
+    
35
+        peaks.append(numpy.max(data[ws:we]))
36
+        times.append( (ws + data[ws:we].argmax()) * dt )
37
+        indices.append( ii + data[ws:we].argmax() )        
38
+
39
+        troughs.append(numpy.min(data[ws:we]))
40
+        times2.append( (ws + (data[ws:we]).argmin()) * dt )
41
+        indices.append( ii + data[ws:we].argmin() )        
42
+
43
+        ws += window
44
+        we += window
45
+        ii += (int)(we-ws)
46
+    
47
+    #return numpy.array(peaks), numpy.array(times)
48
+    
49
+    # Averaging peaks does a good job of removing bias in noise
50
+    return (numpy.array(peaks)-numpy.array(troughs))/2., \
51
+        (numpy.array(times)+numpy.array(times2))/2., \
52
+        indices           
53
+
54
+
55
+#################################################
56
+# Regress for T2 using rpy2 interface
57
+def regressCurve(peaks,times,sigma2=1,intercept=True):
58
+
59
+    # TODO, if regression fails, it might be because there is no exponential
60
+    # term, maybe do a second regression then on a linear model. 
61
+    b1  = 0                  # Bias
62
+    b2  = 0                  # Linear 
63
+    rT2 = 0.3                # T2 regressed
64
+    r   = robjects.r         
65
+
66
+    # Variable shared between R and Python
67
+    robjects.globalenv['b1'] = b1
68
+    robjects.globalenv['b2'] = b2
69
+    robjects.globalenv['rT2'] = rT2
70
+    robjects.globalenv['sigma2'] = sigma2
71
+    value = robjects.FloatVector(peaks)
72
+    times = robjects.FloatVector(numpy.array(times))
73
+    
74
+#    my_weights = robjects.RVector(value/sigma2)
75
+#    robjects.globalenv['my_weigts'] = my_weights
76
+
77
+#    if sigma2 != 0:
78
+#        print "weighting"
79
+#        tw = numpy.array(peaks)/sigma2 
80
+#        my_weights = robjects.RVector( tw/numpy.max(tw) )
81
+#    else:
82
+#        my_weights = robjects.RVector(numpy.ones(len(peaks))) 
83
+
84
+#    robjects.globalenv['my_weights'] = my_weights
85
+    
86
+    if (intercept):
87
+        my_list = robjects.r('list(b1=50, b2=1e2, rT2=0.03)')
88
+        my_lower = robjects.r('list(b1=0, b2=0, rT2=.005)')
89
+        my_upper = robjects.r('list(b1=20000, b2=2000, rT2=.700)')
90
+    else:
91
+        my_list = robjects.r('list(b2=1e2, rT2=0.3)')
92
+        my_lower = robjects.r('list(b2=0, rT2=.005)')
93
+        my_upper = robjects.r('list(b2=2000, rT2=.700)')
94
+
95
+    my_cont = robjects.r('nls.control(maxiter=1000, warnOnly=TRUE, printEval=FALSE)')
96
+
97
+    
98
+    if (intercept):
99
+        #fmla = robjects.RFormula('value ~ b1 + exp(-times/rT2)')
100
+        fmla = robjects.Formula('value ~ b1 + b2*exp(-times/rT2)')
101
+        #fmla = robjects.RFormula('value ~ b1 + b2*times + exp(-times/rT2)')
102
+    else:
103
+        fmla = robjects.Formula('value ~ b2*exp(-times/rT2)')
104
+
105
+    env = fmla.getenvironment()
106
+    env['value'] = value
107
+    env['times'] = times
108
+    
109
+    # ugly, but I get errors with everything else I've tried
110
+    my_weights = robjects.r('rep(1,length(value))')
111
+    for ii in range(len(my_weights)):
112
+        my_weights[ii] *= peaks[ii]/sigma2
113
+    Error = False
114
+    #fit = robjects.r.nls(fmla,start=my_list,control=my_cont,weights=my_weights)
115
+    if (sigma2 != 1):
116
+        print("SIGMA 2")
117
+        #fit = robjects.r.tryCatch(robjects.r.suppressWarnings(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port", \
118
+        #                     weights=my_weights)), 'silent=TRUE')
119
+        fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list,control=my_cont))#, \
120
+                            # weights=my_weights))
121
+    else:
122
+        try:
123
+            fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port"))#,lower=my_lower,upper=my_upper))
124
+        except:
125
+            print("regression issue pass")
126
+            Error = True
127
+    # If failure fall back on zero regression values   
128
+    if not Error:
129
+        #Error = fit[3][0]
130
+        report =  r.summary(fit)
131
+    b1 = 0
132
+    b2 = 0 
133
+    rT2 = 1
134
+    if (intercept):
135
+        if not Error:
136
+            b1  =  r['$'](report,'par')[0]
137
+            b2  =  r['$'](report,'par')[1]
138
+            rT2 =  r['$'](report,'par')[2]
139
+            #print  report
140
+            #print  r['$'](report,'convergence')
141
+            #print  r['convergence'] #(report,'convergence')
142
+            #print  r['$'](report,'par')[13]
143
+            #print  r['$'](report,'par')[14]
144
+        else:
145
+            print("ERROR DETECTED, regressed values set to default")
146
+            b1 = 1e1
147
+            b2 = 1e-2
148
+            rT2 = 1e-2
149
+            #print r['$'](report,'par')[0]
150
+            #print r['$'](report,'par')[1]
151
+            #print r['$'](report,'par')[2]
152
+        return [b1,b2,rT2] 
153
+    else:
154
+        if not Error:
155
+            rT2 =  r['$'](report,'par')[1]
156
+            b2  =  r['$'](report,'par')[0]
157
+        else:
158
+            print("ERROR DETECTED, regressed values set to default")
159
+        return [b2, rT2] 
160
+
161
+#################################################
162
+# Regress for T2 using rpy2 interface
163
+def regressCurve2(peaks,times,sigma2=[None],intercept=True):
164
+
165
+    if sigma2[0] != None:
166
+        my_weights = robjects.FloatVector( sigma2 )
167
+
168
+    # TODO, if regression fails, it might be because there is no exponential
169
+    # term, maybe do a second regression then on a linear model. 
170
+    b1  = 0                  # Bias
171
+    b2  = 0                  # Linear 
172
+    bb2  = 0                 # Linear 
173
+    rT2 = 0.3                # T2 regressed
174
+    rrT2 = 1.3               # T2 regressed
175
+    r   = robjects.r         
176
+
177
+    # Variable shared between R and Python
178
+    robjects.globalenv['b1'] = b1
179
+    robjects.globalenv['b2'] = b2
180
+    robjects.globalenv['rT2'] = rT2
181
+    
182
+    robjects.globalenv['bb2'] = b2
183
+    robjects.globalenv['rrT2'] = rT2
184
+    
185
+    #robjects.globalenv['sigma2'] = sigma2
186
+    value = robjects.FloatVector(peaks)
187
+    times = robjects.FloatVector(numpy.array(times))
188
+    
189
+    
190
+    if (intercept):
191
+        my_list = robjects.r('list(b1=.50, b2=1e2, rT2=0.03, bb2=1e1, rrT2=1.3)')
192
+        my_lower = robjects.r('list(b1=0, b2=0, rT2=.005, bb2=0, rrT2=.005 )')
193
+        my_upper = robjects.r('list(b1=2000, b2=2000, rT2=.700, bb2=2000, rrT2=1.3 )')
194
+    else:
195
+        my_list  = robjects.r('list(b2=.5, rT2=0.3,  bb2=.5, rrT2=1.3)')
196
+        my_lower = robjects.r('list(b2=0,  rT2=.005, bb2=0,  rrT2=.005)')
197
+        my_upper = robjects.r('list(b2=1,  rT2=2.6,    bb2=1,  rrT2=2.6)')
198
+
199
+    my_cont = robjects.r('nls.control(maxiter=1000, warnOnly=TRUE, printEval=FALSE)')
200
+
201
+    
202
+    if (intercept):
203
+        #fmla = robjects.RFormula('value ~ b1 + exp(-times/rT2)')
204
+        fmla = robjects.Formula('value ~ b1 + b2*exp(-times/rT2) + bb2*exp(-times/rrT2)')
205
+        #fmla = robjects.RFormula('value ~ b1 + b2*times + exp(-times/rT2)')
206
+    else:
207
+        fmla = robjects.Formula('value ~ b2*exp(-times/rT2) + bb2*exp(-times/rrT2)')
208
+
209
+    env = fmla.getenvironment()
210
+    env['value'] = value
211
+    env['times'] = times
212
+    
213
+    # ugly, but I get errors with everything else I've tried
214
+    Error = False
215
+    #fit = robjects.r.nls(fmla,start=my_list,control=my_cont,weights=my_weights)
216
+    if (sigma2[0] != None):
217
+        #print("SIGMA 2")
218
+        #fit = robjects.r.tryCatch(robjects.r.suppressWarnings(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port", \
219
+        #                     weights=my_weights)), 'silent=TRUE')
220
+        fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm='port',weights=my_weights,lower=my_lower,upper=my_upper))#, \
221
+                            # weights=my_weights))
222
+    else:
223
+        try:
224
+            fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port"))#,lower=my_lower,upper=my_upper))
225
+        except:
226
+            print("regression issue pass")
227
+            Error = True
228
+    # If failure fall back on zero regression values   
229
+    if not Error:
230
+        #Error = fit[3][0]
231
+        report =  r.summary(fit)
232
+    b1 = 0
233
+    b2 = 0 
234
+    rT2 = 1
235
+    if (intercept):
236
+        if not Error:
237
+            b1  =  r['$'](report,'par')[0]
238
+            b2  =  r['$'](report,'par')[1]
239
+            rT2 =  r['$'](report,'par')[2]
240
+            #print  report
241
+            #print  r['$'](report,'convergence')
242
+            #print  r['convergence'] #(report,'convergence')
243
+            #print  r['$'](report,'par')[13]
244
+            #print  r['$'](report,'par')[14]
245
+        else:
246
+            print("ERROR DETECTED, regressed values set to default")
247
+            b1 = 1e1
248
+            b2 = 1e-2
249
+            rT2 = 1e-2
250
+            #print r['$'](report,'par')[0]
251
+            #print r['$'](report,'par')[1]
252
+            #print r['$'](report,'par')[2]
253
+        return [b1,b2,rT2, bb2, rrT2] 
254
+    else:
255
+        if not Error:
256
+            rT2 =  r['$'](report,'par')[1]
257
+            b2  =  r['$'](report,'par')[0]
258
+            rrT2 =  r['$'](report,'par')[3]
259
+            bb2  =  r['$'](report,'par')[2]
260
+        else:
261
+            print("ERROR DETECTED, regressed values set to default")
262
+        return [b2, rT2, bb2, rrT2] 
263
+
264
+def fun(x, t, y):
265
+    """ Cost function for regression, single exponential, no DC term 
266
+        x[0] = A0
267
+        x[1] = zeta 
268
+        x[2] = df
269
+        x[3] = T2
270
+    """
271
+    # concatenated real and imaginary parts  
272
+    pre =  np.concatenate((x[0]*np.cos(2.*np.pi*x[2]*t + x[1])*np.exp(-t/x[3]), \
273
+                       -1.*x[0]*np.sin(2.*np.pi*x[2]*t + x[1])*np.exp(-t/x[3])))  
274
+    return y-pre
275
+
276
+def fun2(x, t, y):
277
+    """ Cost function for regression, single exponential, no DC term 
278
+        x[0] = A0
279
+        x[1] = zeta 
280
+        x[2] = T2
281
+    """
282
+    # concatenated real and imaginary parts  
283
+    pre =  np.concatenate((x[0]*np.cos(x[1])*np.exp(-t/x[2]), \
284
+                       -1.*x[0]*np.sin(x[1])*np.exp(-t/x[2])))  
285
+    return y-pre
286
+
287
+
288
+def quadratureDetect2(X, Y, tt): 
289
+    """ Pure python quadrature detection using Scipy.  
290
+        X = real part of NMR signal 
291
+        Y = imaginary component of NMR signal 
292
+        tt = time 
293
+    """
294
+    # df 
295
+    x = np.array( [1., 0., 0., .2] )
296
+    res_lsq = least_squares(fun, x, args=(tt, np.concatenate((X, Y))), loss='soft_l1', f_scale=0.1,\
297
+        bounds=( [0., -np.pi, -10, .0] , [1., np.pi, 10, .6] ))
298
+    x = res_lsq.x 
299
+    return res_lsq.success, x[0], x[2], x[1], x[3]
300
+    
301
+    # no df
302
+    #x = np.array( [1., 0., 0.2] )
303
+    #res_lsq = least_squares(fun2, x, args=(tt, np.concatenate((X, Y))), loss='soft_l1', f_scale=0.1)
304
+    #x = res_lsq.x 
305
+    #return conv, E0,df,phi,T2
306
+    #return res_lsq.success, x[0], 0, x[1], x[2]
307
+
308
+def quadratureDetect(X, Y, tt, CorrectFreq=False, BiExp=False, CorrectDC=False):
309
+ 
310
+    r   = robjects.r        
311
+
312
+    if CorrectDC:
313
+        robjects.r(''' 
314
+             Xc1 <- function(E01, df, tt, phi, T2_1, DC) {
315
+	                DC + E01*cos(2*pi*df*tt + phi) * exp(-tt/T2_1)
316
+            }
317
+    
318
+            Yc1 <- function(E01, df, tt, phi, T2_1, DC) {
319
+	                DC - E01*sin(2*pi*df*tt + phi) * exp(-tt/T2_1)
320
+            } 
321
+            ''')
322
+    else:   
323
+        robjects.r(''' 
324
+             Xc1 <- function(E01, df, tt, phi, T2_1) {
325
+	                E01*cos(2*pi*df*tt + phi) * exp(-tt/T2_1)
326
+            }
327
+    
328
+            Yc1 <- function(E01, df, tt, phi, T2_1) {
329
+	                -E01*sin(2*pi*df*tt + phi) * exp(-tt/T2_1)
330
+            } 
331
+            ''')
332
+
333
+    # bi-exponential 
334
+    if CorrectDC:
335
+        robjects.r(''' 
336
+             Xc2 <- function(E01, E02, df, tt, phi, T2_1, T2_2, DC) {
337
+	               DC + E01*cos(2*pi*df*tt + phi) * exp(-tt/T2_1) + 
338
+	                DC + E02*cos(2*pi*df*tt + phi) * exp(-tt/T2_2)
339
+            }
340
+
341
+            Yc2 <- function(E01, E02, df, tt, phi, T2_1, T2_2, DC) {
342
+	                DC - E01*sin(2*pi*df*tt + phi) * exp(-tt/T2_1) + 
343
+	                DC - E02*sin(2*pi*df*tt + phi) * exp(-tt/T2_2)
344
+            } 
345
+            ''')
346
+    else:   
347
+        robjects.r(''' 
348
+             Xc2 <- function(E01, E02, df, tt, phi, T2_1, T2_2) {
349
+	               E01*cos(2*pi*df*tt + phi) * exp(-tt/T2_1) + 
350
+	               E02*cos(2*pi*df*tt + phi) * exp(-tt/T2_2)
351
+            }
352
+
353
+            Yc2 <- function(E01, E02, df, tt, phi, T2_1, T2_2) {
354
+	                -E01*sin(2*pi*df*tt + phi) * exp(-tt/T2_1) + 
355
+	                -E02*sin(2*pi*df*tt + phi) * exp(-tt/T2_2)
356
+            } 
357
+            ''')
358
+
359
+    # Make 0 vector 
360
+    Zero = robjects.FloatVector(numpy.zeros(len(X)))
361
+    
362
+    # Fitted Parameters
363
+    E01 = 0.
364
+    E02 = 0.
365
+    df = 0.
366
+    phi = 0.
367
+    T2_1 = 0.
368
+    T2_2 = 0.
369
+    DC = 0.
370
+    robjects.globalenv['DC'] = DC
371
+    robjects.globalenv['E01'] = E01
372
+    robjects.globalenv['E02'] = E02
373
+    robjects.globalenv['df'] = df
374
+    robjects.globalenv['phi'] = phi
375
+    robjects.globalenv['T2_1'] = T2_1
376
+    robjects.globalenv['T2_2'] = T2_2
377
+    XY = robjects.FloatVector(numpy.concatenate((X,Y)))
378
+    
379
+    # Arrays
380
+    tt = robjects.FloatVector(numpy.array(tt))
381
+    X = robjects.FloatVector(numpy.array(X))
382
+    Y = robjects.FloatVector(numpy.array(Y))
383
+    Zero = robjects.FloatVector(numpy.array(Zero))
384
+
385
+    
386
+
387
+    if BiExp:
388
+        if CorrectDC:
389
+            fmla = robjects.Formula('XY ~ c(Xc2( E01, E02, df, tt, phi, T2_1, T2_2, DC ), Yc2( E01, E02, df, tt, phi, T2_1, T2_2, DC ))')
390
+            if CorrectFreq:    
391
+                start = robjects.r('list(E01=.100, E02=.01,   df=0,    phi=0.    ,  T2_1=.100, T2_2=.01, DC=0.0)')
392
+                lower = robjects.r('list(E01=1e-6, E02=1e-6,  df=-50,  phi=-3.14 ,  T2_1=.001, T2_2=.001, DC=0.0)')
393
+                upper = robjects.r('list(E01=1.00, E02=1.0,   df=50,   phi=3.14  ,  T2_1=.800, T2_2=.8, DC=0.5)')
394
+            else:
395
+                start = robjects.r('list(E01=.100, E02=.01,   phi=0.9   ,  T2_1=.100, T2_2=.01,  DC=0.0)')
396
+                lower = robjects.r('list(E01=1e-6, E02=1e-6,  phi=-3.14 ,  T2_1=.001, T2_2=.001, DC=0.0)')
397
+                upper = robjects.r('list(E01=1.00, E02=1.0,   phi=3.14  ,  T2_1=.800, T2_2=.8,   DC=0.5)')
398
+        else:
399
+            fmla = robjects.Formula('XY ~ c(Xc2( E01, E02, df, tt, phi, T2_1, T2_2 ), Yc2( E01, E02, df, tt, phi, T2_1, T2_2))')
400
+            if CorrectFreq:    
401
+                start = robjects.r('list(E01=.100, E02=.01,   df=0,    phi=0.    ,  T2_1=.100, T2_2=.01)')
402
+                lower = robjects.r('list(E01=1e-6, E02=1e-6,  df=-50,  phi=-3.14 ,  T2_1=.001, T2_2=.001)')
403
+                upper = robjects.r('list(E01=1.00, E02=1.0,   df=50,   phi=3.14  ,  T2_1=.800, T2_2=.8)')
404
+            else:
405
+                start = robjects.r('list(E01=.100, E02=.01,   phi=0.9   ,  T2_1=.100, T2_2=.01)')
406
+                lower = robjects.r('list(E01=1e-6, E02=1e-6,  phi=-3.14 ,  T2_1=.001, T2_2=.001)')
407
+                upper = robjects.r('list(E01=1.00, E02=1.0,   phi=3.14  ,  T2_1=.800, T2_2=.8)')
408
+    else: 
409
+        if CorrectDC:
410
+            fmla = robjects.Formula('XY ~ c(Xc1( E01, df, tt, phi, T2_1, DC), Yc1( E01, df, tt, phi, T2_1,DC))')
411
+            if CorrectFreq:    
412
+                start = robjects.r('list(E01=.100, df=0   , phi=0.   , T2_1=.100, DC=0.0)')
413
+                lower = robjects.r('list(E01=1e-6, df=-50., phi=-3.14, T2_1=.001, DC=0.0)')
414
+                upper = robjects.r('list(E01=1.00, df=50. , phi=3.14 , T2_1=.800, DC=0.5)')
415
+            else:
416
+                start = robjects.r('list(E01=.100, phi= 0.  , T2_1=.100, DC=0.0)')
417
+                lower = robjects.r('list(E01=1e-6, phi=-3.13, T2_1=.001, DC=0.0)')
418
+                upper = robjects.r('list(E01=1.00, phi= 3.13, T2_1=.800, DC=0.5)')
419
+        else:
420
+            fmla = robjects.Formula('XY ~ c(Xc1( E01, df, tt, phi, T2_1), Yc1( E01, df, tt, phi, T2_1))')
421
+            if CorrectFreq:    
422
+                start = robjects.r('list(E01=.100, df=0     , phi=0.   ,  T2_1=.100)')
423
+                lower = robjects.r('list(E01=1e-6, df=-50. , phi=-3.14 ,  T2_1=.001)')
424
+                upper = robjects.r('list(E01=1.00, df=50.  , phi=3.14  ,  T2_1=.800)')
425
+            else:
426
+                start = robjects.r('list(E01=.100, phi= 0.  , T2_1=.100)')
427
+                lower = robjects.r('list(E01=1e-6, phi=-3.13, T2_1=.001)')
428
+                upper = robjects.r('list(E01=1.00, phi= 3.13, T2_1=.800)')
429
+
430
+    env = fmla.getenvironment()
431
+    env['Zero'] = Zero
432
+    env['X'] = X
433
+    env['Y'] = Y
434
+    env['XY'] = XY 
435
+    env['tt'] = tt
436
+
437
+    cont = robjects.r('nls.control(maxiter=10000, warnOnly=TRUE, printEval=FALSE)')
438
+    
439
+    fit = robjects.r.tryCatch(robjects.r.nls(fmla, start=start, control=cont, lower=lower, upper=upper, algorithm='port')) #, \
440
+    #fit = robjects.r.tryCatch(robjects.r.nls(fmla, start=start, control=cont)) #, \
441
+    report =  r.summary(fit)
442
+
443
+    conv = r['$'](fit,'convergence')[0]
444
+    #if conv:
445
+    #    print (report)
446
+    #    print ("conv", conv)
447
+    print ("Conv",  r['$'](fit,'convergence'))  # T2
448
+    print (report)
449
+    
450
+    if BiExp:
451
+        if CorrectFreq:    
452
+            E0   =  r['$'](report,'par')[0]   # E01
453
+            E0  +=  r['$'](report,'par')[1]   # E02
454
+            df  =  r['$'](report,'par')[2]   # offset
455
+            phi =  r['$'](report,'par')[3]   # phase 
456
+            T2  =  r['$'](report,'par')[4]   # T2
457
+        else:
458
+            E0   =  r['$'](report,'par')[0]   # E01
459
+            E0  +=  r['$'](report,'par')[1]   # E02
460
+            phi =  r['$'](report,'par')[2]   # phase 
461
+            T2  =  r['$'](report,'par')[3]   # T2
462
+    else:
463
+        if CorrectFreq:    
464
+            E0   =  r['$'](report,'par')[0]   # E01
465
+            df  =  r['$'](report,'par')[1]   # offset
466
+            phi =  r['$'](report,'par')[2]   # phase 
467
+            T2  =  r['$'](report,'par')[3]   # T2
468
+        else:
469
+            E0   =  r['$'](report,'par')[0]   # E01
470
+            phi =  r['$'](report,'par')[1]   # phase 
471
+            T2  =  r['$'](report,'par')[2]   # T2
472
+    #phi = 0.907655876627
473
+    #phi = 0
474
+    #print ("df", df)# = 0
475
+    return conv, E0,df,phi,T2
476
+    
477
+
478
+#################################################
479
+# Regress for T2 using rpy2 interface
480
+def regressSpec(w, wL, X): #,sigma2=1,intercept=True):
481
+
482
+    # compute s
483
+    s = -1j*w
484
+
485
+    # TODO, if regression fails, it might be because there is no exponential
486
+    # term, maybe do a second regression then on a linear model. 
487
+    a   = 0                  # Linear 
488
+    rT2 = 0.1                # T2 regressed
489
+    r   = robjects.r         
490
+
491
+    # Variable shared between R and Python
492
+    robjects.globalenv['a'] = a
493
+    robjects.globalenv['rT2'] = rT2
494
+    robjects.globalenv['wL'] = wL
495
+    robjects.globalenv['nb'] = 0
496
+
497
+    s = robjects.ComplexVector(numpy.array(s))
498
+    XX = robjects.ComplexVector(X)
499
+    Xr = robjects.FloatVector(numpy.real(X))
500
+    Xi = robjects.FloatVector(numpy.imag(X))
501
+    Xa = robjects.FloatVector(numpy.abs(X))
502
+    Xri = robjects.FloatVector(numpy.concatenate((Xr,Xi)))
503
+    
504
+    #my_lower = robjects.r('list(a=.001, rT2=.001, nb=.0001)')
505
+    my_lower = robjects.r('list(a=.001, rT2=.001)')
506
+    #my_upper = robjects.r('list(a=1.5, rT2=.300, nb =100.)')
507
+    my_upper = robjects.r('list(a=1.5, rT2=.300)')
508
+     
509
+    #my_list = robjects.r('list(a=.2, rT2=0.03, nb=.1)')
510
+    my_list = robjects.r('list(a=.2, rT2=0.03)')
511
+    my_cont = robjects.r('nls.control(maxiter=5000, warnOnly=TRUE, printEval=FALSE)')
512
+    
513
+    #fmla = robjects.Formula('Xri ~ c(a*Re((wL) / (wL^2+(s+1/rT2)^2 )), a*Im((wL)/(wL^2 + (s+1/rT2)^2 )))') # envelope
514
+    ##fmla = robjects.Formula('Xri ~ c(a*Re((wL) / (wL^2+(s+1/rT2)^2 )), a*Im((wL)/(wL^2 + (s+1/rT2)^2 )))') # envelope
515
+    #fmla = robjects.Formula('XX ~ a*(wL) / (wL^2 + (s+1/rT2)^2 )') # complex
516
+    #fmla = robjects.Formula('Xa ~ abs(a*(wL) / (wL^2 + (s+1/rT2)^2 )) + nb') # complex
517
+    fmla = robjects.Formula('Xa ~ abs(a*(wL) / (wL^2 + (s+1/rT2)^2 ))') # complex
518
+ 
519
+    env = fmla.getenvironment()
520
+    env['s'] = s
521
+    env['Xr'] = Xr
522
+    env['Xa'] = Xa
523
+    env['Xi'] = Xi
524
+    env['Xri'] = Xri
525
+    env['XX'] = XX
526
+     
527
+    #fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list, control=my_cont)) #, lower=my_lower, algorithm='port')) #, \
528
+    fit = robjects.r.tryCatch(robjects.r.nls(fmla, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
529
+    report =  r.summary(fit)
530
+    #print report 
531
+    #print  r.warnings()
532
+ 
533
+    a  =  r['$'](report,'par')[0]
534
+    rT2 =  r['$'](report,'par')[1]
535
+    nb =  r['$'](report,'par')[2]
536
+    
537
+    return a, rT2, nb
538
+
539
+#################################################
540
+# Regress for T2 using rpy2 interface
541
+def regressSpecComplex(w, wL, X): #,sigma2=1,intercept=True):
542
+
543
+    # compute s
544
+    s = -1j*w
545
+
546
+    # TODO, if regression fails, it might be because there is no exponential
547
+    # term, maybe do a second regression then on a linear model. 
548
+    a   = 1                  # Linear 
549
+    rT2 = 0.1                # T2 regressed
550
+    r   = robjects.r         
551
+    phi2 = 0                 # phase
552
+    wL2 = wL
553
+
554
+    # Variable shared between R and Python
555
+    robjects.globalenv['a'] = a
556
+    robjects.globalenv['rT2'] = rT2
557
+    robjects.globalenv['wL'] = wL
558
+    robjects.globalenv['wL2'] = 0
559
+    robjects.globalenv['nb'] = 0
560
+    robjects.globalenv['phi2'] = phi2
561
+
562
+    s = robjects.ComplexVector(numpy.array(s))
563
+    XX = robjects.ComplexVector(X)
564
+    Xr = robjects.FloatVector(numpy.real(X))
565
+    Xi = robjects.FloatVector(numpy.imag(X))
566
+    Xa = robjects.FloatVector(numpy.abs(X))
567
+    Xri = robjects.FloatVector(numpy.concatenate((X.real,X.imag)))
568
+
569
+    robjects.r(''' 
570
+        source('kernel.r')
571
+    ''')   
572
+    #Kw = robjects.globalenv['Kwri']
573
+     
574
+    #print (numpy.shape(X))
575
+    
576
+    #my_lower = robjects.r('list(a=.001, rT2=.001, nb=.0001)')
577
+    #my_lower = robjects.r('list(a=.001, rT2=.001)') # Working
578
+    my_lower = robjects.r('list(a=.001, rT2=.001, phi2=-3.14, wL2=wL-5)')
579
+    #my_upper = robjects.r('list(a=1.5, rT2=.300, nb =100.)')
580
+    my_upper = robjects.r('list(a=3.5, rT2=.300, phi2=3.14, wL2=wL+5)')
581
+     
582
+    #my_list = robjects.r('list(a=.2, rT2=0.03, nb=.1)')
583
+    my_list = robjects.r('list(a=.2, rT2=0.03, phi2=0, wL2=wL)')
584
+    my_cont = robjects.r('nls.control(maxiter=5000, warnOnly=TRUE, printEval=FALSE)')
585
+    
586
+    #fmla = robjects.Formula('Xri ~ c(a*Re((wL) / (wL^2+(s+1/rT2)^2 )), a*Im((wL)/(wL^2 + (s+1/rT2)^2 )))') # envelope
587
+    #fmla = robjects.Formula('Xi   ~   Im(a*(sin(phi2)*s + ((1/rT2)*sin(phi2)) + wL*cos(phi2)) / (wL^2+(s+1/rT2)^2 ))') # envelope
588
+    #fmla = robjects.Formula('Xri ~ c(Re(a*(sin(phi2)*s + ((1/rT2)*sin(phi2)) + wL*cos(phi2)) / (wL^2+(s+1/rT2)^2 )), Im(a*(sin(phi2)*s + ((1/rT2)*sin(phi2)) + wL*cos(phi2)) / (wL^2+(s+1/rT2)^2 )))') # envelope
589
+    
590
+    #fmlar = robjects.Formula('Xr ~ (Kwr(a, phi2, s, rT2, wL)) ') # envelope
591
+    #fmlai = robjects.Formula('Xi ~ (Kwi(a, phi2, s, rT2, wL)) ') # envelope
592
+    fmla = robjects.Formula('Xri ~ c(Kwr(a, phi2, s, rT2, wL2), Kwi(a, phi2, s, rT2, wL2) ) ') # envelope
593
+    #fmla = robjects.Formula('Xri ~ (Kwri(a, phi2, s, rT2, wL)) ') # envelope
594
+    
595
+    #fmla = robjects.Formula('Xa ~ (abs(a*(sin(phi2)*s + ((1/rT2)*sin(phi2)) + wL*cos(phi2)) / (wL^2+(s+1/rT2)^2 )))') # envelope
596
+    #fmla = robjects.Formula('XX ~ a*(wL) / (wL^2 + (s+1/rT2)^2 )') # complex
597
+    #fmla = robjects.Formula('Xa ~ abs(a*(wL) / (wL^2 + (s+1/rT2)^2 )) + nb') # complex
598
+    
599
+    #fmla = robjects.Formula('Xri ~ c(a*Re((wL) / (wL^2+(s+1/rT2)^2 )), a*Im((wL)/(wL^2 + (s+1/rT2)^2 )))') # envelope
600
+    
601
+    #        self.Gw[iw, iT2] = ((np.sin(phi2) *  (alpha + 1j*self.w[iw]) + self.wL*np.cos(phi2)) / \
602
+    #                               (self.wL**2 + (alpha+1.j*self.w[iw])**2 ))
603
+    #        self.Gw[iw, iT2] = ds * self.sc*((np.sin(phi2)*( alpha + 1j*self.w[iw]) + self.wL*np.cos(phi2)) / \
604
+    #                               (self.wL**2 + (alpha+1.j*self.w[iw])**2 ))
605
+    
606
+    # Works Amplitude Only!
607
+    #fmla = robjects.Formula('Xa ~ abs(a*(wL) / (wL^2 + (s+1/rT2)^2 ))') # complex
608
+ 
609
+    env = fmla.getenvironment()
610
+    env['s'] = s
611
+    env['Xr'] = Xr
612
+    env['Xa'] = Xa
613
+    env['Xi'] = Xi
614
+    env['Xri'] = Xri
615
+    env['XX'] = XX
616
+     
617
+    fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list, control=my_cont)) #, lower=my_lower, algorithm='port')) #, \
618
+    #fitr = robjects.r.tryCatch(robjects.r.nls(fmlar, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
619
+    
620
+    #env = fmlai.getenvironment()
621
+    #fiti = robjects.r.tryCatch(robjects.r.nls(fmlai, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
622
+    
623
+    #reportr =  r.summary(fitr)
624
+    #reporti =  r.summary(fiti)
625
+    report =  r.summary(fit)
626
+    #print( report )
627
+    #exit()
628
+    #print( reportr )
629
+    #print( reporti  )
630
+    #exit()
631
+    #print  r.warnings()
632
+ 
633
+    #a   =  (r['$'](reportr,'par')[0] + r['$'](reporti,'par')[0]) / 2.
634
+    #rT2 =  (r['$'](reportr,'par')[1] + r['$'](reporti,'par')[1]) / 2.
635
+    #nb  =  (r['$'](reportr,'par')[2] + r['$'](reporti,'par')[2]) / 2.
636
+    a   =  r['$'](report,'par')[0] 
637
+    rT2 =  r['$'](report,'par')[1] 
638
+    nb  =  r['$'](report,'par')[2] #phi2 
639
+
640
+    print ("Python wL2", r['$'](report,'par')[3] )   
641
+    print ("Python zeta", r['$'](report,'par')[2] )   
642
+ 
643
+    return a, rT2, nb
644
+
645
+
646
+
647
+###################################################################
648
+###################################################################
649
+###################################################################
650
+if __name__ == "__main__":
651
+
652
+    dt    = .0001
653
+    T2    = .1
654
+    omega = 2000.*2*numpy.pi
655
+    phi   = .0
656
+    T     = 8.*T2
657
+    
658
+    t = numpy.arange(0, T, dt)
659
+
660
+    # Synthetic data, simple single decaying sinusoid 
661
+    # with a single decay parameter and gaussian noise added 
662
+    data = numpy.exp(-t/T2) * numpy.sin(omega * t + phi) + numpy.random.normal(0,.05,len(t)) \
663
+                         + numpy.random.randint(-1,2,len(t))*numpy.random.exponential(.2,len(t)) 
664
+    cdata = numpy.exp(-t/T2) * numpy.sin(omega * t + phi) #+ numpy.random.normal(0,.25,len(t))
665
+    #data = numpy.random.normal(0,.25,len(t))
666
+
667
+    sigma2 = numpy.std(data[::-len(data)/4])
668
+    #sigma2 = numpy.var(data[::-len(data)/4])
669
+    print("sigma2", sigma2)    
670
+    
671
+    [peaks,times,indices] = peakPicker(data, omega, dt)
672
+    
673
+    [b1,b2,rT2] = regressCurve(peaks,times)
674
+    print("rT2 nonweighted", rT2)
675
+    
676
+    [b1,b2,rT2] = regressCurve(peaks,times,sigma2)
677
+    print("rT2 weighted", rT2)
678
+
679
+    envelope   =  numpy.exp(-t/T2)
680
+    renvelope  =  numpy.exp(-t/rT2)
681
+
682
+    #outf = file('regress.txt','w')
683
+    #for i in range(len(times)):
684
+    #    outf.write(str(times[i]) + "   " +  str(peaks[i]) + "\n")  
685
+    #outf.close()
686
+
687
+    plt.plot(t,data, 'b')
688
+    plt.plot(t,cdata, 'g', linewidth=1)
689
+    plt.plot(t,envelope, color='violet', linewidth=4)
690
+    plt.plot(t,renvelope, 'r', linewidth=4)
691
+    plt.plot(times, numpy.array(peaks), 'bo', markersize=8, alpha=.25)
692
+    plt.legend(['noisy data','clean data','real envelope','regressed env','picks'])
693
+    plt.savefig("regression.pdf")
694
+
695
+
696
+    # FFT check
697
+    fourier = fft(data)
698
+    plt.figure()
699
+    freq = fftfreq(len(data), d=dt)
700
+    plt.plot(freq, (fourier.real))
701
+    
702
+    plt.show()
703
+
704
+    # TODO do a bunch in batch mode to see if T2 estimate is better with or without 
705
+    # weighting and which model is best.
706
+
707
+    # TODO try with real data
708
+
709
+    # TODO test filters (median, FFT, notch)
710
+
711
+    # It looks like weighting is good for relatively low sigma, but for noisy data
712
+    # it hurts us. Check

+ 212
- 0
logbarrier.py View File

@@ -0,0 +1,212 @@
1
+from __future__ import division
2
+import numpy as np
3
+from scipy.sparse.linalg import iterative  as iter
4
+import pylab 
5
+import pprint 
6
+from scipy.optimize import nnls 
7
+
8
+def PhiB(mux, muy, minVal, maxVal, x):
9
+    phib = mux * np.abs( np.sum(np.log( x-minVal)) )
10
+    #phib += np.abs( np.log(1. - maxVal / np.sum(x)) )
11
+    return phib
12
+    #phib += np.log std::log(maxVal - x.segment(ib*block, block).sum());
13
+    
14
+
15
+    #template < typename  Scalar >
16
+    #Scalar PhiB2 (const Scalar& minVal, const Scalar& maxVal, const VectorXr x,
17
+    #              const int& block, const int &nblocks) {
18
+    #    Scalar phib  = std::abs((x.array() - minVal).log().sum());
19
+    #    //phib      += std::abs((maxVal - x.array()).log().sum()*muy);
20
+    #    for (int ib=0; ib<nblocks; ++ib) {
21
+    #        //HiSegments(ib) = x.segment(ib*block, block).sum();
22
+    #        phib += Scalar(block)*std::log(maxVal - x.segment(ib*block, block).sum());
23
+    #    }
24
+    #    return phib;
25
+    #}
26
+
27
+def logBarrier(A, b, T2Bins, x_0=0, xr=0, alpha=10, mu1=10, mu2=10, smooth=False, MAXITER=70, fignum=1000, sigma=1, callback=None):
28
+    """Impliments a log barrier Tikhonov solution to a linear system of equations 
29
+        Ax = b  s.t.  x_min < x < x_max. A log-barrier term is used for the constraint
30
+    """
31
+    # TODO input
32
+    minVal = 0.0
33
+    maxVal = 1e8
34
+
35
+    Wd =    (np.eye(len(b)) / (sigma))        # Wd = eye( sigma )
36
+    WdTWd = (np.eye(len(b)) / (sigma**2))     # Wd = eye( sigma )
37
+
38
+    #print ("calculating AT WdT.Wd A ") # AT WdTWd A with known data matrix 
39
+    #print (" A.shape" , np.shape(A), np.shape(b))
40
+    ATWdTWdA = np.dot(A.conj().transpose(), np.dot( WdTWd, A ))     # TODO, implicit calculation instead?
41
+    N = np.shape(A)[1]                        # number of model
42
+    M = np.shape(A)[0]                        # number of data
43
+    SIGMA = .25  #.25 #.125 # .25 #.01#3e-1
44
+    EPSILON = 1e-35 # was 35 
45
+
46
+    # reference model
47
+    if np.size(xr) == 1:
48
+        xr =  np.zeros(N)     
49
+    
50
+    # initial guess
51
+    if np.size(x_0) == 1:
52
+        x = 1e-10 + np.zeros(N)     
53
+    else:
54
+        x = 1e-10 + x_0
55
+        
56
+    # Construct model constraint base   
57
+    #print ("constructing Phim")
58
+    Phim_base = np.zeros( [N , N] ) 
59
+    a1 = 1.0     # smallest
60
+    
61
+    # calculate largest term            
62
+    D1 = 1./abs(T2Bins[1]-T2Bins[0])
63
+    D2 = 1./abs(T2Bins[2]-T2Bins[1])
64
+    #a2 = 1. #(1./(2.*D1+D2))    # smooth
65
+    
66
+    if smooth == "Both":
67
+        #print ("SMOOTH")
68
+        # Smooth model
69
+        print ("Both small and smooth model")
70
+        for ip in range(N):
71
+            D1 = 0.
72
+            D2 = 0.
73
+            DMAX = 2./(T2Bins[1]-T2Bins[0])
74
+            if ip > 0:
75
+                D1 = 1./abs(T2Bins[ip]-T2Bins[ip-1])
76
+            if ip < N-1:
77
+                D2 = 1./abs(T2Bins[ip+1]-T2Bins[ip])
78
+            if ip > 0:
79
+                Phim_base[ip,ip-1] = -(D1)               # smooth in log space
80
+            if ip == 0:
81
+                Phim_base[ip,ip  ] =  (D1+D2) + a1       # Encourage a little low model, no a1
82
+            elif ip == N-1:
83
+                Phim_base[ip,ip  ] =  (D1+D2) + a1       # Penalize long decays
84
+            else:
85
+                Phim_base[ip,ip  ] =  (D1+D2) + a1       # Smooth and small
86
+            if ip < N-1:
87
+                Phim_base[ip,ip+1] = -(D2)               # smooth in log space
88
+
89
+        #Phim_base /= np.max(Phim_base)
90
+        #Phim_base += a1*np.eye(N)
91
+
92
+    elif smooth == "Smooth":
93
+        print ("Smooth model")
94
+        for ip in range(N):
95
+            if ip > 0:
96
+                Phim_base[ip,ip-1] = -1    # smooth in log space
97
+            if ip == 0:
98
+                Phim_base[ip,ip  ] = 1.0   # Encourage a little low model
99
+            elif ip == N-1:
100
+                Phim_base[ip,ip  ] = 8.0   # Penalize long decays
101
+            else:
102
+                Phim_base[ip,ip  ] = 2.0   # Smooth and small
103
+            if ip < N-1:
104
+                Phim_base[ip,ip+1] = -1    # smooth in log space
105
+        #print(Phim_base)    
106
+
107
+    else: 
108
+        print ("SMALLEST")
109
+        # Smallest model
110
+        for ip in range(N):
111
+            Phim_base[ip,ip  ] = 1.
112
+    
113
+    Phi_m =  alpha*Phim_base
114
+    WmTWm = Phim_base # np.dot(Phim_base, Phim_base.T)            
115
+    b_pre = np.dot(A, x)
116
+
117
+    phid = np.linalg.norm( np.dot(Wd, (b-b_pre)) )**2
118
+    phim = np.linalg.norm( np.dot(Phim_base, (x-xr)) )**2
119
+
120
+    mu2 = phim
121
+    phib = PhiB(mu1, mu2, 0, 1e8, x) 
122
+    mu1 = ((phid + alpha*phim) / phib)
123
+
124
+    #print ("iteration", -1, mu1, mu2, phib, phid, phim, len(b))
125
+    for i in range(MAXITER):
126
+            
127
+        b_pre = np.dot(A, x)
128
+        phid = np.linalg.norm(np.dot(Wd, (b-b_pre)))**2
129
+        # technically phim should not be on WmTWm matrix. So no need to square result. 
130
+        phim = np.linalg.norm(np.dot(Phim_base, (x-xr)))#**2
131
+        phib = PhiB(mu1, mu2, 0, 1e8, x) 
132
+        Phi_m =  alpha*Phim_base
133
+        
134
+        mu1 = ((phid + alpha*phim) / phib) 
135
+
136
+        WmTWm = Phim_base # np.dot(Phim_base, Phim_base.T)            
137
+        #ztilde = x
138
+        #print("ztilde", ztilde)
139
+        phid_old = phid
140
+        inner = 0
141
+        First = True
142
+
143
+        while ( (phib / (phid+alpha*phim)) > EPSILON  or First==True ):
144
+
145
+            First = False
146
+            # Log barrier, keep each element above minVal
147
+            X1 = np.eye(N) * (x-minVal)**-1           
148
+            X2 = np.eye(N) * (x-minVal)**-2         
149
+            
150
+            # Log barrier, keep sum below maxVal TODO normalize by component. Don't want to push all down  
151
+            Y1 = np.eye(N) * (maxVal - np.sum(x))**-1           
152
+            Y2 = np.eye(N) * (maxVal - np.sum(x))**-2         
153
+            
154
+            AA = ATWdTWdA + mu1*X2 + mu2*Y2 + Phi_m 
155
+            M = np.eye( N ) * (1./np.diag(ATWdTWdA + mu1*X2 + mu2*Y2 + Phi_m))
156
+        
157
+            # Solve system (newton step) 
158
+            b2 = np.dot(A.transpose(), np.dot(WdTWd, b-b_pre) ) + 2.*mu1*np.diag(X1) + 2.*mu2*np.diag(Y1) - alpha*np.dot(WmTWm,(x-xr))
159
+            ztilde = iter.cg(AA, b2, M=M) # tol=1e-3*phid, maxiter=200, callback=callback) #, tol=1e-2, maxiter) #, x0=x) #, tol=ttol) #, M=M, x0=x)        
160
+            h = (ztilde[0]) 
161
+            
162
+            # Solve system (direct solution) 
163
+            #b2 = np.dot(A.conj().transpose(), np.dot(WdTWd, b)) + 2.*mu1*np.diag(X1) + 2.*mu2*np.diag(Y1) - alpha*np.dot(WmTWm,(x-xr))
164
+            #ztilde = iter.cg(AA, b2, x0=x) #, tol=1e-2) #, x0=x) #, tol=ttol) #, M=M, x0=x)        
165
+            #h = (ztilde[0].real - x) 
166
+
167
+            # step size
168
+            d = np.min( (1, 0.95 * np.min(x/np.abs(h+1e-120))) )
169
+            
170
+            ##########################################################
171
+            # Update and fix any over/under stepping
172
+            x = x+d*h 
173
+            #x = np.max( ( (minVal+1e-120)*np.ones(N),  x+d*h), 0)
174
+        
175
+            # Determine mu steps to take
176
+            s1 = mu1 * (np.dot(X2, ztilde[0].real) - 2.*np.diag(X1))
177
+            s2 = mu2 * (np.dot(Y2, ztilde[0].real) - 2.*np.diag(Y1))
178
+
179
+            # determine mu for next step
180
+            mu1 = SIGMA/N * np.abs(np.dot(s1, x))
181
+            mu2 = SIGMA/N * np.abs(np.dot(s2, x))
182
+            
183
+            b_pre = np.dot(A, x)
184
+            phid = np.linalg.norm(np.dot(Wd, (b-b_pre)))**2
185
+            phim = np.linalg.norm(np.dot(Phim_base, (x-xr)))#**2
186
+            phib = PhiB(mu1, mu2, minVal, maxVal, x)
187
+            inner += 1
188
+        
189
+        # determine alpha
190
+        scale = (len(b)/phid)
191
+        alpha *= scale  #**(1/6) 
192
+
193
+        score = np.sqrt(phid/(len(b)+1.)) # unbiased  
194
+ 
195
+        # check stopping criteria 
196
+        if score < 1: 
197
+            print ("*overshot* optimal solution found") #, alpha, score)
198
+            break
199
+        if score < 1.1: # or np.linalg.norm(x_old-x) < 1e-5 or phid > phid_old:
200
+            #print ("overshoot")
201
+            #alpha *= 10
202
+            print ("optimal solution found") #, alpha, score)
203
+            break
204
+        if i > 10 and (np.sqrt(phid_old/(len(b)+1.)) - score) < 1e-2: # 1e-2
205
+            print ("slow convergence") #, alpha, score, i, scale, score-np.sqrt(phid_old/len(b)))
206
+            break
207
+            
208
+    print ( "alpha","phid", "iter", "search", "prior" )
209
+    print ( alpha, score, i, scale, np.sqrt(phid_old/(len(b)+1)))
210
+
211
+    return x
212
+

+ 37
- 0
notch.py View File

@@ -0,0 +1,37 @@
1
+from numpy import pi, cos, zeros
2
+
3
+def notch(x, R, fn, dt, scale=True):
4
+    '''Simple Pole and Zero Notch Filter. Note that this filter 
5
+    is not zero phase, if you need a zero phase filter use 
6
+    zpnotch.
7
+    x                        # Input Sequence
8
+    fn                       # Frequency to Filter 
9
+    R                        # Pole and Zero locations
10
+    dt                       # Sampling rate
11
+    '''
12
+    wn = 2. * pi * fn * dt    
13
+    b0 = 1.
14
+    b1 = -2.*cos(wn)
15
+    b2 = 1.
16
+    a1 = -R*2.*cos(wn)
17
+    a2 = R*R
18
+
19
+    if scale == True:
20
+        sc = (1. + a1 + a2)/(b0 + b1 + b2)
21
+        b0 *= sc
22
+        b1 *= sc
23
+        b2 *= sc
24
+
25
+    xnm1 = 0.
26
+    xnm2 = 0.
27
+    ynm1 = 0.
28
+    ynm2 = 0.
29
+    y = zeros(len(x))
30
+    for i in range(len(x)):
31
+        y[i] = b0*x[i]+b1*xnm1+b2*xnm2-a1*ynm1-a2*ynm2
32
+        xnm2 = xnm1
33
+        xnm1 = x[i]
34
+        ynm2 = ynm1
35
+        ynm1 = y[i]
36
+
37
+    return y

+ 67
- 0
plotHE.py View File

@@ -0,0 +1,67 @@
1
+from plotdata import *
2
+
3
+if __name__ == "__main__":
4
+    print sys.argv
5
+    colour = ['blue','red','green']
6
+    ic = 0
7
+        
8
+    #plt.figure(2) 
9
+    #axmine = plt.gca()
10
+    #axmine2 = plt.twinx()
11
+    
12
+    for f in sys.argv[1::]:
13
+        ORS = ORSProc(f)
14
+        ORS.WindowAndStack()
15
+        ORS.T2EnvelopeDetect()
16
+        
17
+        #mono, rfit, [a0,b0,mt2] = ORS.MonoFit()
18
+        #env, mod, Time = ORS.DistFit( a0 )
19
+        #env, mod, Time = ORS.DistFit(  )
20
+
21
+        # Subtract DC term before 2D fit? Kind of kludgy but I don't see a way around it right now. 
22
+
23
+        plt.figure(1)
24
+        print ORS.T2T, ORS.T2D
25
+        plt.plot((1e-6*ORS.T2T**3), np.log(np.imag(ORS.T2D)), 'o', markersize=8)
26
+        #plt.plot(ORS.T2T, np.real(ORS.T2D))
27
+        #plt.plot(ORS.T2T, mono, '--',label=rfit, color=colour[ic])
28
+        #plt.plot(ORS.T2T, a0+env, '-+', color=colour[ic], label='dist')
29
+        #plt.plot(ORS.T2T, env, '-+',color=colour[ic], label='dist')
30
+        #plt.legend()
31
+
32
+        #guess = np.zeros( len(Time.T2Bins ) )
33
+        #total = np.zeros( len(Time.T2Bins ) )
34
+        #ib = 0
35
+        #for bins in Time.T2Bins:
36
+        #    if bins > mt2:
37
+        #        guess[ib] = a0 + b0
38
+        #        total[ib] = np.sum( mod ) # TODO find log norm and plot there instead! 
39
+        #        break
40
+        #    ib += 1
41
+     
42
+        #LogMeanT2 = numpy.exp(numpy.sum(PWC*numpy.log(T2BINS), axis=1) / theta ) 
43
+        #theta = np.sum(mod)
44
+        #LogMeanT2 = np.exp(np.sum( mod * np.log(Time.T2Bins) ) / theta ) 
45
+
46
+        #plt.figure(2) 
47
+        #axmine.plot(Time.T2Bins, mod, linewidth=2, label=f, color=colour[ic])
48
+        #axmine2.plot(Time.T2Bins, guess, '--',color=colour[ic], linewidth=2, label="guess")
49
+        #axmine2.plot(mt2, a0+b0, 'o',color=colour[ic], markersize=8, label="guess")
50
+        #axmine2.plot(Time.T2Bins, guess, '-',color=colour[ic], linewidth=2, label="total")
51
+        #axmine2.plot(LogMeanT2, theta, 's',color=colour[ic], markersize=8, label="total")
52
+        ic += 1
53
+    #axmine2.set_ylim([0, axmine2.get_ylim()[1]])
54
+    #leg = axmine2.legend(numpoints=1)
55
+    plt.xlabel(r"$\tau_e^2$ [s]", color='black', fontsize=16)
56
+    #plt.ylabel(r"$T_2$ [s]", color='black')
57
+    plt.ylabel(r"signal [rku]", color='black', fontsize=16)
58
+    #axmine.set_ylabel(r"$A_0$ [i]", color='black')
59
+    
60
+    #plt.figure(1)
61
+    #plt.savefig(sys.argv[1].strip(".ors")+"_comp1DT2.pdf", facecolor='white', edgecolor='white', dpi=200)
62
+    
63
+    #plt.figure(2)
64
+    #plt.savefig(sys.argv[1].strip(".ors")+"_comp2DT2.pdf", facecolor='white', edgecolor='white', dpi=200)
65
+    plt.savefig("hahn.pdf")
66
+    plt.show()
67
+    exit() 

+ 343
- 0
pwctimeWhite.py View File

@@ -0,0 +1,343 @@
1
+#import matplotlib
2
+#matplotlib.rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']})
3
+#matplotlib.rc('text', usetex = True)
4
+#matplotlib.rc('font', family='times')
5
+
6
+from matplotlib.colors import LogNorm
7
+from math import modf
8
+import matplotlib
9
+import numpy as np
10
+import pylab as plt
11
+from scipy.sparse.linalg import cg
12
+#from scipy.linalg.iterative import bicgstab
13
+from scipy.sparse.linalg import iterative  as iter # import bicgstab 
14
+#from iterative import bicgstab
15
+from matplotlib.ticker import FuncFormatter
16
+
17
+#from smooth import smooth
18
+from logbarrier import logBarrier
19
+from cmath import phase
20
+from decay import *
21
+
22
+class pwcTime:
23
+    """Simple class for evaluating pwc extimation in the time domain"""
24
+
25
+    def __init__(self):
26
+        self.T2Limits = np.zeros(2)
27
+        self.T2Bins   = np.zeros(1)
28
+        self.Larmor   = 0.
29
+        self.wL       = 0.
30
+        self.dt       = 1e-4
31
+        # TODO this is critical and strange in FD, maybe error in mapping
32
+        self.T        = 3. 
33
+        #self.dead     = 0.01327 # Wurks
34
+        self.dead     = 0.0  #15 #1e-3 #031415926 #.05# (np.pi*2)/(2000.)  #0.01 #13
35
+        self.phi      = 0.   # phase 
36
+        self.t        = np.arange(self.dead, self.T, self.dt)
37
+        self.nt       = len(self.t)
38
+        self.G        = np.zeros((2,2))
39
+        self.Gw       = np.zeros((2,2))
40
+
41
+    def setPhi(self, phiIn):
42
+        self.phi = phiIn
43
+
44
+    def setT2(self, low, high, number, spacing):
45
+        """ Sets the low and high limits of the T2 bins"""
46
+        # Log spaced
47
+        self.T2Limits[0] = low
48
+        self.T2Limits[1] = high
49
+        #self.T2Bins = np.arange(np.log(low), np.log(high), (np.log(high)-np.log(low))/number, endpoint=True)
50
+        #self.T2Bins = np.linspace(np.log(low), np.log(high), (np.log(high)-np.log(low))/number, endpoint=True)
51
+        #self.T2Bins = np.exp( self.T2Bins )
52
+        #print ("T2Bins", self.T2Bins)
53
+        if spacing == "Log_10":
54
+            self.T2Bins = np.logspace( np.log10(low), np.log10(high), num=number, endpoint=True, base=10 ) # base=10
55
+        elif spacing == "Log_2":
56
+            self.T2Bins = np.logspace( np.log2(low), np.log2(high), num=number, endpoint=True, base=2 ) # base=10
57
+        elif spacing == "Log_e":
58
+            self.T2Bins = np.logspace( np.log(low), np.log(high), num=number, endpoint=True, base=np.e ) # base=10
59
+        elif spacing == "Linear":
60
+            self.T2Bins = np.linspace( low, high, num=number, endpoint=True ) # base=10
61
+        else:
62
+            print("In setT2, spacing not recognized")
63
+            exit(1)
64
+        #print ("T2Bins", self.T2Bins)
65
+        #exit()
66
+
67
+
68
+
69
+    def setLarmor(self, freq):
70
+        """ Set the Larmor frequency, in Hz"""
71
+        self.Larmor = freq
72
+        self.wL = np.pi * 2. * freq
73
+
74
+    #def setSampling (self, dtin, Tin):
75
+    #    self.T = Tin
76
+    #    self.dt = dtin
77
+    #    self.t  = np.arange(self.dead, self.T, self.dt)
78
+    #    self.nt       = len(self.t)
79
+    
80
+    def setSampling (self, times):
81
+        self.T  = times[-1]
82
+        self.dt = times[1] - times[0]
83
+        self.t  = times 
84
+        self.nt = len(self.t)
85
+
86
+    def getSampling(self):
87
+        return self.dt
88
+
89
+    def getNumT2(self):
90
+        """Returns the number of T2Bins"""
91
+        return len(self.T2Bins)
92
+
93
+    def getNumTime(self):
94
+        """ Returns the number of times """
95
+        return self.nt
96
+        
97
+    def generateGenv(self):
98
+        """ Fills in the matrix G, this is for spin-spin decay only """
99
+        #nt = (int) ( (self.T-self.dead) / self.dt)
100
+        nT2 = len(self.T2Bins)
101
+        
102
+        # rows are times, cols are T2 bins
103
+        self.Genv = np.zeros((self.nt, nT2))
104
+
105
+        # could do this w/o loops but who cares for this
106
+        for iT2 in range(len(self.T2Bins)):
107
+            for it in range(len(self.t)):
108
+                self.Genv[it, iT2] = np.exp(-self.t[it]/self.T2Bins[iT2]) 
109
+    
110
+    def generateJGKernel(self, TauE, Times, D_w, G , DTau):
111
+        gamma_H = 26752. #/ (2*np.pi)     # rad G^-1 s^-1 (wL = 2 pi gamma B0) TODO be aware of 2 pi term.
112
+
113
+        #print( "TauE", TauE )
114
+        #print( "D_w", D_w )
115
+        #print( "G", G )
116
+        #print( "Times", Times )
117
+
118
+        ntt = 0
119
+        for time in TauE:
120
+            ntt += len(Times[time])
121
+
122
+        self.JG = np.zeros( (ntt, len(G)) )
123
+
124
+        #print ("JG", self.JG)               
125
+        
126
+        for gg in range(len(G)):
127
+            it = 0 
128
+            for TE in TauE:
129
+                #print("TE", TE) NOTE in Dunn, this should be \tau == TE/2
130
+                tau = TE/2. 
131
+                #T2D =  ( 3. * (gamma_H**2) * (G[gg]**2) * D_w * ((tau)**2) ) # Dunn
132
+                #T2D = (D_w/3.) * tau * gamma_H**2 * G[gg]**2 # Keating 2009 
133
+                
134
+                T2D = (D_w * (tau**DTau) * (gamma_H**2) * (G[gg]**2) ) / 3. # Keating 2009 
135
+                for t in Times[TE]:
136
+                    self.JG[it, gg] = np.exp( -t * T2D ) 
137
+                    it += 1
138
+
139
+        #plt.matshow(self.JG)
140
+        #plt.colorbar()
141
+        #plt.show()
142
+
143
+    def generateGDenv(self, TauE, Times, D, Gbins, Jg, DTau):
144
+        """ Generates a diffusion sensitivity matrix
145
+            TauE is an array of the TE echo times 
146
+            Times is a Dictionary of the Times so that Times[TauE[0]]...Times[TauE[-1]] are all 1D arrays of times
147
+            D is an array of the D times.
148
+        """
149
+        #G = 10.                  # G/cm ORS Tool 
150
+        #G =  6.                  # G/cm VC Mid field 
151
+        #G =  .6                   # G/cm VC Mid field ?
152
+
153
+        ##################################################
154
+        # Vista Clara with water 
155
+        # G = 100 Iteration 10 5.05285635664        
156
+        # G = 60  Iteration 10 4.75446648427
157
+        # G = 10  Iteration 10 1.8  
158
+        # G =  6  Iteration 10 1.1 
159
+        # G =  3  Iteration 10 0.870649020301 
160
+        #print( "TauE", TauE )
161
+        #print( "D", D )
162
+        #print( "Gbins", Gbins )
163
+        #print( "Times", Times )
164
+
165
+        #G =   .8                   # G/cm VC Mid field 
166
+        eta = 1                   # unitless
167
+                                  # TODO check units of gamma, and D. Are they both in si or cgs? 
168
+        gamma_H = 26752.          # rad G^-1 s^-1 (wL = 2 pi gamma B0) TODO be aware of 2 pi term.
169
+        #D_w = 1e-6 #2.3e-9       # diffusion of bulk water (Hurlimann 1995) diffusion limit
170
+        # 2.34e-9 #  [m^2/s]      # Spyrou.pdf   
171
+        # 2.34e-5 #  [cm^2/s]     # Spyrou.pdf   
172
+        # 
173
+        # D_w = .140 m^2/s        # Hayden --> .0000140   in cgs 
174
+
175
+        # So D *T_2 in cgs yields cm^2 -- which is the same as permeability ?? 
176
+  
177
+        # D_0
178
+        # Hurliman1995         D_0 = 2.3e-9   [m^2/s]  
179
+        # Hayden 2004          D_0 = .140     [m^2/s]
180
+        
181
+        # extrude Genv --> len(Tau_E) times more data and len(D) times more model
182
+        #self.GD = np.tile(self.Genv, (len(TauE)-1, len(D) ))
183
+        ntt = 0
184
+        nT2 = len(self.T2Bins)
185
+        #print (Times)
186
+        #print (np.concatenate(Times))
187
+        for time in TauE:
188
+            ntt += len(Times[time])
189
+
190
+        self.GD = np.zeros( (ntt , nT2*len(D)) )
191
+        #print (np.shape(self.GD))        
192
+      
193
+        """      
194
+                 [  {T2[0]D[0]}  {T2[1]D[0]} ... {T2[nt]D[nd]}  
195
+            G =  [  {T[0]TE[0]}
196
+                 [  {T[1]TE[0]}
197
+                        ...
198
+                 [  {T[0]TE[1]}
199
+                 [  {T[nt]TE[nTE]}
200
+        """ 
201
+        for d in range(len(D)):
202
+            for iT2 in range(len(self.T2Bins)):
203
+                #for it in range(len(self.t)):
204
+                #print ( "T2bins", self.T2Bins[iT2] )
205
+                it = 0 
206
+                for TE in TauE:
207
+                    tau = TE#/2.
208
+                    for t in Times[TE]:
209
+                        ig = 0
210
+                        for gg in Gbins:
211
+                            #print ( "Gradient info, gg=", gg, "J(g)", Jg[ig])
212
+                            #T2D =  (3* (gamma_H**2) * (gg**2) * D[d] * (tau**2) ) # (D[d]/12.) * ((gamma_H*G*(TE/2.))**2) # inverse
213
+                            tau = TE/2.
214
+                            #T2D =  ( 3. * (gamma_H**2) * (G[gg]**2) * D_w * ((tau)**2) ) # Dunn
215
+                            #T2D = (D[d]/3.) * tau * gamma_H**2 * gg**2 # Keating 2009 
216
+                            T2D = (D[d] * tau**DTau * gamma_H**2 * gg**2) / 3. # Keating 2009 
217
+                            self.GD[it, d*nT2+iT2] +=  Jg[ig] * np.exp(-t/self.T2Bins[iT2]) *  np.exp( -t * T2D ) 
218
+                            ig += 1
219
+                        it += 1
220
+        
221
+        # now scale this bastard
222
+        #TEs = np.ones( len(TauE) *  )    
223
+        #plt.figure() 
224
+        #plt.matshow(self.GD)
225
+        #plt.colorbar()
226
+        #plt.show()
227
+        
228
+    def plotGenv(self):
229
+        # Make plot for SEG 
230
+        def tformatter(x, pos):
231
+            return '%1.2f' %(x*self.dt)
232
+        def t2formatter(x, pos):
233
+            return '%0.3f' %(self.T2Bins[0] + x*(self.T2Bins[1]-self.T2Bins[0]))
234
+        fig = plt.figure(997, facecolor='white')
235
+        #self.G += 1. + 1e-3
236
+        plt.imshow(np.abs(self.Genv), aspect='auto', cmap='jet', norm=LogNorm(vmin=1e-4, vmax = 1.)  )
237
+        cb = plt.colorbar()
238
+        ax = plt.gca()
239
+        for t in cb.ax.get_yticklabels():
240
+            t.set_color("black")
241
+            t.set_fontsize(16) 
242
+        for t in cb.ax.get_yticklines():
243
+            t.set_color("black")
244
+        cb.ax.spines['top'].set_color('black')
245
+        cb.ax.spines['bottom'].set_color('black')
246
+        cb.ax.spines['left'].set_color('black')
247
+        cb.ax.spines['right'].set_color('black')
248
+        cb.set_label(r"amplitude", color=('black'))
249
+        formatter = FuncFormatter(tformatter)
250
+        ax.yaxis.set_major_formatter(formatter)
251
+        wformatter = FuncFormatter(t2formatter)
252
+        ax.xaxis.set_major_formatter(wformatter)
253
+        ax.set_xlabel(r"$T_2$ [s]", color = 'black')
254
+        ax.set_ylabel(r"time [s]", color = 'black')
255
+        ax.spines['top'].set_color('black')
256
+        ax.spines['bottom'].set_color('black')
257
+        ax.spines['left'].set_color('black')
258
+        ax.spines['right'].set_color('black')
259
+        for line in ax.yaxis.get_ticklines():
260
+            line.set_color('black')
261
+        for line in ax.yaxis.get_ticklabels():
262
+            line.set_color('black')
263
+            line.set_fontsize(16)
264
+        for line in ax.xaxis.get_ticklines():
265
+            line.set_color('black')
266
+        for line in ax.xaxis.get_minorticklines():
267
+            line.set_color('black')
268
+        for line in ax.xaxis.get_ticklabels():
269
+            line.set_color('black')
270
+            line.set_fontsize(16)
271
+        plt.savefig('timeenvmatrix.png', dpi=200, facecolor='white', edgecolor='white')
272
+
273
+ 
274
+if __name__ == "__main__":
275
+
276
+    Time = pwcTime()
277
+    Time.setT2(.01, .6, 60)
278
+    Time.setSampling(1e-3, 2)
279
+    Time.generateGenv()
280
+
281
+    # Generate some noisy data
282
+    model = np.zeros(Time.getNumT2())
283
+   
284
+    #model[5]  = .05
285
+    model[16]  = .1
286
+    model[17]  = .15
287
+    model[18]  = .15
288
+    model[19]  = .1
289
+    #model[10] = .05
290
+ 
291
+    model[52] = .1
292
+    model[53] = .2
293
+    model[54] = .1
294
+
295
+   
296
+    sigma = .02 * .4 
297
+    noise = np.random.normal(0, sigma, Time.getNumTime())
298
+ 
299
+    dataenv = np.dot(Time.Genv, model) + noise
300
+    cleanenv = np.dot(Time.Genv, model) #+ noise)
301
+    
302
+    # monoexponential fit
303
+    [a0,b0,rt20] =  regressCurve(dataenv, Time.t, intercept=True) #
304
+    print(a0, b0, rt20)
305
+    T2R =  a0 + b0*np.exp(-np.array(Time.t)/rt20)
306
+    guess = np.zeros( len(Time.T2Bins ) )
307
+    ib = 0
308
+    for bins in Time.T2Bins:
309
+        if bins > rt20:
310
+            guess[ib] = a0 + b0
311
+            break
312
+        ib += 1 
313
+    #exit()   
314
+
315
+    #timemodenv  = logBarrier(Time.Genv, dataenv, x_0=guess, xr=guess, MAXITER=500, sigma=sigma, alpha=1e8) #, smooth=True)
316
+    timemodenv  = logBarrier(Time.Genv, dataenv, MAXITER=500, sigma=sigma, alpha=1e10, smooth=True) #, smooth=True)
317
+    preenv = np.dot(Time.Genv, timemodenv) #+ noise)
318
+   
319
+    # Data plot 
320
+    fig = plt.figure(facecolor='white')
321
+    axmine = fig.add_axes([.125,.1,.8,.8])
322
+    axmine.plot(Time.t, dataenv, color='red', alpha=.5, label='noisy data')
323
+    axmine.plot(Time.t, cleanenv, color='blue', linewidth=4, label='true')
324
+    axmine.plot(Time.t, preenv, color='green', linewidth=2, label='multi')
325
+    axmine.plot(Time.t, T2R, color='black', linewidth=2, label='mono')
326
+    #axmine.plot(Time.t, noise, color='purple', linewidth=1, label='noise')
327
+    #axmine.set_xscale("log", nonposx='clip')
328
+    plt.savefig("data2Dfits.pdf", facecolor='white', edgecolor='white', dpi=200)
329
+    plt.legend()
330
+
331
+    # Model plot 
332
+    plt.figure(1000)
333
+    plt.plot(Time.T2Bins, model, linewidth=3, color='green', label='true')
334
+    axmine = plt.gca() 
335
+    plt.semilogx(Time.T2Bins, timemodenv, color='purple', linewidth=2, label="multi-recovered")
336
+    plt.semilogx(Time.T2Bins, guess, color='red', linewidth=2, label="mono-recovered")
337
+    leg = plt.legend()
338
+    axmine.set_xlabel(r"$T_2$ [s]", color='black')
339
+    axmine.set_ylabel(r"$A_0$ [i]", color='black')
340
+    plt.savefig("modelreconstruct.pdf", facecolor='white', edgecolor='white', dpi=200)
341
+
342
+    plt.show()
343
+

+ 102
- 0
smooth.py View File

@@ -0,0 +1,102 @@
1
+import numpy
2
+
3
+def smooth(x,window_len=11,window='hanning'):
4
+    """smooth the data using a window with requested size.
5
+    
6
+    This method is based on the convolution of a scaled window with the signal.
7
+    The signal is prepared by introducing reflected copies of the signal 
8
+    (with the window size) in both ends so that transient parts are minimized
9
+    in the begining and end part of the output signal.
10
+    
11
+    input:
12
+        x: the input signal 
13
+        window_len: the dimension of the smoothing window; should be an odd integer
14
+        window: the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'
15
+            flat window will produce a moving average smoothing.
16
+
17
+    output:
18
+        the smoothed signal
19
+        
20
+    example:
21
+
22
+    t=linspace(-2,2,0.1)
23
+    x=sin(t)+randn(len(t))*0.1
24
+    y=smooth(x)
25
+    
26
+    see also: 
27
+    
28
+    numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve
29
+    scipy.signal.lfilter
30
+ 
31
+    TODO: the window parameter could be the window itself if an array instead of a string   
32
+    """
33
+
34
+    if x.ndim != 1:
35
+        raise ValueError, "smooth only accepts 1 dimension arrays."
36
+
37
+    if x.size < window_len:
38
+        raise ValueError, "Input vector needs to be bigger than window size."
39
+
40
+
41
+    if window_len<3:
42
+        return x
43
+
44
+
45
+    if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']:
46
+        raise ValueError, "Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'"
47
+
48
+
49
+    s=numpy.r_[2*x[0]-x[window_len:1:-1],x,2*x[-1]-x[-1:-window_len:-1]]
50
+    #print(len(s))
51
+    if window == 'flat': #moving average
52
+        w=ones(window_len,'d')
53
+    else:
54
+        w=eval('numpy.'+window+'(window_len)')
55
+
56
+    y=numpy.convolve(w/w.sum(),s,mode='same')
57
+    return y[window_len-1:-window_len+1]
58
+
59
+
60
+
61
+
62
+from numpy import *
63
+from pylab import *
64
+
65
+def smooth_demo():
66
+
67
+    t=linspace(-4,4,100)
68
+    x=sin(t)
69
+    xn=x+randn(len(t))*0.1
70
+    y=smooth(x)
71
+
72
+    ws=31
73
+
74
+    subplot(211)
75
+    plot(ones(ws))
76
+
77
+    windows=['flat', 'hanning', 'hamming', 'bartlett', 'blackman']
78
+
79
+    hold(True)
80
+    for w in windows[1:]:
81
+        eval('plot('+w+'(ws) )')
82
+
83
+    axis([0,30,0,1.1])
84
+
85
+    legend(windows)
86
+    title("The smoothing windows")
87
+    subplot(212)
88
+    plot(x)
89
+    plot(xn)
90
+    for w in windows:
91
+        plot(smooth(xn,10,w))
92
+    l=['original signal', 'signal with noise']
93
+    l.extend(windows)
94
+
95
+    legend(l)
96
+    title("Smoothing a noisy signal")
97
+    show()
98
+
99
+
100
+if __name__=='__main__':
101
+    smooth_demo()
102
+

Loading…
Cancel
Save