66
77
88class DG1D (SpatialDiscretization ):
9- def __init__ (self , n_order : int , mesh : Mesh1D , fluxType = "Upwind" , epsilon = None ,sigma = None ):
9+ def __init__ (self , n_order : int , mesh : Mesh1D , fluxPenalty = 1.0 , epsilon = None ,sigma = None ):
1010 SpatialDiscretization .__init__ (self , mesh )
1111
1212 assert n_order > 0
1313 self .n_order = n_order
1414
15- assert fluxType == "Upwind" or fluxType == "Centered"
16- self .fluxType = fluxType
17-
15+ self .alpha = fluxPenalty
1816 self .n_faces = 2
1917 self .n_fp = 1
18+
19+ if self .mesh .boundary_label ["LEFT" ] != self .mesh .boundary_label ["RIGHT" ]:
20+ raise ValueError ("Boundaries must be of the same type." )
2021
21- alpha = 0
22- beta = 0
2322
2423 # Epsilon implementation in 1D
2524 if epsilon is None :
@@ -48,7 +47,7 @@ def __init__(self, n_order: int, mesh: Mesh1D, fluxType="Upwind",epsilon=None,si
4847 self .vmap_m , self .vmap_p , self .vmap_b , self .map_b = build_maps (
4948 n_order , self .x , etoe , etof )
5049
51- r = jacobiGL (alpha , beta , n_order )
50+ r = jacobiGL (0 , 0 , n_order )
5251 self .fmask , self .fmask_1 , self .fmask_2 = buildFMask (r )
5352
5453 self .mass = mass_matrix (n_order , r )
@@ -99,61 +98,44 @@ def get_impedance(self):
9998 return Z_imp
10099
101100 def fieldsOnBoundaryConditions (self , E , H ):
102- bcType = self .mesh .boundary_label
103-
104- for bdr , label in self .mesh .boundary_label .items ():
105- if bdr == "LEFT" or bdr == "RIGHT" :
106- if label == "PEC" :
107- Ebc = - E .transpose ().take (self .vmap_b )
108- Hbc = H .transpose ().take (self .vmap_b )
109- elif label == "PMC" :
110- Hbc = - H .transpose ().take (self .vmap_b )
111- Ebc = E .transpose ().take (self .vmap_b )
112- elif label == "SMA" :
113- Hbc = H .transpose ().take (self .vmap_b ) * 0.0
114- Ebc = E .transpose ().take (self .vmap_b ) * 0.0
115- elif label == "Periodic" :
116- Ebc = E .transpose ().take (self .vmap_b [::- 1 ])
117- Hbc = H .transpose ().take (self .vmap_b [::- 1 ])
118- else :
119- raise ValueError ("Invalid boundary label." )
120- return Ebc , Hbc
101+ label = self .mesh .boundary_label ["LEFT" ]
102+ Eb = E .transpose ().take (self .vmap_b )
103+ Hb = H .transpose ().take (self .vmap_b )
104+ if label == "PEC" :
105+ Ebc = - Eb
106+ Hbc = Hb
107+ elif label == "PMC" :
108+ Hbc = - Hb
109+ Ebc = Eb
110+ elif label == "Null" :
111+ Hbc = Hb
112+ Ebc = Eb
113+ elif label == "Double" :
114+ Hbc = - (Eb + Hb )/ 2
115+ Ebc = - (Eb - Hb )/ 2
116+ elif label == "ABC" :
117+ Ebc = (Eb + self .nx .take (self .map_b ) * Hb )* 0.5
118+ Hbc = (Hb + self .nx .take (self .map_b ) * Eb )* 0.5
119+ elif label == "SMA" :
120+ Hbc = Hb * 0.0
121+ Ebc = Eb * 0.0
122+ elif label == "Periodic" :
123+ Ebc = E .transpose ().take (self .vmap_b [::- 1 ])
124+ Hbc = H .transpose ().take (self .vmap_b [::- 1 ])
125+ else :
126+ raise ValueError ("Invalid boundary label." )
127+ return Ebc , Hbc
121128
122129 def computeFluxE (self , E , H ):
123130 dE , dH = self .computeJumps (E , H )
124-
125- if self .fluxType == "Upwind" :
126- flux_E = 1 / self .Z_imp_sum * (self .nx * self .Z_imp_p * dH - dE )
127- elif self .fluxType == "Centered" :
128- flux_E = 1 / self .Z_imp_sum * (self .nx * self .Z_imp_p * dH )
129- else :
130- raise ValueError ("Invalid fluxType label" )
131+ flux_E = 1 / self .Z_imp_sum * (self .nx * self .Z_imp_p * dH - self .alpha * dE )
131132 return flux_E
132133
133134 def computeFluxH (self , E , H ):
134135 dE , dH = self .computeJumps (E , H )
135-
136- if self .fluxType == "Upwind" :
137- flux_H = 1 / self .Y_imp_sum * (self .nx * self .Y_imp_p * dE - dH )
138- elif self .fluxType == "Centered" :
139- flux_H = 1 / self .Y_imp_sum * (self .nx * self .Y_imp_p * dE )
140- else :
141- raise ValueError ("Invalid fluxType label" )
136+ flux_H = 1 / self .Y_imp_sum * (self .nx * self .Y_imp_p * dE - self .alpha * dH )
142137 return flux_H
143138
144- def computeFlux (self , E , H ):
145- dE , dH = self .computeJumps (E , H )
146-
147- if self .fluxType == "Upwind" :
148- flux_E = 1 / self .Z_imp_sum * (self .nx * self .Z_imp_p * dH - dE )
149- flux_H = 1 / self .Y_imp_sum * (self .nx * self .Y_imp_p * dE - dH )
150- elif self .fluxType == "Centered" :
151- flux_E = 1 / self .Z_imp_sum * (self .nx * self .Z_imp_p * dH )
152- flux_H = 1 / self .Y_imp_sum * (self .nx * self .Y_imp_p * dE )
153- else :
154- raise ValueError ("Invalid fluxType label" )
155- return flux_E , flux_H
156-
157139 def computeJumps (self , E , H ):
158140 Ebc , Hbc = self .fieldsOnBoundaryConditions (E , H )
159141 dE = E .transpose ().take (self .vmap_m ) - E .transpose ().take (self .vmap_p )
@@ -237,35 +219,78 @@ def buildEvolutionOperator(self):
237219 A [:, i ] = q0 [:, 0 ]
238220 return A
239221
240- def reorder_array (self , A , ordering ):
222+ def reorder_by_elements (self , A ):
241223 # Assumes that the original array contains all DoF ordered as:
242224 # [ E_0, ..., E_{K-1}, H_0, ..., H_{K-1} ]
243225 N = A .shape [0 ]
244226 K = self .mesh .number_of_elements ()
245227 Np = self .number_of_nodes_per_element ()
246228 new_order = np .arange (N , dtype = int )
247- if ordering == 'byElements' :
248- for i in range (N ):
249- node = i % Np
250- elem = int (np .floor (i / Np )) % K
251- if i < N / 2 :
252- new_order [2 * elem * Np + node ] = i
253- else :
254- new_order [2 * elem * Np + Np + node ] = i
255- if ordering == 'interleaved' :
256- for i in range (N ):
257- node = i % Np
258- elem = int (np .floor (i / Np )) % K
259- if i < N / 2 :
260- new_order [2 * elem * Np + node * 2 ] = i
261- else :
262- new_order [2 * elem * Np + node * 2 + 1 ] = i
229+ for i in range (N ):
230+ node = i % Np
231+ elem = int (np .floor (i / Np )) % K
232+ if i < N / 2 :
233+ new_order [2 * elem * Np + node ] = i
234+ else :
235+ new_order [2 * elem * Np + Np + node ] = i
263236 if (len (A .shape ) == 1 ):
264237 A1 = [A [i ] for i in new_order ]
265238 elif (len (A .shape ) == 2 ):
266239 A1 = [[A [i ][j ] for j in new_order ] for i in new_order ]
267240 return np .array (A1 )
268241
242+ def number_of_unknowns (self , field = 'all' ):
243+ if field == 'all' :
244+ return self .number_of_unknowns ('E' ) \
245+ + self .number_of_unknowns ('H' )
246+ else :
247+ f = self .buildFields ()
248+ return f [field ].size
249+
250+ def buildStiffnessMatrix (self ):
251+ Np = self .number_of_nodes_per_element ()
252+ K = self .mesh .number_of_elements ()
253+ N = self .number_of_unknowns ()
254+ A = np .zeros ((N , N ))
255+ for i in range (N ):
256+ fields = self .buildFields ()
257+ E = fields ['E' ]
258+ H = fields ['H' ]
259+ self .setFieldWithIndex (fields , i , 1.0 )
260+ rhs_drE = np .matmul (self .diff_matrix , E )
261+ rhs_drH = np .matmul (self .diff_matrix , H )
262+ rhsE = 1 / self .epsilon * np .multiply (- 1 * self .rx , rhs_drH )
263+ rhsH = 1 / self .mu * np .multiply (- 1 * self .rx , rhs_drE )
264+ q0 = np .vstack ([
265+ rhsE .reshape (Np * K , 1 , order = 'F' ),
266+ rhsH .reshape (Np * K , 1 , order = 'F' )
267+ ])
268+ A [:, i ] = q0 [:, 0 ]
269+ return A
270+
271+
272+
273+ def buildFluxMatrix (self ):
274+ Np = self .number_of_nodes_per_element ()
275+ K = self .mesh .number_of_elements ()
276+ N = self .number_of_unknowns ()
277+ A = np .zeros ((N , N ))
278+ for i in range (N ):
279+ fields = self .buildFields ()
280+ E = fields ['E' ]
281+ H = fields ['H' ]
282+ self .setFieldWithIndex (fields , i , 1.0 )
283+ flux_E = self .computeFluxE (E , H )
284+ flux_H = self .computeFluxH (E , H )
285+ rhsE = 1 / self .epsilon * np .matmul (self .lift , self .f_scale * flux_E )
286+ rhsH = 1 / self .mu * (np .matmul (self .lift , self .f_scale * flux_H ))
287+ q0 = np .vstack ([
288+ rhsE .reshape (Np * K , 1 , order = 'F' ),
289+ rhsH .reshape (Np * K , 1 , order = 'F' )
290+ ])
291+ A [:, i ] = q0 [:, 0 ]
292+ return A
293+
269294 def buildGlobalMassMatrix (self ):
270295 Np = self .number_of_nodes_per_element ()
271296 K = self .mesh .number_of_elements ()
@@ -282,17 +307,55 @@ def buildGlobalMassMatrix(self):
282307 def getEnergy (self , field ):
283308 '''
284309 Gets energy stored in field by computing
285- field^T * MassMatrix * field * Jacobian.
310+ (1/2) * field^T * MassMatrix * field * Jacobian.
286311 for each element and then the sum.
312+ WRONG IF EVOLVED BY A STAGGERED SCHEME.
287313 '''
288314 Np = self .number_of_nodes_per_element ()
289315 K = self .mesh .number_of_elements ()
290316 assert field .shape == (Np , K )
291317 energy = 0.0
292318 for k in range (K ):
293- energy += np .inner (
319+ energy += 0.5 * np .inner (
294320 field [:, k ].dot (self .mass ),
295321 field [:, k ]* self .jacobian [:, k ]
296322 )
297323
298324 return energy
325+
326+ def getTotalEnergy (self , G , fields ):
327+ N = self .number_of_unknowns ()
328+ NE = self .number_of_unknowns ('E' )
329+ NH = self .number_of_unknowns ('H' )
330+
331+ L_E = np .zeros ((N , N ))
332+ L_E [:NE , :NE ] = np .eye (NE )
333+ L_H = np .zeros ((N , N ))
334+ L_H [NE :, NE :] = np .eye (NH )
335+
336+ M = self .buildGlobalMassMatrix ()
337+
338+ P = 0.5 * ( M
339+ + 0.5 * L_E .dot (G .T ).dot (L_H ).dot (M ).dot (L_H )
340+ + 0.5 * L_H .dot (M ).dot (L_H ).dot (G ).dot (L_E )
341+ )
342+
343+ q = self .fieldsAsStateVector (fields )
344+ return q .T .dot (P ).dot (q )
345+
346+ def buildConnectedOperators (self , element = 0 , neighbors = 1 ):
347+
348+ neighs = neighbors
349+
350+ local_indices , neigh_indices = self .buildLocalAndNeighborIndices (element , neighs )
351+
352+ G = self .reorder_by_elements (self .buildEvolutionOperator ())
353+ Mg = self .reorder_by_elements (self .buildGlobalMassMatrix ())
354+ A = G [local_indices ][:,local_indices ]
355+ B = G [local_indices ][:,neigh_indices ]
356+ C = G [neigh_indices ][:,local_indices ]
357+ D = G [neigh_indices ][:,neigh_indices ]
358+ Mk = Mg [local_indices ][:,local_indices ]
359+ Mn = Mg [neigh_indices ][:,neigh_indices ]
360+
361+ return A , B , C , D , Mk , Mn
0 commit comments