Skip to content

Commit 80bc39b

Browse files
committed
docs: add helmholtz
1 parent 59e53b8 commit 80bc39b

16 files changed

+241
-77
lines changed
584 Bytes
Loading

eigensolver/output/eigenmodes.png

-741 Bytes
Loading

helmholtz/demo_one_D_helmholtz.py helmholtz/demo1d.py

+34-15
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@
4141
# Solve
4242
residuals = []
4343
x = sa.solve(b, x0=x0, residuals=residuals, **SA_solve_args)
44+
residuals1 = residuals
4445

45-
print("*************************************************************")
46-
print("A single constant mode for interpolation is an inefficient.")
47-
for i, r in enumerate(residuals):
48-
print("residual at iteration {0:2}: {1:^6.2e}".format(i, r))
46+
#print("SA with B=1:")
47+
#for i, r in enumerate(residuals):
48+
# print("residual at iteration {0:2}: {1:^6.2e}".format(i, r))
4949

5050
# Construct solver using the wave-like modes for B
5151
sa = pyamg.smoothed_aggregation_solver(A,
@@ -59,27 +59,46 @@
5959
# Solve
6060
residuals = []
6161
x = sa.solve(b, x0=x0, residuals=residuals, **SA_solve_args)
62+
residualswave = residuals
6263

63-
print("*************************************************************")
64-
print("The performance improves with wave-like interpolation.")
65-
for i, r in enumerate(residuals):
66-
print("residual at iteration {0:2}: {1:^6.2e}".format(i, r))
64+
#print("SA with B=waves:")
65+
#for i, r in enumerate(residuals):
66+
# print("residual at iteration {0:2}: {1:^6.2e}".format(i, r))
67+
fig, ax = plt.subplots()
68+
ax.semilogy(residuals1, label='AMG with $B=1$')
69+
ax.semilogy(residualswave, label='AMG with $B=$wave')
70+
plt.legend()
71+
ax.set_title('AMG convergence for the 1D Helmholtz problem')
72+
figname = f'./output/helmholtz1dconv.png'
73+
import sys
74+
if '--savefig' in sys.argv:
75+
plt.savefig(figname, bbox_inches='tight', dpi=150)
76+
else:
77+
plt.show()
6778

6879
# plot B vs. the lowest right singular vector, which represents
6980
# the near null-space, for a segment of the domain
70-
f, ax = plt.subplots(1, 1)
81+
fig, ax = plt.subplots()
82+
7183
indys = np.arange(0, min(75, h))
7284
line_styles = ["-b", "--m", ":k"]
7385
for i in range(B.shape[1]):
74-
plt.plot(vertices[indys, 0], np.real(B[indys, i]),
75-
line_styles[i], label='NNS Mode {}'.format(i))
86+
ax.plot(vertices[indys, 0], np.real(B[indys, i]),
87+
line_styles[i], label='NNS Mode {}'.format(i))
7688

7789
[U, S, V] = sla.svd(A.todense())
7890
V = V.T.copy()
7991
scale = 0.9 / max(np.real(V[indys, -1]))
80-
plt.plot(vertices[indys, 0], scale * np.real(np.ravel(V[indys, -1])),
81-
line_styles[i + 1], label='Re$(\\nu)$')
8292

83-
plt.title('Near Null-Space (NNS) vs. Lowest Right Singular Vector $\\nu$')
93+
ax.plot(vertices[indys, 0], scale * np.real(np.ravel(V[indys, -1])),
94+
line_styles[i + 1], label='Re$(\\nu)$')
95+
96+
ax.set_title('Near Null-Space (NNS) vs. Lowest Right Singular Vector $\\nu$')
8497
plt.legend(framealpha=1.0)
85-
plt.show()
98+
99+
figname = f'./output/helmholtz1dwaves.png'
100+
import sys
101+
if '--savefig' in sys.argv:
102+
plt.savefig(figname, bbox_inches='tight', dpi=150)
103+
else:
104+
plt.show()

helmholtz/demo_two_D_helmholtz.py helmholtz/demo2d.py

+57-7
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
"""
44
import numpy as np
55
import pyamg
6+
import matplotlib.pyplot as plt
7+
from matplotlib.patches import Polygon
8+
from matplotlib.collections import PatchCollection
69

710
from smoothed_aggregation_helmholtz_solver import smoothed_aggregation_helmholtz_solver, planewaves
811

9-
from my_vis import my_vis, shrink_elmts
10-
1112
# Retrieve 2-D Helmholtz Operator and problem data.
1213
# This is operator was discretized with a local
1314
# discontinuous Galerkin method.
@@ -39,7 +40,7 @@
3940
('symmetric', {'theta': 0.00})]
4041

4142
# Prolongator smoother
42-
smooth = ('energy', {'krylov': 'cgnr'})
43+
smooth = ('energy', {'krylov': 'cgnr', 'weighting': 'diagonal'})
4344

4445
# Aggregation -- non-standard 'naive' aggregation is done on level 0 so that
4546
# only algebraic neighbors at the same spatial location are aggregated
@@ -86,7 +87,6 @@
8687
residuals = []
8788
x = sa.solve(b, x0=x0, residuals=residuals, **SA_solve_args)
8889

89-
print("*************************************************************")
9090
print("Using only a constant mode for interpolation yields an inefficient solver.")
9191
print("This is due to aliasing oscillatory, but algebraically smooth, modes on the coarse levels.")
9292
for i, r in enumerate(residuals):
@@ -125,10 +125,60 @@
125125
# Solve
126126
residuals = []
127127
x = sa.solve(b, x0=x0, residuals=residuals, **SA_solve_args)
128-
print("*************************************************************")
129128
print("Note the improved performance from using planewaves in B.")
130129
for i, r in enumerate(residuals):
131130
print("residual at iteration {0:2}: {1:^6.2e}".format(i, r))
132131

133-
elements2, vertices2 = shrink_elmts(elements, vertices)
134-
my_vis(sa, vertices2, error=abs(x), fname='helmholtz_', E2V=elements2)
132+
#elements2, vertices2 = shrink_elmts(elements, vertices)
133+
#my_vis(sa, vertices2, error=abs(x), fname='helmholtz_', E2V=elements2)
134+
135+
print(sa)
136+
# first shrink the elements
137+
E = elements
138+
Vs = np.zeros((3*E.shape[0], 3))
139+
shrink = 0.75
140+
for i, e in enumerate(E):
141+
xy = vertices[e, :]
142+
xymean = xy.mean(axis=0)
143+
Vs[e,:] = shrink * xy + (1-shrink) * np.kron(xy.mean(axis=0), np.ones((3, 1)))
144+
145+
AggOp = sa.levels[0].AggOp
146+
count = np.array(AggOp.sum(axis=0)).ravel()
147+
Vc = AggOp.T @ vertices
148+
Vc[:,0] /= count
149+
Vc[:,1] /= count
150+
I = E.ravel()
151+
J = AggOp.indices[I]
152+
Ec = J.reshape(E.shape)
153+
154+
fig, ax = plt.subplots()
155+
ax.triplot(Vs[:,0], Vs[:,1], E, lw=0.5)
156+
157+
from cycler import cycler
158+
for aggs in AggOp.T:
159+
I = aggs.indices
160+
ax.plot(Vs[I,0], Vs[I,1], 'o', ms=2)
161+
ax.set_title('Level-0 aggregates')
162+
ax.axis('square')
163+
ax.axis('off')
164+
figname = f'./output/helmholtz2dagg.png'
165+
import sys
166+
if '--savefig' in sys.argv:
167+
plt.savefig(figname, bbox_inches='tight', dpi=150)
168+
else:
169+
plt.show()
170+
171+
fig, axs = plt.subplots(nrows=2, ncols=2)
172+
for i, ax in enumerate(axs.ravel()):
173+
B0 = sa.levels[1].B[:,i]
174+
ax.tripcolor(Vc[:,0], Vc[:,1], B0.real, Ec, lw=1.5)
175+
ax.set_title(f'Level-1 $B_{i}$')
176+
ax.axis('square')
177+
ax.axis('off')
178+
179+
figname = f'./output/helmholtz2dB.png'
180+
import sys
181+
if '--savefig' in sys.argv:
182+
plt.savefig(figname, bbox_inches='tight', dpi=150)
183+
else:
184+
plt.show()

helmholtz/output/helmholtz1dconv.png

44 KB
Loading

helmholtz/output/helmholtz1dwaves.png

139 KB
Loading

helmholtz/output/helmholtz2dB.png

98.4 KB
Loading

helmholtz/output/helmholtz2dagg.png

348 KB
Loading

helmholtz/readme.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
The example focusses on the indefinite Helmholtz wave problem. The first
2+
part highlights the value in using waves to represent the near-null space, `B`.
3+
In addition, we observe the waves to resemble the (lowest) right singular vectors
4+
of the problem.
5+
6+
In the case of 2D, discontinuous Galerkin is used, yielding multiple
7+
degrees of freedom at each spatial location. As a result,
8+
the fine level (level-0) aggregates of the discontinuous
9+
elemeents, largely group neigboring vertices. The wave-like near
10+
null-space is then enforced on the first coarse grid (level-1), resulting
11+
in four modes.

helmholtz/smoothed_aggregation_helmholtz_solver.py

+29-29
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from pyamg.aggregation.smooth import jacobi_prolongation_smoother, \
1919
richardson_prolongation_smoother, energy_prolongation_smoother
2020

21-
21+
2222
__all__ = ['smoothed_aggregation_helmholtz_solver', 'planewaves']
2323

2424
def planewaves(X, Y, omega=1.0, angles=[0.0]):
@@ -34,21 +34,21 @@ def planewaves(X, Y, omega=1.0, angles=[0.0]):
3434
Helmholtz wave number, Laplace(u) + omega^2 u = f
3535
angles : {list}
3636
List of angles in [0, 2 pi] from which to generate planewaves
37-
37+
3838
Returns
3939
-------
4040
Array of planewaves
41-
42-
"""
41+
42+
"""
4343

4444
L = 2*len(angles)
4545
dimen = max(X.shape)
4646
W = numpy.zeros((L, dimen),dtype=complex)
47-
47+
4848
if L == 0:
4949
W = W.T.copy()
5050
return W
51-
51+
5252
X = numpy.ravel(X)
5353
Y = numpy.ravel(Y)
5454

@@ -61,13 +61,13 @@ def planewaves(X, Y, omega=1.0, angles=[0.0]):
6161
W[counter,:] = numpy.real(wave)
6262
W[counter+1,:] = numpy.imag(wave)
6363
counter += 2
64-
64+
6565
# write W row-wise for efficiency
6666
W = W.T.copy()
6767
return W
6868

6969
def preprocess_planewaves(planewaves, max_levels):
70-
# Helper function for smoothed_aggregation_solver.
70+
# Helper function for smoothed_aggregation_solver.
7171
# Will extend planewaves to a length max_levels list, repeating
7272
# the final element of planewaves if necessary.
7373

@@ -87,13 +87,13 @@ def unpack_arg(v):
8787
else:
8888
return v,{}
8989

90-
def smoothed_aggregation_helmholtz_solver(A, planewaves, use_constant=(True, {'last_level':0}),
90+
def smoothed_aggregation_helmholtz_solver(A, planewaves, use_constant=(True, {'last_level':0}),
9191
symmetry='symmetric', strength='symmetric', aggregate='standard',
9292
smooth=('energy', {'krylov': 'gmres'}),
9393
presmoother=('gauss_seidel_nr',{'sweep':'symmetric'}),
9494
postsmoother=('gauss_seidel_nr',{'sweep':'symmetric'}),
9595
improve_candidates='default', max_levels = 10, max_coarse = 100, **kwargs):
96-
96+
9797
"""
9898
Create a multilevel solver using Smoothed Aggregation (SA) for a 2D Helmholtz operator
9999
@@ -105,19 +105,19 @@ def smoothed_aggregation_helmholtz_solver(A, planewaves, use_constant=(True, {'l
105105
[pw_0, pw_1, ..., pw_n], where the k-th tuple pw_k is of the form (fn,
106106
args). fn is a callable and args is a dictionary of arguments for fn.
107107
This k-th tuple is used to define any new planewaves (i.e., new coarse
108-
grid basis functions) to be appended to the existing B_k at that level.
109-
The function fn must return functions defined on the finest level,
108+
grid basis functions) to be appended to the existing B_k at that level.
109+
The function fn must return functions defined on the finest level,
110110
i.e., a collection of vector(s) of length A.shape[0]. These vectors
111-
are then restricted to the appropriate level, where they enrich the
111+
are then restricted to the appropriate level, where they enrich the
112112
coarse space.
113113
Instead of a tuple, None can be used to stipulate no introduction
114-
of planewaves at that level. If len(planewaves) < max_levels, the
114+
of planewaves at that level. If len(planewaves) < max_levels, the
115115
last entry is used to define coarser level planewaves.
116116
use_constant : {tuple}
117-
Tuple of the form (bool, {'last_level':int}). The boolean denotes
117+
Tuple of the form (bool, {'last_level':int}). The boolean denotes
118118
whether to introduce the constant in B at level 0. 'last_level' denotes
119119
the final level to use the constant in B. That is, if 'last_level' is 1,
120-
then the vector in B corresponding to the constant on level 0 is dropped
120+
then the vector in B corresponding to the constant on level 0 is dropped
121121
from B at level 2.
122122
This is important, because using constant based interpolation beyond
123123
the Nyquist rate will result in poor solver performance.
@@ -131,7 +131,7 @@ def smoothed_aggregation_helmholtz_solver(A, planewaves, use_constant=(True, {'l
131131
Method used to determine the strength of connection between unknowns of
132132
the linear system. Method-specific parameters may be passed in using a
133133
tuple, e.g. strength=('symmetric',{'theta' : 0.25 }). If strength=None,
134-
all nonzero entries of the matrix are considered strong.
134+
all nonzero entries of the matrix are considered strong.
135135
See notes below for varying this parameter on a per level basis. Also,
136136
see notes below for using a predefined strength matrix on each level.
137137
aggregate : ['standard', 'lloyd', 'naive', ('predefined', {'AggOp' : csr_matrix})]
@@ -159,12 +159,12 @@ def smoothed_aggregation_helmholtz_solver(A, planewaves, use_constant=(True, {'l
159159
max_levels : {integer} : default 10
160160
Maximum number of levels to be used in the multilevel solver.
161161
max_coarse : {integer} : default 500
162-
Maximum number of variables permitted on the coarse grid.
162+
Maximum number of variables permitted on the coarse grid.
163163
164164
Other Parameters
165165
----------------
166166
coarse_solver : ['splu','lu', ... ]
167-
Solver used at the coarsest level of the MG hierarchy
167+
Solver used at the coarsest level of the MG hierarchy
168168
169169
Returns
170170
-------
@@ -186,7 +186,7 @@ def smoothed_aggregation_helmholtz_solver(A, planewaves, use_constant=(True, {'l
186186
levels, use a list as input so that the ith entry defines the method at
187187
the ith level. If there are more levels in the hierarchy than list
188188
entries, the last entry will define the method for all levels lower.
189-
189+
190190
Examples are:
191191
smooth=[('jacobi', {'omega':1.0}), None, 'jacobi']
192192
presmoother=[('block_gauss_seidel', {'sweep':symmetric}), 'sor']
@@ -202,7 +202,7 @@ def smoothed_aggregation_helmholtz_solver(A, planewaves, use_constant=(True, {'l
202202
degree-of-freedom in C0 represents a supernode. For instance to
203203
predefine a three-level hierarchy, use [('predefined', {'C' : C0}),
204204
('predefined', {'C' : C1}) ].
205-
205+
206206
Similarly for predefined aggregation, use a list of tuples. For instance
207207
to predefine a three-level hierarchy, use [('predefined', {'AggOp' :
208208
Agg0}), ('predefined', {'AggOp' : Agg1}) ], where the dimensions of A,
@@ -231,14 +231,14 @@ def smoothed_aggregation_helmholtz_solver(A, planewaves, use_constant=(True, {'l
231231
raise TypeError('argument A must have type csr_matrix or bsr_matrix')
232232

233233
A = A.asfptype()
234-
234+
235235
if (symmetry != 'symmetric') and (symmetry != 'hermitian') and (symmetry != 'nonsymmetric'):
236236
raise ValueError('expected \'symmetric\', \'nonsymmetric\' or \'hermitian\' for the symmetry parameter ')
237237
A.symmetry = symmetry
238238

239239
if A.shape[0] != A.shape[1]:
240240
raise ValueError('expected square matrix')
241-
241+
242242
##
243243
# Preprocess and extend planewaves to length max_levels
244244
planewaves = preprocess_planewaves(planewaves, max_levels)
@@ -249,14 +249,14 @@ def smoothed_aggregation_helmholtz_solver(A, planewaves, use_constant=(True, {'l
249249
first_planewave_level += 1
250250
if pw is not None:
251251
break
252-
##
252+
##
253253
if (use_const == False) and (planewaves[0] == None):
254254
raise ValueError('No functions defined for B on the finest level, ' + \
255255
'either use_constant must be true, or planewaves must be defined for level 0')
256256
elif (use_const == True) and (args['last_level'] < first_planewave_level-1):
257257
raise ValueError('Some levels have no function(s) defined for B. ' + \
258258
'Change use_constant and/or planewave arguments.')
259-
259+
260260
##
261261
# Levelize the user parameters, so that they become lists describing the
262262
# desired user option on each level.
@@ -291,7 +291,7 @@ def smoothed_aggregation_helmholtz_solver(A, planewaves, use_constant=(True, {'l
291291
# As in alpha-SA, relax the candidates before restriction
292292
if improve_candidates[0] is not None:
293293
Bcoarse2 = relaxation_as_linear_operator(improve_candidates[0], A, zeros_0)*Bcoarse2
294-
294+
295295
##
296296
# Restrict Bcoarse2 to current level
297297
for i in range(len(levels)-1):
@@ -308,17 +308,17 @@ def smoothed_aggregation_helmholtz_solver(A, planewaves, use_constant=(True, {'l
308308
if use_const and len(levels) == 1:
309309
# If level 0, and the constant is to be used in interpolation
310310
levels[0].B = numpy.hstack( (numpy.ones((A.shape[0],1), dtype=A.dtype), Bcoarse2) )
311-
elif use_const and args['last_level'] == len(levels)-2:
311+
elif use_const and args['last_level'] == len(levels)-2:
312312
# If the previous level was the last level to use the constant, then remove the
313313
# coarse grid function based on the constant from B
314314
levels[-1].B = numpy.hstack( (levels[-1].B[:,1:], Bcoarse2) )
315315
else:
316316
levels[-1].B = numpy.hstack((levels[-1].B, Bcoarse2))
317-
317+
318318
##
319319
# Create and Append new level
320320
_extend_hierarchy(levels, strength, aggregate, smooth, [None for i in range(max_levels)] ,keep=True)
321-
321+
322322
ml = multilevel_solver(levels, **kwargs)
323323
change_smoothers(ml, presmoother, postsmoother)
324324
return ml
15 Bytes
Loading
28 Bytes
Loading

0 commit comments

Comments
 (0)