adaptation.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. import numpy as np
  2. from numpy import concatenate as cat
  3. import matplotlib.pyplot as plt
  4. import warnings
  5. from matplotlib import rc
  6. from .preprocess import shape, discretization
  7. plt.rc('text', usetex=True)
  8. plt.rc('font', family='serif')
  9. # supress the deprecation warning
  10. warnings.filterwarnings("ignore", ".*GUI is implemented.*")
  11. class hdpg1d(object):
  12. """
  13. 1D HDG solver
  14. """
  15. def __init__(self, coeff):
  16. self.numEle = coeff.numEle
  17. self.numBasisFuncs = coeff.pOrder + 1
  18. self.tau_pos = coeff.tauPlus
  19. self.tau_neg = coeff.tauMinus
  20. self.mesh = np.linspace(0, 1, self.numEle + 1)
  21. self.c = coeff.convection
  22. self.kappa = coeff.diffusion
  23. self.coeff = coeff
  24. self.u = []
  25. self.estErrorList = []
  26. self.trueErrorList = []
  27. def bc(self, case, t=None):
  28. # boundary condition
  29. if case == 0:
  30. # advection-diffusion
  31. bc = [0, 0]
  32. if case == 1:
  33. # simple convection
  34. # bc = np.sin(2*np.pi*t)
  35. # adjoint boundary
  36. bc = [0, 1]
  37. return bc
  38. def forcing(self, x):
  39. # f = np.cos(2*np.pi*x)
  40. # f = 4*pi**2*sin(2*pi*x)
  41. f = 1
  42. return f
  43. def plotU(self, counter):
  44. """Plot solution u with smooth higher oredr quadrature"""
  45. uSmooth = np.array([])
  46. uNode = np.zeros(self.numEle + 1)
  47. xSmooth = np.array([])
  48. u = self.u[int(len(self.u) / 2):len(self.u)]
  49. # quadrature rule
  50. gorder = 10 * self.numBasisFuncs
  51. xi, wi = np.polynomial.legendre.leggauss(gorder)
  52. shp, shpx = shape(xi, self.numBasisFuncs)
  53. for j in range(1, self.numEle + 1):
  54. xSmooth = np.hstack((xSmooth, (self.mesh[(j - 1)] + self.mesh[j]) / 2 + (
  55. self.mesh[j] - self.mesh[j - 1]) / 2 * xi))
  56. uSmooth = np.hstack(
  57. (uSmooth, shp.T.dot(u[(j - 1) * self.numBasisFuncs:j * self.numBasisFuncs])))
  58. uNode[j - 1] = u[(j - 1) * self.numBasisFuncs]
  59. uNode[-1] = u[-1]
  60. plt.figure(1)
  61. plt.plot(xSmooth, uSmooth, '-', color='C3')
  62. plt.plot(self.mesh, uNode, 'C3.')
  63. plt.xlabel('$x$', fontsize=17)
  64. plt.ylabel('$u$', fontsize=17)
  65. plt.axis([-0.05, 1.05, 0, 1.3])
  66. plt.grid()
  67. plt.pause(1e-1)
  68. plt.clf()
  69. def meshAdapt(self, index):
  70. """Given the index list, adapt the mesh"""
  71. inValue = np.zeros(len(index))
  72. for i in np.arange(len(index)):
  73. inValue[i] = (self.mesh[index[i]] +
  74. self.mesh[index[i] - 1]) / 2
  75. self.mesh = np.sort(np.insert(self.mesh, 0, inValue))
  76. def solveLocal(self):
  77. """Solve the primal problem"""
  78. if 'matLocal' in locals():
  79. # if matLocal exists,
  80. # only change the mesh instead of initialize again
  81. matLocal.mesh = self.mesh
  82. else:
  83. matLocal = discretization(self.coeff, self.mesh)
  84. matLocal.matGen()
  85. # solve
  86. K = -cat((matLocal.C.T, matLocal.G), axis=1)\
  87. .dot(np.linalg.inv(np.bmat([[matLocal.A, -matLocal.B], [matLocal.B.T, matLocal.D]]))
  88. .dot(cat((matLocal.C, matLocal.E)))) + matLocal.H
  89. F_hat = np.array([matLocal.L]).T - cat((matLocal.C.T, matLocal.G), axis=1)\
  90. .dot(np.linalg.inv(np.bmat([[matLocal.A, -matLocal.B], [matLocal.B.T, matLocal.D]])))\
  91. .dot(np.array([cat((matLocal.R, matLocal.F))]).T)
  92. uFace = np.linalg.solve(K, F_hat)
  93. u = np.linalg.inv(np.bmat([[matLocal.A, -matLocal.B], [matLocal.B.T, matLocal.D]]))\
  94. .dot(np.array([np.concatenate((matLocal.R, matLocal.F))]).T -
  95. cat((matLocal.C, matLocal.E)).dot(uFace))
  96. # self.u = u.A1
  97. return u.A1, uFace.A1
  98. def solveAdjoint(self):
  99. """Solve the adjoint problem"""
  100. # solve in the enriched space
  101. self.coeff.pOrder += 1
  102. if 'matAdjoint' in locals():
  103. matAdjoint.mesh = self.mesh
  104. else:
  105. matAdjoint = discretization(self.coeff, self.mesh)
  106. matAdjoint.matGen()
  107. self.coeff.pOrder = self.coeff.pOrder - 1
  108. # add adjoint LHS conditions
  109. matAdjoint.F = np.zeros(len(matAdjoint.F))
  110. matAdjoint.R[-1] = -self.bc(1)[1]
  111. # assemble global matrix LHS
  112. LHS = np.bmat([[matAdjoint.A, -matAdjoint.B, matAdjoint.C],
  113. [matAdjoint.B.T, matAdjoint.D, matAdjoint.E],
  114. [matAdjoint.C.T, matAdjoint.G, matAdjoint.H]])
  115. # solve
  116. U = np.linalg.solve(LHS.T, np.concatenate(
  117. (matAdjoint.R, matAdjoint.F, matAdjoint.L)))
  118. return U[0:2 * len(matAdjoint.C)], U[len(matAdjoint.C):len(U)]
  119. def residual(self, U, hat_U, z, hat_z):
  120. numEle = self.numEle
  121. p = self.numBasisFuncs + 1
  122. p_l = p - 1
  123. # order of gauss quadrature
  124. gorder = 2 * p
  125. # shape function and gauss quadrature
  126. xi, wi = np.polynomial.legendre.leggauss(gorder)
  127. shp, shpx = shape(xi, p)
  128. shp_l, shpx_l = shape(xi, p_l)
  129. # ---------------------------------------------------------------------
  130. # advection constant
  131. con = self.c
  132. # diffusion constant
  133. kappa = self.kappa
  134. z_q, z_u, z_hat = np.zeros(p * numEle), \
  135. np.zeros(p *
  136. numEle), np.zeros(numEle - 1)
  137. q, u, lamba = np.zeros(p_l * numEle), \
  138. np.zeros(p_l * numEle), np.zeros(numEle - 1)
  139. for i in np.arange(p * numEle):
  140. z_q[i] = z[i]
  141. z_u[i] = z[i + p * numEle]
  142. for i in np.arange(p_l * numEle):
  143. q[i] = U[i]
  144. u[i] = U[i + p_l * numEle]
  145. for i in np.arange(numEle - 1):
  146. z_hat[i] = hat_z[i]
  147. # add boundary condtions to U_hat
  148. U_hat = np.zeros(numEle + 1)
  149. for i, x in enumerate(hat_U):
  150. U_hat[i + 1] = x
  151. U_hat[0] = self.bc(0)[0]
  152. U_hat[-1] = self.bc(0)[1]
  153. # L, easy in 1d
  154. L = np.zeros(numEle + 1)
  155. # R, easy in 1d
  156. RR = np.zeros(p * numEle)
  157. # elemental forcing vector
  158. dist = np.zeros(numEle)
  159. F = np.zeros(p * numEle)
  160. for i in range(1, numEle + 1):
  161. dist[i - 1] = self.mesh[i] - self.mesh[i - 1]
  162. f = dist[i - 1] / 2 * shp.dot(
  163. wi * self.forcing(self.mesh[i - 1] + 1 / 2 * (1 + xi) * dist[i - 1]))
  164. F[(i - 1) * p:(i - 1) * p + p] = f
  165. # elemental h
  166. h = np.zeros((2, 2))
  167. h[0, 0], h[-1, -1] = -con - self.tau_pos, con - self.tau_neg
  168. # mappinng matrix
  169. map_h = np.zeros((2, numEle), dtype=int)
  170. map_h[:, 0] = np.arange(2)
  171. for i in np.arange(1, numEle):
  172. map_h[:, i] = np.arange(
  173. map_h[2 - 1, i - 1], map_h[2 - 1, i - 1] + 2)
  174. # assemble H and eliminate boundaries
  175. H = np.zeros((numEle + 1, numEle + 1))
  176. for i in range(numEle):
  177. for j in range(2):
  178. m = map_h[j, i]
  179. for k in range(2):
  180. n = map_h[k, i]
  181. H[m, n] += h[j, k]
  182. H = H[1:numEle][:, 1:numEle]
  183. # elemental g
  184. g = np.zeros((2, p_l))
  185. g[0, 0], g[-1, -1] = self.tau_pos, self.tau_neg
  186. # mapping matrix
  187. map_g_x = map_h
  188. map_g_y = np.arange(p_l * numEle, dtype=int).reshape(numEle, p_l).T
  189. # assemble global G
  190. G = np.zeros((numEle + 1, p_l * numEle))
  191. for i in range(numEle):
  192. for j in range(2):
  193. m = map_g_x[j, i]
  194. for k in range(p_l):
  195. n = map_g_y[k, i]
  196. G[m, n] += g[j, k]
  197. G = G[1:numEle, :]
  198. # elemental c
  199. c = np.zeros((p_l, 2))
  200. c[0, 0], c[-1, -1] = -1, 1
  201. # mapping matrix
  202. map_e_x = np.arange(p_l * numEle, dtype=int).reshape(numEle, p_l).T
  203. map_e_y = map_h
  204. # assemble global C
  205. C = np.zeros((p_l * numEle, numEle + 1))
  206. for i in range(numEle):
  207. for j in range(p_l):
  208. m = map_e_x[j, i]
  209. for k in range(2):
  210. n = map_e_y[k, i]
  211. C[m, n] += c[j, k]
  212. C = C[:, 1:numEle]
  213. # L, easy in 1d
  214. L = np.zeros(numEle - 1)
  215. # residual vector
  216. R = np.zeros(self.numEle)
  217. for i in np.arange(self.numEle):
  218. a = dist[i] / 2 * 1 / kappa * \
  219. ((shp.T).T).dot(np.diag(wi).dot(shp_l.T))
  220. b = ((shpx.T) * np.ones((gorder, p))
  221. ).T.dot(np.diag(wi).dot(shp_l.T))
  222. b_t = ((shpx_l.T) * np.ones((gorder, p_l))
  223. ).T.dot(np.diag(wi).dot(shp.T))
  224. d = dist[i] / 2 * shp.dot(np.diag(wi).dot(shp_l.T))
  225. d[0, 0] += self.tau_pos
  226. d[-1, -1] += self.tau_neg
  227. h = np.zeros((2, 2))
  228. h[0, 0], h[-1, -1] = -con - self.tau_pos, con - self.tau_neg
  229. g = np.zeros((2, p_l))
  230. g[0, 0], g[-1, -1] = self.tau_pos, self.tau_neg
  231. e = np.zeros((p, 2))
  232. e[0, 0], e[-1, -1] = -con - self.tau_pos, con - self.tau_neg
  233. c = np.zeros((p, 2))
  234. c[0, 0], c[-1, -1] = -1, 1
  235. m = np.zeros((2, p_l))
  236. m[0, 0], m[-1, -1] = -1, 1
  237. # local error
  238. R[i] = (np.concatenate((a.dot(q[p_l * i:p_l * i + p_l]) + -b.dot(u[p_l * i:p_l * i + p_l]) + c.dot(U_hat[i:i + 2]),
  239. b_t.T.dot(q[p_l * i:p_l * i + p_l]) + d.dot(u[p_l * i:p_l * i + p_l]) + e.dot(U_hat[i:i + 2]))) - np.concatenate((RR[p * i:p * i + p], F[p * i:p * i + p]))).dot(1 - np.concatenate((z_q[p * i:p * i + p], z_u[p * i:p * i + p])))
  240. com_index = np.argsort(np.abs(R))
  241. # select \theta% elements with the large error
  242. theta = 0.15
  243. refine_index = com_index[int(self.numEle * (1 - theta)):len(R)]
  244. # global error
  245. R_g = (C.T.dot(q) + G.dot(u) + H.dot(U_hat[1:-1])).dot(1 - z_hat)
  246. return np.abs(np.sum(R) + np.sum(R_g)), refine_index + 1
  247. def adaptive(self):
  248. tol = 1e-12
  249. estError = 10
  250. counter = 0
  251. ceilCounter = 100
  252. trueErrorList = [[], []]
  253. estErrorList = [[], []]
  254. while estError > tol or counter > ceilCounter:
  255. # solve
  256. u, uFace = self.solveLocal()
  257. adjoint, adjointFace = self.solveAdjoint()
  258. self.u = u
  259. # plot the solution at certain counter
  260. if counter in [0, 4, 9, 19]:
  261. self.plotU(counter)
  262. # record error
  263. trueErrorList[0].append(self.numEle)
  264. trueErrorList[1].append(np.abs(
  265. u[self.numEle * self.numBasisFuncs - 1] - np.sqrt(self.kappa)))
  266. estError, index = self.residual(u, uFace, adjoint, adjointFace)
  267. estErrorList[0].append(self.numEle)
  268. estErrorList[1].append(estError)
  269. # adapt
  270. index = index.tolist()
  271. self.meshAdapt(index)
  272. self.numEle = self.numEle + len(index)
  273. counter += 1
  274. # save error
  275. self.trueErrorList = trueErrorList
  276. self.estErrorList = estErrorList