Browse Source

Akvo changes for kernel calculation (#1)

* Work towards loop info

* improvements, table is getting smarter about loop transmission

* better loop logic

* currently broken, but refactoring to enable processing of legacy data

* Binary file read is restored, working on ASCII

* Legacy FID's can be loaded

* Added option for python phasing rather than R

* Logging improvements with saved files

* A few tweaks

* reduced search for nls
add-license-1
Trevor Irons 6 years ago
parent
commit
566074546b
No account linked to committer's email address
6 changed files with 909 additions and 508 deletions
  1. 141
    54
      akvo/gui/akvoGUI.py
  2. 183
    135
      akvo/gui/main.ui
  3. 340
    170
      akvo/tressel/decay.py
  4. 211
    117
      akvo/tressel/mrsurvey.py
  5. 33
    32
      akvo/tressel/rotate.py
  6. 1
    0
      setup.py

+ 141
- 54
akvo/gui/akvoGUI.py View File

@@ -53,27 +53,37 @@ class VectorXr(yaml.YAMLObject):
53 53
         return "np.array(%r)" % (self.data)
54 54
 
55 55
 from collections import OrderedDict
56
-def represent_ordereddict(dumper, data):
57
-    value = []
58
-    for item_key, item_value in data.items():
59
-        node_key = dumper.represent_data(item_key)
60
-        node_value = dumper.represent_data(item_value)
61
-        value.append((node_key, node_value))
62
-    return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', value)
63
-yaml.add_representer(OrderedDict, represent_ordereddict)
56
+#def represent_ordereddict(dumper, data):
57
+#    print("representing IN DA HOUSE!!!!!!!!!!!!!!!!!!!!!")
58
+#    value = []
59
+#    for item_key, item_value in data.items():
60
+#        node_key = dumper.represent_data(item_key)
61
+#        node_value = dumper.represent_data(item_value)
62
+#        value.append((node_key, node_value))
63
+#    return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', value)
64
+#yaml.add_representer(OrderedDict, represent_ordereddict)
65
+
66
+def setup_yaml():
67
+    """ https://stackoverflow.com/a/8661021 """
68
+    represent_dict_order = lambda self, data:  self.represent_mapping('tag:yaml.org,2002:map', data.items())
69
+    yaml.add_representer(OrderedDict, represent_dict_order)    
70
+setup_yaml()
64 71
 
65 72
 class AkvoYamlNode(yaml.YAMLObject):
66
-    yaml_tag = u'!AkvoData'
73
+    yaml_tag = u'AkvoData'
67 74
     def __init__(self):
68 75
         self.Akvo_VERSION = version
69
-        self.Import = {}
76
+        self.Import = OrderedDict() # {}
70 77
         self.Processing = OrderedDict() 
71
-        #self.ProcessingFlow = []
72
-        #self.Data = {}
73
-    # For going the other way, data import based on Yaml serialization, 
78
+    #def __init__(self, node):
79
+    #    self.Akvo_VERSION = node["version"]
80
+    #    self.Import = OrderedDict( node["Import"] ) # {}
81
+    #    self.Processing = OrderedDict( node["Processing"] ) 
74 82
     def __repr__(self):
75 83
         return "%s(name=%r, Akvo_VESION=%r, Import=%r, Processing=%r)" % (
76
-            self.__class__.__name__, self.Akvo_VERSION, self.Import, OrderedDict(dict(self.Processing)) ) 
84
+            #self.__class__.__name__, self.Akvo_VERSION, self.Import, self.Processing )
85
+            self.__class__.__name__, self.Akvo_VERSION, self.Import, OrderedDict(self.Processing) ) 
86
+            #self.__class__.__name__, self.Akvo_VERSION, self.Import, OrderedDict(self.Processing)) 
77 87
     
78 88
 try:    
79 89
     import thread 
@@ -172,35 +182,48 @@ class ApplicationWindow(QtWidgets.QMainWindow):
172 182
         self.ui.mplwidget_navigator_2.setCanvas(self.ui.mplwidget_2)
173 183
 
174 184
         ##########################################################################
175
-        # modelling Table 
176
-        self.ui.loopTableWidget.setRowCount(40)       
177
-        self.ui.loopTableWidget.setColumnCount(5)      
178
-        self.ui.loopTableWidget.setHorizontalHeaderLabels( ["ch. tag", "Northing [m]","Easting [m]","Height [m]", "Radius"] )
179
-        self.ui.loopTableWidget.cellChanged.connect(self.cellChanged) 
185
+        # Loop Table 
186
+        self.ui.loopTableWidget.setRowCount(80)       
187
+        self.ui.loopTableWidget.setColumnCount(6)      
188
+        self.ui.loopTableWidget.setHorizontalHeaderLabels( ["ch. tag", \
189
+            "Northing [m]","Easting [m]","Height [m]", "Radius","Tx"] )
190
+        
191
+        for ir in range(0, self.ui.loopTableWidget.rowCount() ):
192
+            for ic in range(1, self.ui.loopTableWidget.columnCount() ):
193
+                pCell = QtWidgets.QTableWidgetItem()
194
+                #pCell.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
195
+                pCell.setFlags(QtCore.Qt.NoItemFlags) # not selectable 
196
+                pCell.setBackground( QtGui.QColor("lightgrey").lighter(110) )
197
+                self.ui.loopTableWidget.setItem(ir, ic, pCell)
198
+
199
+        self.ui.loopTableWidget.cellChanged.connect(self.loopCellChanged)
200
+        #self.ui.loopTableWidget.cellPressed.connect(self.loopCellChanged) 
201
+        self.ui.loopTableWidget.itemClicked.connect(self.loopCellClicked) 
202
+        #self.ui.loopTableWidget.cellPressed.connect(self.loopCellClicked) 
180 203
 
181 204
         self.ui.loopTableWidget.setDragDropOverwriteMode(False)
182
-        self.ui.loopTableWidget.setDragEnabled(True)
183
-        self.ui.loopTableWidget.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
184
-        
205
+        self.ui.loopTableWidget.setDragEnabled(False)
206
+        #self.ui.loopTableWidget.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
207
+
208
+        self.loops = {}        
209
+
185 210
         ##########################################################################
186 211
         # layer Table 
187 212
         self.ui.layerTableWidget.setRowCount(80)       
188 213
         self.ui.layerTableWidget.setColumnCount(3)      
189 214
         self.ui.layerTableWidget.setHorizontalHeaderLabels( [r"top [m]", r"bottom [m]", "σ [ Ωm]" ] )
190 215
 
191
-
216
+        # do we want this
192 217
         self.ui.layerTableWidget.setDragDropOverwriteMode(False)
193 218
         self.ui.layerTableWidget.setDragEnabled(True)
194 219
         self.ui.layerTableWidget.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
195 220
         
196 221
         pCell = QtWidgets.QTableWidgetItem()
197
-        #pCell.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
198 222
         pCell.setFlags(QtCore.Qt.NoItemFlags) # not selectable 
199
-        #pCell.setBackground( QtGui.QColor("lightblue").lighter(125) )
200 223
         pCell.setBackground( QtGui.QColor("lightgrey").lighter(110) )
201 224
         self.ui.layerTableWidget.setItem(0, 1, pCell)
202
-        for ir in range(1, 80):
203
-            for ic in range(0, 3):
225
+        for ir in range(1, self.ui.layerTableWidget.rowCount() ):
226
+            for ic in range(0, self.ui.layerTableWidget.columnCount() ):
204 227
                 pCell = QtWidgets.QTableWidgetItem()
205 228
                 #pCell.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
206 229
                 pCell.setFlags(QtCore.Qt.NoItemFlags) # not selectable 
@@ -208,6 +231,7 @@ class ApplicationWindow(QtWidgets.QMainWindow):
208 231
                 self.ui.layerTableWidget.setItem(ir, ic, pCell)
209 232
         self.ui.layerTableWidget.cellChanged.connect(self.sigmaCellChanged) 
210 233
 
234
+
211 235
     def sigmaCellChanged(self):
212 236
         self.ui.layerTableWidget.cellChanged.disconnect(self.sigmaCellChanged) 
213 237
         # TODO consider building the model whenever this is called. Would be nice to be able to 
@@ -259,7 +283,6 @@ class ApplicationWindow(QtWidgets.QMainWindow):
259 283
                     self.ui.layerTableWidget.cellChanged.connect(self.sigmaCellChanged) 
260 284
                     return
261 285
 
262
-            print("I'm here joey")
263 286
             # enable next layer
264 287
             pCell4 = self.ui.layerTableWidget.item(ii+1, jj)
265 288
             pCell4.setBackground( QtGui.QColor("lightblue") ) #.lighter(110))
@@ -277,27 +300,77 @@ class ApplicationWindow(QtWidgets.QMainWindow):
277 300
 
278 301
         self.ui.layerTableWidget.cellChanged.connect(self.sigmaCellChanged) 
279 302
 
303
+    def loopCellClicked(self, item):
304
+        print("checkstate", item.checkState(),item.row())
280 305
 
281
-    def cellChanged(self):
282
-        # TODO consider building the model whenever this is called. Would be nice to be able to 
283
-        # do that. Would require instead dist of T2 I guess. 
306
+        #self.ui.loopTableWidget.itemClicked.disconnect(self.loopCellClicked)
307
+        jj = item.column() 
308
+        ii = item.row()
309
+        tp = type(self.ui.loopTableWidget.item(ii, 0))
310
+        print("tp", tp, ii, jj)
311
+        if str(tp) == "<class 'NoneType'>": 
312
+            return 
313
+        #print("Clicked", ii, jj)
314
+        if jj == 5 and self.ui.loopTableWidget.item(ii, 0).text() in self.loops.keys():
315
+            #print("jj=5")
316
+            self.loops[ self.ui.loopTableWidget.item(ii, 0).text() ]["Tx"] = self.ui.loopTableWidget.item(ii, 5).checkState()
317
+            # update surrogates  
318
+            print("updating surrogates")
319
+            for point in self.loops[ self.ui.loopTableWidget.item(ii, 0).text() ]["points"][1:]:
320
+                pCell = self.ui.loopTableWidget.item(point, 5)
321
+                if self.ui.loopTableWidget.item(ii, 5).checkState():
322
+                    pCell.setCheckState(QtCore.Qt.Checked);
323
+                else:
324
+                    pCell.setCheckState(QtCore.Qt.Unchecked);
325
+ 
326
+            #print( "loops",  self.loops[ self.ui.loopTableWidget.item(ii, 0).text() ]["Tx"]) 
327
+         
328
+        #self.ui.loopTableWidget.itemClicked.connect(self.loopCellClicked) 
329
+
330
+    def loopCellChanged(self):
331
+        self.ui.loopTableWidget.cellChanged.disconnect(self.loopCellChanged) 
332
+        
284 333
         jj = self.ui.loopTableWidget.currentColumn()
285 334
         ii = self.ui.loopTableWidget.currentRow()
286
-            
287
-        #if self.ui.loopTableWidget.item(ii, jj) == None:
288
-        #    return
289 335
 
290
-        try:
291
-            eval (str( self.ui.loopTableWidget.item(ii, jj).text() ))
292
-        except:
293
-            if jj != 0:
294
-                Error = QtWidgets.QMessageBox()
295
-                Error.setWindowTitle("Error!")
296
-                Error.setText("Non-numeric value encountered")
297
-                Error.setDetailedText("Modelling parameters must be able to be cast into numeric values.")
298
-                Error.exec_()
336
+        if jj == 0 and len( self.ui.loopTableWidget.item(ii, jj).text().strip()) == 0:
337
+            for jjj in range(jj+1,jj+6): 
338
+                pCell = self.ui.loopTableWidget.item(ii, jjj)
339
+                pCell.setBackground( QtGui.QColor("white") )
340
+                pCell.setFlags( QtCore.Qt.NoItemFlags | QtCore.Qt.ItemIsUserCheckable   ) # not selectable 
341
+        elif jj == 0 and len( self.ui.loopTableWidget.item(ii, jj).text().strip() ): # ch. tag modified
342
+            for jjj in range(jj+1,jj+5): 
343
+                pCell = self.ui.loopTableWidget.item(ii, jjj)
344
+                pCell.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled )
345
+                pCell.setBackground( QtGui.QColor("lightblue") )
346
+            if self.ui.loopTableWidget.item(ii, jj).text() not in self.loops.keys():
347
+                # This is a new loop ID 
348
+                self.loops[ self.ui.loopTableWidget.item(ii, jj).text() ] = {}
349
+                self.loops[ self.ui.loopTableWidget.item(ii, jj).text() ]["Tx"] = self.ui.loopTableWidget.item(ii, 5).checkState()
350
+                self.loops[ self.ui.loopTableWidget.item(ii, jj).text() ]["points"] = [ii] 
351
+                # Transmitter cell 
352
+                pCell = self.ui.loopTableWidget.item(ii, jj+5)
353
+                pCell.setCheckState(QtCore.Qt.Unchecked)
354
+                pCell.setFlags( QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled  )
355
+                pCell.setBackground( QtGui.QColor("lightblue") ) 
356
+            else:
357
+                # This is an existing loop ID 
358
+                self.loops[ self.ui.loopTableWidget.item(ii, jj).text() ]["points"].append( ii ) 
359
+                pCell = self.ui.loopTableWidget.item(ii, jj+5)
360
+                pCell.setFlags(QtCore.Qt.NoItemFlags) # not selectable 
361
+                if self.loops[ self.ui.loopTableWidget.item(ii, 0).text() ]["Tx"]:
362
+                    pCell.setCheckState(QtCore.Qt.Checked);
363
+                else:
364
+                    pCell.setCheckState(QtCore.Qt.Unchecked);
365
+                #pCell.setFlags( )
366
+                pCell.setBackground( QtGui.QColor("lightblue") )
367
+
368
+
299 369
             
370
+ 
300 371
         self.plotLoops()
372
+        self.ui.loopTableWidget.cellChanged.connect(self.loopCellChanged) 
373
+
301 374
 
302 375
     def plotLoops(self):
303 376
                
@@ -310,8 +383,10 @@ class ApplicationWindow(QtWidgets.QMainWindow):
310 383
         for ii in range( self.ui.loopTableWidget.rowCount() ):
311 384
             for jj in range( self.ui.loopTableWidget.columnCount() ):
312 385
                 tp = type(self.ui.loopTableWidget.item(ii, jj))
313
-                if str(tp) == "<class 'NoneType'>":  # ugly hack needed by PySide for some strange reason.
314
-                    pass #print ("NONE")
386
+                if str(tp) == "<class 'NoneType'>":  
387
+                    pass 
388
+                elif not len(self.ui.loopTableWidget.item(ii, jj).text()): 
389
+                    pass
315 390
                 else:
316 391
                     if jj == 0: 
317 392
                         idx = self.ui.loopTableWidget.item(ii, 0).text()
@@ -331,6 +406,8 @@ class ApplicationWindow(QtWidgets.QMainWindow):
331 406
                 self.ui.mplwidget_3.ax1.plot(  np.array(nor[ii]), np.array(eas[ii])  )
332 407
             except:
333 408
                 pass 
409
+        #self.ui.mplwidget_3.figure.axes().set
410
+        plt.gca().set_aspect('equal') #, adjustable='box')
334 411
         self.ui.mplwidget_3.draw()
335 412
 
336 413
     def about(self):
@@ -356,7 +433,8 @@ class ApplicationWindow(QtWidgets.QMainWindow):
356 433
         self.RAWDataProc.updateProcTrigger.connect(self.updateProc)
357 434
 
358 435
     def openGMRRAWDataset(self):
359
-
436
+        """ Opens a GMR header file
437
+        """
360 438
         try:
361 439
             with open('.gmr.last.path') as f: 
362 440
                 fpath = f.readline()  
@@ -495,8 +573,9 @@ class ApplicationWindow(QtWidgets.QMainWindow):
495 573
             # Window centres 
496 574
 
497 575
         with open(SaveStr, 'w') as outfile:
498
-            for line in self.logText:
499
-                outfile.write(line+"\n")
576
+            #for line in self.logText:
577
+            #    outfile.write(line+"\n")
578
+            yaml.dump(self.YamlNode, outfile)   
500 579
             yaml.dump(INFO, outfile, default_flow_style=False)   
501 580
  
502 581
     def SavePreprocess(self):
@@ -536,8 +615,6 @@ class ApplicationWindow(QtWidgets.QMainWindow):
536 615
         INFO["transFreq"] = self.RAWDataProc.transFreq
537 616
         INFO["headerstr"] = str(self.headerstr)
538 617
         INFO["log"] = yaml.dump( self.YamlNode )  #self.logText  #MAK 20170127
539
-        
540
-        print ("YAML NODE", yaml.dump( self.YamlNode ) )
541 618
     
542 619
         self.RAWDataProc.DATADICT["INFO"] = INFO 
543 620
 
@@ -546,7 +623,8 @@ class ApplicationWindow(QtWidgets.QMainWindow):
546 623
 
547 624
     # Export XML file suitable for USGS ScienceBase Data Release
548 625
     def ExportXML(self):
549
-
626
+        """ This is a filler function for use by USGS collaborators 
627
+        """
550 628
         return 42
551 629
 
552 630
     def OpenPreprocess(self):
@@ -601,7 +679,15 @@ class ApplicationWindow(QtWidgets.QMainWindow):
601 679
             #print ( self.RAWDataProc.DATADICT["INFO"]["log"] )
602 680
         
603 681
         self.logText = self.RAWDataProc.DATADICT["INFO"]["log"] # YAML 
604
-        self.YamlNode = yaml.load( self.logText ) 
682
+
683
+        self.YamlNode = AkvoYamlNode( )  #self.logText )
684
+        self.YamlNode.Akvo_VERSION = (yaml.load( self.logText )).Akvo_VERSION
685
+        self.YamlNode.Import = OrderedDict((yaml.load( self.logText )).Import)
686
+        self.YamlNode.Processing = OrderedDict((yaml.load( self.logText )).Processing)
687
+        #self.YamlNode.Akvo_VERSION =  2 #yaml.load( self.logText )["Akvo_VERSION"] #, Loader=yaml.RoundTripLoader) # offending line! 
688
+        #self.YamlNode = AkvoYamlNode( self.logText ) # offending line! 
689
+        #print("import type", type( self.YamlNode.Processing ))
690
+        self.Log() 
605 691
             #self.ui.logTextBrowser.append( yaml.dump(self.YamlNode)) #, default_flow_style=False)  )
606 692
         #except KeyError:
607 693
         #    pass
@@ -779,8 +865,7 @@ class ApplicationWindow(QtWidgets.QMainWindow):
779 865
         #for line in nlogText: 
780 866
         #    self.ui.logTextBrowser.append( line )
781 867
         #    self.logText.append( line ) 
782
-            
783
-        self.ui.logTextBrowser.clear() 
868
+        self.ui.logTextBrowser.clear()
784 869
         self.ui.logTextBrowser.append( yaml.dump(self.YamlNode)) #, default_flow_style=False)  )
785 870
 
786 871
     def disable(self):
@@ -885,7 +970,9 @@ class ApplicationWindow(QtWidgets.QMainWindow):
885 970
 
886 971
     def calcQ(self):
887 972
         if "Calc Q" not in self.YamlNode.Processing.keys():
973
+            print("In CalcQ", yaml.dump(self.YamlNode.Processing)  )
888 974
             self.YamlNode.Processing["Calc Q"] = True
975
+            print( yaml.dump(self.YamlNode.Processing)  )
889 976
             self.Log()
890 977
         else:
891 978
             err_msg = "Q values have already been calculated"

+ 183
- 135
akvo/gui/main.ui View File

@@ -73,8 +73,8 @@
73 73
         <rect>
74 74
          <x>0</x>
75 75
          <y>0</y>
76
-         <width>982</width>
77
-         <height>921</height>
76
+         <width>967</width>
77
+         <height>922</height>
78 78
         </rect>
79 79
        </property>
80 80
        <layout class="QHBoxLayout" name="horizontalLayout_2">
@@ -96,7 +96,7 @@
96 96
            <enum>Qt::LeftToRight</enum>
97 97
           </property>
98 98
           <property name="currentIndex">
99
-           <number>0</number>
99
+           <number>2</number>
100 100
           </property>
101 101
           <widget class="QWidget" name="tab">
102 102
            <property name="minimumSize">
@@ -801,7 +801,7 @@ background: dark grey;
801 801
               <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
802 802
 &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
803 803
 p, li { white-space: pre-wrap; }
804
-&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:8pt; font-weight:400; font-style:italic;&quot;&gt;
804
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:8pt; font-weight:400; font-style:italic;&quot;&gt;
805 805
 &lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'DejaVu Serif'; font-size:9pt;&quot;&gt;Load supported RAW Dataset header from file menu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
806 806
              </property>
807 807
             </widget>
@@ -854,7 +854,7 @@ p, li { white-space: pre-wrap; }
854 854
               <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
855 855
 &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
856 856
 p, li { white-space: pre-wrap; }
857
-&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:italic;&quot;&gt;
857
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:italic;&quot;&gt;
858 858
 &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'DejaVu Serif'; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
859 859
              </property>
860 860
             </widget>
@@ -2489,7 +2489,7 @@ background: dark grey;
2489 2489
                 <rect>
2490 2490
                  <x>20</x>
2491 2491
                  <y>37</y>
2492
-                 <width>111</width>
2492
+                 <width>121</width>
2493 2493
                  <height>16</height>
2494 2494
                 </rect>
2495 2495
                </property>
@@ -2513,8 +2513,8 @@ background: dark grey;
2513 2513
               <widget class="QLabel" name="label_36">
2514 2514
                <property name="geometry">
2515 2515
                 <rect>
2516
-                 <x>560</x>
2517
-                 <y>40</y>
2516
+                 <x>10</x>
2517
+                 <y>190</y>
2518 2518
                  <width>61</width>
2519 2519
                  <height>16</height>
2520 2520
                 </rect>
@@ -2526,88 +2526,157 @@ background: dark grey;
2526 2526
               <widget class="QLineEdit" name="locEdit">
2527 2527
                <property name="geometry">
2528 2528
                 <rect>
2529
-                 <x>560</x>
2530
-                 <y>60</y>
2531
-                 <width>351</width>
2532
-                 <height>81</height>
2529
+                 <x>10</x>
2530
+                 <y>210</y>
2531
+                 <width>441</width>
2532
+                 <height>51</height>
2533 2533
                 </rect>
2534 2534
                </property>
2535 2535
               </widget>
2536
-              <widget class="QDoubleSpinBox" name="decSpinBox">
2536
+              <widget class="Line" name="line">
2537 2537
                <property name="geometry">
2538 2538
                 <rect>
2539
-                 <x>420</x>
2540
-                 <y>70</y>
2541
-                 <width>101</width>
2542
-                 <height>31</height>
2539
+                 <x>20</x>
2540
+                 <y>160</y>
2541
+                 <width>371</width>
2542
+                 <height>16</height>
2543 2543
                 </rect>
2544 2544
                </property>
2545
-               <property name="decimals">
2546
-                <number>1</number>
2545
+               <property name="orientation">
2546
+                <enum>Qt::Horizontal</enum>
2547 2547
                </property>
2548
-               <property name="minimum">
2549
-                <double>-90.000000000000000</double>
2548
+              </widget>
2549
+              <widget class="QLabel" name="label_52">
2550
+               <property name="geometry">
2551
+                <rect>
2552
+                 <x>0</x>
2553
+                 <y>260</y>
2554
+                 <width>191</width>
2555
+                 <height>31</height>
2556
+                </rect>
2550 2557
                </property>
2551
-               <property name="maximum">
2552
-                <double>90.000000000000000</double>
2558
+               <property name="text">
2559
+                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Comments and field notes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
2553 2560
                </property>
2554
-               <property name="value">
2555
-                <double>0.000000000000000</double>
2561
+              </widget>
2562
+              <widget class="QTextBrowser" name="txtComments">
2563
+               <property name="geometry">
2564
+                <rect>
2565
+                 <x>10</x>
2566
+                 <y>300</y>
2567
+                 <width>441</width>
2568
+                 <height>221</height>
2569
+                </rect>
2570
+               </property>
2571
+               <property name="readOnly">
2572
+                <bool>false</bool>
2556 2573
                </property>
2557 2574
               </widget>
2558
-              <widget class="QDoubleSpinBox" name="incSpinBox">
2575
+              <widget class="QTimeEdit" name="timeEdit">
2559 2576
                <property name="geometry">
2560 2577
                 <rect>
2561
-                 <x>420</x>
2562
-                 <y>30</y>
2563
-                 <width>101</width>
2564
-                 <height>31</height>
2578
+                 <x>150</x>
2579
+                 <y>110</y>
2580
+                 <width>118</width>
2581
+                 <height>29</height>
2565 2582
                 </rect>
2566 2583
                </property>
2567
-               <property name="decimals">
2568
-                <number>1</number>
2584
+               <property name="calendarPopup">
2585
+                <bool>true</bool>
2569 2586
                </property>
2570
-               <property name="minimum">
2571
-                <double>-90.000000000000000</double>
2587
+              </widget>
2588
+              <widget class="QDateEdit" name="dateEdit">
2589
+               <property name="geometry">
2590
+                <rect>
2591
+                 <x>150</x>
2592
+                 <y>70</y>
2593
+                 <width>112</width>
2594
+                 <height>29</height>
2595
+                </rect>
2572 2596
                </property>
2573
-               <property name="maximum">
2574
-                <double>90.000000000000000</double>
2597
+               <property name="calendarPopup">
2598
+                <bool>true</bool>
2575 2599
                </property>
2576
-               <property name="value">
2577
-                <double>45.000000000000000</double>
2600
+              </widget>
2601
+              <widget class="QLabel" name="label_50">
2602
+               <property name="geometry">
2603
+                <rect>
2604
+                 <x>24</x>
2605
+                 <y>117</y>
2606
+                 <width>81</width>
2607
+                 <height>16</height>
2608
+                </rect>
2609
+               </property>
2610
+               <property name="text">
2611
+                <string>Survey time</string>
2578 2612
                </property>
2579 2613
               </widget>
2580
-              <widget class="QLabel" name="label_37">
2614
+              <widget class="QDoubleSpinBox" name="tempSpinBox">
2581 2615
                <property name="geometry">
2582 2616
                 <rect>
2583
-                 <x>300</x>
2617
+                 <x>150</x>
2584 2618
                  <y>30</y>
2585 2619
                  <width>111</width>
2586
-                 <height>20</height>
2620
+                 <height>29</height>
2587 2621
                 </rect>
2588 2622
                </property>
2589
-               <property name="text">
2590
-                <string>B Inclination [°]</string>
2623
+               <property name="value">
2624
+                <double>20.000000000000000</double>
2591 2625
                </property>
2592 2626
               </widget>
2593
-              <widget class="QLabel" name="label_38">
2627
+              <widget class="QTableWidget" name="loopTableWidget">
2594 2628
                <property name="geometry">
2595 2629
                 <rect>
2596
-                 <x>300</x>
2597
-                 <y>75</y>
2598
-                 <width>111</width>
2599
-                 <height>20</height>
2630
+                 <x>10</x>
2631
+                 <y>560</y>
2632
+                 <width>641</width>
2633
+                 <height>291</height>
2634
+                </rect>
2635
+               </property>
2636
+               <property name="toolTip">
2637
+                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This table is used to enter coil geometries the format is as follows: each row specifies a single point on a coil. The first column is the coil index (using the GMR channel is useful), the next three colums specify the point in Northing, Easting, and Elevation. These can either be local coordinates or global ones. The final column specifies the loop radius if it is a circle or figure 8, for non circular or figure 8 loops leave this column blank. For figure-8 loops the coils do not need to be touching (see Irons and Kass, 2017). If a given index has 1 row it will be a circular loop, two rows will be a figure 8, and more than that will be a polygonal representation of the points, linearlly interpolated between them. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
2638
+               </property>
2639
+              </widget>
2640
+              <widget class="QLabel" name="label_54">
2641
+               <property name="geometry">
2642
+                <rect>
2643
+                 <x>10</x>
2644
+                 <y>540</y>
2645
+                 <width>91</width>
2646
+                 <height>16</height>
2600 2647
                 </rect>
2601 2648
                </property>
2602 2649
                <property name="text">
2603
-                <string>B Declination [°] </string>
2650
+                <string>Surface loops</string>
2651
+               </property>
2652
+              </widget>
2653
+              <widget class="MyDynamicMplCanvas" name="mplwidget_3" native="true">
2654
+               <property name="geometry">
2655
+                <rect>
2656
+                 <x>460</x>
2657
+                 <y>0</y>
2658
+                 <width>500</width>
2659
+                 <height>500</height>
2660
+                </rect>
2661
+               </property>
2662
+               <property name="sizePolicy">
2663
+                <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2664
+                 <horstretch>0</horstretch>
2665
+                 <verstretch>0</verstretch>
2666
+                </sizepolicy>
2667
+               </property>
2668
+               <property name="minimumSize">
2669
+                <size>
2670
+                 <width>500</width>
2671
+                 <height>500</height>
2672
+                </size>
2604 2673
                </property>
2605 2674
               </widget>
2606 2675
               <widget class="QDoubleSpinBox" name="intensitySpinBox">
2607 2676
                <property name="geometry">
2608 2677
                 <rect>
2609
-                 <x>420</x>
2610
-                 <y>110</y>
2678
+                 <x>790</x>
2679
+                 <y>675</y>
2611 2680
                  <width>101</width>
2612 2681
                  <height>31</height>
2613 2682
                 </rect>
@@ -2622,108 +2691,113 @@ background: dark grey;
2622 2691
                 <double>50000.000000000000000</double>
2623 2692
                </property>
2624 2693
               </widget>
2625
-              <widget class="QLabel" name="label_51">
2694
+              <widget class="QLabel" name="label_38">
2626 2695
                <property name="geometry">
2627 2696
                 <rect>
2628
-                 <x>300</x>
2629
-                 <y>115</y>
2697
+                 <x>670</x>
2698
+                 <y>640</y>
2630 2699
                  <width>111</width>
2631 2700
                  <height>20</height>
2632 2701
                 </rect>
2633 2702
                </property>
2634 2703
                <property name="text">
2635
-                <string>B Intensity [nT]</string>
2704
+                <string>B Declination [°] </string>
2636 2705
                </property>
2637 2706
               </widget>
2638
-              <widget class="Line" name="line">
2707
+              <widget class="QLabel" name="label_37">
2639 2708
                <property name="geometry">
2640 2709
                 <rect>
2641
-                 <x>20</x>
2642
-                 <y>160</y>
2643
-                 <width>901</width>
2644
-                 <height>16</height>
2710
+                 <x>670</x>
2711
+                 <y>600</y>
2712
+                 <width>111</width>
2713
+                 <height>20</height>
2645 2714
                 </rect>
2646 2715
                </property>
2647
-               <property name="orientation">
2648
-                <enum>Qt::Horizontal</enum>
2716
+               <property name="text">
2717
+                <string>B Inclination [°]</string>
2649 2718
                </property>
2650 2719
               </widget>
2651
-              <widget class="QLabel" name="label_52">
2720
+              <widget class="QDoubleSpinBox" name="decSpinBox">
2652 2721
                <property name="geometry">
2653 2722
                 <rect>
2654
-                 <x>10</x>
2655
-                 <y>180</y>
2656
-                 <width>191</width>
2723
+                 <x>790</x>
2724
+                 <y>635</y>
2725
+                 <width>101</width>
2657 2726
                  <height>31</height>
2658 2727
                 </rect>
2659 2728
                </property>
2660
-               <property name="text">
2661
-                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Comments and field notes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
2729
+               <property name="decimals">
2730
+                <number>1</number>
2662 2731
                </property>
2663
-              </widget>
2664
-              <widget class="QTextBrowser" name="txtComments">
2665
-               <property name="geometry">
2666
-                <rect>
2667
-                 <x>30</x>
2668
-                 <y>220</y>
2669
-                 <width>881</width>
2670
-                 <height>631</height>
2671
-                </rect>
2732
+               <property name="minimum">
2733
+                <double>-90.000000000000000</double>
2672 2734
                </property>
2673
-               <property name="readOnly">
2674
-                <bool>false</bool>
2735
+               <property name="maximum">
2736
+                <double>90.000000000000000</double>
2737
+               </property>
2738
+               <property name="value">
2739
+                <double>0.000000000000000</double>
2675 2740
                </property>
2676 2741
               </widget>
2677
-              <widget class="QTimeEdit" name="timeEdit">
2742
+              <widget class="QDoubleSpinBox" name="incSpinBox">
2678 2743
                <property name="geometry">
2679 2744
                 <rect>
2680
-                 <x>140</x>
2681
-                 <y>110</y>
2682
-                 <width>118</width>
2683
-                 <height>29</height>
2745
+                 <x>790</x>
2746
+                 <y>595</y>
2747
+                 <width>101</width>
2748
+                 <height>31</height>
2684 2749
                 </rect>
2685 2750
                </property>
2686
-               <property name="calendarPopup">
2687
-                <bool>true</bool>
2751
+               <property name="decimals">
2752
+                <number>1</number>
2753
+               </property>
2754
+               <property name="minimum">
2755
+                <double>-90.000000000000000</double>
2756
+               </property>
2757
+               <property name="maximum">
2758
+                <double>90.000000000000000</double>
2759
+               </property>
2760
+               <property name="value">
2761
+                <double>45.000000000000000</double>
2688 2762
                </property>
2689 2763
               </widget>
2690
-              <widget class="QDateEdit" name="dateEdit">
2764
+              <widget class="QLabel" name="label_51">
2691 2765
                <property name="geometry">
2692 2766
                 <rect>
2693
-                 <x>140</x>
2694
-                 <y>70</y>
2695
-                 <width>112</width>
2696
-                 <height>29</height>
2767
+                 <x>670</x>
2768
+                 <y>680</y>
2769
+                 <width>111</width>
2770
+                 <height>20</height>
2697 2771
                 </rect>
2698 2772
                </property>
2699
-               <property name="calendarPopup">
2700
-                <bool>true</bool>
2773
+               <property name="text">
2774
+                <string>B Intensity [nT]</string>
2701 2775
                </property>
2702 2776
               </widget>
2703
-              <widget class="QLabel" name="label_50">
2777
+              <widget class="QLabel" name="label_57">
2704 2778
                <property name="geometry">
2705 2779
                 <rect>
2706
-                 <x>24</x>
2707
-                 <y>117</y>
2708
-                 <width>81</width>
2780
+                 <x>670</x>
2781
+                 <y>560</y>
2782
+                 <width>121</width>
2709 2783
                  <height>16</height>
2710 2784
                 </rect>
2711 2785
                </property>
2712 2786
                <property name="text">
2713
-                <string>Survey time</string>
2787
+                <string>Magnetic field</string>
2714 2788
                </property>
2715 2789
               </widget>
2716
-              <widget class="QDoubleSpinBox" name="tempSpinBox">
2790
+              <widget class="Line" name="line_3">
2717 2791
                <property name="geometry">
2718 2792
                 <rect>
2719
-                 <x>140</x>
2720
-                 <y>30</y>
2721
-                 <width>111</width>
2722
-                 <height>29</height>
2793
+                 <x>670</x>
2794
+                 <y>540</y>
2795
+                 <width>251</width>
2796
+                 <height>20</height>
2723 2797
                 </rect>
2724 2798
                </property>
2725
-               <property name="value">
2726
-                <double>20.000000000000000</double>
2799
+               <property name="orientation">
2800
+                <enum>Qt::Horizontal</enum>
2727 2801
                </property>
2728 2802
               </widget>
2729 2803
              </widget>
@@ -2734,19 +2808,6 @@ background: dark grey;
2734 2808
            <attribute name="title">
2735 2809
             <string>Kernel calc</string>
2736 2810
            </attribute>
2737
-           <widget class="QTableWidget" name="loopTableWidget">
2738
-            <property name="geometry">
2739
-             <rect>
2740
-              <x>20</x>
2741
-              <y>70</y>
2742
-              <width>441</width>
2743
-              <height>271</height>
2744
-             </rect>
2745
-            </property>
2746
-            <property name="toolTip">
2747
-             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This table is used to enter coil geometries the format is as follows: each row specifies a single point on a coil. The first column is the coil index (using the GMR channel is useful), the next three colums specify the point in Northing, Easting, and Elevation. These can either be local coordinates or global ones. The final column specifies the loop radius if it is a circle or figure 8, for non circular or figure 8 loops leave this column blank. For figure-8 loops the coils do not need to be touching (see Irons and Kass, 2017). If a given index has 1 row it will be a circular loop, two rows will be a figure 8, and more than that will be a polygonal representation of the points, linearlly interpolated between them. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
2748
-            </property>
2749
-           </widget>
2750 2811
            <widget class="Line" name="line_2">
2751 2812
             <property name="geometry">
2752 2813
              <rect>
@@ -2760,20 +2821,7 @@ background: dark grey;
2760 2821
              <enum>Qt::Horizontal</enum>
2761 2822
             </property>
2762 2823
            </widget>
2763
-           <widget class="QLabel" name="label_54">
2764
-            <property name="geometry">
2765
-             <rect>
2766
-              <x>20</x>
2767
-              <y>40</y>
2768
-              <width>91</width>
2769
-              <height>16</height>
2770
-             </rect>
2771
-            </property>
2772
-            <property name="text">
2773
-             <string>Surface loops</string>
2774
-            </property>
2775
-           </widget>
2776
-           <widget class="MyDynamicMplCanvas" name="mplwidget_3" native="true">
2824
+           <widget class="MyDynamicMplCanvas" name="mplwidget_4" native="true">
2777 2825
             <property name="geometry">
2778 2826
              <rect>
2779 2827
               <x>480</x>
@@ -3098,8 +3146,8 @@ background: red;
3098 3146
              <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
3099 3147
 &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
3100 3148
 p, li { white-space: pre-wrap; }
3101
-&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
3102
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;All processing steps are recorded here for your records&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
3149
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
3150
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans Serif'; font-size:9pt;&quot;&gt;All processing steps are recorded here for your records&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
3103 3151
             </property>
3104 3152
            </widget>
3105 3153
            <widget class="QLabel" name="label_53">
@@ -3130,7 +3178,7 @@ p, li { white-space: pre-wrap; }
3130 3178
      <x>0</x>
3131 3179
      <y>0</y>
3132 3180
      <width>1000</width>
3133
-     <height>19</height>
3181
+     <height>25</height>
3134 3182
     </rect>
3135 3183
    </property>
3136 3184
    <widget class="QMenu" name="menuFile">

+ 340
- 170
akvo/tressel/decay.py View File

@@ -1,5 +1,7 @@
1
-import numpy, pylab,array #,rpy2
2
-
1
+import numpy, array #,rpy2
2
+from matplotlib import pyplot as plt
3
+import numpy as np
4
+from scipy.optimize import least_squares
3 5
 from rpy2.robjects.packages import importr
4 6
 
5 7
 import rpy2.robjects as robjects
@@ -52,7 +54,7 @@ def peakPicker(data, omega, dt):
52 54
 
53 55
 #################################################
54 56
 # Regress for T2 using rpy2 interface
55
-def regressCurve(peaks,times,sigma2=None  ,intercept=True):
57
+def regressCurve(peaks,times,sigma2=1,intercept=True):
56 58
 
57 59
     # TODO, if regression fails, it might be because there is no exponential
58 60
     # term, maybe do a second regression then on a linear model. 
@@ -65,23 +67,21 @@ def regressCurve(peaks,times,sigma2=None  ,intercept=True):
65 67
     robjects.globalenv['b1'] = b1
66 68
     robjects.globalenv['b2'] = b2
67 69
     robjects.globalenv['rT2'] = rT2
68
-    #robjects.globalenv['sigma2'] = sigma2
70
+    robjects.globalenv['sigma2'] = sigma2
69 71
     value = robjects.FloatVector(peaks)
70 72
     times = robjects.FloatVector(numpy.array(times))
71 73
     
72 74
 #    my_weights = robjects.RVector(value/sigma2)
73 75
 #    robjects.globalenv['my_weigts'] = my_weights
74 76
 
75
-    if sigma2 != None:
76
-        # print ("weighting")
77
-        #tw = numpy.array(peaks)/sigma2 
78
-        my_weights = robjects.FloatVector( sigma2 )
79
-    #else:
80
-    #    my_weights = robjects.FloatVector(numpy.ones(len(peaks))) 
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))) 
81 83
 
82
-        robjects.globalenv['my_weights'] = my_weights
83
-        #print (my_weights)
84
-        #print (len(peaks))
84
+#    robjects.globalenv['my_weights'] = my_weights
85 85
     
86 86
     if (intercept):
87 87
         my_list = robjects.r('list(b1=50, b2=1e2, rT2=0.03)')
@@ -107,27 +107,121 @@ def regressCurve(peaks,times,sigma2=None  ,intercept=True):
107 107
     env['times'] = times
108 108
     
109 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
-
114
-
110
+    my_weights = robjects.r('rep(1,length(value))')
111
+    for ii in range(len(my_weights)):
112
+        my_weights[ii] *= peaks[ii]/sigma2
115 113
     Error = False
116 114
     #fit = robjects.r.nls(fmla,start=my_list,control=my_cont,weights=my_weights)
117
-    if (sigma2 != None):
118
-        #print("SIGMA 2")
115
+    if (sigma2 != 1):
116
+        print("SIGMA 2")
119 117
         #fit = robjects.r.tryCatch(robjects.r.suppressWarnings(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port", \
120 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:
121 122
         try:
122
-            fit = robjects.r.tryCatch(    robjects.r.nls(fmla, start=my_list, control=my_cont, weights=my_weights, algorithm="port" , \
123
-                lower=my_lower,upper=my_upper))
123
+            fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port"))#,lower=my_lower,upper=my_upper))
124 124
         except:
125 125
             print("regression issue pass")
126 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))#, \
127 221
                             # weights=my_weights))
128 222
     else:
129 223
         try:
130
-            fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port",lower=my_lower,upper=my_upper))
224
+            fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list,control=my_cont,algorithm="port"))#,lower=my_lower,upper=my_upper))
131 225
         except:
132 226
             print("regression issue pass")
133 227
             Error = True
@@ -156,41 +250,138 @@ def regressCurve(peaks,times,sigma2=None  ,intercept=True):
156 250
             #print r['$'](report,'par')[0]
157 251
             #print r['$'](report,'par')[1]
158 252
             #print r['$'](report,'par')[2]
159
-        return [b1,b2,rT2] 
253
+        return [b1,b2,rT2, bb2, rrT2] 
160 254
     else:
161 255
         if not Error:
162 256
             rT2 =  r['$'](report,'par')[1]
163 257
             b2  =  r['$'](report,'par')[0]
258
+            rrT2 =  r['$'](report,'par')[3]
259
+            bb2  =  r['$'](report,'par')[2]
164 260
         else:
165 261
             print("ERROR DETECTED, regressed values set to default")
166
-        return [b2, rT2] 
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.sin(2.*np.pi*x[2]*t + x[1])*np.exp(-t/x[3]), \
273
+                           +x[0]*np.cos(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, x0="None"): 
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
+    print("Pure Python Quad Det")
295
+    # df 
296
+    if x0=="None":
297
+        x0 = np.array( [1., 0., 0., .2] )
298
+        res_lsq = least_squares(fun, x0, args=(tt, np.concatenate((X, Y))), loss='cauchy', f_scale=1.0,\
299
+            bounds=( [1., -np.pi, -5, .005] , [1000., np.pi, 5, .800] ))
300
+    else:
301
+        res_lsq = least_squares(fun, x0, args=(tt, np.concatenate((X, Y))), loss='cauchy', f_scale=1.0,\
302
+            bounds=( [1., -np.pi, -5, .005] , [1000., np.pi, 5, .800] ))
167 303
 
168
-def quadratureDetect(X, Y, tt):
169
-    
170
-    r   = robjects.r         
304
+        #bounds=( [0., 0, -20, .0] , [1., np.pi, 20, .6] ))
171 305
 
172
-    robjects.r(''' 
173
-         Xc <- function(E0, df, tt, phi, T2) {
174
-	            E0 * -sin(2*pi*df*tt + phi) * exp(-tt/T2)
175
-                }
306
+    x = res_lsq.x 
307
+    return res_lsq.success, x[0], x[2], x[1], x[3]
308
+    
309
+    # no df
310
+    #x = np.array( [1., 0., 0.2] )
311
+    #res_lsq = least_squares(fun2, x, args=(tt, np.concatenate((X, Y))), loss='soft_l1', f_scale=0.1)
312
+    #x = res_lsq.x 
313
+    #return conv, E0,df,phi,T2
314
+    #return res_lsq.success, x[0], 0, x[1], x[2]
176 315
 
177
-         Yc <- function(E0, df, tt, phi, T2) {
178
-	            E0 * cos(2*pi*df*tt + phi) * exp(-tt/T2)
179
-                } 
180
-         ''')  
316
+def quadratureDetect(X, Y, tt, CorrectFreq=False, BiExp=False, CorrectDC=False):
317
+ 
318
+    r   = robjects.r        
319
+
320
+    if CorrectDC:
321
+        robjects.r(''' 
322
+             Xc1 <- function(E01, df, tt, phi, T2_1, DC) {
323
+	                DC + E01*cos(2*pi*df*tt + phi) * exp(-tt/T2_1)
324
+            }
325
+    
326
+            Yc1 <- function(E01, df, tt, phi, T2_1, DC) {
327
+	                DC - E01*sin(2*pi*df*tt + phi) * exp(-tt/T2_1)
328
+            } 
329
+            ''')
330
+    else:   
331
+        robjects.r(''' 
332
+             Xc1 <- function(E01, df, tt, phi, T2_1) {
333
+	                E01*cos(2*pi*df*tt + phi) * exp(-tt/T2_1)
334
+            }
335
+    
336
+            Yc1 <- function(E01, df, tt, phi, T2_1) {
337
+	                -E01*sin(2*pi*df*tt + phi) * exp(-tt/T2_1)
338
+            } 
339
+            ''')
340
+
341
+    # bi-exponential 
342
+    if CorrectDC:
343
+        robjects.r(''' 
344
+             Xc2 <- function(E01, E02, df, tt, phi, T2_1, T2_2, DC) {
345
+	               DC + E01*cos(2*pi*df*tt + phi) * exp(-tt/T2_1) + 
346
+	                DC + E02*cos(2*pi*df*tt + phi) * exp(-tt/T2_2)
347
+            }
348
+
349
+            Yc2 <- function(E01, E02, df, tt, phi, T2_1, T2_2, DC) {
350
+	                DC - E01*sin(2*pi*df*tt + phi) * exp(-tt/T2_1) + 
351
+	                DC - E02*sin(2*pi*df*tt + phi) * exp(-tt/T2_2)
352
+            } 
353
+            ''')
354
+    else:   
355
+        robjects.r(''' 
356
+             Xc2 <- function(E01, E02, df, tt, phi, T2_1, T2_2) {
357
+	               E01*cos(2*pi*df*tt + phi) * exp(-tt/T2_1) + 
358
+	               E02*cos(2*pi*df*tt + phi) * exp(-tt/T2_2)
359
+            }
360
+
361
+            Yc2 <- function(E01, E02, df, tt, phi, T2_1, T2_2) {
362
+	                -E01*sin(2*pi*df*tt + phi) * exp(-tt/T2_1) + 
363
+	                -E02*sin(2*pi*df*tt + phi) * exp(-tt/T2_2)
364
+            } 
365
+            ''')
181 366
 
182 367
     # Make 0 vector 
183 368
     Zero = robjects.FloatVector(numpy.zeros(len(X)))
184 369
     
185 370
     # Fitted Parameters
186
-    E0 = 0.
371
+    E01 = 0.
372
+    E02 = 0.
187 373
     df = 0.
188 374
     phi = 0.
189
-    T2 = 0.
190
-    robjects.globalenv['E0'] = E0
375
+    T2_1 = 0.
376
+    T2_2 = 0.
377
+    DC = 0.
378
+    robjects.globalenv['DC'] = DC
379
+    robjects.globalenv['E01'] = E01
380
+    robjects.globalenv['E02'] = E02
191 381
     robjects.globalenv['df'] = df
192 382
     robjects.globalenv['phi'] = phi
193
-    robjects.globalenv['T2'] = T2
383
+    robjects.globalenv['T2_1'] = T2_1
384
+    robjects.globalenv['T2_2'] = T2_2
194 385
     XY = robjects.FloatVector(numpy.concatenate((X,Y)))
195 386
     
196 387
     # Arrays
@@ -199,10 +390,50 @@ def quadratureDetect(X, Y, tt):
199 390
     Y = robjects.FloatVector(numpy.array(Y))
200 391
     Zero = robjects.FloatVector(numpy.array(Zero))
201 392
 
202
-    #fmla = robjects.Formula('Zero ~ QI( E0, df, tt, phi, T2, X, Y )')
203
-    #fmla = robjects.Formula('X ~ Xc( E0, df, tt, phi, T2 )')
204
-    #fmla = robjects.Formula('Y ~ Yc( E0, df, tt, phi, T2 )')
205
-    fmla = robjects.Formula('XY ~ c(Xc( E0, df, tt, phi, T2 ), Yc( E0, df, tt, phi, T2 ))')
393
+    
394
+
395
+    if BiExp:
396
+        if CorrectDC:
397
+            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 ))')
398
+            if CorrectFreq:    
399
+                start = robjects.r('list(E01=.100, E02=.01,   df=0,    phi=0.    ,  T2_1=.100, T2_2=.01, DC=0.0)')
400
+                lower = robjects.r('list(E01=1e-6, E02=1e-6,  df=-50,  phi=-3.14 ,  T2_1=.001, T2_2=.001, DC=0.0)')
401
+                upper = robjects.r('list(E01=1.00, E02=1.0,   df=50,   phi=3.14  ,  T2_1=.800, T2_2=.8, DC=0.5)')
402
+            else:
403
+                start = robjects.r('list(E01=.100, E02=.01,   phi=0.9   ,  T2_1=.100, T2_2=.01,  DC=0.0)')
404
+                lower = robjects.r('list(E01=1e-6, E02=1e-6,  phi=-3.14 ,  T2_1=.001, T2_2=.001, DC=0.0)')
405
+                upper = robjects.r('list(E01=1.00, E02=1.0,   phi=3.14  ,  T2_1=.800, T2_2=.8,   DC=0.5)')
406
+        else:
407
+            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))')
408
+            if CorrectFreq:    
409
+                start = robjects.r('list(E01=.100, E02=.01,   df=0,    phi=0.    ,  T2_1=.100, T2_2=.01)')
410
+                lower = robjects.r('list(E01=1e-6, E02=1e-6,  df=-50,  phi=-3.14 ,  T2_1=.001, T2_2=.001)')
411
+                upper = robjects.r('list(E01=1.00, E02=1.0,   df=50,   phi=3.14  ,  T2_1=.800, T2_2=.8)')
412
+            else:
413
+                start = robjects.r('list(E01=.100, E02=.01,   phi=0.9   ,  T2_1=.100, T2_2=.01)')
414
+                lower = robjects.r('list(E01=1e-6, E02=1e-6,  phi=-3.14 ,  T2_1=.001, T2_2=.001)')
415
+                upper = robjects.r('list(E01=1.00, E02=1.0,   phi=3.14  ,  T2_1=.800, T2_2=.8)')
416
+    else: 
417
+        if CorrectDC:
418
+            fmla = robjects.Formula('XY ~ c(Xc1( E01, df, tt, phi, T2_1, DC), Yc1( E01, df, tt, phi, T2_1,DC))')
419
+            if CorrectFreq:    
420
+                start = robjects.r('list(E01=.100, df=0   , phi=0.   , T2_1=.100, DC=0.0)')
421
+                lower = robjects.r('list(E01=1e-6, df=-50., phi=-3.14, T2_1=.001, DC=0.0)')
422
+                upper = robjects.r('list(E01=1.00, df=50. , phi=3.14 , T2_1=.800, DC=0.5)')
423
+            else:
424
+                start = robjects.r('list(E01=.100, phi= 0.  , T2_1=.100, DC=0.0)')
425
+                lower = robjects.r('list(E01=1e-6, phi=-3.13, T2_1=.001, DC=0.0)')
426
+                upper = robjects.r('list(E01=1.00, phi= 3.13, T2_1=.800, DC=0.5)')
427
+        else:
428
+            fmla = robjects.Formula('XY ~ c(Xc1( E01, df, tt, phi, T2_1), Yc1( E01, df, tt, phi, T2_1))')
429
+            if CorrectFreq:    
430
+                start = robjects.r('list(E01=.100, df=0     , phi=0.   ,  T2_1=.100)')
431
+                lower = robjects.r('list(E01=1e-6, df=-50. , phi=-3.14 ,  T2_1=.001)')
432
+                upper = robjects.r('list(E01=1.00, df=50.  , phi=3.14  ,  T2_1=.800)')
433
+            else:
434
+                start = robjects.r('list(E01=.100, phi= 0.  , T2_1=.100)')
435
+                lower = robjects.r('list(E01=1e-6, phi=-3.13, T2_1=.001)')
436
+                upper = robjects.r('list(E01=1.00, phi= 3.13, T2_1=.800)')
206 437
 
207 438
     env = fmla.getenvironment()
208 439
     env['Zero'] = Zero
@@ -210,95 +441,51 @@ def quadratureDetect(X, Y, tt):
210 441
     env['Y'] = Y
211 442
     env['XY'] = XY 
212 443
     env['tt'] = tt
213
-    
214
-    # Bounds and control    
215
-    start = robjects.r('list(E0=100,   df= 0   , phi=   0.00,  T2=.100)')
216
-    lower = robjects.r('list(E0=1,     df=-13.0, phi=  -3.14,  T2=.005)')
217
-    upper = robjects.r('list(E0=1000,  df= 13.0, phi=   3.14,  T2=.800)')
218 444
 
219 445
     cont = robjects.r('nls.control(maxiter=10000, warnOnly=TRUE, printEval=FALSE)')
220 446
     
221 447
     fit = robjects.r.tryCatch(robjects.r.nls(fmla, start=start, control=cont, lower=lower, upper=upper, algorithm='port')) #, \
448
+    #fit = robjects.r.tryCatch(robjects.r.nls(fmla, start=start, control=cont)) #, \
222 449
     report =  r.summary(fit)
223
-    #print (report)
224
-    
225
-    E0  =  r['$'](report,'par')[0]
226
-    df  =  r['$'](report,'par')[1]
227
-    phi =  r['$'](report,'par')[2]
228
-    T2  =  r['$'](report,'par')[3]
229
-    #print ( E0,df,phi,T2 )
230
-    return E0,df,phi,T2
231
-    
232
-
233
-#################################################
234
-# Regress for T2 using rpy2 interface
235
-def regressSpec(w, wL, X): #,sigma2=1,intercept=True):
236 450
 
237
-    # compute s
238
-    s = -1j*w
239
-
240
-    # TODO, if regression fails, it might be because there is no exponential
241
-    # term, maybe do a second regression then on a linear model. 
242
-    a   = 0                  # Linear 
243
-    rT2 = 0.1                # T2 regressed
244
-    r   = robjects.r         
245
-
246
-    # Variable shared between R and Python
247
-    robjects.globalenv['a'] = a
248
-    #robjects.globalenv['w'] = w
249
-    robjects.globalenv['rT2'] = rT2
250
-    robjects.globalenv['wL'] = wL
251
-    robjects.globalenv['nb'] = 0
252
-
253
-    #s = robjects.ComplexVector(numpy.array(s))
254
-    w = robjects.FloatVector(numpy.array(w))
255
-    XX = robjects.FloatVector(X)
256
-    #Xr = robjects.FloatVector(numpy.real(X))
257
-    #Xi = robjects.FloatVector(numpy.imag(X))
258
-    #Xa = robjects.FloatVector(numpy.abs(X))
259
-    #Xri = robjects.FloatVector(numpy.concatenate((Xr,Xi)))
260
-    
261
-    #my_lower = robjects.r('list(a=.001, rT2=.001, nb=.0001)')
262
-    my_lower = robjects.r('list(a=.001, rT2=.001)')
263
-    #my_upper = robjects.r('list(a=1.5, rT2=.300, nb =100.)')
264
-    my_upper = robjects.r('list(a=1.5, rT2=.300)')
265
-     
266
-    #my_list = robjects.r('list(a=.2, rT2=0.03, nb=.1)')
267
-    my_list = robjects.r('list(a=.2, rT2=0.03)')
268
-    my_cont = robjects.r('nls.control(maxiter=5000, warnOnly=TRUE, printEval=FALSE)')
269
-    
270
-    #fmla = robjects.Formula('Xri ~ c(a*Re((wL) / (wL^2+(s+1/rT2)^2 )), a*Im((wL)/(wL^2 + (s+1/rT2)^2 )))') # envelope
271
-    ##fmla = robjects.Formula('Xri ~ c(a*Re((wL) / (wL^2+(s+1/rT2)^2 )), a*Im((wL)/(wL^2 + (s+1/rT2)^2 )))') # envelope
272
-    #fmla = robjects.Formula('XX ~ a*(wL) / (wL^2 + (s+1/rT2)^2 )') # complex
273
-    #fmla = robjects.Formula('Xa ~ abs(a*(wL) / (wL^2 + (s+1/rT2)^2 )) + nb') # complex
274
-    #fmla = robjects.Formula('XX ~ Re(a*( s + 1./rT2 )  / (wL^2 + (s+1/rT2)^2 ))') # complex
275
-    fmla = robjects.Formula('XX ~ a*(.5/rT2)  / ((1./rT2)^2 + (w-wL)^2 )')
276
-    #fmla = robjects.Formula('Xa ~ (s + 1./T2) / ( wL**2 + (1/T2 + 1j*w)**2 ) ')
277
- 
278
-    env = fmla.getenvironment()
279
-    #env['s'] = s
280
-    env['w'] = w
281
-    #env['Xr'] = Xr
282
-    #env['Xa'] = Xa
283
-    #env['Xi'] = Xi
284
-    #env['Xri'] = Xri
285
-    env['XX'] = XX
286
-     
287
-    #fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list, control=my_cont)) #, lower=my_lower, algorithm='port')) #, \
288
-    fit = robjects.r.tryCatch(robjects.r.nls(fmla, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
289
-    report =  r.summary(fit)
290
-    #print report 
291
-    #print(r.warnings())
292
- 
293
-    a  =  r['$'](report,'par')[0]
294
-    rT2 =  r['$'](report,'par')[1]
295
-    nb =  r['$'](report,'par')[2]
451
+    conv = r['$'](fit,'convergence')[0]
452
+    #if conv:
453
+    #    print (report)
454
+    #    print ("conv", conv)
455
+    print ("Conv",  r['$'](fit,'convergence'))  # T2
456
+    print (report)
457
+    
458
+    if BiExp:
459
+        if CorrectFreq:    
460
+            E0   =  r['$'](report,'par')[0]   # E01
461
+            E0  +=  r['$'](report,'par')[1]   # E02
462
+            df  =  r['$'](report,'par')[2]   # offset
463
+            phi =  r['$'](report,'par')[3]   # phase 
464
+            T2  =  r['$'](report,'par')[4]   # T2
465
+        else:
466
+            E0   =  r['$'](report,'par')[0]   # E01
467
+            E0  +=  r['$'](report,'par')[1]   # E02
468
+            phi =  r['$'](report,'par')[2]   # phase 
469
+            T2  =  r['$'](report,'par')[3]   # T2
470
+    else:
471
+        if CorrectFreq:    
472
+            E0   =  r['$'](report,'par')[0]   # E01
473
+            df  =  r['$'](report,'par')[1]   # offset
474
+            phi =  r['$'](report,'par')[2]   # phase 
475
+            T2  =  r['$'](report,'par')[3]   # T2
476
+        else:
477
+            E0   =  r['$'](report,'par')[0]   # E01
478
+            phi =  r['$'](report,'par')[1]   # phase 
479
+            T2  =  r['$'](report,'par')[2]   # T2
480
+    #phi = 0.907655876627
481
+    #phi = 0
482
+    #print ("df", df)# = 0
483
+    return conv, E0,df,phi,T2
296 484
     
297
-    return a, rT2, nb
298 485
 
299 486
 #################################################
300 487
 # Regress for T2 using rpy2 interface
301
-def regressModulus(w, wL, X): #,sigma2=1,intercept=True):
488
+def regressSpec(w, wL, X): #,sigma2=1,intercept=True):
302 489
 
303 490
     # compute s
304 491
     s = -1j*w
@@ -355,11 +542,11 @@ def regressModulus(w, wL, X): #,sigma2=1,intercept=True):
355 542
     rT2 =  r['$'](report,'par')[1]
356 543
     nb =  r['$'](report,'par')[2]
357 544
     
358
-    return a, rT2
545
+    return a, rT2, nb
359 546
 
360 547
 #################################################
361 548
 # Regress for T2 using rpy2 interface
362
-def regressSpecComplex(w, wL, X, known=True, win=None): #,sigma2=1,intercept=True):
549
+def regressSpecComplex(w, wL, X): #,sigma2=1,intercept=True):
363 550
 
364 551
     # compute s
365 552
     s = -1j*w
@@ -394,19 +581,14 @@ def regressSpecComplex(w, wL, X, known=True, win=None): #,sigma2=1,intercept=Tru
394 581
      
395 582
     #print (numpy.shape(X))
396 583
     
397
-    #######################################################################
398
-
399
-    if known:
400
-        # known Frequency
401
-        my_lower = robjects.r('list(a=.001, rT2=.001, phi2=-3.14)')
402
-        my_upper = robjects.r('list(a=3.5, rT2=.300, phi2=3.14)')
403
-        my_list = robjects.r('list(a=.2, rT2=0.03, phi2=0)')
404
-    else:
405
-        # Unknown Frequency
406
-        my_lower = robjects.r('list(a=.001, rT2=.001, phi2=-3.14, wL2=wL-5)')
407
-        my_upper = robjects.r('list(a=3.5, rT2=.300, phi2=3.14, wL2=wL+5)')
408
-        my_list = robjects.r('list(a=.2, rT2=0.03, phi2=0, wL2=wL)')
409
-    
584
+    #my_lower = robjects.r('list(a=.001, rT2=.001, nb=.0001)')
585
+    #my_lower = robjects.r('list(a=.001, rT2=.001)') # Working
586
+    my_lower = robjects.r('list(a=.001, rT2=.001, phi2=-3.14, wL2=wL-5)')
587
+    #my_upper = robjects.r('list(a=1.5, rT2=.300, nb =100.)')
588
+    my_upper = robjects.r('list(a=3.5, rT2=.300, phi2=3.14, wL2=wL+5)')
589
+     
590
+    #my_list = robjects.r('list(a=.2, rT2=0.03, nb=.1)')
591
+    my_list = robjects.r('list(a=.2, rT2=0.03, phi2=0, wL2=wL)')
410 592
     my_cont = robjects.r('nls.control(maxiter=5000, warnOnly=TRUE, printEval=FALSE)')
411 593
     
412 594
     #fmla = robjects.Formula('Xri ~ c(a*Re((wL) / (wL^2+(s+1/rT2)^2 )), a*Im((wL)/(wL^2 + (s+1/rT2)^2 )))') # envelope
@@ -415,18 +597,7 @@ def regressSpecComplex(w, wL, X, known=True, win=None): #,sigma2=1,intercept=Tru
415 597
     
416 598
     #fmlar = robjects.Formula('Xr ~ (Kwr(a, phi2, s, rT2, wL)) ') # envelope
417 599
     #fmlai = robjects.Formula('Xi ~ (Kwi(a, phi2, s, rT2, wL)) ') # envelope
418
-    
419
-
420
-    
421
-    if known:
422
-        ###########################################3
423
-        # KNOWN freq 
424
-        fmla = robjects.Formula('Xri ~ c(Kwr(a, phi2, s, rT2, wL), Kwi(a, phi2, s, rT2, wL) ) ') # envelope
425
-    else:
426
-        ####################################################################################################3
427
-        # unknown frequency
428
-        fmla = robjects.Formula('Xri ~ c(Kwr(a, phi2, s, rT2, wL2), Kwi(a, phi2, s, rT2, wL2) ) ') # envelope
429
-
600
+    fmla = robjects.Formula('Xri ~ c(Kwr(a, phi2, s, rT2, wL2), Kwi(a, phi2, s, rT2, wL2) ) ') # envelope
430 601
     #fmla = robjects.Formula('Xri ~ (Kwri(a, phi2, s, rT2, wL)) ') # envelope
431 602
     
432 603
     #fmla = robjects.Formula('Xa ~ (abs(a*(sin(phi2)*s + ((1/rT2)*sin(phi2)) + wL*cos(phi2)) / (wL^2+(s+1/rT2)^2 )))') # envelope
@@ -451,9 +622,8 @@ def regressSpecComplex(w, wL, X, known=True, win=None): #,sigma2=1,intercept=Tru
451 622
     env['Xri'] = Xri
452 623
     env['XX'] = XX
453 624
      
454
-    #fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list, control=my_cont)) #, lower=my_lower, algorithm='port')) #, \
455
-    #fit = robjects.r.tryCatch(robjects.r.nls(fmlar, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
456
-    fit = robjects.r.tryCatch(robjects.r.nls(fmla, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
625
+    fit = robjects.r.tryCatch(robjects.r.nls(fmla,start=my_list, control=my_cont)) #, lower=my_lower, algorithm='port')) #, \
626
+    #fitr = robjects.r.tryCatch(robjects.r.nls(fmlar, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
457 627
     
458 628
     #env = fmlai.getenvironment()
459 629
     #fiti = robjects.r.tryCatch(robjects.r.nls(fmlai, start=my_list, control=my_cont, lower=my_lower, upper=my_upper, algorithm='port')) #, \
@@ -466,7 +636,7 @@ def regressSpecComplex(w, wL, X, known=True, win=None): #,sigma2=1,intercept=Tru
466 636
     #print( reportr )
467 637
     #print( reporti  )
468 638
     #exit()
469
-    #print ( r.warnings())
639
+    #print  r.warnings()
470 640
  
471 641
     #a   =  (r['$'](reportr,'par')[0] + r['$'](reporti,'par')[0]) / 2.
472 642
     #rT2 =  (r['$'](reportr,'par')[1] + r['$'](reporti,'par')[1]) / 2.
@@ -475,8 +645,8 @@ def regressSpecComplex(w, wL, X, known=True, win=None): #,sigma2=1,intercept=Tru
475 645
     rT2 =  r['$'](report,'par')[1] 
476 646
     nb  =  r['$'](report,'par')[2] #phi2 
477 647
 
478
-    #print ("Python wL2", r['$'](report,'par')[3] )   
479
-    #print ("Python zeta", r['$'](report,'par')[2] )   
648
+    print ("Python wL2", r['$'](report,'par')[3] )   
649
+    print ("Python zeta", r['$'](report,'par')[2] )   
480 650
  
481 651
     return a, rT2, nb
482 652
 
@@ -517,27 +687,27 @@ if __name__ == "__main__":
517 687
     envelope   =  numpy.exp(-t/T2)
518 688
     renvelope  =  numpy.exp(-t/rT2)
519 689
 
520
-    outf = file('regress.txt','w')
521
-    for i in range(len(times)):
522
-        outf.write(str(times[i]) + "   " +  str(peaks[i]) + "\n")  
523
-    outf.close()
690
+    #outf = file('regress.txt','w')
691
+    #for i in range(len(times)):
692
+    #    outf.write(str(times[i]) + "   " +  str(peaks[i]) + "\n")  
693
+    #outf.close()
524 694
 
525
-    pylab.plot(t,data, 'b')
526
-    pylab.plot(t,cdata, 'g', linewidth=1)
527
-    pylab.plot(t,envelope, color='violet', linewidth=4)
528
-    pylab.plot(t,renvelope, 'r', linewidth=4)
529
-    pylab.plot(times, numpy.array(peaks), 'bo', markersize=8, alpha=.25)
530
-    pylab.legend(['noisy data','clean data','real envelope','regressed env','picks'])
531
-    pylab.savefig("regression.pdf")
695
+    plt.plot(t,data, 'b')
696
+    plt.plot(t,cdata, 'g', linewidth=1)
697
+    plt.plot(t,envelope, color='violet', linewidth=4)
698
+    plt.plot(t,renvelope, 'r', linewidth=4)
699
+    plt.plot(times, numpy.array(peaks), 'bo', markersize=8, alpha=.25)
700
+    plt.legend(['noisy data','clean data','real envelope','regressed env','picks'])
701
+    plt.savefig("regression.pdf")
532 702
 
533 703
 
534 704
     # FFT check
535 705
     fourier = fft(data)
536
-    pylab.figure()
706
+    plt.figure()
537 707
     freq = fftfreq(len(data), d=dt)
538
-    pylab.plot(freq, (fourier.real))
708
+    plt.plot(freq, (fourier.real))
539 709
     
540
-    pylab.show()
710
+    plt.show()
541 711
 
542 712
     # TODO do a bunch in batch mode to see if T2 estimate is better with or without 
543 713
     # weighting and which model is best.

+ 211
- 117
akvo/tressel/mrsurvey.py View File

@@ -5,6 +5,7 @@ import pylab
5 5
 import sys
6 6
 import scipy
7 7
 import copy
8
+import struct
8 9
 from scipy.io.matlab import mio
9 10
 from numpy import pi
10 11
 from math import floor
@@ -15,6 +16,9 @@ import matplotlib.pyplot as plt
15 16
 import matplotlib.ticker 
16 17
 from matplotlib.ticker import MaxNLocator
17 18
 
19
+import multiprocessing 
20
+import itertools 
21
+
18 22
 import akvo.tressel.adapt as adapt
19 23
 #import akvo.tressel.cadapt as adapt # cython for more faster
20 24
 import akvo.tressel.decay as decay
@@ -30,6 +34,48 @@ plt.register_cmap(name='inferno_r', cmap=cmaps.inferno_r)
30 34
 plt.register_cmap(name='magma', cmap=cmaps.magma)
31 35
 plt.register_cmap(name='magma_r', cmap=cmaps.magma_r)
32 36
 
37
+
38
+def loadGMRBinaryFID( rawfname, istack, info ):
39
+    """ Reads a single binary GMR file and fills into DATADICT
40
+    """
41
+
42
+    #################################################################################
43
+    # figure out key data indices
44
+    # Pulse        
45
+    nps  = (int)((info["prePulseDelay"])*info["samp"])
46
+    npul   = (int)(self.pulseLength[0]*self.samp) #+ 100 
47
+
48
+    # Data 
49
+    nds  = nps+npul+(int)((self.deadTime)*self.samp);        # indice pulse 1 data starts 
50
+    nd1 = (int)(1.*self.samp)                                # samples in first pulse
51
+
52
+    invGain = 1./self.RxGain        
53
+    invCGain = self.CurrentGain        
54
+
55
+    pulse = "Pulse 1"
56
+    chan = self.DATADICT[pulse]["chan"] 
57
+    rchan = self.DATADICT[pulse]["rchan"] 
58
+        
59
+    rawFile = open( rawfname, 'rb')
60
+        
61
+    T = N_samp * self.dt 
62
+    TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
63
+
64
+    for ipm in range(self.nPulseMoments):
65
+        buf1 = rawFile.read(4)
66
+        buf2 = rawFile.read(4)
67
+                
68
+        N_chan = struct.unpack('>i', buf1 )[0]
69
+        N_samp = struct.unpack('>i', buf2 )[0]
70
+
71
+        DATA = np.zeros([N_samp, N_chan+1])
72
+        for ichan in range(N_chan):
73
+            DATADUMP = rawFile.read(4*N_samp)
74
+            for irec in range(N_samp):
75
+                DATA[irec,ichan] = struct.unpack('>f', DATADUMP[irec*4:irec*4+4])[0]
76
+        
77
+    return DATA, TIMES
78
+
33 79
 class SNMRDataProcessor(QObject):
34 80
     """ Revised class for preprocessing sNMR Data. 
35 81
         Derived types can read GMR files  
@@ -183,13 +229,14 @@ class GMRDataProcessor(SNMRDataProcessor):
183 229
         self.transFreq       = HEADER[1]
184 230
         self.maxBusV         = HEADER[2]
185 231
         self.pulseLength     = pulseLengthDict.get((int)(HEADER[0]))(1e-3*HEADER[3])
186
-        self.interpulseDelay = 1e-3*HEADER[4]   # for T2, Spin Echo
187
-        self.repetitionDelay = HEADER[5]        # delay between first pulse
188
-        self.nPulseMoments   = (int)(HEADER[6]) # Number of pulse moments per stack
189
-        self.TuneCapacitance = HEADER[7]        # tuning capacitance in uF
190
-        self.nTransVersion   = HEADER[8]        # Transmitter version
191
-        self.nDAQVersion     = HEADER[9]        # DAQ software version 
192
-        self.nInterleaves    = HEADER[10]       # num interleaves
232
+        self.interpulseDelay = 1e-3*HEADER[4]      # for T2, Spin Echo
233
+        self.repetitionDelay = HEADER[5]           # delay between first pulse
234
+        self.nPulseMoments   = (int)(HEADER[6])    # Number of pulse moments per stack
235
+        self.TuneCapacitance = HEADER[7]           # tuning capacitance in uF
236
+        self.nTransVersion   = HEADER[8]           # Transmitter version
237
+        self.nDAQVersion     = HEADER[9]           # DAQ software version 
238
+        self.nInterleaves    = HEADER[10]          # num interleaves
239
+
193 240
         self.gain()
194 241
         
195 242
         # default 
@@ -198,7 +245,9 @@ class GMRDataProcessor(SNMRDataProcessor):
198 245
 
199 246
         # newer header files contain 64 entries
200 247
         if self.nDAQVersion >= 2:
201
-            #print "new file format"
248
+           #self.deadtime       = HEADER[11]
249
+           #self.unknown        = HEADER[12]
250
+           #self.PreAmpGain     = HEADER[13]
202 251
             self.samp           = HEADER[14]     # sampling frequency
203 252
             self.dt             = 1./self.samp   # sampling rate 
204 253
             self.deadTime       = .0055          # instrument dead time before measurement
@@ -241,7 +290,7 @@ class GMRDataProcessor(SNMRDataProcessor):
241 290
         # Current gain
242 291
         if floor(self.nDAQVersion) == 1:
243 292
             self.CurrentGain = 150.
244
-        elif floor(self.nDAQVersion) ==2:
293
+        elif floor(self.nDAQVersion) == 2:
245 294
             self.CurrentGain = 180.
246 295
 
247 296
     def updateProgress(self):
@@ -524,7 +573,7 @@ class GMRDataProcessor(SNMRDataProcessor):
524 573
         NRmax = {}
525 574
         REmax = {}
526 575
         IMmax = {}
527
-
576
+        E0,phi,df,T2 = 100.,0,0,.2
528 577
         first = False
529 578
         self.sigma = {}
530 579
         for pulse in self.DATADICT["PULSES"]:
@@ -560,7 +609,13 @@ class GMRDataProcessor(SNMRDataProcessor):
560 609
                     IP[pulse][chan][ipm,:] = np.angle(ht)[clip::]
561 610
                     #############################################################
562 611
                     # Rotated amplitude
563
-                    [E0, df, phi, T2] = decay.quadratureDetect( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"] )
612
+                    #if ipm != 0:
613
+                    [success, E0, df, phi, T2] = decay.quadratureDetect2( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"], (E0,phi,df,T2))
614
+                    #[ E0, df, phi, T2] = decay.quadratureDetect( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"] )
615
+                    #else:
616
+                    #    [success, E0, df, phi, T2] = decay.quadratureDetect2( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"])
617
+                    #[success, E0, df, phi, T2] = decay.quadratureDetect( ht.real, ht.imag, self.DATADICT[pulse]["TIMES"] )
618
+                    print("success", success, "E0", E0, "phi", phi, "df", df, "T2", T2)
564 619
                     D = self.RotateAmplitude( ht.real, ht.imag, phi, df, self.DATADICT[pulse]["TIMES"] )
565 620
                     CA[pulse][chan][ipm,:] = D.imag[clip::]  # amplitude data 
566 621
                     NR[pulse][chan][ipm,:] = D.real[clip::]  # noise data
@@ -711,49 +766,49 @@ class GMRDataProcessor(SNMRDataProcessor):
711 766
                 for ipm in range(0, self.DATADICT["nPulseMoments"]):
712 767
 
713 768
                     # Time since pulse rather than since record starts...
714
-                    if clip > 0:
715
-                        time_sp =  1e3 * (self.DATADICT[pulse]["TIMES"][clip-1::] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
716
-                    else:
717
-                        time_sp =  1e3 * (self.DATADICT[pulse]["TIMES"] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
769
+                    #if clip > 0:
770
+                    #    time_sp =  1e3 * (self.DATADICT[pulse]["TIMES"][clip:] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
771
+                    #else:
772
+                    time_sp =  1e3 * (self.DATADICT[pulse]["TIMES"] - self.DATADICT[pulse]["PULSE_TIMES"][-1] )
718 773
 
719 774
                     #GT, GD, GTT, sig_stack, isum      = rotate.gateIntegrate( self.DATADICT["CA"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
720 775
                     #GT2, GP, GTT, sig_stack_err, isum = rotate.gateIntegrate( self.DATADICT["NR"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 ) 
721 776
                     
722
-                    GT, GCA, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["CA"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
723
-                    GT, GNR, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["NR"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
724
-                    GT, GRE, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["RE"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
725
-                    GT, GIM, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["IM"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
726
-                    GT, GIP, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["IP"][pulse][chan][ipm,:], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
777
+                    GT, GCA, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["CA"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
778
+                    GT, GNR, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["NR"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
779
+                    GT, GRE, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["RE"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
780
+                    GT, GIM, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["IM"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
781
+                    GT, GIP, GTT, sig_stack, isum  = rotate.gateIntegrate( self.DATADICT["IP"][pulse][chan][ipm], time_sp, gpd, self.sigma[pulse][chan], 1.5 )
727 782
                     
728 783
                     if ipm == 0:
729 784
                     #    self.GATED[chan]["DATA"]  = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
730 785
                     #    self.GATED[chan]["ERR"]   = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
731 786
                     #    self.GATED[chan]["SIGMA"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
732
-                        self.GATED[chan]["CA"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
733
-                        self.GATED[chan]["NR"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
734
-                        self.GATED[chan]["RE"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
735
-                        self.GATED[chan]["IM"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
736
-                        self.GATED[chan]["IP"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)) )
787
+                        self.GATED[chan]["CA"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
788
+                        self.GATED[chan]["NR"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
789
+                        self.GATED[chan]["RE"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
790
+                        self.GATED[chan]["IM"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
791
+                        self.GATED[chan]["IP"] = np.zeros( ( self.DATADICT["nPulseMoments"], len(GT)-clip) )
737 792
                         self.GATED[chan]["isum"] = isum
738 793
 
739 794
                     #self.GATED[chan]["DATA"][ipm] = GD.real
740
-                    self.GATEDABSCISSA = GT
741
-                    self.GATEDWINDOW = GTT
795
+                    self.GATEDABSCISSA = GT[clip:]
796
+                    self.GATEDWINDOW = GTT[clip:]
742 797
                     #self.GATED[chan]["SIGMA"][ipm] =  sig_stack #_err # GP.real
743 798
                     #self.GATED[chan]["ERR"][ipm] =  GP.real
744 799
                     
745
-                    self.GATED[chan]["CA"][ipm] = GCA.real
746
-                    self.GATED[chan]["NR"][ipm] = GNR.real
747
-                    self.GATED[chan]["RE"][ipm] = GRE.real
748
-                    self.GATED[chan]["IM"][ipm] = GIM.real
749
-                    self.GATED[chan]["IP"][ipm] = GIP.real
800
+                    self.GATED[chan]["CA"][ipm] = GCA.real[clip:]
801
+                    self.GATED[chan]["NR"][ipm] = GNR.real[clip:]
802
+                    self.GATED[chan]["RE"][ipm] = GRE.real[clip:]
803
+                    self.GATED[chan]["IM"][ipm] = GIM.real[clip:]
804
+                    self.GATED[chan]["IP"][ipm] = GIP.real[clip:]
750 805
                     
751 806
                     percent = int(1e2* (float)((ipm)+ichan*self.DATADICT["nPulseMoments"]) / 
752 807
                                        (float)(self.DATADICT["nPulseMoments"] * len(self.DATADICT[pulse]["chan"])))
753 808
                     self.progressTrigger.emit(percent)
754 809
 
755
-                self.GATED[chan]["GTT"] = GTT
756
-                self.GATED[chan]["GT"] = GT
810
+                self.GATED[chan]["GTT"] = GTT[clip:]
811
+                self.GATED[chan]["GT"] = GT[clip:]
757 812
                 self.GATED[chan]["QQ"] = QQ
758 813
                 ichan += 1
759 814
         self.doneTrigger.emit() 
@@ -1754,11 +1809,107 @@ class GMRDataProcessor(SNMRDataProcessor):
1754 1809
         self.doneTrigger.emit() 
1755 1810
         self.updateProcTrigger.emit()  
1756 1811
 
1812
+    def loadGMRBinaryFID( self, rawfname, istack ):
1813
+        """ Reads a single binary GMR file and fills into DATADICT
1814
+        """
1815
+
1816
+        #################################################################################
1817
+        # figure out key data indices
1818
+        # Pulse        
1819
+        nps  = (int)((self.prePulseDelay)*self.samp)
1820
+        npul   = (int)(self.pulseLength[0]*self.samp) #+ 100 
1821
+
1822
+        # Data 
1823
+        nds  = nps+npul+(int)((self.deadTime)*self.samp);        # indice pulse 1 data starts 
1824
+        nd1 = (int)(1.*self.samp)                                # samples in first pulse
1825
+
1826
+        invGain = 1./self.RxGain        
1827
+        invCGain = self.CurrentGain        
1828
+
1829
+        pulse = "Pulse 1"
1830
+        chan = self.DATADICT[pulse]["chan"] 
1831
+        rchan = self.DATADICT[pulse]["rchan"] 
1832
+        
1833
+        rawFile = open( rawfname, 'rb')
1834
+
1835
+        for ipm in range(self.nPulseMoments):
1836
+            buf1 = rawFile.read(4)
1837
+            buf2 = rawFile.read(4)
1838
+                
1839
+            N_chan = struct.unpack('>i', buf1 )[0]
1840
+            N_samp = struct.unpack('>i', buf2 )[0]
1841
+ 
1842
+            T = N_samp * self.dt 
1843
+            TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
1844
+
1845
+            DATA = np.zeros([N_samp, N_chan+1])
1846
+            for ichan in range(N_chan):
1847
+                DATADUMP = rawFile.read(4*N_samp)
1848
+                for irec in range(N_samp):
1849
+                    DATA[irec,ichan] = struct.unpack('>f', DATADUMP[irec*4:irec*4+4])[0]
1850
+                           
1851
+            # Save into Data Cube 
1852
+            for ichan in chan:
1853
+                self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,eval(ichan)+3][nds:nds+nd1] * invGain 
1854
+                self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
1855
+                self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,1][nps:nps+npul] * invCGain
1856
+                self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul] 
1857
+
1858
+            # reference channels?
1859
+            for ichan in rchan:
1860
+                self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,eval(ichan)+3][nds:nds+nd1] * invGain 
1861
+                self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
1862
+    
1863
+    def loadGMRASCIIFID( self, rawfname, istack ):
1864
+        """Based on the geoMRI instrument manufactured by VistaClara. Imports 
1865
+        a suite of raw .lvm files with the following format (on one line)
1866
+
1867
+        time(s) DC_Bus/100(V) Current+/75(A)  Curr-/75(A)  Voltage+/200(V) \  
1868
+        Ch1(V) Ch2(V) Ch3(V) Ch4(V)
1869
+
1870
+        Sampling rate is assumed at 50 kHz 
1871
+        """
1872
+        import pandas as pd 
1873
+        #################################################################################
1874
+        # figure out key data indices
1875
+        # Pulse        
1876
+        nps  = (int)((self.prePulseDelay)*self.samp)
1877
+        npul   = (int)(self.pulseLength[0]*self.samp) #+ 100 
1878
+
1879
+        # Data 
1880
+        nds  = nps+npul+(int)((self.deadTime)*self.samp);        # indice pulse 1 data starts 
1881
+        nd1 = (int)(1.*self.samp) - nds                          # samples in first pulse
1882
+        ndr = (int)(1.*self.samp)                                # samples in record 
1883
+
1884
+        invGain = 1./self.RxGain        
1885
+        invCGain = self.CurrentGain        
1886
+
1887
+        pulse = "Pulse 1"
1888
+        chan = self.DATADICT[pulse]["chan"] 
1889
+        rchan = self.DATADICT[pulse]["rchan"] 
1890
+            
1891
+        T = 1.5 #N_samp * self.dt 
1892
+        TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
1893
+        
1894
+        self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
1895
+        self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul]
1896
+
1897
+        # pandas is much faster than numpy for io
1898
+        #DATA = np.loadtxt(rawfname)
1899
+        DATA = pd.read_csv(rawfname, header=None, sep="\t").values
1900
+        for ipm in range(self.nPulseMoments):
1901
+            for ichan in np.append(chan,rchan):
1902
+                self.DATADICT["Pulse 1"][ichan][ipm][istack] =  DATA[:, eval(ichan)+4][nds:(nds+nd1)] * invGain
1903
+                self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,2][nps:nps+npul] * invCGain
1904
+            nds += ndr
1905
+            nps += ndr 
1906
+
1907
+
1757 1908
     def loadFIDData(self, base, procStacks, chanin, rchanin, FIDProc, canvas, deadTime, plot):
1758 1909
         '''
1759
-            Loads a GMR FID dataset, reads OLD ASCII files
1910
+            Loads a GMR FID dataset, reads binary and ASCII format files 
1760 1911
         '''
1761
-        import struct
1912
+
1762 1913
         canvas.reAx3(True,False)
1763 1914
 
1764 1915
         chan = []
@@ -1774,18 +1925,7 @@ class GMRDataProcessor(SNMRDataProcessor):
1774 1925
         self.deadTime       = deadTime       # instrument dead time before measurement
1775 1926
         self.samp = 50000.                   # in case this is a reproc, these might have 
1776 1927
         self.dt   = 1./self.samp             # changed
1777
-        invGain = 1./self.RxGain        
1778
-        invCGain = self.CurrentGain        
1779 1928
 
1780
-        #################################################################################
1781
-        # figure out key data indices
1782
-        # Pulse        
1783
-        nps  = (int)((self.prePulseDelay)*self.samp)
1784
-        npul   = (int)(self.pulseLength[0]*self.samp) #+ 100 
1785
-
1786
-        # Data 
1787
-        nds  = nps+npul+(int)((self.deadTime)*self.samp);        # indice pulse 1 data starts 
1788
-        nd1 = (int)(1.*self.samp)                              # samples in first pulse
1789 1929
 
1790 1930
         #################################################################################
1791 1931
         # Data structures     
@@ -1813,83 +1953,41 @@ class GMRDataProcessor(SNMRDataProcessor):
1813 1953
         ##############################################
1814 1954
         # Read in binary (.lvm) data
1815 1955
         iistack = 0
1816
-        hack = False
1956
+        fnames = []
1817 1957
         for istack in procStacks:
1818
-            rawFile = open(base + "_" + str(istack) + ".lvm", 'rb')
1819
-            if hack:
1820
-                subFile = open(base + "sub" + "_" + str(istack) + ".lvm", 'wb')
1821
-            for ipm in range(self.nPulseMoments):
1822
-                
1823
-                # frequency cycing modulation
1824
-                #mod = (-1)**(ipm%2) * (-1)**(istack%2)
1825
-                
1826
-                buf1 = rawFile.read(4)
1827
-                buf2 = rawFile.read(4)
1828
-                
1829
-                if hack:
1830
-                    # hack to do some data analysis
1831
-                    subFile.write(buf1)
1832
-                    subFile.write(buf2)
1833
-                    # end hack
1834
-                
1835
-                #N_chan = struct.unpack('>i', rawFile.read(4))[0]
1836
-                #N_samp = struct.unpack('>i', rawFile.read(4))[0]
1958
+            if self.nDAQVersion < 2.3:
1959
+                #rawfname = base + "_" + str(istack) 
1960
+                self.loadGMRASCIIFID( base + "_" + str(istack), istack )
1961
+            else:
1962
+                self.loadGMRBinaryFID( base + "_" + str(istack) + ".lvm", istack )
1963
+                #fnames.append( base + "_" + str(istack) + ".lvm" )
1837 1964
                 
1838
-                N_chan = struct.unpack('>i', buf1 )[0]
1839
-                N_samp = struct.unpack('>i', buf2 )[0]
1840
-        
1841
- 
1842
-                T = N_samp * self.dt 
1843
-                TIMES = np.arange(0, T, self.dt) - .0002 # small offset in GMR DAQ?
1844
-
1845
-                DATA = np.zeros([N_samp, N_chan+1])
1846
-                for ichan in range(N_chan):
1847
-                    DATADUMP = rawFile.read(4*N_samp)
1848
-                    #subFile.write(DATADUMP)
1849
-                    for irec in range(N_samp):
1850
-                        DATA[irec,ichan] = struct.unpack('>f', DATADUMP[irec*4:irec*4+4])[0]
1965
+            percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1))  / (len(procStacks)*self.nPulseMoments)))
1966
+            self.progressTrigger.emit(percent) 
1967
+            iistack += 1
1851 1968
 
1852
-                if hack:                       
1853
-                    # hack to do some data analysis (array survey)
1854
-                    for ichan in range(5):
1855
-                        for irec in range(N_samp): 
1856
-                            bdata = struct.pack( '>f', DATA[irec,ichan] )               
1857
-                            subFile.write(bdata)
1858
-                    for ichan in range(5,6):
1859
-                        for irec in range(N_samp): 
1860
-                            bdata = struct.pack( '>f', DATA[irec,ichan] + DATA[irec,ichan+2] )               
1861
-                            #bdata = struct.pack( '>f', .23 )               
1862
-                            subFile.write(bdata)
1863
-                    for ichan in range(6,N_chan):
1864
-                        for irec in range(N_samp): 
1865
-                            bdata = struct.pack( '>f', DATA[irec,ichan] )               
1866
-                            subFile.write(bdata)
1969
+        # multiprocessing load data
1970
+        #info = {}
1971
+        #info["prePulseDelay"] = self.prePulseDelay
1972
+        #with multiprocessing.Pool() as pool: 
1973
+        #    results = pool.starmap( loadGMRBinaryFID, zip(itertools.repeat(self), fnames, info ) ) # zip(np.tile(vc, (ns, 1)), np.tile(vgc, (ns,1)), itertools.repeat(sys.argv[1]), itertools.repeat(sys.argv[2]), EPS_CMR))
1867 1974
 
1868
-                if plot: 
1975
+        # Plotting
1976
+        if plot: 
1977
+            iistack = 0
1978
+            for istack in procStacks:
1979
+                for ipm in range(self.nPulseMoments):
1869 1980
                     canvas.ax1.clear()
1870 1981
                     canvas.ax2.clear()
1871 1982
                     canvas.ax3.clear()
1872 1983
                            
1873
-                # Save into Data Cube 
1874
-                for ichan in chan:
1875
-                    self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,eval(ichan)+3][nds:nds+nd1] * invGain 
1876
-                    self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
1877
-                    self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] = DATA[:,1][nps:nps+npul] * invCGain
1878
-                    self.DATADICT["Pulse 1"]["PULSE_TIMES"] = TIMES[nps:nps+npul] 
1879
-                    if plot:
1880
-                        #canvas.ax2.plot(self.DATADICT["Pulse 1"]["TIMES"],       self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID data ch. "+str(ichan)) #, color='blue')
1984
+                    for ichan in chan:
1881 1985
                         canvas.ax1.plot(self.DATADICT["Pulse 1"]["PULSE_TIMES"], self.DATADICT["Pulse 1"]["CURRENT"][ipm][istack] , color='black')
1882 1986
                         canvas.ax3.plot(self.DATADICT["Pulse 1"]["TIMES"],       self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID data ch. "+str(ichan)) #, color='blue')
1883
-                        #canvas.draw()
1884 1987
 
1885
-                # plot reference channels?
1886
-                for ichan in rchan:
1887
-                    self.DATADICT["Pulse 1"][ichan][ipm][istack] = DATA[:,eval(ichan)+3][nds:nds+nd1] * invGain 
1888
-                    self.DATADICT["Pulse 1"]["TIMES"] = TIMES[nds:nds+nd1]
1889
-                    if plot:
1988
+                    for ichan in rchan:
1890 1989
                         canvas.ax2.plot(self.DATADICT["Pulse 1"]["TIMES"], self.DATADICT["Pulse 1"][ichan][ipm][istack], label="Pulse 1 FID ref ch. "+str(ichan)) #, color='blue')
1891 1990
 
1892
-                if plot:
1893 1991
                     canvas.ax3.legend(prop={'size':6})
1894 1992
                     canvas.ax2.legend(prop={'size':6})
1895 1993
                     
@@ -1904,18 +2002,14 @@ class GMRDataProcessor(SNMRDataProcessor):
1904 2002
                     canvas.ax2.set_xlabel("time [s]", fontsize=8)
1905 2003
                     canvas.ax2.ticklabel_format(style='sci', scilimits=(0,0), axis='y') 
1906 2004
                     canvas.ax3.ticklabel_format(style='sci', scilimits=(0,0), axis='y') 
1907
-
1908 2005
                     canvas.draw()
2006
+
1909 2007
                 percent = (int) (1e2*((float)((iistack*self.nPulseMoments+ipm+1))  / (len(procStacks)*self.nPulseMoments)))
1910
-                #self.progressTrigger.emit(percent) 
1911 2008
                 self.progressTrigger.emit(percent) 
1912
-
1913
-            iistack += 1
1914
-
2009
+                iistack += 1
1915 2010
 
1916 2011
         self.enableDSP()    
1917 2012
         self.doneTrigger.emit()
1918
-
1919 2013
     
1920 2014
     def load4PhaseT1Data(self, base, procStacks, chan, rchan, FIDProc, canvas, deadTime, plot): 
1921 2015
 

+ 33
- 32
akvo/tressel/rotate.py View File

@@ -25,7 +25,7 @@ def quadrature(T, vL, wL, dt, xn, DT, t):
25 25
         # decimate
26 26
     # blind decimation
27 27
     # 1 instead of T 
28
-    irsamp = (T) * int(  (1./vL) / dt) # real 
28
+    irsamp = int(T) * int(  (1./vL) / dt) # real 
29 29
     iisamp =       int(  ((1./vL)/ dt) * ( .5*np.pi / (2.*np.pi) ) ) # imaginary
30 30
    
31 31
 
@@ -78,7 +78,7 @@ def quadrature(T, vL, wL, dt, xn, DT, t):
78 78
     #############################################################
79 79
     ## In-phase 
80 80
     #2*np.cos(wL*t)  
81
-    dw = -2.*np.pi*2
81
+    dw = 0 # -2.*np.pi*2
82 82
     Q = signal.filtfilt(b, a, xn*2*np.cos((wL+dw)*t))  # X
83 83
     I = signal.filtfilt(b, a, xn*2*np.sin((wL+dw)*t))  # Y
84 84
 
@@ -133,6 +133,11 @@ def RotateAmplitude(X, Y, zeta, df, t):
133 133
 
134 134
 def gateIntegrate(T2D, T2T, gpd, sigma, stackEfficiency=2.):
135 135
     """ Gate integrate the signal to gpd, gates per decade
136
+        T2D = the time series to gate integrate, complex 
137
+        T2T = the abscissa values 
138
+        gpd = gates per decade 
139
+        sigma = estimate of standard deviation for theoretical gate noise 
140
+        stackEfficiency = exponential in theoretical gate noise, 2 represents ideal stacking
136 141
     """
137 142
     
138 143
     # use artificial time gates so that early times are fully captured
@@ -140,48 +145,44 @@ def gateIntegrate(T2D, T2T, gpd, sigma, stackEfficiency=2.):
140 145
     T2TD = T2T[0] - (T2T[1]-T2T[0])
141 146
     T2T -= T2TD
142 147
     
143
-    # calculate total number of decades
144
-    nd = np.log10(T2T[-1]/T2T[0]) #np.log10(self.T2T[-1]) -  np.log10(self.T2T[-1])
148
+    #####################################
149
+    # calculate total number of decades #
150
+    # windows edges are approximate until binning but will be adjusted to reflect data timing, this 
151
+    # primarily impacts bins with a few samples  
152
+    nd = np.log10(T2T[-1]/T2T[0])               
145 153
     tdd = np.logspace( np.log10(T2T[0]), np.log10(T2T[-1]), (int)(gpd*nd)+1, base=10, endpoint=True) 
146
-    tdl = tdd[0:-1]     # these are left edges
147
-    tdr = tdd[1::]      # these are left edges
148
-    td = (tdl+tdr) / 2. # window centres
154
+    tdl = tdd[0:-1]                 # approximate window left edges
155
+    tdr = tdd[1::]                  # approximate window right edges
156
+    td = (tdl+tdr) / 2.             # approximate window centres
149 157
 
150
-    Vars = np.ones( len(td) ) * sigma**2 #* .15**2
158
+
159
+    Vars = np.zeros( len(td) ) 
151 160
     htd = np.zeros( len(td), dtype=complex )
152
-    isum = np.zeros( len(td) )
153
-    ii = 0
161
+    isum = np.zeros( len(td), dtype=int )  
154 162
 
155
-    SIGSTACK = {}
156
-    SIGSTACK[ii]= []
163
+    ii = 0
157 164
     for itd in range(len(T2T)):
158 165
         if ( T2T[itd] > tdr[ii] ):
159
-            if (ii < len(td)-1):
160
-                ii += 1
161
-                SIGSTACK[ii] = []
162
-            else:
163
-                #print "overshoot??", ii
164
-                break
166
+            ii += 1
167
+            # correct window edges to centre about data 
168
+            tdr[ii-1] = (T2T[itd-1]+T2T[itd])*.5 
169
+            tdl[ii  ] = (T2T[itd-1]+T2T[itd])*.5
165 170
         isum[ii] += 1
166 171
         htd[ii] += T2D[ itd ]
167
-        SIGSTACK[ii].append(T2D[itd])
168 172
         Vars[ii] += sigma**2
173
+        
174
+    td = (tdl+tdr) / 2.             # actual window centres
175
+    sigma2 = np.sqrt( Vars * ((1/(isum))**stackEfficiency) ) 
169 176
 
170
-    sigma = np.sqrt( Vars  * (1/isum)**stackEfficiency ) 
171
-    for ii in range(len(td)):
172
-        if len(SIGSTACK[ii]) > 30:
173
-            sigma[ii] = np.var(SIGSTACK[ii]) / ((len(SIGSTACK[ii])-1)**(1/stackEfficiency))
174
-            #sigma[ii] = np.std(SIGSTACK[ii]) * ( 1./(len(SIGSTACK[ii]))**stackEfficiency)
177
+    # Reset abscissa where isum == 1 
178
+    # when there is no windowing going on 
179
+    td[isum==1] = T2T[0:len(td)][isum==1]
175 180
 
176
-    # RESET times where isum == 1
177
-    ii = 0
178
-    while (isum[ii] == 1):
179
-        td[ii] = T2T[ii]
180
-        ii += 1
181
+    tdd = np.append(tdl, tdr[-1])
181 182
 
182
-    htd /= isum
183
-    T2T += T2TD  
184
-    return td+T2TD, htd, tdd+T2TD, sigma, isum  # centre abscissa, data, window edges, error  
183
+    htd /= isum # average
184
+    T2T += T2TD 
185
+    return td+T2TD, htd, tdd+T2TD, sigma2, isum  # centre abscissa, data, window edges, error 
185 186
 
186 187
 if __name__ == "__main__":
187 188
 

+ 1
- 0
setup.py View File

@@ -40,6 +40,7 @@ setup(name='Akvo',
40 40
           'numpy',
41 41
           'PyQt5',
42 42
           'pyyaml',
43
+          'pandas',
43 44
           'pyqt-distutils',
44 45
           'cmocean'
45 46
       ],

Loading…
Cancel
Save