Borehole NMR processing
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pwctimeWhite.py 13KB

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