Skip to content

Commit 2ba2568

Browse files
authored
Merge pull request #15 from lmdiazangulo/dev
Dev
2 parents 5308c34 + 60153e3 commit 2ba2568

File tree

13 files changed

+951
-343
lines changed

13 files changed

+951
-343
lines changed

maxwell/dg/dg1d.py

Lines changed: 135 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,19 @@
66

77

88
class 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

maxwell/driver.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .integrators.LF2V import *
1313
from .integrators.EULER import *
1414

15+
import copy
1516

1617
class MaxwellDriver:
1718
def __init__(self,
@@ -79,16 +80,47 @@ def run_until(self, final_time):
7980
def __getitem__(self, key):
8081
return self.fields[key]
8182

82-
def buildDrivedEvolutionOperator(self):
83+
def buildDrivedEvolutionOperator(self, reduceToEssentialDoF=True):
8384
N = self.sp.number_of_unknowns()
8485
A = np.zeros((N,N))
86+
87+
oldFields = copy.deepcopy(self.fields)
88+
8589
for i in range(N):
8690
self.fields = self.sp.buildFields()
8791
self.sp.setFieldWithIndex(self.fields, i, 1.0)
8892
self.step()
8993
q = self.sp.fieldsAsStateVector(self.fields)
9094
A[:,i] = q[:]
9195

92-
self.fields = self.sp.buildFields()
96+
self.fields = oldFields
97+
98+
if reduceToEssentialDoF and self.sp.isStaggered():
99+
A = self.sp.reduceToEssentialDoF(A)
100+
101+
return A
102+
103+
def buildPowerOperator(self):
104+
G = self.buildDrivedEvolutionOperator()
105+
Mg = self.sp.buildGlobalMassMatrix()
106+
return (1/self.dt)*(G.T.dot(Mg).dot(G) - Mg)
107+
108+
109+
def buildCausallyConnectedOperators(self, element=0, neighbors=-1):
110+
if neighbors == -1:
111+
neighs = self.timeIntegrator.N_STAGES
112+
else:
113+
neighs = neighbors
114+
115+
local_indices, neigh_indices = self.sp.buildLocalAndNeighborIndices(element, neighs)
116+
117+
G = self.sp.reorder_by_elements(self.buildDrivedEvolutionOperator())
118+
Mg = self.sp.reorder_by_elements(self.sp.buildGlobalMassMatrix())
119+
A = G[local_indices][:,local_indices]
120+
B = G[local_indices][:,neigh_indices]
121+
C = G[neigh_indices][:,local_indices]
122+
D = G[neigh_indices][:,neigh_indices]
123+
Mk = Mg[local_indices][:,local_indices]
124+
Mn = Mg[neigh_indices][:,neigh_indices]
93125

94-
return A
126+
return A, B, C, D, Mk, Mn

0 commit comments

Comments
 (0)