preprocess.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import numpy as np
  2. from collections import namedtuple
  3. from scipy.linalg import block_diag
  4. def shape(x, p):
  5. """generate p shape functions and its first order derivative
  6. (order p-1) at the given location x"""
  7. A = np.array([np.linspace(-1, 1, p)]).T**np.arange(p)
  8. C = np.linalg.inv(A).T
  9. x = np.array([x]).T
  10. shp = C.dot((x**np.arange(p)).T)
  11. shpx = C[:, 1::1].dot((x**np.arange(p - 1) * np.arange(1, p)).T)
  12. return shp, shpx
  13. def forcing(x):
  14. f = 1
  15. return f
  16. def boundaryCondition(case, t=None):
  17. # boundary condition
  18. if case == 0:
  19. # primal problem
  20. bc = [0, 0]
  21. if case == 1:
  22. # adjoint problem
  23. bc = [0, 1]
  24. return bc
  25. class discretization(object):
  26. """Given the problem statement, construct the discretization matrices"""
  27. def __init__(self, coeff, mesh, enrich=None):
  28. self.mesh = mesh
  29. self.coeff = coeff
  30. self.enrich = enrich
  31. # the following init are for the sake of simplicity
  32. self.numBasisFuncs = coeff.pOrder + 1
  33. self.tau_pos = coeff.tauPlus
  34. self.tau_neg = coeff.tauMinus
  35. self.conv = coeff.convection
  36. self.kappa = coeff.diffusion
  37. self.n_ele = len(mesh) - 1
  38. self.dist = self.distGen()
  39. # shape function and gauss quadrature
  40. self.gqOrder = 5 * self.numBasisFuncs
  41. self.xi, self.wi = np.polynomial.legendre.leggauss(self.gqOrder)
  42. self.shp, self.shpx = shape(self.xi, self.numBasisFuncs)
  43. # enrich the space if the enrich argument is given
  44. if enrich is not None:
  45. self.shpEnrich, self.shpxEnrich = shape(
  46. self.xi, self.numBasisFuncs + enrich)
  47. def distGen(self):
  48. dist = np.zeros(self.n_ele)
  49. for i in range(1, self.n_ele + 1):
  50. dist[i - 1] = self.mesh[i] - self.mesh[i - 1]
  51. return dist
  52. def matGroup(self):
  53. lhsMat = self.lhsGen()
  54. F, L, R = lhsMat.F, lhsMat.L, lhsMat.R
  55. eleMat = self.eleGen()
  56. A, B, BonU, D = eleMat.A, eleMat.B, eleMat.BonU, eleMat.D
  57. faceMat = self.interfaceGen()
  58. C, E, G, H = faceMat.C, faceMat.E, faceMat.G, faceMat.H
  59. matGroup = namedtuple(
  60. 'matGroup', ['A', 'B', 'BonU', 'C', 'D', 'E', 'F', 'G', 'H', 'L', 'R'])
  61. return matGroup(A, B, BonU, C, D, E, F, G, H, L, R)
  62. def lhsGen(self):
  63. """Generate matrices associated with left hand side"""
  64. if self.enrich is not None:
  65. # when enrich is given
  66. # provide matrices used in the DWR residual calculation
  67. numBasisFuncs = self.numBasisFuncs + self.enrich
  68. shp = self.shpEnrich
  69. else:
  70. numBasisFuncs = self.numBasisFuncs
  71. shp = self.shp
  72. # forcing vector F
  73. F = np.zeros(numBasisFuncs * self.n_ele)
  74. for i in range(1, self.n_ele + 1):
  75. f = self.dist[i - 1] / 2 \
  76. * shp.dot(self.wi * forcing(self.mesh[i - 1] +
  77. 1 / 2 * (1 + self.xi) *
  78. self.dist[i - 1]))
  79. F[(i - 1) * numBasisFuncs:i * numBasisFuncs] = f
  80. F[0] += (self.conv + self.tau_pos) * boundaryCondition(0)[0]
  81. F[-1] += (-self.conv + self.tau_neg) * boundaryCondition(0)[1]
  82. # L, easy in 1d
  83. L = np.zeros(self.n_ele - 1)
  84. # R, easy in 1d
  85. R = np.zeros(numBasisFuncs * self.n_ele)
  86. R[0] = boundaryCondition(0)[0]
  87. R[-1] = -boundaryCondition(0)[1]
  88. lhsMat = namedtuple('lhsMat', ['F', 'L', 'R'])
  89. return lhsMat(F, L, R)
  90. def eleGen(self):
  91. """Generate matrices associated with elements"""
  92. if self.enrich is not None:
  93. numBasisFuncsEnrich = self.numBasisFuncs + self.enrich
  94. shpEnrich = self.shpEnrich
  95. shpxEnrich = self.shpxEnrich
  96. b = (self.shpx.T * np.ones((self.gqOrder, self.numBasisFuncs))
  97. ).T.dot(np.diag(self.wi).dot(shpEnrich.T))
  98. # BonU is only used in calculating DWR residual
  99. BonU = block_diag(*[b] * (self.n_ele)).T
  100. else:
  101. numBasisFuncsEnrich = self.numBasisFuncs
  102. shpEnrich = self.shp
  103. shpxEnrich = self.shpx
  104. BonU = None
  105. a = 1 / self.kappa * shpEnrich.dot(np.diag(self.wi).dot(self.shp.T))
  106. A = np.repeat(self.dist, self.numBasisFuncs) / \
  107. 2 * block_diag(*[a] * (self.n_ele))
  108. b = (shpxEnrich.T * np.ones((self.gqOrder, numBasisFuncsEnrich))
  109. ).T.dot(np.diag(self.wi).dot(self.shp.T))
  110. B = block_diag(*[b] * (self.n_ele))
  111. d = shpEnrich.dot(np.diag(self.wi).dot(self.shp.T))
  112. # assemble D
  113. d_face = np.zeros((numBasisFuncsEnrich, self.numBasisFuncs))
  114. d_face[0, 0] = self.tau_pos
  115. d_face[-1, -1] = self.tau_neg
  116. D = np.repeat(self.dist, self.numBasisFuncs) / 2 * \
  117. block_diag(*[d] * (self.n_ele)) + \
  118. block_diag(*[d_face] * (self.n_ele))
  119. eleMat = namedtuple('eleMat', ['A', 'B', 'BonU', 'D'])
  120. return eleMat(A, B, BonU, D)
  121. def interfaceGen(self):
  122. """Generate matrices associated with interfaces"""
  123. if self.enrich is not None:
  124. # when enrich is given
  125. # provide matrices used in the DWR residual calculation
  126. numBasisFuncs = self.numBasisFuncs + self.enrich
  127. else:
  128. numBasisFuncs = self.numBasisFuncs
  129. tau_pos, tau_neg, conv = self.tau_pos, self.tau_neg, self.conv
  130. # elemental h
  131. h = np.zeros((2, 2))
  132. h[0, 0], h[-1, -1] = -conv - tau_pos, conv - tau_neg
  133. # mappinng matrix
  134. map_h = np.zeros((2, self.n_ele), dtype=int)
  135. map_h[:, 0] = np.arange(2)
  136. for i in np.arange(1, self.n_ele):
  137. map_h[:, i] = np.arange(
  138. map_h[2 - 1, i - 1], map_h[2 - 1, i - 1] + 2)
  139. # assemble H and eliminate boundaries
  140. H = np.zeros((self.n_ele + 1, self.n_ele + 1))
  141. for i in range(self.n_ele):
  142. for j in range(2):
  143. m = map_h[j, i]
  144. for k in range(2):
  145. n = map_h[k, i]
  146. H[m, n] += h[j, k]
  147. H = H[1:self.n_ele][:, 1:self.n_ele]
  148. # elemental e
  149. e = np.zeros((numBasisFuncs, 2))
  150. e[0, 0], e[-1, -1] = -conv - tau_pos, conv - tau_neg
  151. # mapping matrix
  152. map_e_x = np.arange(numBasisFuncs * self.n_ele,
  153. dtype=int).reshape(self.n_ele,
  154. numBasisFuncs).T
  155. map_e_y = map_h
  156. # assemble global E
  157. E = np.zeros((numBasisFuncs * self.n_ele, self.n_ele + 1))
  158. for i in range(self.n_ele):
  159. for j in range(numBasisFuncs):
  160. m = map_e_x[j, i]
  161. for k in range(2):
  162. n = map_e_y[k, i]
  163. E[m, n] += e[j, k]
  164. E = E[:, 1:self.n_ele]
  165. # elemental c
  166. c = np.zeros((numBasisFuncs, 2))
  167. c[0, 0], c[-1, -1] = -1, 1
  168. # assemble global C
  169. C = np.zeros((numBasisFuncs * self.n_ele, self.n_ele + 1))
  170. for i in range(self.n_ele):
  171. for j in range(numBasisFuncs):
  172. m = map_e_x[j, i]
  173. for k in range(2):
  174. n = map_e_y[k, i]
  175. C[m, n] += c[j, k]
  176. C = C[:, 1:self.n_ele]
  177. # elemental g
  178. g = np.zeros((2, numBasisFuncs))
  179. g[0, 0], g[-1, -1] = tau_pos, tau_neg
  180. # mapping matrix
  181. map_g_x = map_h
  182. map_g_y = np.arange(numBasisFuncs * self.n_ele,
  183. dtype=int).reshape(self.n_ele,
  184. numBasisFuncs).T
  185. # assemble global G
  186. G = np.zeros((self.n_ele + 1, numBasisFuncs * self.n_ele))
  187. for i in range(self.n_ele):
  188. for j in range(2):
  189. m = map_g_x[j, i]
  190. for k in range(numBasisFuncs):
  191. n = map_g_y[k, i]
  192. G[m, n] += g[j, k]
  193. G = G[1:self.n_ele, :]
  194. faceMat = namedtuple('faceMat', ['C', 'E', 'G', 'H'])
  195. return faceMat(C, E, G, H)