# ОСНОВНОЙ GPU-решатель (с SDF + Bouzidi): цилиндр, три версии (без AMR / 2× / 2×+4×). # Граница тела — по знаковому полю расстояний + интерполированное отражение (Bouzidi); # стенки канала — тот же механизм с q=0.5 (обычный halfway). Математика идентична _amr_anims_sdf.py. # backend — CuPy (GPU) с откатом на numpy. В конце — сводное сравнение с версией без SDF. # Запуск: python amr_cylinder_sdf_gpu.py (после amr_cylinder_gpu.py — для сводного сравнения) import os, time, numpy as np import amr_gpu_core as core HERE = os.path.dirname(os.path.abspath(__file__)) ANIM = os.path.join(HERE, "anim"); os.makedirs(ANIM, exist_ok=True) FIGS = os.path.join(HERE, "figures"); os.makedirs(FIGS, exist_ok=True) print(f"[ОСНОВНОЙ · SDF+Bouzidi] backend={core.BACKEND}; домен {core.Nx}x{core.Ny}; steps={core.STEPS}; " f"L1(2×) {core.P1['Nfx']}x{core.P1['Nfy']}; L2(4×) {core.P2['Nfx']}x{core.P2['Nfy']}") def make_progress(label, width=32, every=0.1): """Колбэк-индикатор: рисует ASCII-шкалу по выполненным шагам (t из STEPS), обновляется на месте.""" st = {"t0": time.perf_counter(), "last": 0.0} def cb(t, total): now = time.perf_counter(); done = (t >= total) if not done and (now - st["last"] < every): # троттлинг, чтобы не спамить консоль return st["last"] = now; frac = (t + 1) / (total + 1) filled = int(round(width * frac)); bar = "#" * filled + "-" * (width - filled) el = now - st["t0"]; eta = (el / frac - el) if frac > 0 else 0.0 print(f"\r {label:7}[{bar}] {frac*100:5.1f}% ({t}/{total}) {el:5.1f}s ETA {eta:5.1f}s", end=("\n" if done else ""), flush=True) return cb t0 = time.perf_counter() R0 = core.run("none", True, make_progress("none")); print(" none :", round(time.perf_counter()-t0, 1), "s") R1 = core.run("amr2x", True, make_progress("amr2x")); print(" amr2x :", round(time.perf_counter()-t0, 1), "s") R2 = core.run("nested", True, make_progress("nested")); print(" nested :", round(time.perf_counter()-t0, 1), "s") core.save_gif(R1, "amr2x_cyl_sdf.gif", ANIM, " + SDF (Bouzidi)") core.save_gif(R2, "amr_nested_cyl_sdf.gif", ANIM, " + SDF (Bouzidi)") AS = [core.analyze(R0), core.analyze(R1), core.analyze(R2)] core.print_table(AS, "С SDF — сравнение на реально собранных данных (установившийся режим)") np.savez(os.path.join(HERE, "_amr_probe_data_sdf.npz"), St=np.array([a["St"] for a in AS]), Cd=np.array([a["Cd"] for a in AS]), Clrms=np.array([a["Clrms"] for a in AS]), uyrms=np.array([a["uyrms"] for a in AS]), Cl0=AS[0]["Cl_s"], Cl1=AS[1]["Cl_s"], Cl2=AS[2]["Cl_s"], uy0=AS[0]["uy"], uy1=AS[1]["uy"], uy2=AS[2]["uy"]) core.probe_figure(AS, R2, os.path.join(FIGS, "11f_amr_sdf_probe_comparison.png"), "AMR на цилиндре (SDF+Bouzidi): сравнение трёх версий на данных зондов") # ---- сводное сравнение: SDF против без-SDF (если есть свежие данные без SDF) ---- nosdf = os.path.join(HERE, "_amr_probe_data.npz") if os.path.exists(nosdf) and ("St" in np.load(nosdf).files): import matplotlib; matplotlib.use("Agg"); import matplotlib.pyplot as plt N = np.load(nosdf); Stn, Cdn = N["St"], N["Cd"] Sts = np.array([a["St"] for a in AS]); Cds = np.array([a["Cd"] for a in AS]) print("\n=== СВОДНО: SDF vs без SDF (Cd, St) ===") print(f"{'версия':14}{'St(noSDF)':>11}{'St(SDF)':>9}{'Cd(noSDF)':>11}{'Cd(SDF)':>9}") for k, lab in enumerate(["без AMR", "2×", "2×+4×"]): print(f"{lab:14}{Stn[k]:>11.3f}{Sts[k]:>9.3f}{Cdn[k]:>11.3f}{Cds[k]:>9.3f}") print(f"{'лит. Re≈150':14}{0.183:>11.3f}{0.183:>9.3f}{1.330:>11.3f}{1.330:>9.3f}") x = np.arange(3); w = 0.38 fig, axs = plt.subplots(1, 2, figsize=(13.5, 4.6)) axs[0].bar(x-w/2, Cdn, w, color="#9e9e9e", label="без SDF (ступенч.)") axs[0].bar(x+w/2, Cds, w, color="#1f6feb", label="с SDF (Bouzidi)") axs[0].axhline(1.33, color="k", ls="--", alpha=0.6, label="лит. Cd≈1.33") axs[0].set_xticks(x); axs[0].set_xticklabels(["без AMR", "2×", "2×+4×"]); axs[0].set_ylabel("") axs[0].set_title("Сопротивление: SDF убирает завышение от ступенчатой границы") axs[0].legend(fontsize=8); axs[0].grid(alpha=0.3, axis="y") axs[1].bar(x-w/2, Stn, w, color="#9e9e9e", label="без SDF"); axs[1].bar(x+w/2, Sts, w, color="#1f6feb", label="с SDF") axs[1].axhline(0.183, color="k", ls="--", alpha=0.6, label="лит. St≈0.18") axs[1].set_xticks(x); axs[1].set_xticklabels(["без AMR", "2×", "2×+4×"]); axs[1].set_ylabel("St") axs[1].set_title("Число Струхаля"); axs[1].legend(fontsize=8); axs[1].grid(alpha=0.3, axis="y") fig.suptitle("Цилиндр: SDF (Bouzidi) против ступенчатого bounce-back — на реальных данных", fontweight="bold") fig.savefig(os.path.join(FIGS, "11g_sdf_vs_nosdf.png"), dpi=110, bbox_inches="tight"); plt.close(fig) print("saved 11g_sdf_vs_nosdf.png") else: print("\n(нет свежего _amr_probe_data.npz — сначала запусти amr_cylinder_gpu.py)") print("DONE (SDF)")