Skip to content

Commit f8d13b4

Browse files
committed
add a test case for the qbmax line expansion
1 parent 6496ec5 commit f8d13b4

1 file changed

Lines changed: 180 additions & 0 deletions

File tree

test/test_qbmax_line_expansion.py

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
import numpy as np
2+
import pytest
3+
import pyopencl as cl
4+
5+
import meshmode.mesh.generation as mgen
6+
from meshmode.array_context import PyOpenCLArrayContext
7+
from meshmode.discretization import Discretization
8+
from meshmode.discretization.poly_element import InterpolatoryQuadratureSimplexGroupFactory
9+
10+
from pytential import GeometryCollection, bind, sym
11+
from pytential.qbx import QBXLayerPotentialSource
12+
13+
from sumpy.kernel import YukawaKernel
14+
from sumpy.expansion.local import AsymptoticDividingLineTaylorExpansion
15+
from sumpy.qbx import LayerPotentialMatrixGenerator
16+
17+
from arraycontext import flatten
18+
from pytools.convergence import EOCRecorder
19+
20+
import mpmath
21+
22+
23+
def asym_yukawa(dim, lam=None):
24+
"""Asymptotic function of the Yukawa kernel."""
25+
from pymbolic import primitives, var
26+
from sumpy.symbolic import pymbolic_real_norm_2, SpatialConstant
27+
28+
b = pymbolic_real_norm_2(primitives.make_sym_vector("b", dim))
29+
30+
if lam:
31+
expr = var("exp")(-lam * b * (1 - var('tau')))
32+
else:
33+
lam = SpatialConstant("lam")
34+
expr = var("exp")(-lam * b * (1 - var('tau')))
35+
return expr
36+
37+
38+
def utrue(lam, r, tau, targets_h, f_mode, side):
39+
"""Test convergence of QBMAX (asymptotic Yukawa expansion) on a unit circle
40+
with density φ(y) = exp(imθ_y)"""
41+
mpmath.mp.dps = 25
42+
43+
angles = np.arctan2(targets_h[1, :], targets_h[0, :])
44+
n_points = len(angles)
45+
result = np.zeros(n_points, dtype=np.complex128)
46+
47+
for i in range(n_points):
48+
r_i = float(r[i])
49+
50+
if side == -1:
51+
coeff = float(mpmath.besselk(f_mode, lam) *
52+
mpmath.besseli(f_mode, lam * (1 - (1 - tau) * r_i)))
53+
else:
54+
coeff = float(mpmath.besseli(f_mode, lam) *
55+
mpmath.besselk(f_mode, lam * (1 + (1 - tau) * r_i)))
56+
57+
result[i] = coeff * np.exp(1j * f_mode * angles[i])
58+
59+
return result
60+
61+
62+
def test_qbmax_yukawa_convergence():
63+
"""Test convergence of QBMAX (asymptotic Yukawa expansion) for various τ values."""
64+
cl_ctx = cl.create_some_context()
65+
queue = cl.CommandQueue(cl_ctx)
66+
actx = PyOpenCLArrayContext(queue)
67+
68+
69+
lam = 15
70+
f_mode = 7
71+
nelements = [20, 40, 60]
72+
qbx_order = 4
73+
target_order = 5
74+
upsampling_factor = 5
75+
extra_kwargs = {'lam': lam}
76+
77+
knl = YukawaKernel(2)
78+
asym_knl = asym_yukawa(2)
79+
80+
np.random.seed(42)
81+
t = np.random.uniform(0, 1, 10)
82+
targets_h = np.array([np.cos(2 * np.pi * t), np.sin(2 * np.pi * t)])
83+
targets = actx.from_numpy(targets_h)
84+
85+
for tau in [0, 0.5, 1]:
86+
eoc_in = EOCRecorder()
87+
eoc_out = EOCRecorder()
88+
89+
asym_expn = AsymptoticDividingLineTaylorExpansion(
90+
knl, asym_knl, qbx_order, tau=tau)
91+
92+
for nelement in nelements:
93+
mesh = mgen.make_curve_mesh(
94+
mgen.circle, np.linspace(0, 1, nelement+1), target_order)
95+
pre_density_discr = Discretization(
96+
actx, mesh,
97+
InterpolatoryQuadratureSimplexGroupFactory(target_order))
98+
99+
qbx = QBXLayerPotentialSource(
100+
pre_density_discr,
101+
upsampling_factor * target_order,
102+
qbx_order,
103+
fmm_order=False)
104+
105+
places = GeometryCollection({"qbx": qbx}, auto_where=('qbx'))
106+
107+
source_discr = places.get_discretization(
108+
'qbx', sym.QBX_SOURCE_QUAD_STAGE2)
109+
sources = source_discr.nodes()
110+
sources_h = actx.to_numpy(flatten(sources, actx)).reshape(2, -1)
111+
112+
dofdesc = sym.DOFDescriptor("qbx", sym.QBX_SOURCE_QUAD_STAGE2)
113+
weights_nodes = bind(
114+
places,
115+
sym.weights_and_area_elements(
116+
ambient_dim=2, dim=1, dofdesc=dofdesc))(actx)
117+
weights_nodes_h = actx.to_numpy(flatten(weights_nodes, actx))
118+
119+
angle = np.arctan2(sources_h[1, :], sources_h[0, :])
120+
sigma = np.exp(1j * f_mode * angle) * weights_nodes_h
121+
122+
expansion_radii_h = np.ones(targets_h.shape[1]) * np.pi / nelement
123+
centers_in = actx.from_numpy((1 - expansion_radii_h) * targets_h)
124+
centers_out = actx.from_numpy((1 + expansion_radii_h) * targets_h)
125+
126+
mat_asym_gen = LayerPotentialMatrixGenerator(
127+
actx.context,
128+
expansion=asym_expn,
129+
source_kernels=(knl,),
130+
target_kernels=(knl,))
131+
132+
_, (mat_asym_in,) = mat_asym_gen(
133+
actx.queue,
134+
targets=targets,
135+
sources=actx.from_numpy(sources_h),
136+
expansion_radii=expansion_radii_h,
137+
centers=centers_in,
138+
**extra_kwargs)
139+
140+
mat_asym_in = actx.to_numpy(mat_asym_in)
141+
weighted_mat_asym_in = mat_asym_in * sigma[None, :]
142+
asym_eval_in = (np.sum(weighted_mat_asym_in, axis=1) *
143+
np.exp(-lam * expansion_radii_h * (1 - tau)))
144+
145+
_, (mat_asym_out,) = mat_asym_gen(
146+
actx.queue,
147+
targets=targets,
148+
sources=actx.from_numpy(sources_h),
149+
expansion_radii=expansion_radii_h,
150+
centers=centers_out,
151+
**extra_kwargs)
152+
153+
mat_asym_out = actx.to_numpy(mat_asym_out)
154+
weighted_mat_asym_out = mat_asym_out * sigma[None, :]
155+
asym_eval_out = (np.sum(weighted_mat_asym_out, axis=1) *
156+
np.exp(-lam * expansion_radii_h * (1 - tau)))
157+
158+
utrue_in = utrue(lam, expansion_radii_h, tau, targets_h, f_mode, -1)
159+
utrue_out = utrue(lam, expansion_radii_h, tau, targets_h, f_mode, 1)
160+
161+
err_in = (np.max(np.abs(asym_eval_in - utrue_in)) /
162+
np.max(np.abs(utrue_in)))
163+
err_out = (np.max(np.abs(asym_eval_out - utrue_out)) /
164+
np.max(np.abs(utrue_out)))
165+
166+
h_max = actx.to_numpy(
167+
bind(places, sym.h_max(places.ambient_dim))(actx))
168+
169+
eoc_in.add_data_point(h_max, err_in)
170+
eoc_out.add_data_point(h_max, err_out)
171+
172+
assert eoc_in.order_estimate() > qbx_order, \
173+
f"Interior convergence too slow: {eoc_in.order_estimate()}"
174+
175+
assert eoc_out.order_estimate() > qbx_order, \
176+
f"Exterior convergence too slow: {eoc_out.order_estimate()}"
177+
178+
179+
if __name__ == "__main__":
180+
test_qbmax_yukawa_convergence()

0 commit comments

Comments
 (0)