Browse Source

Added non-linear refinement to inversion, and improved inversion output

tags/1.6.1
Trevor Irons 3 years ago
parent
commit
b6316a1ebe
5 changed files with 171 additions and 23 deletions
  1. 2
    1
      akvo/tressel/calcAkvoKernel.py
  2. 103
    12
      akvo/tressel/invertTA.py
  3. 17
    6
      akvo/tressel/logbarrier.py
  4. 45
    0
      akvo/tressel/nonlinearinv.py
  5. 4
    4
      setup.py

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

64
 
64
 
65
     if len(sys.argv) < 2:
65
     if len(sys.argv) < 2:
66
         print ("usage  python calcAkvoKernel.py   AkvoDataset.yaml  Coil1.yaml kparams.yaml  SaveString.yaml " )
66
         print ("usage  python calcAkvoKernel.py   AkvoDataset.yaml  Coil1.yaml kparams.yaml  SaveString.yaml " )
67
+        print ("usage  akvoKO   AkvoDataset.yaml   kparams.yaml  SaveString.yaml " )
67
         exit()
68
         exit()
68
 
69
 
69
     AKVO = loadAkvoData(sys.argv[1])
70
     AKVO = loadAkvoData(sys.argv[1])
100
             Coil1.SetCurrent(1.)
101
             Coil1.SetCurrent(1.)
101
             Kern.PushCoil( rx.split('.yml')[0], Coil1 )
102
             Kern.PushCoil( rx.split('.yml')[0], Coil1 )
102
         else:
103
         else:
103
-            print("reuse")
104
+            print("reuse tx coil")
104
         RX.append( rx.split('.yml')[0] )
105
         RX.append( rx.split('.yml')[0] )
105
     
106
     
106
 
107
 

+ 103
- 12
akvo/tressel/invertTA.py View File

17
 from matplotlib.colors import Normalize
17
 from matplotlib.colors import Normalize
18
 
18
 
19
 import cmocean
19
 import cmocean
20
-from akvo.tressel.lemma_yaml import * 
20
+from akvo.tressel.lemma_yaml import *
21
+from akvo.tressel import nonlinearinv as nl 
21
 
22
 
22
 import pandas as pd
23
 import pandas as pd
23
 
24
 
28
     nlay, nq = np.shape(K0)
29
     nlay, nq = np.shape(K0)
29
     nt2 = len(T2Bins)
30
     nt2 = len(T2Bins)
30
     nt = len(tg)
31
     nt = len(tg)
31
-    KQT = np.zeros( ( nq*nt,nt2*nlay) )
32
+    KQT = np.zeros( ( nq*nt,nt2*nlay), dtype=np.complex128 )
32
     for iq in range(nq):
33
     for iq in range(nq):
33
         for it in range(nt):
34
         for it in range(nt):
34
             for ilay in range(nlay):
35
             for ilay in range(nlay):
87
         K0 = yaml.load(f, Loader=yaml.Loader)
88
         K0 = yaml.load(f, Loader=yaml.Loader)
88
     K = catLayers(K0.K0)
89
     K = catLayers(K0.K0)
89
     ifaces = np.array(K0.Interfaces.data)
90
     ifaces = np.array(K0.Interfaces.data)
90
-    return ifaces, np.abs(K)
91
+    return ifaces, K
92
+    #return ifaces, np.abs(K)
91
 
93
 
92
 
94
 
93
 
95
 
131
     for ik in range(1, len(K0)):
133
     for ik in range(1, len(K0)):
132
         K0[0] = np.concatenate( (K0[0].T, K0[ik].T) ).T
134
         K0[0] = np.concatenate( (K0[0].T, K0[ik].T) ).T
133
     K0 = K0[0]
135
     K0 = K0[0]
134
-    #plt.matshow(K0)
136
+
137
+    #plt.matshow(np.real(K0))
138
+    #plt.show()
139
+    #exit()
135
 
140
 
136
     ###################    
141
     ###################    
137
     # VERY Simple DOI #
142
     # VERY Simple DOI #
138
-    maxq = np.argmax(K0, axis=1)
139
-    maxK = .1 *  K0[ np.arange(0,len(ifaces)-1), maxq ] # 10% water is arbitrary  
143
+    maxq = np.argmax(np.abs(K0), axis=1)
144
+    maxK = .1 *  np.abs(K0)[ np.arange(0,len(ifaces)-1), maxq ] # 10% water is arbitrary  
140
     SNR = maxK / (VS[0][0])
145
     SNR = maxK / (VS[0][0])
141
 
146
 
142
     #SNR[SNR>1] = 1
147
     #SNR[SNR>1] = 1
163
     # Build full kernel
168
     # Build full kernel
164
     ############################################### 
169
     ############################################### 
165
     T2Bins = np.logspace( np.log10(cont["T2Bins"]["low"]), np.log10(cont["T2Bins"]["high"]), cont["T2Bins"]["number"], endpoint=True, base=10)  
170
     T2Bins = np.logspace( np.log10(cont["T2Bins"]["low"]), np.log10(cont["T2Bins"]["high"]), cont["T2Bins"]["number"], endpoint=True, base=10)  
166
-    KQT = buildKQT(K0,tg,T2Bins)
167
- 
171
+    KQT = np.real(buildKQT(np.abs(K0),tg,T2Bins))
172
+
173
+    # model resolution matrix 
174
+    np.linalg.svd(KQT)
175
+
176
+    exit()
177
+
168
     ###############################################
178
     ###############################################
169
-    # Invert
179
+    # Linear Inversion 
170
     ############################################### 
180
     ############################################### 
171
     print("Calling inversion", flush=True)
181
     print("Calling inversion", flush=True)
172
-    inv, ibreak, errn, phim, phid, mkappa = logBarrier(KQT, np.ravel(V), T2Bins, "lcurve", MAXITER=150, sigma=np.ravel(VS), alpha=1e6, smooth="Smallest" ) 
182
+    inv, ibreak, errn, phim, phid, mkappa, Wd, Wm, alphastar = logBarrier(KQT, np.ravel(V), T2Bins, "lcurve", MAXITER=150, sigma=np.ravel(VS), alpha=1e6, smooth="Smallest" ) 
173
 
183
 
184
+    ###############################################
185
+    # Non-linear refinement! 
186
+    ###############################################   
187
+ 
188
+    KQTc = buildKQT(K0, tg, T2Bins)
189
+    prec = np.abs(np.dot(KQTc, inv))
190
+    phidc = np.linalg.norm(np.dot(Wd,prec-np.ravel(V)))**2
191
+    #PREc = np.reshape( prec, np.shape(V)  )
192
+    print("PHID linear=", errn, "PHID complex=", phidc/len(np.ravel(V)))
193
+    
194
+    res = nl.nonlinearinversion(inv, Wd, KQTc, np.ravel(V), Wm, alphastar )   
195
+    if res.success == True:    
196
+        INVc = np.reshape(res.x, (len(ifaces)-1,cont["T2Bins"]["number"]) )
197
+        prec = np.abs(np.dot(KQTc, res.x))
198
+        phidc = np.linalg.norm(np.dot(Wd,prec-np.ravel(V)))**2
199
+        #PREc = np.reshape( prec, np.shape(V)  )
200
+        print("PHID linear=", errn, "PHID nonlinear=", phidc/len(np.ravel(V)))
201
+   
202
+    # Perform second fit around results of first  
203
+    res = nl.nonlinearinversion(res.x, Wd, KQTc, np.ravel(V), Wm, alphastar )   
204
+    if res.success == True:    
205
+        INVc = np.reshape(res.x, (len(ifaces)-1,cont["T2Bins"]["number"]) )
206
+        prec = np.abs(np.dot(KQTc, res.x))
207
+        phidc = np.linalg.norm(np.dot(Wd,prec-np.ravel(V)))**2
208
+        #PREc = np.reshape( prec, np.shape(V)  )
209
+        print("PHID linear=", errn, "PHID nonlinear=", phidc/len(np.ravel(V)))
210
+
211
+    #plt.matshow(INVc)
212
+    #KQTc = buildKQT(K0,tg,T2Bins)
213
+
214
+    #plt.matshow(PREc, cmap='Blues')
215
+    #plt.gca().set_title("complex predicted")
216
+    #plt.colorbar()
174
 
217
 
175
     ###############################################
218
     ###############################################
176
     # Appraise
219
     # Appraise
177
     ###############################################
220
     ###############################################
221
+
222
+
223
+
224
+
178
  
225
  
179
     pre = np.dot(KQT,inv) 
226
     pre = np.dot(KQT,inv) 
180
     PRE = np.reshape( pre, np.shape(V)  )
227
     PRE = np.reshape( pre, np.shape(V)  )
192
     plt.gca().set_title("observed")
239
     plt.gca().set_title("observed")
193
     plt.colorbar()
240
     plt.colorbar()
194
 
241
 
242
+
195
     T2Bins = np.append( T2Bins, T2Bins[-1] + (T2Bins[-1]-T2Bins[-2]) )
243
     T2Bins = np.append( T2Bins, T2Bins[-1] + (T2Bins[-1]-T2Bins[-2]) )
196
-    
197
     INV = np.reshape(inv, (len(ifaces)-1,cont["T2Bins"]["number"]) )
244
     INV = np.reshape(inv, (len(ifaces)-1,cont["T2Bins"]["number"]) )
198
 
245
 
199
     #alphas = np.tile(SNR, (len(T2Bins)-1,1))
246
     #alphas = np.tile(SNR, (len(T2Bins)-1,1))
205
 
252
 
206
     #greys = np.full((*(INV.T).shape, 3), 70, dtype=np.uint8)
253
     #greys = np.full((*(INV.T).shape, 3), 70, dtype=np.uint8)
207
 
254
 
255
+    ##############  LINEAR RESULT   ##########################
256
+
208
     Y,X = meshgrid( ifaces, T2Bins )
257
     Y,X = meshgrid( ifaces, T2Bins )
209
     fig = plt.figure( figsize=(pc2in(20.0),pc2in(22.)) )
258
     fig = plt.figure( figsize=(pc2in(20.0),pc2in(22.)) )
210
     ax1 = fig.add_axes( [.2,.15,.6,.7] )
259
     ax1 = fig.add_axes( [.2,.15,.6,.7] )
242
 
291
 
243
     plt.savefig("akvoInversion.pdf")
292
     plt.savefig("akvoInversion.pdf")
244
 
293
 
294
+
295
+    ##############  NONLINEAR RESULT   ##########################
296
+
297
+    Y,X = meshgrid( ifaces, T2Bins )
298
+    fig = plt.figure( figsize=(pc2in(20.0),pc2in(22.)) )
299
+    ax1 = fig.add_axes( [.2,.15,.6,.7] )
300
+    im = ax1.pcolor(X, Y, INVc.T, cmap=cmocean.cm.tempo) #cmap='viridis')
301
+    #im = ax1.pcolor(X[0:SNRidx,:], Y[0:SNRidx,:], INV.T[0:SNRidx,:], cmap=cmocean.cm.tempo) #cmap='viridis')
302
+    #im = ax1.pcolor(X[SNRidx::,:], Y[SNRidx::,:], INV.T[SNRidx::,:], cmap=cmocean.cm.tempo, alpha=.5) #cmap='viridis')
303
+    #im = ax1.pcolormesh(X, Y, INV.T, alpha=alphas) #, cmap=cmocean.cm.tempo) #cmap='viridis')
304
+    #im = ax1.pcolormesh(X, Y, INV.T, alpha=alphas) #, cmap=cmocean.cm.tempo) #cmap='viridis')
305
+    #ax1.axhline( y=ifaces[SNRidx], xmin=T2Bins[0], xmax=T2Bins[-1], color='black'  )
306
+    im.set_edgecolor('face')
307
+    ax1.set_xlim( T2Bins[0], T2Bins[-1] )
308
+    ax1.set_ylim( ifaces[-1], ifaces[0] )
309
+    cb = plt.colorbar(im, label = u"PWC (m$^3$/m$^3$)") #, format='%1.1f')
310
+    cb.locator = MaxNLocator( nbins = 4)
311
+    cb.ax.yaxis.set_offset_position('left')                         
312
+    cb.update_ticks()
313
+ 
314
+    ax1.set_xlabel(u"$T_2^*$ (ms)")
315
+    ax1.set_ylabel(u"depth (m)")
316
+    
317
+    ax1.get_xaxis().set_major_formatter(FormatStrFormatter('%1.0f'))
318
+    ax1.get_yaxis().set_major_formatter(FormatStrFormatter('%1.0f'))
319
+    ax1.xaxis.set_major_locator( MaxNLocator(nbins = 4) )   
320
+
321
+    #ax1.xaxis.set_label_position('top') 
322
+
323
+    ax2 = ax1.twiny()
324
+    ax2.plot( np.sum(INVc, axis=1), (ifaces[1:]+ifaces[0:-1])/2 ,  color='red' )
325
+    ax2.set_xlabel(u"total water (m$^3$/m$^3$)")
326
+    ax2.set_ylim( ifaces[-1], ifaces[0] )
327
+    ax2.xaxis.set_major_locator( MaxNLocator(nbins = 3) )   
328
+    ax2.get_xaxis().set_major_formatter(FormatStrFormatter('%0.2f'))
329
+    #ax2.axhline( y=ifaces[SNRidx], xmin=0, xmax=1, color='black', linestyle='dashed'  )
330
+    #ax2.xaxis.set_label_position('bottom') 
331
+    fig.suptitle("Non linear inversion")
332
+    plt.savefig("akvoInversionNL.pdf")
333
+
334
+
335
+
245
     #############
336
     #############
246
     # water plot#
337
     # water plot#
247
 
338
 
265
     ax.set_ylim( ifaces[-1], ifaces[0] )
356
     ax.set_ylim( ifaces[-1], ifaces[0] )
266
     ax.set_xlim( 0, ax.get_xlim()[1] )
357
     ax.set_xlim( 0, ax.get_xlim()[1] )
267
     
358
     
268
-    ax.axhline( y=ifaces[SNRidx], xmin=0, xmax=1, color='black', linestyle='dashed'  )
359
+    #ax.axhline( y=ifaces[SNRidx], xmin=0, xmax=1, color='black', linestyle='dashed'  )
269
     
360
     
270
     plt.savefig("akvoInversionWC.pdf")
361
     plt.savefig("akvoInversionWC.pdf")
271
     plt.legend()  
362
     plt.legend()  

+ 17
- 6
akvo/tressel/logbarrier.py View File

128
     ALPHA = []
128
     ALPHA = []
129
     ALPHA.append(alpha)
129
     ALPHA.append(alpha)
130
     #ALPHA = np.linspace( alpha, 1, MAXITER  )
130
     #ALPHA = np.linspace( alpha, 1, MAXITER  )
131
+    print ("{:^10} {:^15} {:^15} {:^15} {:^15} {:^10} {:^10}".format("iteration",  "lambda", "phi_d", "phi_m","phi","kappa","kappa dist."), flush=True) 
132
+    print ("{:^10} {:>15} {:<15} {:<15} {:<15} {:<10} {:<10}".format("----------", "---------------", "---------------","---------------","---------------","----------","----------"), flush=True) 
131
     for i in range(MAXITER):
133
     for i in range(MAXITER):
132
         #alpha = ALPHA[i]
134
         #alpha = ALPHA[i]
133
 
135
 
202
         PHID.append(phid)      
204
         PHID.append(phid)      
203
         MOD.append(np.copy(x))  
205
         MOD.append(np.copy(x))  
204
 
206
 
207
+        tphi = phid + alpha*phim
208
+
205
         # determine alpha
209
         # determine alpha
206
         scale = 1.5*(len(b)/phid)
210
         scale = 1.5*(len(b)/phid)
207
         #alpha *= np.sqrt(scale)
211
         #alpha *= np.sqrt(scale)
211
         ALPHA.append(alpha)
215
         ALPHA.append(alpha)
212
         #alpha = ALPHA[i+1]
216
         #alpha = ALPHA[i+1]
213
             
217
             
214
-        print("inversion progress", i, alpha, np.sqrt(phid/len(b)), phim, flush=True)       
218
+        #print("inversion progress", i, alpha, np.sqrt(phid/len(b)), phim, flush=True)      
219
+        #print ("{:<8} {:<15} {:<10} {:<10}".format(i, alpha, np.sqrt(phid/len(b)), phim), flush=True) 
215
         
220
         
221
+        if i < 4:        
222
+            print ("{:^10} {:>15.4f} {:>15.4f} {:>15.4f} {:>15.4f}".format(i, alpha, np.sqrt(phid/len(b)), phim, tphi ), flush=True) 
216
 
223
 
217
 #         if np.sqrt(phid/len(b)) < 0.97: 
224
 #         if np.sqrt(phid/len(b)) < 0.97: 
218
 #             ibreak = -1
225
 #             ibreak = -1
234
             if i > 4: 
241
             if i > 4: 
235
                 kappa = curvaturefd(np.log(np.array(PHIM)), np.log(np.array(PHID)), ALPHA[0:i+1])#ALPHA[0:-1])
242
                 kappa = curvaturefd(np.log(np.array(PHIM)), np.log(np.array(PHID)), ALPHA[0:i+1])#ALPHA[0:-1])
236
                 #kappa = curvatureg(np.log(np.array(PHIM)), np.log(np.array(PHID)))
243
                 #kappa = curvatureg(np.log(np.array(PHIM)), np.log(np.array(PHID)))
237
-                print("max kappa", np.argmax(kappa), "distance from", i-np.argmax(kappa)) 
244
+                #print("max kappa", np.argmax(kappa), "distance from", i-np.argmax(kappa)) 
245
+                print ("{:^10} {:>15.4f} {:>15.4f} {:>15.4f} {:>15.4f} {:^10} {:^10}".format(i, alpha, np.sqrt(phid/len(b)), phim, tphi, np.argmax(kappa), i-np.argmax(kappa)), flush=True) 
238
             if i > 4 and (i-np.argmax(kappa)) > 4: # ((np.sqrt(phid_old/len(b))-np.sqrt(phid/len(b))) < 1e-4) : 
246
             if i > 4 and (i-np.argmax(kappa)) > 4: # ((np.sqrt(phid_old/len(b))-np.sqrt(phid/len(b))) < 1e-4) : 
239
             #if np.sqrt(phid/len(b)) < 3.0 and ((np.sqrt(phid_old/len(b))-np.sqrt(phid/len(b))) < 1e-3): 
247
             #if np.sqrt(phid/len(b)) < 3.0 and ((np.sqrt(phid_old/len(b))-np.sqrt(phid/len(b))) < 1e-3): 
240
                 ibreak = 1
248
                 ibreak = 1
241
                 MOD = np.array(MOD)
249
                 MOD = np.array(MOD)
242
-                print ("###########################") #slow convergence", alpha, "phid_old", np.sqrt(phid_old/len(b)), "phid", np.sqrt(phid/len(b)), ibreak)
250
+                print ("################################") #slow convergence", alpha, "phid_old", np.sqrt(phid_old/len(b)), "phid", np.sqrt(phid/len(b)), ibreak)
243
                 print ("Using L-curve criteria") 
251
                 print ("Using L-curve criteria") 
244
                 #kappa = curvaturefd(np.log(np.array(PHIM)), np.log(np.array(PHID)), ALPHA[0:-1])
252
                 #kappa = curvaturefd(np.log(np.array(PHIM)), np.log(np.array(PHID)), ALPHA[0:-1])
245
                 #kappa2 = curvatureg(np.log(np.array(PHIM)), np.log(np.array(PHID)))
253
                 #kappa2 = curvatureg(np.log(np.array(PHIM)), np.log(np.array(PHID)))
246
                 #kappa = curvature( np.array(PHIM), np.array(PHID))
254
                 #kappa = curvature( np.array(PHIM), np.array(PHID))
247
                 x = MOD[ np.argmax(kappa) ]
255
                 x = MOD[ np.argmax(kappa) ]
256
+                alphastar = ALPHA[ np.argmax(kappa) ]
248
                 b_pre = np.dot(A, x)
257
                 b_pre = np.dot(A, x)
249
                 phid = np.linalg.norm( np.dot(Wd, (b-b_pre)))**2
258
                 phid = np.linalg.norm( np.dot(Wd, (b-b_pre)))**2
250
                 phim = np.linalg.norm( np.dot(Phim_base, (x-xr)) )**2
259
                 phim = np.linalg.norm( np.dot(Phim_base, (x-xr)) )**2
251
                 mu1 = ((phid + alpha*phim) / phib) 
260
                 mu1 = ((phid + alpha*phim) / phib) 
252
-                print ("L-curve selected", alpha, "phid_old", np.sqrt(phid_old/len(b)), "phid", np.sqrt(phid/len(b)), ibreak)
253
-                print ("###########################")
261
+                print ("L-curve selected: iteration=", np.argmax(kappa)) #, " lambda*=", alpha, "phid_old=", np.sqrt(phid_old/len(b)), "phid=", np.sqrt(phid/len(b)), ibreak)
262
+                print ("################################")
254
                 if np.sqrt(phid/len(b)) <= 1:
263
                 if np.sqrt(phid/len(b)) <= 1:
255
                     ibreak=0
264
                     ibreak=0
256
 
265
 
291
         mu1 = ((phid + alpha*phim) / phib) 
300
         mu1 = ((phid + alpha*phim) / phib) 
292
 
301
 
293
     if lambdastar == "lcurve":
302
     if lambdastar == "lcurve":
294
-        return x, ibreak, np.sqrt(phid/len(b)), PHIM, PHID/len(b), np.argmax(kappa)
303
+        #print("Returning L curve result")
304
+        return x, ibreak, np.sqrt(phid/len(b)), PHIM, PHID/len(b), np.argmax(kappa), Wd, Phim_base, alphastar
295
     else:
305
     else:
306
+        print("")
296
         return x, ibreak, np.sqrt(phid/len(b))
307
         return x, ibreak, np.sqrt(phid/len(b))
297
 
308
 
298
 
309
 

+ 45
- 0
akvo/tressel/nonlinearinv.py View File

1
+import numpy as np
2
+import scipy.optimize as so 
3
+
4
+def phid(Wd, K, m, d_obs):
5
+    """
6
+        Wd = data weighting matrix 
7
+        K = complex valued forward model kernel 
8
+        m = model 
9
+        d_obs = observed data 
10
+    """
11
+    #print("phid=", np.linalg.norm(np.dot(Wd, np.abs(np.dot(K,m)) - d_obs))**2 / len(d_obs) )
12
+    return np.linalg.norm(np.dot(Wd, np.abs(np.dot(K,m)) - d_obs))**2
13
+
14
+def phim(Wm, m):
15
+    """
16
+        Wm = model weighting matrix 
17
+        x = model 
18
+    """
19
+    return np.linalg.norm(np.dot(Wm, m))**2
20
+
21
+def PHI(m, Wd, K, d_obs, Wm, alphastar):
22
+    """
23
+        Global objective function 
24
+        x = model to be fit 
25
+        Wd = data weighting matrix 
26
+        K = complex forward modelling kernel 
27
+        d_obs = observed data (modulus)
28
+        Wm = model weighting matrix 
29
+        alphastar = regularisation to use
30
+    """
31
+    return phid(Wd, K, m, d_obs) + alphastar*phim(Wm, m)
32
+
33
+# main 
34
+def nonlinearinversion( x0, Wd, K, d_obs, Wm, alphastar):
35
+    print("Performing non-linear inversion")
36
+    args = (Wd, K, d_obs, Wm, alphastar)
37
+    #return so.minimize(PHI, np.zeros(len(x0)), args, 'Nelder-Mead')
38
+    bounds = np.zeros((len(x0),2))
39
+    bounds[:,0] = x0*0.75
40
+    bounds[:,1] = x0*1.25
41
+    return so.minimize(PHI, x0, args, 'L-BFGS-B', bounds=bounds)     # Works well 
42
+    #return so.minimize(PHI, x0, args, 'Powell', bounds=bounds)       # Slow but works 
43
+    #return so.minimize(PHI, x0, args, 'trust-constr', bounds=bounds) # very Slow 
44
+    #return so.minimize(PHI, x0, args, 'TNC', bounds=bounds)          # slow 
45
+    #return so.minimize(PHI, x0, args, 'SLSQP', bounds=bounds)        # slow 

+ 4
- 4
setup.py View File

20
 with open("README.md", "r") as fh:
20
 with open("README.md", "r") as fh:
21
     long_description = fh.read()
21
     long_description = fh.read()
22
 
22
 
23
-setup(name='Akvo',
24
-      version='1.5.2',
25
-      python_requires='>3.7.0', # due to pyLemma
23
+setup(name='Akvo',     
24
+      version='1.5.2', 
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,
28
       long_description_content_type='text/markdown',
28
       long_description_content_type='text/markdown',
56
           'pyLemma >= 0.4.0'
56
           'pyLemma >= 0.4.0'
57
       ],
57
       ],
58
       packages=['akvo', 'akvo.tressel', 'akvo.gui'],
58
       packages=['akvo', 'akvo.tressel', 'akvo.gui'],
59
-      license=['GPL 4.0'],
59
+      license='GPL 4.0',
60
       entry_points = {
60
       entry_points = {
61
               'console_scripts': [
61
               'console_scripts': [
62
                   'akvo = akvo.gui.akvoGUI:main',                  
62
                   'akvo = akvo.gui.akvoGUI:main',                  

Loading…
Cancel
Save