Browse Source

Work towards kernel calc inside Akvo

tags/1.6.1
Trevor Irons 4 years ago
parent
commit
5b25a43c51

+ 110
- 0
akvo/gui/Loop.ui View File

1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<ui version="4.0">
3
+ <class>LoopAdd</class>
4
+ <widget class="QDialog" name="LoopAdd">
5
+  <property name="geometry">
6
+   <rect>
7
+    <x>0</x>
8
+    <y>0</y>
9
+    <width>400</width>
10
+    <height>300</height>
11
+   </rect>
12
+  </property>
13
+  <property name="windowTitle">
14
+   <string>Dialog</string>
15
+  </property>
16
+  <layout class="QFormLayout" name="formLayout">
17
+   <item row="0" column="0">
18
+    <widget class="QLabel" name="label_2">
19
+     <property name="text">
20
+      <string>Label</string>
21
+     </property>
22
+    </widget>
23
+   </item>
24
+   <item row="1" column="0">
25
+    <widget class="QLabel" name="label">
26
+     <property name="text">
27
+      <string>Type</string>
28
+     </property>
29
+    </widget>
30
+   </item>
31
+   <item row="1" column="1">
32
+    <widget class="QComboBox" name="comboBox">
33
+     <item>
34
+      <property name="text">
35
+       <string>Circular</string>
36
+      </property>
37
+     </item>
38
+     <item>
39
+      <property name="text">
40
+       <string>figure-8</string>
41
+      </property>
42
+     </item>
43
+     <item>
44
+      <property name="text">
45
+       <string>square-8</string>
46
+      </property>
47
+     </item>
48
+     <item>
49
+      <property name="text">
50
+       <string>New Item</string>
51
+      </property>
52
+     </item>
53
+     <item>
54
+      <property name="text">
55
+       <string>polygon</string>
56
+      </property>
57
+     </item>
58
+    </widget>
59
+   </item>
60
+   <item row="2" column="0" colspan="2">
61
+    <widget class="QDialogButtonBox" name="buttonBox">
62
+     <property name="orientation">
63
+      <enum>Qt::Horizontal</enum>
64
+     </property>
65
+     <property name="standardButtons">
66
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
67
+     </property>
68
+    </widget>
69
+   </item>
70
+   <item row="0" column="1">
71
+    <widget class="QLineEdit" name="lineEdit"/>
72
+   </item>
73
+  </layout>
74
+ </widget>
75
+ <resources/>
76
+ <connections>
77
+  <connection>
78
+   <sender>buttonBox</sender>
79
+   <signal>accepted()</signal>
80
+   <receiver>LoopAdd</receiver>
81
+   <slot>accept()</slot>
82
+   <hints>
83
+    <hint type="sourcelabel">
84
+     <x>248</x>
85
+     <y>254</y>
86
+    </hint>
87
+    <hint type="destinationlabel">
88
+     <x>157</x>
89
+     <y>274</y>
90
+    </hint>
91
+   </hints>
92
+  </connection>
93
+  <connection>
94
+   <sender>buttonBox</sender>
95
+   <signal>rejected()</signal>
96
+   <receiver>LoopAdd</receiver>
97
+   <slot>reject()</slot>
98
+   <hints>
99
+    <hint type="sourcelabel">
100
+     <x>316</x>
101
+     <y>260</y>
102
+    </hint>
103
+    <hint type="destinationlabel">
104
+     <x>286</x>
105
+     <y>274</y>
106
+    </hint>
107
+   </hints>
108
+  </connection>
109
+ </connections>
110
+</ui>

+ 30
- 1
akvo/gui/akvoGUI.py View File

18
 from akvo.gui.addCircularLoop_ui import Ui_circularLoopAdd
18
 from akvo.gui.addCircularLoop_ui import Ui_circularLoopAdd
19
 from akvo.gui.addFigure8Loop_ui import Ui_figure8LoopAdd
19
 from akvo.gui.addFigure8Loop_ui import Ui_figure8LoopAdd
20
 from akvo.gui.addPolygonalLoop_ui import Ui_polygonalLoopAdd
20
 from akvo.gui.addPolygonalLoop_ui import Ui_polygonalLoopAdd
21
+from akvo.gui.redirect_ui import Ui_callScript
22
+from akvo.gui.callScript import callScript
23
+
21
 from akvo.tressel import mrsurvey 
24
 from akvo.tressel import mrsurvey 
22
 
25
 
23
 from pyLemma import LemmaCore  
26
 from pyLemma import LemmaCore  
158
         self.ui.plotGI.setEnabled(False) 
161
         self.ui.plotGI.setEnabled(False) 
159
         self.ui.plotGI.pressed.connect( self.plotGI )
162
         self.ui.plotGI.pressed.connect( self.plotGI )
160
 
163
 
164
+        # Kernel         
165
+        self.ui.calcK0.pressed.connect( self.calcK0 )       
166
+
167
+
161
         # META 
168
         # META 
162
         self.ui.locEdit.editingFinished.connect( self.logSite )
169
         self.ui.locEdit.editingFinished.connect( self.logSite )
163
         self.ui.UTMzone.currentIndexChanged.connect( self.logSite )
170
         self.ui.UTMzone.currentIndexChanged.connect( self.logSite )
299
             self.ui.ProcTabs.removeTab(0)    
306
             self.ui.ProcTabs.removeTab(0)    
300
             self.ui.ProcTabs.removeTab(0)    
307
             self.ui.ProcTabs.removeTab(0)    
301
     
308
     
309
+    def calcK0(self):
310
+        
311
+        try:
312
+            with open('.akvo.last.path') as f: 
313
+                fpath = f.readline()  
314
+                pass
315
+        except IOError as e:
316
+            fpath = '.'
317
+
318
+        akvoData = QtWidgets.QFileDialog.getOpenFileName(self, 'Open Datafile File', fpath, r"Akvo datafiles (*.yaml)")[0] # arg2 = File Type 'All Files (*)'
319
+        txCoil = QtWidgets.QFileDialog.getOpenFileName(self, 'Open Tx File', fpath, r"Akvo datafiles (*.yaml, *.yml)")[0] # arg2 = File Type 'All Files (*)'
320
+        saveStr = QtWidgets.QFileDialog.getSaveFileName(self, "Save kernel as", fpath, r"Merlin KernelV0 (*.yml)")[0] #[0]
321
+
322
+        callBox = callScript(  ) #QtWidgets.QDialog() 
323
+        
324
+        callBox.ui = Ui_callScript()
325
+        callBox.ui.setupUi( callBox )
326
+        callBox.setupCB( akvoData, txCoil, saveStr  )
327
+        
328
+        callBox.exec_()
329
+        callBox.show()
330
+    
302
     def loopAdd(self):
331
     def loopAdd(self):
303
 
332
 
304
         #print(self.ui.loopLabel.text())
333
         #print(self.ui.loopLabel.text())
329
                     turns = dialog.ui.loopTurns.value()
358
                     turns = dialog.ui.loopTurns.value()
330
                     ns = dialog.ui.segments.value()
359
                     ns = dialog.ui.segments.value()
331
                     cwise = dialog.ui.cwiseBox.currentIndex()
360
                     cwise = dialog.ui.cwiseBox.currentIndex()
332
-                    print("cwise", cwise)
361
+                    #print("cwise", cwise)
333
                     #dip = dialog.ui.dip.value()
362
                     #dip = dialog.ui.dip.value()
334
                     #azimuth = dialog.ui.az.value()
363
                     #azimuth = dialog.ui.az.value()
335
                     
364
                     

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

1
+from PyQt5 import uic
2
+from PyQt5 import QtGui, QtCore
3
+from PyQt5.QtWidgets import QMainWindow, QPushButton, QApplication, QTextEdit, QApplication, QDialog
4
+
5
+def p(x):
6
+    print (x)
7
+
8
+class callScript(QDialog):
9
+
10
+    #def __init__(self):
11
+    #    super().__init__()
12
+    
13
+    def setupCB(self, akvoData, TxCoil, SaveStr):
14
+
15
+        #QtGui.QWidget.__init__(self)
16
+        #uic.loadUi('redirect.ui', self)
17
+
18
+        print ('Connecting process')
19
+        self.process = QtCore.QProcess(self)
20
+        self.process.readyReadStandardOutput.connect(self.stdoutReady)
21
+        self.process.readyReadStandardError.connect(self.stderrReady)
22
+        self.process.started.connect(lambda: p('Started!'))
23
+        self.process.finished.connect(lambda: p('Finished!'))
24
+
25
+        print ('Starting process')
26
+        self.process.start('python', ['calcAkvoKernel.py', akvoData, TxCoil, SaveStr])
27
+
28
+    def append(self, text):
29
+        cursor = self.ui.textEdit.textCursor()
30
+        cursor.movePosition(cursor.End)
31
+        cursor.insertText(text)
32
+        self.ui.textEdit.ensureCursorVisible()
33
+        #MyTextEdit.verticalScrollBar()->setValue(MyTextEdit.verticalScrollBar()->maximum());
34
+
35
+
36
+    def stdoutReady(self):
37
+        text = str(self.process.readAllStandardOutput(), encoding='utf-8')
38
+        #print (text) 
39
+        self.append(text)
40
+
41
+    def stderrReady(self):
42
+        text = str(self.process.readAllStandardError())
43
+        #print (text) #.strip())
44
+        self.append(text)
45
+
46
+
47
+#def main():
48
+#    import sys
49
+#    app = QApplication(sys.argv)
50
+#    win = MainWindow()
51
+#    win.show()
52
+#    sys.exit(app.exec_())
53
+    
54
+#if __name__ == '__main__':
55
+   # main()

+ 3370
- 0
akvo/gui/main-bu.ui
File diff suppressed because it is too large
View File


+ 16
- 3
akvo/gui/main.ui View File

3825
               <x>30</x>
3825
               <x>30</x>
3826
               <y>320</y>
3826
               <y>320</y>
3827
               <width>351</width>
3827
               <width>351</width>
3828
-              <height>501</height>
3828
+              <height>431</height>
3829
              </rect>
3829
              </rect>
3830
             </property>
3830
             </property>
3831
             <property name="toolTip">
3831
             <property name="toolTip">
3832
              <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>
3832
              <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>
3833
             </property>
3833
             </property>
3834
            </widget>
3834
            </widget>
3835
+           <widget class="QPushButton" name="calcK0">
3836
+            <property name="geometry">
3837
+             <rect>
3838
+              <x>400</x>
3839
+              <y>330</y>
3840
+              <width>105</width>
3841
+              <height>39</height>
3842
+             </rect>
3843
+            </property>
3844
+            <property name="text">
3845
+             <string>Calc Kernel</string>
3846
+            </property>
3847
+           </widget>
3835
           </widget>
3848
           </widget>
3836
           <widget class="QWidget" name="ModelTab">
3849
           <widget class="QWidget" name="ModelTab">
3837
            <attribute name="title">
3850
            <attribute name="title">
3854
               <rect>
3867
               <rect>
3855
                <x>0</x>
3868
                <x>0</x>
3856
                <y>0</y>
3869
                <y>0</y>
3857
-               <width>100</width>
3858
-               <height>30</height>
3870
+               <width>96</width>
3871
+               <height>26</height>
3859
               </rect>
3872
               </rect>
3860
              </property>
3873
              </property>
3861
              <attribute name="label">
3874
              <attribute name="label">

+ 57
- 0
akvo/gui/redirect.ui View File

1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<ui version="4.0">
3
+ <class>callScript</class>
4
+ <widget class="QDialog" name="callScript">
5
+  <property name="geometry">
6
+   <rect>
7
+    <x>0</x>
8
+    <y>0</y>
9
+    <width>691</width>
10
+    <height>852</height>
11
+   </rect>
12
+  </property>
13
+  <property name="windowTitle">
14
+   <string>Dialog</string>
15
+  </property>
16
+  <layout class="QVBoxLayout" name="verticalLayout">
17
+   <item>
18
+    <widget class="QStatusBar" name="statusbar"/>
19
+   </item>
20
+   <item>
21
+    <widget class="QTextEdit" name="textEdit">
22
+     <property name="sizePolicy">
23
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
24
+       <horstretch>0</horstretch>
25
+       <verstretch>0</verstretch>
26
+      </sizepolicy>
27
+     </property>
28
+     <property name="font">
29
+      <font>
30
+       <family>Monospace</family>
31
+      </font>
32
+     </property>
33
+     <property name="cursor" stdset="0">
34
+      <cursorShape>IBeamCursor</cursorShape>
35
+     </property>
36
+     <property name="mouseTracking">
37
+      <bool>false</bool>
38
+     </property>
39
+     <property name="readOnly">
40
+      <bool>true</bool>
41
+     </property>
42
+     <property name="overwriteMode">
43
+      <bool>true</bool>
44
+     </property>
45
+     <property name="acceptRichText">
46
+      <bool>false</bool>
47
+     </property>
48
+     <property name="placeholderText">
49
+      <string>This windows outputs STDOUT from a seperate process.</string>
50
+     </property>
51
+    </widget>
52
+   </item>
53
+  </layout>
54
+ </widget>
55
+ <resources/>
56
+ <connections/>
57
+</ui>

+ 2
- 4
akvo/terminal/plotyaml.py View File

52
     return AKVO 
52
     return AKVO 
53
 
53
 
54
 def plotQt( akvo ):
54
 def plotQt( akvo ):
55
- 
56
     plt.style.use('ggplot')
55
     plt.style.use('ggplot')
57
     #plt.style.use('seaborn-white')
56
     #plt.style.use('seaborn-white')
58
     for pulse in akvo.Gated:
57
     for pulse in akvo.Gated:
100
 
99
 
101
     #plt.matshow(RE)
100
     #plt.matshow(RE)
102
     #plt.matshow(IM)
101
     #plt.matshow(IM)
103
-
104
-
105
-
102
+    plt.savefig("data.pdf")
106
     plt.show()
103
     plt.show()
104
+
107
 if __name__ == "__main__":
105
 if __name__ == "__main__":
108
     akvo = loadAkvoData( sys.argv[1] ) #, "Chan. 1")
106
     akvo = loadAkvoData( sys.argv[1] ) #, "Chan. 1")
109
     plotQt(akvo)
107
     plotQt(akvo)

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

1
+
2
+import os, sys
3
+import numpy as np
4
+from ruamel import yaml
5
+
6
+import pyLemma.LemmaCore as lc 
7
+import pyLemma.Merlin as mrln 
8
+import pyLemma.FDEM1D as em1d 
9
+
10
+import numpy as np
11
+
12
+import matplotlib.pyplot as plt 
13
+import seaborn as sns
14
+sns.set(style="ticks")
15
+import cmocean 
16
+from SEGPlot import *
17
+from matplotlib.ticker import FormatStrFormatter
18
+import matplotlib.ticker as plticker
19
+
20
+
21
+# Converts Lemma/Merlin/Akvo serialized Eigen arrays into numpy ones for use by Python 
22
+class VectorXr(yaml.YAMLObject):
23
+    """
24
+    Converts Lemma/Merlin/Akvo serialized Eigen arrays into numpy ones for use by Python 
25
+    """
26
+    yaml_tag = u'VectorXr'
27
+    def __init__(self, array):
28
+        self.size = np.shape(array)[0]
29
+        self.data = array.tolist()
30
+    def __repr__(self):
31
+        # Converts to numpy array on import 
32
+        return "np.array(%r)" % (self.data)
33
+
34
+class AkvoData(yaml.YAMLObject):
35
+    """
36
+    Reads an Akvo serialized dataset into a standard python dictionary 
37
+    """
38
+    yaml_tag = u'AkvoData'
39
+    def __init__(self, array):
40
+        pass
41
+        #self.size = np.shape(array)[0]
42
+        #self.Imp = array.tolist()
43
+    def __repr__(self):
44
+        # Converts to a dictionary with Eigen vectors represented as Numpy arrays 
45
+        return self
46
+
47
+def loadAkvoData(fnamein):
48
+    """ Loads data from an Akvo YAML file. The 0.02 is hard coded as the pulse length. This needs to be 
49
+        corrected in future kernel calculations. The current was reported but not the pulse length. 
50
+    """
51
+    fname = (os.path.splitext(fnamein)[0])
52
+    with open(fnamein, 'r') as stream:
53
+        try:
54
+            AKVO = (yaml.load(stream, Loader=yaml.Loader))
55
+        except yaml.YAMLError as exc:
56
+            print(exc)
57
+    return AKVO 
58
+
59
+if __name__ == "__main__":
60
+
61
+    if len(sys.argv) < 3:
62
+        print ("usage  python calcAkvoKernel.py   AkvoDataset.yaml  Coil1.yaml  " )
63
+        exit()
64
+    
65
+    gamma = 2.67518e8
66
+    fL = 2289.
67
+    B0 = (fL*2.*np.pi) /gamma * 1e9
68
+
69
+    AKVO = loadAkvoData(sys.argv[1])
70
+    Coil1 = em1d.PolygonalWireAntenna.DeSerialize( sys.argv[2] )
71
+    Coil1.SetNumberOfFrequencies(1)
72
+    Coil1.SetFrequency(0, fL) 
73
+    Coil1.SetCurrent(1.)
74
+
75
+
76
+    lmod = em1d.LayeredEarthEM() 
77
+    lmod.SetNumberOfLayers(4)
78
+    lmod.SetLayerThickness([15.49, 28.18])
79
+    lmod.SetLayerConductivity([0.0, 1./16.91, 1./24.06, 1./33.23])
80
+    lmod.SetMagneticFieldIncDecMag( 68.9, 0, B0, lc.NANOTESLA )
81
+
82
+
83
+    Kern = mrln.KernelV0()
84
+    Kern.PushCoil( "Coil 1", Coil1 )
85
+    Kern.SetLayeredEarthEM( lmod );
86
+    Kern.SetIntegrationSize( (200,200,200) )
87
+    Kern.SetIntegrationOrigin( (0,0,0) )
88
+    Kern.SetTolerance( 1e-9 )
89
+    Kern.SetMinLevel( 3 )
90
+    Kern.SetHankelTransformType( lc.FHTKEY201 )
91
+    Kern.AlignWithAkvoDataset( sys.argv[1] )
92
+
93
+    thick = np.geomspace(.5, 10,num=40)
94
+    iface = np.cumsum(thick)
95
+    Kern.SetDepthLayerInterfaces(iface)
96
+    #Kern.SetDepthLayerInterfaces(np.geomspace(1, 110, num=40))
97
+    #Kern.SetDepthLayerInterfaces(np.linspace(1, 110, num=50))
98
+    #Kern.SetDepthLayerInterfaces(np.geomspace(1, 110, num=40))
99
+ 
100
+    # autAkvoDataNode = YAML::LoadFile(argv[4]);
101
+    # Kern->AlignWithAkvoDataset( AkvoDataNode );
102
+
103
+    #Kern.SetPulseDuration(0.040)
104
+    #Kern.SetPulseCurrent(  [1.6108818092452406, 1.7549935078885168, 1.7666319459646016, 1.9270787752430283,
105
+    #    1.9455431806179229, 2.111931346726564, 2.1466747256211747, 2.3218217392379588,
106
+    #    2.358359967649008, 2.5495654202189058, 2.5957289164577992, 2.8168532605800802,
107
+    #    2.85505242699187, 3.1599429539069774, 3.2263673040205068, 3.6334182368296544,
108
+    #    3.827985200119751, 4.265671313014058, 4.582237014873297, 5.116839616183394,
109
+    #    5.515173073160611, 6.143620383280934, 6.647972282096122, 7.392577402979211,
110
+    #    8.020737177449933, 8.904435233295793, 9.701975105606063, 10.74508217792577,
111
+    #    11.743887525923592, 12.995985956061467, 14.23723766879807, 15.733870137824457,
112
+    #    17.290155933625808, 19.07016662950366, 21.013341340455703, 23.134181634845618,
113
+    #    25.570925414182238, 28.100862178905476, 31.13848909847073, 34.16791099558486,
114
+    #    37.95775984680512, 41.589619321873165, 46.327607251605286, 50.667786337299205,
115
+    #    56.60102493062895, 61.81174065797068, 69.23049946198458, 75.47409803238031,
116
+    #    84.71658869065816, 92.1855007134236, 103.77129947551164, 112.84577430578537,
117
+    #    127.55127257092909, 138.70199812969176, 157.7443764728878, 171.39653462998626]
118
+    #)
119
+
120
+    Kern.CalculateK0( ["Coil 1"], ["Coil 1"], False )
121
+
122
+    yml = open('akvoK3-' + str(Kern.GetTolerance()) + '.yaml', 'w')
123
+    print(Kern, file=yml)
124
+
125
+    K0 = Kern.GetKernel()
126
+    plt.matshow(np.abs(K0))
127
+    plt.show()

+ 1
- 1
akvo/tressel/mrsurvey.py View File

1778
                                                      M, mu, PCA, flambda, H[pulse][ichan])
1778
                                                      M, mu, PCA, flambda, H[pulse][ichan])
1779
                             iloop = 0
1779
                             iloop = 0
1780
                             #while False: 
1780
                             #while False: 
1781
-                            while (np.linalg.norm( H[pulse][ichan] - hm1) > .05):
1781
+                            while (np.linalg.norm( H[pulse][ichan] - hm1) > .05): # threshold for recall 
1782
                                 hm1 = np.copy(H[pulse][ichan]) 
1782
                                 hm1 = np.copy(H[pulse][ichan]) 
1783
                                 [e, H[pulse][ichan]] = Filt.adapt_filt_Ref( self.DATADICT[pulse][ichan][ipm][istack][::-1],\
1783
                                 [e, H[pulse][ichan]] = Filt.adapt_filt_Ref( self.DATADICT[pulse][ichan][ipm][istack][::-1],\
1784
                                                         RX,\
1784
                                                         RX,\

+ 4
- 0
pyuic.json View File

15
         [
15
         [
16
             "akvo/gui/addPolygonalLoop.ui",
16
             "akvo/gui/addPolygonalLoop.ui",
17
             "akvo/gui"
17
             "akvo/gui"
18
+        ],
19
+        [
20
+            "akvo/gui/redirect.ui",
21
+            "akvo/gui"
18
         ]
22
         ]
19
     ],
23
     ],
20
     "pyrcc": "pyrcc5",
24
     "pyrcc": "pyrcc5",

+ 2
- 1
setup.py View File

21
     long_description = fh.read()
21
     long_description = fh.read()
22
 
22
 
23
 setup(name='Akvo',
23
 setup(name='Akvo',
24
-      version='1.3.3',
24
+      version='1.3.4',
25
       python_requires='>3.7.0', # due to pyLemma
25
       python_requires='>3.7.0', # due to pyLemma
26
       description='Surface nuclear magnetic resonance workbench',
26
       description='Surface nuclear magnetic resonance workbench',
27
       long_description=long_description,
27
       long_description=long_description,
59
       entry_points = {
59
       entry_points = {
60
               'console_scripts': [
60
               'console_scripts': [
61
                   'akvo = akvo.gui.akvoGUI:main',                  
61
                   'akvo = akvo.gui.akvoGUI:main',                  
62
+                  'akvoK0 = akvo.tressel.calcAkvoKernel:main',                  
62
               ],              
63
               ],              
63
           },
64
           },
64
       #cmdclass = cmdclass,
65
       #cmdclass = cmdclass,

Loading…
Cancel
Save