adaptation.py 10 KB

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