Browse Source

Inversion within the GUI is functional.

tags/1.6.1
Trevor Irons 4 years ago
parent
commit
c8ebc72d49

+ 45
- 0
akvo/gui/akvoGUI.py View File

@@ -164,6 +164,8 @@ class ApplicationWindow(QtWidgets.QMainWindow):
164 164
         # Kernel         
165 165
         self.ui.calcK0.pressed.connect( self.calcK0 )       
166 166
 
167
+        # Inversion
168
+        self.ui.invertButton.pressed.connect( self.QTInv )
167 169
 
168 170
         # META 
169 171
         self.ui.locEdit.editingFinished.connect( self.logSite )
@@ -316,6 +318,49 @@ class ApplicationWindow(QtWidgets.QMainWindow):
316 318
             self.ui.ProcTabs.removeTab(0)    
317 319
             self.ui.ProcTabs.removeTab(0)    
318 320
     
321
+    def QTInv(self):
322
+        print("Big RED INVERT BUTTON")
323
+        
324
+        try:
325
+            with open('.akvo.last.path') as f: 
326
+                fpath = f.readline()  
327
+                pass
328
+        except IOError as e:
329
+            fpath = '.'
330
+        
331
+        akvoDatafile = QtWidgets.QFileDialog.getOpenFileName(self, 'Select Datafile File', fpath, r"Akvo datafiles (*.yaml)")[0]
332
+        K0file = QtWidgets.QFileDialog.getOpenFileName(self, 'Select Kernel File', fpath, r"Akvo kernels (*.yml)")[0]
333
+
334
+        T2lo = self.ui.T2low.value()
335
+        T2hi = self.ui.T2hi.value()
336
+        NT2 = self.ui.NT2.value()
337
+        dataChan = self.ui.invChan.currentText()
338
+       
339
+        invDict = dict()
340
+        invDict["data"] = dict()
341
+        invDict["data"] = dict() 
342
+        invDict["data"][akvoDatafile] = dict()
343
+        invDict["data"][akvoDatafile]["channels"] = [dataChan,]
344
+        invDict["K0"] = [K0file,]
345
+        invDict["T2Bins"] = dict()
346
+        invDict["T2Bins"]["low"] = T2lo
347
+        invDict["T2Bins"]["high"] = T2hi
348
+        invDict["T2Bins"]["number"] = NT2
349
+        
350
+        node = yaml.YAML()
351
+        kpo = open( "invert.yml", 'w' )
352
+        node.dump(invDict, kpo)
353
+ 
354
+        callBox = callScript(  ) #QtWidgets.QDialog() 
355
+        
356
+        callBox.ui = Ui_callScript()
357
+        callBox.ui.setupUi( callBox )
358
+        callBox.setupQTInv( "invert.yml" )
359
+        
360
+        callBox.exec_()
361
+        callBox.show()
362
+            
363
+
319 364
     def calcK0(self):
320 365
         
321 366
         try:

+ 16
- 0
akvo/gui/callScript.py View File

@@ -26,6 +26,22 @@ class callScript(QDialog):
26 26
         #self.process.start('python', ['calcAkvoKernel.py', akvoData, TxCoil, SaveStr])
27 27
         self.process.start('akvoK0', [ akvoData, TxCoil, kernelParams, SaveStr])
28 28
 
29
+    def setupQTInv(self, params):
30
+
31
+        #QtGui.QWidget.__init__(self)
32
+        #uic.loadUi('redirect.ui', self)
33
+
34
+        #print ('Connecting process')
35
+        self.process = QtCore.QProcess(self)
36
+        self.process.readyReadStandardOutput.connect(self.stdoutReady)
37
+        self.process.readyReadStandardError.connect(self.stderrReady)
38
+        self.process.started.connect(lambda: p('Started!'))
39
+        self.process.finished.connect(lambda: p('Finished!'))
40
+
41
+        #print ('Starting process')
42
+        #self.process.start('python', ['calcAkvoKernel.py', akvoData, TxCoil, SaveStr])
43
+        self.process.start('akvoQT', [params])
44
+
29 45
     def append(self, text):
30 46
         cursor = self.ui.textEdit.textCursor()
31 47
         cursor.movePosition(cursor.End)

+ 131
- 23
akvo/gui/main.ui View File

@@ -3759,10 +3759,10 @@ background: dark grey;
3759 3759
                   <number>1</number>
3760 3760
                  </property>
3761 3761
                  <property name="maximum">
3762
-                  <number>14</number>
3762
+                  <number>18</number>
3763 3763
                  </property>
3764 3764
                  <property name="value">
3765
-                  <number>3</number>
3765
+                  <number>8</number>
3766 3766
                  </property>
3767 3767
                 </widget>
3768 3768
                </item>
@@ -4008,8 +4008,8 @@ background: dark grey;
4008 4008
               <rect>
4009 4009
                <x>0</x>
4010 4010
                <y>0</y>
4011
-               <width>100</width>
4012
-               <height>30</height>
4011
+               <width>96</width>
4012
+               <height>26</height>
4013 4013
               </rect>
4014 4014
              </property>
4015 4015
              <attribute name="label">
@@ -4021,8 +4021,8 @@ background: dark grey;
4021 4021
               <rect>
4022 4022
                <x>0</x>
4023 4023
                <y>0</y>
4024
-               <width>411</width>
4025
-               <height>67</height>
4024
+               <width>96</width>
4025
+               <height>26</height>
4026 4026
               </rect>
4027 4027
              </property>
4028 4028
              <attribute name="label">
@@ -4033,29 +4033,137 @@ background: dark grey;
4033 4033
           </widget>
4034 4034
           <widget class="QWidget" name="InvertTab">
4035 4035
            <attribute name="title">
4036
-            <string>INV</string>
4036
+            <string>QT Inversion</string>
4037 4037
            </attribute>
4038
-           <widget class="QPushButton" name="invertButton">
4039
-            <property name="geometry">
4040
-             <rect>
4041
-              <x>130</x>
4042
-              <y>140</y>
4043
-              <width>311</width>
4044
-              <height>141</height>
4045
-             </rect>
4046
-            </property>
4047
-            <property name="styleSheet">
4048
-             <string notr="true">#invertButton {
4038
+           <layout class="QVBoxLayout" name="verticalLayout_12">
4039
+            <item>
4040
+             <widget class="QGroupBox" name="groupBox_2">
4041
+              <property name="maximumSize">
4042
+               <size>
4043
+                <width>16777215</width>
4044
+                <height>200</height>
4045
+               </size>
4046
+              </property>
4047
+              <property name="title">
4048
+               <string>QT T2* distribution</string>
4049
+              </property>
4050
+              <layout class="QGridLayout" name="gridLayout_18">
4051
+               <item row="0" column="0">
4052
+                <widget class="QLabel" name="label_76">
4053
+                 <property name="text">
4054
+                  <string>T2* Low (ms)</string>
4055
+                 </property>
4056
+                </widget>
4057
+               </item>
4058
+               <item row="0" column="1">
4059
+                <widget class="QDoubleSpinBox" name="T2low">
4060
+                 <property name="minimum">
4061
+                  <double>1.000000000000000</double>
4062
+                 </property>
4063
+                 <property name="maximum">
4064
+                  <double>30.000000000000000</double>
4065
+                 </property>
4066
+                 <property name="value">
4067
+                  <double>10.000000000000000</double>
4068
+                 </property>
4069
+                </widget>
4070
+               </item>
4071
+               <item row="1" column="0">
4072
+                <widget class="QLabel" name="label_77">
4073
+                 <property name="text">
4074
+                  <string>T2* High (ms)</string>
4075
+                 </property>
4076
+                </widget>
4077
+               </item>
4078
+               <item row="1" column="1">
4079
+                <widget class="QDoubleSpinBox" name="T2hi">
4080
+                 <property name="minimum">
4081
+                  <double>10.000000000000000</double>
4082
+                 </property>
4083
+                 <property name="maximum">
4084
+                  <double>2000.000000000000000</double>
4085
+                 </property>
4086
+                 <property name="value">
4087
+                  <double>1000.000000000000000</double>
4088
+                 </property>
4089
+                </widget>
4090
+               </item>
4091
+               <item row="2" column="0">
4092
+                <widget class="QLabel" name="label_78">
4093
+                 <property name="text">
4094
+                  <string>N T2*</string>
4095
+                 </property>
4096
+                </widget>
4097
+               </item>
4098
+               <item row="2" column="1">
4099
+                <widget class="QSpinBox" name="NT2">
4100
+                 <property name="minimum">
4101
+                  <number>10</number>
4102
+                 </property>
4103
+                 <property name="value">
4104
+                  <number>35</number>
4105
+                 </property>
4106
+                </widget>
4107
+               </item>
4108
+              </layout>
4109
+             </widget>
4110
+            </item>
4111
+            <item>
4112
+             <widget class="QGroupBox" name="groupBox_6">
4113
+              <property name="title">
4114
+               <string>QT Inversion</string>
4115
+              </property>
4116
+              <layout class="QGridLayout" name="gridLayout_19">
4117
+               <item row="1" column="0">
4118
+                <widget class="QLabel" name="label_79">
4119
+                 <property name="text">
4120
+                  <string>Data channel</string>
4121
+                 </property>
4122
+                </widget>
4123
+               </item>
4124
+               <item row="1" column="1">
4125
+                <widget class="QComboBox" name="invChan">
4126
+                 <item>
4127
+                  <property name="text">
4128
+                   <string>Chan. 1</string>
4129
+                  </property>
4130
+                 </item>
4131
+                 <item>
4132
+                  <property name="text">
4133
+                   <string>Chan. 2</string>
4134
+                  </property>
4135
+                 </item>
4136
+                 <item>
4137
+                  <property name="text">
4138
+                   <string>Chan. 3</string>
4139
+                  </property>
4140
+                 </item>
4141
+                 <item>
4142
+                  <property name="text">
4143
+                   <string>Chan. 4</string>
4144
+                  </property>
4145
+                 </item>
4146
+                </widget>
4147
+               </item>
4148
+               <item row="2" column="0" colspan="2">
4149
+                <widget class="QPushButton" name="invertButton">
4150
+                 <property name="styleSheet">
4151
+                  <string notr="true">#invertButton {
4049 4152
 font-size:29pt;
4050 4153
 font-weight: bold;
4051 4154
 color: white;
4052 4155
 background: red;
4053 4156
 }</string>
4054
-            </property>
4055
-            <property name="text">
4056
-             <string>Invert</string>
4057
-            </property>
4058
-           </widget>
4157
+                 </property>
4158
+                 <property name="text">
4159
+                  <string>Invert</string>
4160
+                 </property>
4161
+                </widget>
4162
+               </item>
4163
+              </layout>
4164
+             </widget>
4165
+            </item>
4166
+           </layout>
4059 4167
           </widget>
4060 4168
           <widget class="QWidget" name="AppraiseTab">
4061 4169
            <attribute name="title">

+ 1
- 0
akvo/terminal/plotKernel.py View File

@@ -19,6 +19,7 @@ def catLayers(K0):
19 19
         #print(K0["layer-"+str(lay)].data) #    print (lay)
20 20
         K[lay] = K0["layer-"+str(lay)].data #    print (lay)
21 21
     return K
22
+
22 23
 if __name__ == "__main__":
23 24
 
24 25
     with open(sys.argv[1]) as f:

+ 34
- 0
akvo/tressel/SlidesPlot.py View File

@@ -0,0 +1,34 @@
1
+#################################################################################
2
+# GJI final pub specs                                                           #
3
+import matplotlib                                                               #
4
+from matplotlib import rc                                                           #
5
+matplotlib.rcParams['text.latex.preamble']=[r"\usepackage{timet,amsmath,amssymb}"]  #
6
+rc('font',**{'family':'sans-serif','serif':['timet']})                                   #
7
+rc('font',**{'size':11})                                                             #
8
+rc('text', usetex=True)                                                             #
9
+# converts pc that GJI is defined in to inches                                      # 
10
+# In GEOPHYSICS \textwidth = 42pc                                               #
11
+#        \columnwidth = 20pc                                                    #
12
+#        one column widthe figures are 20 picas                                 #
13
+#        one and one third column figures are 26 picas                          #
14
+def pc2in(pc):                                                                  #
15
+    return pc*12/72.27                                                          #
16
+#################################################################################
17
+import numpy as np
18
+light_grey = np.array([float(248)/float(255)]*3)
19
+
20
+def fixLeg(legend):
21
+    rect = legend.get_frame()
22
+    #rect.set_color('None')
23
+    rect.set_facecolor(light_grey)
24
+    rect.set_linewidth(0.0)
25
+    rect.set_alpha(0.5)
26
+
27
+def deSpine(ax1):
28
+    spines_to_remove = ['top', 'right']
29
+    for spine in spines_to_remove:
30
+        ax1.spines[spine].set_visible(False)
31
+    #ax1.xaxis.set_ticks_position('none')
32
+    #ax1.yaxis.set_ticks_position('none')
33
+    ax1.get_xaxis().tick_bottom()
34
+    ax1.get_yaxis().tick_left()

+ 1
- 0
akvo/tressel/calcAkvoKernel.py View File

@@ -147,6 +147,7 @@ def main():
147 147
     yml = open( sys.argv[4], 'w' )
148 148
     print(Kern, file=yml)
149 149
 
150
+    # 
150 151
     K0 = Kern.GetKernel()
151 152
     plt.matshow(np.abs(K0))
152 153
     plt.show()

+ 218
- 0
akvo/tressel/invertTA.py View File

@@ -0,0 +1,218 @@
1
+from akvo.tressel.SlidesPlot import *
2
+import numpy as np
3
+import sys
4
+import matplotlib.pyplot as plt
5
+import cmocean
6
+from pylab import meshgrid 
7
+from akvo.tressel.logbarrier import *
8
+import yaml,os
9
+
10
+from matplotlib.colors import LogNorm
11
+from matplotlib.colors import LightSource
12
+from matplotlib.ticker import ScalarFormatter
13
+from matplotlib.ticker import MaxNLocator
14
+from matplotlib.ticker import AutoMinorLocator
15
+from matplotlib.ticker import LogLocator
16
+from matplotlib.ticker import FormatStrFormatter
17
+import cmocean
18
+from akvo.tressel.lemma_yaml import * 
19
+
20
+def buildKQT(K0,tg,T2Bins):
21
+    """ 
22
+        Constructs a QT inversion kernel from an initial amplitude one.  
23
+    """
24
+    nlay, nq = np.shape(K0)
25
+    nt2 = len(T2Bins)
26
+    nt = len(tg)
27
+    KQT = np.zeros( ( nq*nt,nt2*nlay) )
28
+    for iq in range(nq):
29
+        for it in range(nt):
30
+            for ilay in range(nlay):
31
+                for it2 in range(nt2):
32
+                    #KQT[iq*nt + it,ilay*nt2+it2] = K0[ilay,iq]*np.exp(-((10+tg[it])*1e-3)/(1e-3*T2Bins[it2]))
33
+                    KQT[iq*nt + it,ilay*nt2+it2] = K0[ilay,iq]*np.exp(-((10+tg[it])*1e-3)/(1e-3*T2Bins[it2]))
34
+    return KQT
35
+
36
+def loadAkvoData(fnamein, chan):
37
+    """ Loads data from an Akvo YAML file. The 0.02 is hard coded as the pulse length. This needs to be 
38
+        corrected in future kernel calculations. The current was reported but not the pulse length. 
39
+    """
40
+    fname = (os.path.splitext(fnamein)[0])
41
+    with open(fnamein, 'r') as stream:
42
+        try:
43
+            AKVO = (yaml.load(stream, Loader=yaml.Loader))
44
+        except yaml.YAMLError as exc:
45
+            print(exc)
46
+            exit()
47
+
48
+    Z = np.zeros( (AKVO.nPulseMoments, AKVO.Gated["Pulse 1"]["abscissa"].size ) )
49
+    ZS = np.zeros( (AKVO.nPulseMoments, AKVO.Gated["Pulse 1"]["abscissa"].size ) )
50
+    for q in range(AKVO.nPulseMoments):
51
+        Z[q] = AKVO.Gated["Pulse 1"][chan]["Q-"+str(q) + " CA"].data
52
+        if chan == "Chan. 1":
53
+            ZS[q] = AKVO.Gated["Pulse 1"][chan]["STD"].data
54
+        elif chan == "Chan. 2":
55
+            ZS[q] = AKVO.Gated["Pulse 1"][chan]["STD"].data
56
+        elif chan == "Chan. 3":
57
+            ZS[q] = AKVO.Gated["Pulse 1"][chan]["STD"].data
58
+        elif chan == "Chan. 4":
59
+            ZS[q] = AKVO.Gated["Pulse 1"][chan]["STD"].data
60
+        else:
61
+            print("DOOM!!!")
62
+            exit()
63
+    #Z *= 1e-9 
64
+    #ZS *= 1e-9 
65
+
66
+    J = AKVO.Pulses["Pulse 1"]["current"].data 
67
+    J = np.append(J,J[-1]+(J[-1]-J[-2]))
68
+    Q = AKVO.pulseLength[0]*J
69
+    return Z, ZS, AKVO.Gated["Pulse 1"]["abscissa"].data  #, Q
70
+
71
+def catLayers(K0):
72
+    K = np.zeros( (len(K0.keys()), len(K0["layer-0"].data)) , dtype=complex )
73
+    for lay in range(len(K0.keys())):
74
+        #print(K0["layer-"+str(lay)].data) #    print (lay)
75
+        K[lay] =K0["layer-"+str(lay)].data #    print (lay)
76
+    return 1e9*K                           # invert in nV
77
+
78
+def loadK0(fname):
79
+    """ Loads in initial amplitude kernel
80
+    """
81
+    print("loading K0", fname)
82
+    with open(fname) as f:
83
+        K0 = yaml.load(f, Loader=yaml.Loader)
84
+    K = catLayers(K0.K0)
85
+    ifaces = np.array(K0.Interfaces.data)
86
+    return ifaces, np.abs(K)
87
+
88
+
89
+
90
+def main():
91
+
92
+    if (len (sys.argv) < 3):
93
+        print ("python invertTA.py  K0.dat  Data.yaml")
94
+    print (sys.argv[1])
95
+    with open(sys.argv[1], 'r') as stream:
96
+        try:
97
+            cont = (yaml.load(stream, Loader=yaml.Loader))
98
+        except yaml.YAMLError as exc:
99
+            print(exc)
100
+    ###############################################
101
+    # Load in data
102
+    ###############################################
103
+    V = []
104
+    VS = []
105
+    tg = 0
106
+    for dat in cont['data']:
107
+        for ch in cont['data'][dat]['channels']:
108
+            print("dat", dat, "ch", ch)
109
+            v,vs,tg = loadAkvoData(dat, ch)
110
+            V.append(v)
111
+            VS.append(vs)
112
+    for iv in range(1, len(V)):
113
+        V[0] = np.concatenate( (V[0], V[iv]) )
114
+        VS[0] = np.concatenate( (VS[0], VS[iv]) )
115
+    V = V[0]
116
+    VS = VS[0]
117
+    
118
+    #plt.matshow(V, cmap='RdBu', vmin=-np.max(np.abs(V)), vmax=np.max(np.abs(V)))
119
+    #plt.title("data")
120
+    #plt.colorbar()
121
+    #plt.show()
122
+    #exit()    
123
+
124
+    #plt.matshow(VS, cmap='inferno', vmin=-np.max(np.abs(VS)), vmax=np.max(np.abs(VS)))
125
+    #plt.title("error")
126
+    #plt.colorbar()
127
+
128
+    ###############################################
129
+    # Load in kernels
130
+    ###############################################
131
+    K0 = []
132
+    for kern in cont["K0"]:
133
+        ifaces,k0 = loadK0( kern )
134
+        K0.append(k0)
135
+    for ik in range(1, len(K0)):
136
+        K0[0] = np.concatenate( (K0[0].T, K0[ik].T) ).T
137
+    K0 = K0[0]
138
+    #plt.matshow(K0)
139
+    
140
+    ###############################################
141
+    # Build full kernel
142
+    ############################################### 
143
+    T2Bins = np.logspace( np.log10(cont["T2Bins"]["low"]), np.log10(cont["T2Bins"]["high"]), cont["T2Bins"]["number"], endpoint=True, base=10)  
144
+    KQT = buildKQT(K0,tg,T2Bins)
145
+    #plt.matshow(KQT)
146
+
147
+    # Chan. 1    reg = 1.5   
148
+    # Chan. 2    reg = 1.35
149
+    # Chan. 4    reg = 1.95  
150
+ 
151
+    ###############################################
152
+    # Invert
153
+    ############################################### 
154
+    print("Calling inversion", flush=True)
155
+    inv, ibreak, errn, phim, phid, mkappa = logBarrier(KQT, np.ravel(V), T2Bins, "lcurve", MAXITER=150, sigma=np.ravel(VS), alpha=1e6, smooth="Smallest" ) #, smooth=True) # 
156
+                     #logBarrier(A, b, T2Bins, lambdastar, x_0=0, xr=0, alpha=10, mu1=10, mu2=10, smooth=False, MAXITER=70, fignum=1000, sigma=1, callback=None):
157
+    #return x, ibreak, np.sqrt(phid/len(b)), PHIM, PHID/len(b), np.argmax(kappa)
158
+
159
+
160
+    ###############################################
161
+    # Appraise
162
+    ############################################### 
163
+    pre = np.dot(KQT,inv) 
164
+    PRE = np.reshape( pre, np.shape(V)  )
165
+    plt.matshow(PRE, cmap='Blues')
166
+    plt.gca().set_title("predicted")
167
+    plt.colorbar()
168
+
169
+    DIFF = (PRE-V) / VS
170
+    md = np.max(np.abs(DIFF))
171
+    plt.matshow(DIFF, cmap=cmocean.cm.balance, vmin=-md, vmax=md)
172
+    plt.gca().set_title("misfit / $\widehat{\sigma}$")
173
+    plt.colorbar()
174
+    
175
+    plt.matshow(V, cmap='Blues')
176
+    plt.gca().set_title("observed")
177
+    plt.colorbar()
178
+
179
+    T2Bins = np.append( T2Bins, T2Bins[-1] + (T2Bins[-1]-T2Bins[-2]) )
180
+    
181
+    print( np.shape(inv), len(ifaces)-1,cont["T2Bins"]["number"] )
182
+    INV = np.reshape(inv, (len(ifaces)-1,cont["T2Bins"]["number"]) )
183
+    Y,X = meshgrid( ifaces, T2Bins )
184
+    fig = plt.figure( figsize=(pc2in(17.0),pc2in(18.)) )
185
+    ax1 = fig.add_axes( [.2,.15,.6,.7] )
186
+    im = ax1.pcolor(X, Y, INV.T, cmap=cmocean.cm.tempo) #cmap='viridis')
187
+    im.set_edgecolor('face')
188
+    ax1.set_xlim( T2Bins[0], T2Bins[-1] )
189
+    ax1.set_ylim( ifaces[-1], ifaces[0] )
190
+    cb = plt.colorbar(im, label = u"PWC (m$^3$/m$^3$)") #, format='%1.1f')
191
+    cb.locator = MaxNLocator( nbins = 4)
192
+    cb.ax.yaxis.set_offset_position('left')                         
193
+    cb.update_ticks()
194
+ 
195
+    #ax1.set_xlabel(u"$T_2^*$ (ms)")
196
+    #ax1.set_ylabel(u"depth (m)$")
197
+    
198
+    ax1.get_xaxis().set_major_formatter(FormatStrFormatter('%1.0f'))
199
+    ax1.get_yaxis().set_major_formatter(FormatStrFormatter('%1.0f'))
200
+    ax1.xaxis.set_major_locator( MaxNLocator(nbins = 4) )   
201
+
202
+    #ax1.xaxis.set_label_position('top') 
203
+
204
+    ax2 = ax1.twiny()
205
+    ax2.plot( np.sum(INV, axis=1), (ifaces[1:]+ifaces[0:-1])/2 ,  color='red' )
206
+    ax2.set_xlabel(u"total water (m$^3$/m$^3$)")
207
+    ax2.set_ylim( ifaces[-1], ifaces[0] )
208
+    ax2.xaxis.set_major_locator( MaxNLocator(nbins = 3) )   
209
+    ax2.get_xaxis().set_major_formatter(FormatStrFormatter('%0.2f'))
210
+    #ax2.xaxis.set_label_position('bottom') 
211
+
212
+    plt.savefig("akvoInversion.pdf")
213
+    
214
+    plt.show()
215
+
216
+if __name__ == "__main__":
217
+    main() 
218
+

+ 121
- 0
akvo/tressel/lemma_yaml.py View File

@@ -0,0 +1,121 @@
1
+import yaml
2
+    
3
+def Complex_ctor(loader, node):
4
+    re = eval(node.value[0][1].value)
5
+    im = eval(node.value[1][1].value)
6
+    return re + 1j*im 
7
+
8
+yaml.add_constructor(r'Complex', Complex_ctor)
9
+
10
+class MatrixXr(yaml.YAMLObject):
11
+    yaml_tag = u'MatrixXr'
12
+    def __init__(self, rows, cols, data):
13
+        self.rows = rows
14
+        self.cols = cols
15
+        self.data = np.zeros((rows,cols))
16
+    def __repr__(self):
17
+        return "%s(rows=%r, cols=%r, data=%r)" % (self.__class__.__name__, self.rows, self.cols, self.data) 
18
+
19
+class VectorXr(yaml.YAMLObject):
20
+    yaml_tag = r'VectorXr'
21
+    def __init__(self, array):
22
+        self.size = np.shape(array)[0]
23
+        self.data = array.tolist()
24
+    def __repr__(self):
25
+        # Converts to numpy array on import 
26
+        return "np.array(%r)" % (self.data)
27
+
28
+class VectorXcr(yaml.YAMLObject):
29
+    yaml_tag = r'VectorXcr'
30
+    def __init__(self, array):
31
+        self.size = np.shape(array)[0]
32
+        self.datar = array.tolist()
33
+    def __repr__(self):
34
+        # Converts to numpy array on import 
35
+        #return "np.array(%r)" % (self.data)
36
+        return "np.array(%r)" % (3)
37
+
38
+class Vector3r(yaml.YAMLObject):
39
+    yaml_tag = r'Vector3r'
40
+    def __init__(self, array):
41
+        self.size = 3 #np.shape(array)[0]
42
+        self.data = array.tolist()
43
+    def __repr__(self):
44
+        # Converts to numpy array on import 
45
+        return "np.array(%r)" % (self.data)
46
+
47
+class Vector3Xcr(yaml.YAMLObject):
48
+    yaml_tag = r'Vector3Xcr'
49
+    def __init__(self, array):
50
+        self.size = 3 #np.shape(array)[0]
51
+        self.data = array.tolist()
52
+    def __repr__(self):
53
+        # Converts to numpy array on import 
54
+        return "np.array(%r)" % (self.data)
55
+
56
+class Vector3Xr(yaml.YAMLObject):
57
+    yaml_tag = r'Vector3Xr'
58
+    def __init__(self, array):
59
+        self.size = 3 #np.shape(array)[0]
60
+        self.data = array.tolist()
61
+    def __repr__(self):
62
+        # Converts to numpy array on import 
63
+        return "np.array(%r)" % (self.data)
64
+
65
+#class KernelV0( ):
66
+    #yaml_tag = r'KernelV0'
67
+#    def __init__(self):
68
+#        self.name = "hello"
69
+
70
+#def KernelV0_constructor(loader, node):
71
+    #...     value = loader.construct_scalar(node)
72
+    #...     a, b = map(int, value.split('d'))
73
+#    return KernelV0( )
74
+
75
+
76
+# class KervnelV0(yaml.YAMLObject):
77
+#     yaml_loader = yaml.Loader
78
+#     yaml_dumper = yaml.Dumper
79
+# 
80
+#     yaml_tag = u'!KernelV0'
81
+#     #yaml_flow_style = ...
82
+# 
83
+#     def __init__(self):
84
+#         self.val = 7
85
+# 
86
+#     @classmethod
87
+#     def from_yaml(cls, loader, node):
88
+#         # ...
89
+#         data = 0
90
+#         return data
91
+# 
92
+#     @classmethod
93
+#     def to_yaml(cls, dumper, data):
94
+#         # ...
95
+#         return node
96
+
97
+class KervnelV0(yaml.YAMLObject):
98
+    yaml_tag = u'KernelV0'
99
+    def __init__(self, val):
100
+        self.val = val
101
+
102
+class LayeredEarthEM(yaml.YAMLObject):
103
+    yaml_tag = u'LayeredEarthEM'
104
+    def __init__(self, val):
105
+        self.val = val
106
+
107
+class PolygonalWireAntenna(yaml.YAMLObject):
108
+    yaml_tag = u'PolygonalWireAntenna'
109
+    def __init__(self, val):
110
+        self.val = val
111
+
112
+class AkvoData(yaml.YAMLObject):
113
+    yaml_tag = u'AkvoData'
114
+    def __init__(self, obj): #nPulseMoments, pulseLength):
115
+    #def __init__(self, rows, cols, data):
116
+        #self.nPulseMoments = nPulseMoments
117
+        #self.pulseLength = pulseLength 
118
+        #for key in obj.keys:
119
+        #    self[key] = obj.key
120
+        pass
121
+

+ 283
- 0
akvo/tressel/logbarrier.py View File

@@ -0,0 +1,283 @@
1
+from __future__ import division
2
+import numpy as np
3
+from scipy.sparse.linalg import iterative  as iter
4
+from scipy.sparse import eye as seye
5
+import pylab 
6
+import pprint 
7
+from scipy.optimize import nnls 
8
+
9
+import matplotlib.pyplot as plt
10
+
11
+def PhiB(mux, minVal, x):
12
+    phib = mux * np.abs( np.sum(np.log( x-minVal)) )
13
+    return phib
14
+
15
+def curvaturefd(x, y, t):
16
+    x1 = np.gradient(x,t) 
17
+    x2 = np.gradient(x1,t) 
18
+    y1 = np.gradient(y,t) 
19
+    y2 = np.gradient(y1,t) 
20
+    return np.abs(x1*y2 - y1*x2) / np.power(x1**2 + y1**2, 3./2)
21
+
22
+def curvatureg(x, y):
23
+    from scipy.ndimage import gaussian_filter1d
24
+    #first and second derivative
25
+    x1 = gaussian_filter1d(x, sigma=1, order=1)#, mode='constant', cval=x[-1])
26
+    x2 = gaussian_filter1d(x1, sigma=1, order=1)#, mode='constant', cval=y[-1])
27
+    y1 = gaussian_filter1d(y, sigma=1, order=1)#, mode='constant', cval=x1[-1])
28
+    y2 = gaussian_filter1d(y1, sigma=1, order=1)#, mode='constant', cval=y1[-1])
29
+    return np.abs(x1*y2 - y1*x2) / np.power(x1**2 + y1**2, 3./2)
30
+
31
+def logBarrier(A, b, T2Bins, lambdastar, x_0=0, xr=0, alpha=10, mu1=10, mu2=10, smooth=False, MAXITER=70, fignum=1000, sigma=1, callback=None):
32
+    """Impliments a log barrier Tikhonov solution to a linear system of equations 
33
+        Ax = b  s.t.  x_min < x < x_max. A log-barrier term is used for the constraint
34
+    """
35
+    # TODO input
36
+    minVal = 0.0
37
+    #maxVal = 1e8
38
+
39
+    Wd =    (np.eye(len(b)) / (sigma))        # Wd = eye( sigma )
40
+    WdTWd = (np.eye(len(b)) / (sigma**2))     # Wd = eye( sigma )
41
+
42
+    ATWdTWdA = np.dot(A.conj().transpose(), np.dot( WdTWd, A ))     # TODO, implicit calculation instead?
43
+    N = np.shape(A)[1]                        # number of model
44
+    M = np.shape(A)[0]                        # number of data
45
+    SIGMA   = .25 # .25 # lower is more aggresive relaxation of log barrier  
46
+    EPSILON = 1e-25 #1e-35 
47
+
48
+    # reference model
49
+    if np.size(xr) == 1:
50
+        xr =  np.zeros(N)     
51
+    
52
+    # initial guess
53
+    if np.size(x_0) == 1:
54
+        x = 1e-10 + np.zeros(N)     
55
+    else:
56
+        x = 1e-10 + x_0
57
+        
58
+    # Construct model constraint base   
59
+    Phim_base = np.zeros( [N , N] ) 
60
+    a1 = .05     # smallest too
61
+    
62
+    # calculate largest term            
63
+    D1 = 1./abs(T2Bins[1]-T2Bins[0])
64
+    D2 = 1./abs(T2Bins[2]-T2Bins[1])
65
+    #a2 = 1. #(1./(2.*D1+D2))    # smooth
66
+    
67
+    if smooth == "Both":
68
+        #print ("Both small and smooth model")
69
+        for ip in range(N):
70
+            D1 = 0.
71
+            D2 = 0.
72
+            if ip > 0:
73
+                #D1 = np.sqrt(1./abs(T2Bins[ip]-T2Bins[ip-1]))**.5
74
+                D1 = (1./abs(T2Bins[ip]-T2Bins[ip-1])) #**2
75
+            if ip < N-1:
76
+                #D2 = np.sqrt(1./abs(T2Bins[ip+1]-T2Bins[ip]))**.5
77
+                D2 = (1./abs(T2Bins[ip+1]-T2Bins[ip])) #**2
78
+            if ip > 0:
79
+                Phim_base[ip,ip-1] =   -(D1)      
80
+            if ip == 0:
81
+                Phim_base[ip,ip  ] = 2.*(D1+D2)  
82
+            elif ip == N-1:
83
+                Phim_base[ip,ip  ] = 2.*(D1+D2) 
84
+            else:
85
+                Phim_base[ip,ip  ] = 2.*(D1+D2)
86
+            if ip < N-1:
87
+                Phim_base[ip,ip+1] =   -(D2)  
88
+        Phim_base /= np.max(Phim_base)            # normalize 
89
+        Phim_base += a1*np.eye(N)
90
+
91
+    elif smooth == "Smooth":
92
+        #print ("Smooth model")
93
+        for ip in range(N):
94
+            if ip > 0:
95
+                Phim_base[ip,ip-1] = -1    # smooth in log space
96
+            if ip == 0:
97
+                Phim_base[ip,ip  ] = 2.05   # Encourage a little low model
98
+            elif ip == N-1:
99
+                Phim_base[ip,ip  ] = 2.5   # Penalize long decays
100
+            else:
101
+                Phim_base[ip,ip  ] = 2.1   # Smooth and small
102
+            if ip < N-1:
103
+                Phim_base[ip,ip+1] = -1    # smooth in log space
104
+
105
+    elif smooth == "Smallest":
106
+        for ip in range(N):
107
+            Phim_base[ip,ip  ] = 1.
108
+    else: 
109
+        print("non valid model constraint:", smooth)
110
+        exit()
111
+    
112
+    Phi_m =  alpha*Phim_base
113
+    WmTWm = Phim_base # np.dot(Phim_base, Phim_base.T)            
114
+    b_pre = np.dot(A, x)
115
+    phid = np.linalg.norm( np.dot(Wd, (b-b_pre)) )**2
116
+    phim = np.linalg.norm( np.dot(Phim_base, (x-xr)) )**2
117
+
118
+    mu2 = phim
119
+    phib = PhiB(mu1, 0, x) 
120
+    mu1 = ((phid + alpha*phim) / phib)
121
+
122
+    PHIM = []
123
+    PHID = []
124
+    MOD = []
125
+
126
+    ALPHA = []
127
+    ALPHA.append(alpha)
128
+    #ALPHA = np.linspace( alpha, 1, MAXITER  )
129
+    for i in range(MAXITER):
130
+        #alpha = ALPHA[i]
131
+
132
+        Phi_m =  alpha*Phim_base
133
+        
134
+        # reset mu1 at each iteration 
135
+        # Calvetti -> No ; Li -> Yes   
136
+        # without this, non monotonic convergence occurs...which is OK if you really trust your noise 
137
+        mu1 = ((phid + alpha*phim) / phib) 
138
+
139
+        WmTWm = Phim_base # np.dot(Phim_base, Phim_base.T)            
140
+        phid_old = phid
141
+        inner = 0
142
+
143
+        First = True # guarantee entry 
144
+
145
+        xp = np.copy(x) # prior step x 
146
+
147
+        while ( (phib / (phid+alpha*phim)) > EPSILON  or First==True ):
148
+        #while ( First==True ):
149
+
150
+            First = False
151
+            # Log barrier, keep each element above minVal
152
+            X1 = np.eye(N) * (x-minVal)**-1           
153
+            X2 = np.eye(N) * (x-minVal)**-2         
154
+            
155
+            # Log barrier, keep sum below maxVal TODO normalize by component. Don't want to push all down  
156
+            #Y1 = np.eye(N) * (maxVal - np.sum(x))**-1           
157
+            #Y2 = np.eye(N) * (maxVal - np.sum(x))**-2         
158
+            
159
+            AA = ATWdTWdA + mu1*X2 + Phi_m 
160
+            M = np.eye( N ) * (1./np.diag(ATWdTWdA + mu1*X2 + Phi_m))
161
+            #M = seye( N ).dot(1./np.diag(ATWdTWdA + mu1*X2 + Phi_m))
162
+        
163
+            # Solve system (newton step) (Li)
164
+            b2 = np.dot(A.conj().transpose(), np.dot(WdTWd, b-b_pre) ) + 2.*mu1*np.diag(X1) - alpha*np.dot(WmTWm,(x-xr))
165
+            ztilde = iter.cg(AA, b2, M = M) 
166
+            h = (ztilde[0].real) 
167
+            
168
+            # Solve system (direct solution) (Calvetti) 
169
+            #b2 = np.dot(A.conj().transpose(), np.dot(WdTWd, b)) + 2.*mu1*np.diag(X1) - alpha*np.dot(WmTWm,(x-xr))
170
+            #ztilde = iter.cg(AA, b2, M=M, x0=x) 
171
+            #h = (ztilde[0].real - x) 
172
+
173
+            # step size
174
+            d = np.min( (1, 0.95 * np.min(x/np.abs(h+1e-120))) )
175
+            
176
+            ##########################################################
177
+            # Update and fix any over/under stepping
178
+            x += d*h
179
+        
180
+            # Determine mu steps to take
181
+            s1 = mu1 * (np.dot(X2, ztilde[0].real) - 2.*np.diag(X1))
182
+            #s2 = mu2 * (np.dot(Y2, ztilde[0].real) - 2.*np.diag(Y1))
183
+
184
+            # determine mu for next step
185
+            mu1 = SIGMA/N * np.abs(np.dot(s1, x))
186
+            #mu2 = SIGMA/N * np.abs(np.dot(s2, x))
187
+            
188
+            b_pre = np.dot(A, x)
189
+            phid = np.linalg.norm( np.dot(Wd, (b-b_pre)))**2
190
+            phim = np.linalg.norm( np.dot(Phim_base, (x-xr)) )**2
191
+            phib = PhiB(mu1, minVal, x)
192
+            inner += 1
193
+ 
194
+        PHIM.append(phim)      
195
+        PHID.append(phid)      
196
+        MOD.append(np.copy(x))  
197
+
198
+        # determine alpha
199
+        scale = 1.5*(len(b)/phid)
200
+        #alpha *= np.sqrt(scale)
201
+        alpha *= min(scale, .95) # was .85...
202
+        #print("alpha", min(scale, 0.99))
203
+        #alpha *= .99 # was .85...
204
+        ALPHA.append(alpha)
205
+        #alpha = ALPHA[i+1]
206
+            
207
+        print("inversion progress", i, alpha, np.sqrt(phid/len(b)), phim, flush=True)       
208
+        
209
+
210
+#         if np.sqrt(phid/len(b)) < 0.97: 
211
+#             ibreak = -1
212
+#             print ("------------overshot--------------------", alpha, np.sqrt(phid/len(b)), ibreak)
213
+#             alpha *= 2. #0
214
+#             x -= d*h
215
+#             b_pre = np.dot(A, x)
216
+#             phid = np.linalg.norm( np.dot(Wd, (b-b_pre)))**2
217
+#             phim = np.linalg.norm( np.dot(Phim_base, (x-xr)) )#**2
218
+#             mu1 = ((phid + alpha*phim) / phib)
219
+        if lambdastar == "discrepency": 
220
+            if np.sqrt(phid/len(b)) < 1.00 or alpha < 1e-5: 
221
+                ibreak = 1
222
+                print ("optimal solution found", alpha, np.sqrt(phid/len(b)), ibreak)
223
+                break
224
+        # slow convergence, bail and use L-curve 
225
+        # TI- only use L-curve. Otherwise results for perlin noise are too spurious for paper.  
226
+        if lambdastar == "lcurve":
227
+            if i > 4: 
228
+                kappa = curvaturefd(np.log(np.array(PHIM)), np.log(np.array(PHID)), ALPHA[0:i+1])#ALPHA[0:-1])
229
+                #kappa = curvatureg(np.log(np.array(PHIM)), np.log(np.array(PHID)))
230
+                print("max kappa", np.argmax(kappa), "distance from", i-np.argmax(kappa)) 
231
+            if i > 4 and (i-np.argmax(kappa)) > 4: # ((np.sqrt(phid_old/len(b))-np.sqrt(phid/len(b))) < 1e-4) : 
232
+            #if np.sqrt(phid/len(b)) < 3.0 and ((np.sqrt(phid_old/len(b))-np.sqrt(phid/len(b))) < 1e-3): 
233
+                ibreak = 1
234
+                MOD = np.array(MOD)
235
+                print ("###########################") #slow convergence", alpha, "phid_old", np.sqrt(phid_old/len(b)), "phid", np.sqrt(phid/len(b)), ibreak)
236
+                print ("Using L-curve criteria") 
237
+                #kappa = curvaturefd(np.log(np.array(PHIM)), np.log(np.array(PHID)), ALPHA[0:-1])
238
+                #kappa2 = curvatureg(np.log(np.array(PHIM)), np.log(np.array(PHID)))
239
+                #kappa = curvature( np.array(PHIM), np.array(PHID))
240
+                x = MOD[ np.argmax(kappa) ]
241
+                b_pre = np.dot(A, x)
242
+                phid = np.linalg.norm( np.dot(Wd, (b-b_pre)))**2
243
+                phim = np.linalg.norm( np.dot(Phim_base, (x-xr)) )**2
244
+                mu1 = ((phid + alpha*phim) / phib) 
245
+                print ("L-curve selected", alpha, "phid_old", np.sqrt(phid_old/len(b)), "phid", np.sqrt(phid/len(b)), ibreak)
246
+                print ("###########################")
247
+                if np.sqrt(phid/len(b)) <= 1:
248
+                    ibreak=0
249
+
250
+                plt.figure()
251
+                #plt.plot( (np.array(PHIM)),  np.log(np.array(PHID)/len(b)), '.-')
252
+                #plt.plot(  ((np.array(PHIM))[np.argmax(kappa)]) , np.log( (np.array(PHID)/len(b))[np.argmax(kappa)] ), '.', markersize=12)
253
+                #plt.axhline()
254
+                plt.plot( np.log(np.array(PHIM)),  np.log(np.sqrt(np.array(PHID)/len(b))), '.-')
255
+                plt.plot( np.log(np.array(PHIM))[np.argmax(kappa)], np.log(np.sqrt(np.array(PHID)/len(b))[np.argmax(kappa)]), '.', markersize=12)
256
+                ax2 = plt.twinx()
257
+                ax2.plot( np.log(np.array(PHIM)), kappa, color='green', label="lcurve" )
258
+                plt.legend()
259
+                plt.savefig('lcurve.pdf')
260
+                break
261
+
262
+    PHIM = np.array(PHIM)
263
+    PHID = np.array(PHID)
264
+
265
+    if (i == MAXITER-1 ):
266
+        ibreak = 2
267
+        print("Reached max iterations!!", alpha, np.sqrt(phid/len(b)), ibreak)
268
+        kappa = curvaturefd(np.log(np.array(PHIM)), np.log(np.array(PHID)), ALPHA[0:-1])
269
+        x = MOD[ np.argmax(kappa) ]
270
+        b_pre = np.dot(A, x)
271
+        phid = np.linalg.norm( np.dot(Wd, (b-b_pre)))**2
272
+        phim = np.linalg.norm( np.dot(Phim_base, (x-xr)) )**2
273
+        mu1 = ((phid + alpha*phim) / phib) 
274
+
275
+    if lambdastar == "lcurve":
276
+        return x, ibreak, np.sqrt(phid/len(b)), PHIM, PHID/len(b), np.argmax(kappa)
277
+    else:
278
+        return x, ibreak, np.sqrt(phid/len(b))
279
+
280
+
281
+
282
+if __name__ == "__main__":
283
+    print("Test")

+ 2
- 1
setup.py View File

@@ -21,7 +21,7 @@ with open("README.md", "r") as fh:
21 21
     long_description = fh.read()
22 22
 
23 23
 setup(name='Akvo',
24
-      version='1.3.5',
24
+      version='1.3.6',
25 25
       python_requires='>3.7.0', # due to pyLemma
26 26
       description='Surface nuclear magnetic resonance workbench',
27 27
       long_description=long_description,
@@ -61,6 +61,7 @@ setup(name='Akvo',
61 61
               'console_scripts': [
62 62
                   'akvo = akvo.gui.akvoGUI:main',                  
63 63
                   'akvoK0 = akvo.tressel.calcAkvoKernel:main',                  
64
+                  'akvoQT = akvo.tressel.invertTA:main',                  
64 65
               ],              
65 66
           },
66 67
       #cmdclass = cmdclass,

Loading…
Cancel
Save