Skip to content

Commit f451c6b

Browse files
committed
Writing the 2d test for the SPH velocity
1 parent 8b5becf commit f451c6b

File tree

1 file changed

+226
-6
lines changed

1 file changed

+226
-6
lines changed

src/struphy/pic/tests/test_sph.py

Lines changed: 226 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,18 +1060,238 @@ def du_xyz(x, y, z):
10601060
assert err_ux < 2.5e-2
10611061
else:
10621062
assert err_ux < 1.84e-1
1063+
1064+
1065+
@pytest.mark.parametrize("boxes_per_dim", [(12, 12, 1)])
1066+
@pytest.mark.parametrize("kernel", ["trigonometric_2d", "gaussian_2d", "linear_2d"])
1067+
@pytest.mark.parametrize("derivative", [0, 1, 2])
1068+
@pytest.mark.parametrize("bc_x", ["periodic", "mirror", "fixed"])
1069+
@pytest.mark.parametrize("bc_y", ["periodic", "mirror", "fixed"])
1070+
@pytest.mark.parametrize("eval_pts", [11, 16])
1071+
def test_sph_velocity_evaluation_2d(
1072+
boxes_per_dim,
1073+
kernel,
1074+
derivative,
1075+
bc_x,
1076+
bc_y,
1077+
eval_pts,
1078+
tesselation,
1079+
show_plot=False,
1080+
):
1081+
1082+
if isinstance(MPI.COMM_WORLD, MockComm):
1083+
comm = None
1084+
rank = 0
1085+
else:
1086+
comm = MPI.COMM_WORLD
1087+
rank = comm.Get_rank()
1088+
1089+
dom_type = "Cuboid"
1090+
dom_params = {"l1": 0.0, "r1": 1.0, "l2": 0.0, "r2": 1.0, "l3": 0.0, "r3": 1.0}
1091+
domain_class = getattr(domains, dom_type)
1092+
domain = domain_class(**dom_params)
1093+
1094+
if tesselation:
1095+
ppb = 50
1096+
loading_params = LoadingParameters(ppb=ppb, seed=1607, loading="tesselation")
1097+
else:
1098+
ppb = 400
1099+
loading_params = LoadingParameters(ppb=ppb, seed=223)
1100+
1101+
Lx = dom_params["r1"] - dom_params["l1"]
1102+
Ly = dom_params["r2"] - dom_params["l2"]
1103+
1104+
# analytic 2D velocity field:
1105+
# u_x = cos(2π e1) * cos(2π e2)
1106+
# u_y = sin(2π e1) * sin(2π e2)
1107+
# u_z = 0
1108+
def u_xyz(e1, e2, e3):
1109+
ux = xp.cos(2 * xp.pi/Lx*e1) * xp.cos(2 * xp.pi/Ly * e2)
1110+
uy = xp.sin(2 * xp.pi/Lx* e1) * xp.sin(2 * xp.pi/Ly * e2)
1111+
uz = 0.0 * e1
1112+
return (ux, uy, uz)
1113+
1114+
# derivatives:
1115+
# derivative == 1 -> ∂/∂e1
1116+
# derivative == 2 -> ∂/∂e2
1117+
def du_de1(e1, e2, e3):
1118+
dux = -2 * xp.pi/Lx * xp.sin(2 * xp.pi/Lx * e1) * xp.cos(2 * xp.pi/Ly * e2)
1119+
duy = 2 * xp.pi/Lx * xp.cos(2 * xp.pi/Lx * e1) * xp.sin(2 * xp.pi/Ly * e2)
1120+
duz = 0.0 * e1
1121+
return (dux, duy, duz)
1122+
1123+
def du_de2(e1, e2, e3):
1124+
dux = -2 * xp.pi/Ly * xp.cos(2 * xp.pi/Lx * e1) * xp.sin(2 * xp.pi/Ly * e2)
1125+
duy = 2 * xp.pi/Ly * xp.sin(2 * xp.pi/Lx * e1) * xp.cos(2 * xp.pi/Ly * e2)
1126+
duz = 0.0 * e1
1127+
return (dux, duy, duz)
1128+
1129+
background = GenericCartesianFluidEquilibrium(u_xyz=u_xyz)
1130+
background.domain = domain
1131+
1132+
boundary_params = BoundaryParameters(bc_sph=(bc_x, bc_y, "periodic"))
1133+
1134+
particles = ParticlesSPH(
1135+
comm_world=comm,
1136+
loading_params=loading_params,
1137+
boundary_params=boundary_params,
1138+
boxes_per_dim=boxes_per_dim,
1139+
bufsize=2.0,
1140+
box_bufsize=4.0,
1141+
domain=domain,
1142+
background=background,
1143+
n_as_volume_form=True,
1144+
verbose=False,
1145+
)
1146+
1147+
# evaluation grid
1148+
eta1 = xp.linspace(0, 1.0, eval_pts)
1149+
eta2 = xp.linspace(0, 1.0, eval_pts)
1150+
eta3 = xp.array([0.0])
1151+
ee1, ee2, ee3 = xp.meshgrid(eta1, eta2, eta3, indexing="ij")
1152+
1153+
# initialize particles
1154+
particles.draw_markers(sort=True, verbose=False)
1155+
if comm is not None:
1156+
particles.mpi_sort_markers()
1157+
particles.initialize_weights()
1158+
1159+
# evaluate velocity (and derivatives) via SPH
1160+
h1 = 1 / boxes_per_dim[0]
1161+
h2 = 1 / boxes_per_dim[1]
1162+
h3 = 1 / boxes_per_dim[2]
1163+
1164+
v1, v2, v3 = particles.eval_velocity(
1165+
ee1,
1166+
ee2,
1167+
ee3,
1168+
h1=h1,
1169+
h2=h2,
1170+
h3=h3,
1171+
kernel_type=kernel,
1172+
derivative=derivative,
1173+
)
1174+
1175+
if derivative == 0:
1176+
v1_e, v2_e, v3_e = background.u_xyz(ee1, ee2, ee3)
1177+
elif derivative == 1:
1178+
v1_e, v2_e, v3_e = du_de1(ee1, ee2, ee3)
1179+
else: # derivative == 2
1180+
v1_e, v2_e, v3_e = du_de2(ee1, ee2, ee3)
1181+
1182+
1183+
if comm is not None:
1184+
all_velo1 = xp.zeros_like(v1)
1185+
all_velo2 = xp.zeros_like(v2)
1186+
all_velo3 = xp.zeros_like(v3)
1187+
comm.Allreduce(v1, all_velo1, op=MPI.SUM)
1188+
comm.Allreduce(v2, all_velo2, op=MPI.SUM)
1189+
comm.Allreduce(v3, all_velo3, op=MPI.SUM)
1190+
else:
1191+
all_velo1, all_velo2, all_velo3 = v1, v2, v3
1192+
1193+
def abs_err(num, exact):
1194+
max_exact = xp.max(xp.abs(exact))
1195+
#if max_exact == 0:
1196+
#return xp.max(xp.abs(num))
1197+
return xp.max(xp.abs(num - exact)) / max_exact
1198+
1199+
err_ux = abs_err(all_velo1, v1_e)
1200+
err_uy = abs_err(all_velo2, v2_e)
1201+
1202+
1203+
if rank == 0:
1204+
print(f"\n{boxes_per_dim = }")
1205+
print(f"{kernel = }, {derivative = }")
1206+
print(f"{bc_x = }, {bc_y = }, {eval_pts = }")
1207+
print(f"Velocity errors: ux={err_ux:.3e}, uy={err_uy:.3e}")
1208+
1209+
if show_plot:
1210+
plt.figure(figsize=(12, 24))
1211+
# --- vx plots ---
1212+
plt.subplot(3, 2, 1)
1213+
plt.pcolor(ee1.squeeze(), ee2.squeeze(), v1_e.squeeze())
1214+
plt.title("Exact v₁ (uₓ)")
1215+
plt.colorbar()
1216+
1217+
plt.subplot(3, 2, 3)
1218+
plt.pcolor(ee1.squeeze(), ee2.squeeze(), all_velo1.squeeze())
1219+
plt.title("SPH v₁ (uₓ)")
1220+
plt.colorbar()
1221+
1222+
plt.subplot(3, 2, 5)
1223+
plt.pcolor(ee1.squeeze(), ee2.squeeze(), (all_velo1 - v1_e).squeeze())
1224+
plt.title("Error v₁ (uₓ)")
1225+
plt.colorbar()
1226+
1227+
# --- vy plots ---
1228+
plt.subplot(3, 2, 2)
1229+
plt.pcolor(ee1.squeeze(), ee2.squeeze(), v2_e.squeeze())
1230+
plt.title("Exact v₂ (u_y)")
1231+
plt.colorbar()
1232+
1233+
plt.subplot(3, 2, 4)
1234+
plt.pcolor(ee1.squeeze(), ee2.squeeze(), all_velo2.squeeze())
1235+
plt.title("SPH v₂ (u_y)")
1236+
plt.colorbar()
1237+
1238+
plt.subplot(3, 2, 6)
1239+
plt.pcolor(ee1.squeeze(), ee2.squeeze(), (all_velo2 - v2_e).squeeze())
1240+
plt.title("Error v₂ (u_y)")
1241+
plt.colorbar()
1242+
1243+
plt.tight_layout()
1244+
plt.savefig("image_test_2d.png")
1245+
plt.show()
1246+
1247+
plt.figure(figsize=(8, 8))
1248+
plt.quiver(ee1.squeeze(), ee2.squeeze(), all_velo1.squeeze(), all_velo2.squeeze(),
1249+
scale=30, pivot='mid', color='blue')
1250+
plt.title("SPH Velocity Field (v₁, v₂)")
1251+
plt.xlabel("x")
1252+
plt.ylabel("y")
1253+
plt.axis("equal")
1254+
plt.tight_layout()
1255+
plt.savefig("image_test_2d_quiver.png")
1256+
plt.show()
1257+
1258+
1259+
# tolerances: conservative values aligned with your 2D density thresholds
1260+
#if derivative == 0:
1261+
# assert err_ux < 0.031
1262+
# assert err_uy < 0.031
1263+
#else:
1264+
# assert err_ux < 0.069
1265+
# assert err_uy < 0.069
1266+
1267+
# ensure z-component is negligible (absolute)
1268+
#assert err_uz_abs < 1e-6
1269+
10631270

10641271

10651272
if __name__ == "__main__":
1066-
test_sph_velocity_evaluation(
1067-
(12, 1, 1),
1068-
"gaussian_1d",
1069-
1,
1273+
test_sph_velocity_evaluation_2d(
1274+
(24,24,1),
1275+
"gaussian_2d",
1276+
0,
1277+
"periodic",
10701278
"periodic",
10711279
11,
1072-
tesselation=False,
1073-
show_plot=True,
1280+
tesselation=True,
1281+
show_plot= True
10741282
)
1283+
1284+
1285+
1286+
# test_sph_velocity_evaluation(
1287+
# (12, 1, 1),
1288+
# "gaussian_1d",
1289+
# 1,
1290+
# "periodic",
1291+
# 11,
1292+
# tesselation=False,
1293+
# show_plot=True,
1294+
# )
10751295

10761296
# test_sph_evaluation_1d(
10771297
# (24, 1, 1),

0 commit comments

Comments
 (0)