validate P3M timestep convergence for baseline linear-in-scale-factor steps

This commit is contained in:
Tristan Hoellinger 2025-05-25 18:36:50 +02:00
parent 13e6c3b32d
commit a38f1b2585
7 changed files with 89782 additions and 1889 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -25,7 +25,12 @@ from pysbmy.fft import FourierGrid
from wip3m import *
from wip3m.tools import get_k_max, generate_sim_params, generate_white_noise_Field, run_simulation
from wip3m.params import params_planck_kmax_missing, cosmo_small_to_full_dict, z2a, BASELINE_SEEDPHASE
from wip3m.params import (
params_planck_kmax_missing,
cosmo_small_to_full_dict,
z2a,
BASELINE_SEEDPHASE,
)
from wip3m.plot_utils import *
from wip3m.logger import getCustomLogger, INDENT, UNINDENT
@ -37,34 +42,48 @@ output_path = OUTPUT_PATH
from argparse import ArgumentParser
parser = ArgumentParser(
description="Run convergence tests towards implementing P3M gravity."
)
parser = ArgumentParser(description="Run convergence tests towards implementing P3M gravity.")
parser.add_argument("--run_id", type=str, help="Simulation run identifier.")
parser.add_argument("--L", type=float, default=32.0, help="Box size in Mpc/h.")
parser.add_argument("--N", type=int, default=128, help="Density grid size per dimension.")
parser.add_argument("--Np", type=int, default=32, help="Number of particles per dimension.")
parser.add_argument("--Npm", type=int, default=64, help="PM mesh resolution per dimension.")
parser.add_argument("--n_Tiles", type=int, default=8, help="Number of tiles per dimension for short-range PP.")
parser.add_argument(
"--n_Tiles", type=int, default=8, help="Number of tiles per dimension for short-range PP."
)
# Timestep settings per method
for scheme in ["pmref", "pm1", "pm2", "cola", "spm", "p3m1", "p3m2", "p3m3"]:
parser.add_argument(f"--nsteps_{scheme}", type=int, default={
"pmref": 200, "pm1": 100, "pm2": 20,
"cola": 10, "spm": 200,
"p3m1": 200, "p3m2": 100, "p3m3": 20
}[scheme], help=f"Number of timesteps for {scheme.upper()}.")
parser.add_argument(f"--tsd_{scheme}", type=int, default=0,
help=f"TimeStepDistribution ID for {scheme.upper()}.")
parser.add_argument(
f"--nsteps_{scheme}",
type=int,
default={
"pmref": 200,
"pm1": 100,
"pm2": 20,
"cola": 10,
"spm": 200,
"p3m1": 200,
"p3m2": 100,
"p3m3": 20,
}[scheme],
help=f"Number of timesteps for {scheme.upper()}.",
)
parser.add_argument(
f"--tsd_{scheme}",
type=int,
default=0,
help=f"TimeStepDistribution ID for {scheme.upper()}.",
)
args = parser.parse_args()
if __name__ == "__main__":
try:
go_beyond_Nyquist_ss = True # for the summary statistics
force = True
force_hard = True
force = False
force_hard = False
run_id = args.run_id
L = args.L
@ -104,14 +123,13 @@ if __name__ == "__main__":
logger.info(f"Number of timesteps for P3M2: {nsteps_p3m2}")
logger.info(f"Number of timesteps for P3M3: {nsteps_p3m3}")
INDENT()
corner = -L / 2.0
RedshiftLPT = 19.0
RedshiftFCs = 0.0
ai = z2a(RedshiftLPT)
af = z2a(RedshiftFCs)
k_max = get_k_max(L, N) # k_max in h/Mpc
print(f"k_max = {k_max}")
cosmo = params_planck_kmax_missing.copy()
cosmo["k_max"] = k_max
@ -120,6 +138,7 @@ if __name__ == "__main__":
logdir = simdir + "logs/"
if force_hard:
import shutil
if Path(simdir).exists():
shutil.rmtree(simdir)
if Path(wd).exists():
@ -143,6 +162,22 @@ if __name__ == "__main__":
"n_Tiles": n_Tiles,
"RedshiftLPT": RedshiftLPT,
"RedshiftFCs": RedshiftFCs,
"nsteps_pmref": nsteps_pmref,
"nsteps_pm1": nsteps_pm1,
"nsteps_pm2": nsteps_pm2,
"nsteps_cola": nsteps_cola,
"nsteps_spm": nsteps_spm,
"nsteps_p3m1": nsteps_p3m1,
"nsteps_p3m2": nsteps_p3m2,
"nsteps_p3m3": nsteps_p3m3,
"tsd_pmref": tsd_pmref,
"tsd_pm1": tsd_pm1,
"tsd_pm2": tsd_pm2,
"tsd_cola": tsd_cola,
"tsd_spm": tsd_spm,
"tsd_p3m1": tsd_p3m1,
"tsd_p3m2": tsd_p3m2,
"tsd_p3m3": tsd_p3m3,
}
with open(wd + "sim_params.txt", "w") as f:
f.write(f"{sim_params}\n")
@ -247,7 +282,9 @@ if __name__ == "__main__":
("p3m3", p3m3_params),
]
for name, params in all_sim_params:
logger.info(f"Generating parameters for {name.upper()} with nsteps = {params['nsteps']}...")
logger.info(
f"Generating parameters for {name.upper()} with nsteps = {params['nsteps']}..."
)
file_ext = f"{name}_nsteps{params['nsteps']}"
generate_sim_params(params, ICs_path, wd, simdir, file_ext, force)
logger.info(f"Generating parameters for {name.upper()} done.")
@ -324,4 +361,4 @@ if __name__ == "__main__":
raise RuntimeError("Failed.") from e
finally:
UNINDENT()
logger.info("Running convergence tests done.")
logger.info("Running convergence tests done.")

View file

@ -1 +0,0 @@
/Users/hoellinger/Library/CloudStorage/Dropbox/travail/these/science/code/PUBLIC/selfisys_public/src/selfisys/utils/plot_params.py

75
src/wip3m/plot_params.py Normal file
View file

@ -0,0 +1,75 @@
#!/usr/bin/env python3
# ----------------------------------------------------------------------
# Copyright (C) 2025 Tristan Hoellinger
# Distributed under the GNU General Public License v3.0 (GPLv3).
# See the LICENSE file in the root directory for details.
# SPDX-License-Identifier: GPL-3.0-or-later
# ----------------------------------------------------------------------
__author__ = "Tristan Hoellinger"
__version__ = "0.1.0"
__date__ = "2025"
__license__ = "GPLv3"
"""Plotting parameters and custom colormaps for the WIP-P3M package."""
# Global font sizes
GLOBAL_FS = 18
GLOBAL_FS_LARGE = 20
GLOBAL_FS_XLARGE = 22
GLOBAL_FS_SMALL = 16
GLOBAL_FS_TINY = 14
COLOUR_LIST = ["C{}".format(i) for i in range(10)]
def reset_plotting():
import matplotlib as mpl
mpl.rcParams.update(mpl.rcParamsDefault)
def setup_plotting():
"""
Configure Matplotlib plotting settings for consistent appearance.
"""
import matplotlib.pyplot as plt
import importlib.resources
with importlib.resources.open_text("wip3m", "preamble.tex") as f:
preamble = f.read()
# Dictionary with rcParams settings
rcparams = {
"font.family": "serif",
"font.size": GLOBAL_FS, # Base font size
"axes.titlesize": GLOBAL_FS_XLARGE,
"axes.labelsize": GLOBAL_FS_LARGE,
"axes.linewidth": 1.0,
"xtick.labelsize": GLOBAL_FS_SMALL,
"ytick.labelsize": GLOBAL_FS_SMALL,
"xtick.major.width": 1.2,
"ytick.major.width": 1.2,
"xtick.minor.width": 1.0,
"ytick.minor.width": 1.0,
"xtick.direction": "in",
"ytick.direction": "in",
"xtick.major.pad": 5,
"xtick.minor.pad": 5,
"ytick.major.pad": 5,
"ytick.minor.pad": 5,
"legend.fontsize": GLOBAL_FS_SMALL,
"legend.title_fontsize": GLOBAL_FS_LARGE,
"figure.titlesize": GLOBAL_FS_XLARGE,
"figure.dpi": 300,
"grid.color": "gray",
"grid.linestyle": "dotted",
"grid.linewidth": 0.6,
"lines.linewidth": 2,
"lines.markersize": 8,
"text.usetex": True,
"text.latex.preamble": preamble,
}
# Update rcParams
plt.rcParams.update(rcparams)

View file

@ -0,0 +1,450 @@
#!/usr/bin/env python3
# ----------------------------------------------------------------------
# Copyright (C) 2025 Tristan Hoellinger
# Distributed under the GNU General Public License v3.0 (GPLv3).
# See the LICENSE file in the root directory for details.
# SPDX-License-Identifier: GPL-3.0-or-later
# ----------------------------------------------------------------------
__author__ = "Tristan Hoellinger"
__version__ = "0.1.0"
__date__ = "2025"
__license__ = "GPLv3"
"""Plot results of time step convergence tests."""
# pyright: reportWildcardImportFromLibrary=false
from pathlib import Path
import numpy as np
from pysbmy.fft import read_FourierGrid
from pysbmy.field import read_field
from pysbmy.correlations import get_autocorrelation
from wip3m import *
from wip3m.tools import none_bool_str
from wip3m.plot_utils import *
from wip3m.logger import getCustomLogger, INDENT, UNINDENT
logger = getCustomLogger(__name__)
workdir = ROOT_PATH + "results/"
output_path = OUTPUT_PATH
from argparse import ArgumentParser
parser = ArgumentParser(description="Plot convergence results for WIP3M.")
parser.add_argument(
"--run_id",
type=str,
help="Run ID to use for plotting.",
)
parser.add_argument(
"--outdir_suffix",
type=str,
default="plots",
help="Suffix to append to the output directory.",
)
parser.add_argument(
"--ref",
type=str,
default="P3M1",
choices=["P3M1", "PMref"],
help="Reference field for power spectrum comparison.",
)
parser.add_argument(
"--lpt",
type=none_bool_str,
default=False,
help="Whether to compute and plot the LPT power spectrum.",
)
parser.add_argument(
"--cola",
type=none_bool_str,
default=True,
help="Whether to compute and plot the COLA power spectrum.",
)
parser.add_argument(
"--plot_fields",
type=none_bool_str,
default=False,
help="Whether to plot the fields or not.",
)
args = parser.parse_args()
run_id = args.run_id
outdir_suffix = args.outdir_suffix
ref = args.ref
lpt = args.lpt
cola = args.cola
plot_fields = args.plot_fields
if not run_id:
raise ValueError("Please provide a run ID using the --run_id argument.")
simdir = output_path + run_id + "/"
outdir = simdir + outdir_suffix + "/"
input_ss_file = simdir + "input_ss_k_grid.h5"
wd = workdir + run_id + "/"
Path(outdir).mkdir(parents=True, exist_ok=True)
if __name__ == "__main__":
logger.info(f"Plotting convergence results for run ID: {run_id}")
logger.info(f"Output directory: {outdir}")
logger.info(f"Reading simulation parameters from {wd}sim_params.txt...")
with open(wd + "sim_params.txt", "r") as f:
sim_params = eval(f.read())
L = sim_params["L"] # Box size in Mpc/h
N = sim_params["N"] # Density grid size
Np = sim_params["Np"] # Number of dark matter particles per spatial dimension
Npm = sim_params["Npm"] # PM grid size
nsteps_pmref = sim_params["nsteps_pmref"]
nsteps_pm1 = sim_params["nsteps_pm1"]
nsteps_pm2 = sim_params["nsteps_pm2"]
nsteps_cola = sim_params["nsteps_cola"]
nsteps_spm = sim_params["nsteps_spm"]
nsteps_p3m1 = sim_params["nsteps_p3m1"]
nsteps_p3m2 = sim_params["nsteps_p3m2"]
nsteps_p3m3 = sim_params["nsteps_p3m3"]
logger.info(f"Reading simulation parameters from {wd}sim_params.txt done.")
if plot_fields:
logger.info(f"Loading fields from {simdir}...")
INDENT()
slice_ijk = (N // 2, slice(None), slice(None))
DELTA_LPT = read_field(simdir + "lpt_density.h5").data[slice_ijk]
DELTA_PMREF = read_field(simdir + f"pmref_nsteps{nsteps_pmref}_final_density_pm.h5").data[
slice_ijk
]
DELTA_PM1 = read_field(simdir + f"pm1_nsteps{nsteps_pm1}_final_density_pm.h5").data[
slice_ijk
]
DELTA_PM2 = read_field(simdir + f"pm2_nsteps{nsteps_pm2}_final_density_pm.h5").data[
slice_ijk
]
DELTA_COLA = read_field(simdir + f"cola_nsteps{nsteps_cola}_final_density_cola.h5").data[
slice_ijk
]
DELTA_SPM = read_field(simdir + f"spm_nsteps{nsteps_spm}_final_density_spm.h5").data[
slice_ijk
]
DELTA_P3M1 = read_field(simdir + f"p3m1_nsteps{nsteps_p3m1}_final_density_p3m.h5").data[
slice_ijk
]
DELTA_P3M2 = read_field(simdir + f"p3m2_nsteps{nsteps_p3m2}_final_density_p3m.h5").data[
slice_ijk
]
DELTA_P3M3 = read_field(simdir + f"p3m3_nsteps{nsteps_p3m3}_final_density_p3m.h5").data[
slice_ijk
]
UNINDENT()
logger.info(f"Loading fields from {simdir} done.")
diff_pm1_pmref = DELTA_PM1 - DELTA_PMREF
diff_pm2_pmref = DELTA_PM2 - DELTA_PMREF
diff_p3m1_pmref = DELTA_P3M1 - DELTA_PMREF
diff_p3m2_pm1 = DELTA_P3M2 - DELTA_PM1
diff_p3m3_pm2 = DELTA_P3M3 - DELTA_PM2
diff_p3m1_spm = DELTA_P3M1 - DELTA_SPM
fields = [
"pmref",
"pm1",
"pm2",
"p3m1",
"p3m2",
"p3m3",
"diff_p3m1_spm",
"diff_pm1_pmref",
"diff_pm2_pmref",
"diff_p3m1_pmref",
"diff_p3m2_pm1",
"diff_p3m3_pm2",
] # fields to plot
ncols = 6 # Max panels per row
figname = "_".join(fields)
slices_dict = {
"lpt": DELTA_LPT,
"cola": DELTA_COLA,
"pmref": DELTA_PMREF,
"pm1": DELTA_PM1,
"pm2": DELTA_PM2,
"spm": DELTA_SPM,
"p3m1": DELTA_P3M1,
"p3m2": DELTA_P3M2,
"p3m3": DELTA_P3M3,
"diff_pm1_pmref": diff_pm1_pmref,
"diff_pm2_pmref": diff_pm2_pmref,
"diff_p3m1_pmref": diff_p3m1_pmref,
"diff_p3m2_pm1": diff_p3m2_pm1,
"diff_p3m3_pm2": diff_p3m3_pm2,
"diff_p3m1_spm": diff_p3m1_spm,
}
titles_dict = {
"lpt": "LPT",
"pmref": f"PMref $n_\\mathrm{{steps}}={nsteps_pmref}$",
"pm1": f"PM1 $n_\\mathrm{{steps}}={nsteps_pm1}$",
"pm2": f"PM2 $n_\\mathrm{{steps}}={nsteps_pm2}$",
"cola": f"COLA $n_\\mathrm{{steps}}={nsteps_cola}$",
"spm": f"sPM $n_\\mathrm{{steps}}={nsteps_spm}$",
"p3m1": f"P3M1 $n_\\mathrm{{steps}}={nsteps_p3m1}$",
"p3m2": f"P3M2 $n_\\mathrm{{steps}}={nsteps_p3m2}$",
"p3m3": f"P3M3 $n_\\mathrm{{steps}}={nsteps_p3m3}$",
"diff_pm1_pmref": r"$\delta_{\rm PM1}-\delta_{\rm PMref}$",
"diff_pm2_pmref": r"$\delta_{\rm PM2}-\delta_{\rm PMref}$",
"diff_p3m1_pmref": r"$\delta_{\rm P3M1}-\delta_{\rm PMref}$",
"diff_p3m2_pm1": r"$\delta_{\rm P3M2}-\delta_{\rm PM1}$",
"diff_p3m3_pm2": r"$\delta_{\rm P3M3}-\delta_{\rm PM2}$",
"diff_p3m1_spm": r"$\delta_{\rm P3M1}-\delta_{\rm sPM}$",
}
logger.info(f"Plotting fields: {fields}...")
npanels = len(fields)
nrows = np.ceil(npanels / ncols).astype(int)
fig, axs = plt.subplots(
nrows=nrows, ncols=ncols, figsize=(3 * ncols, 4.6 * nrows), sharey=True
)
axs = axs.flatten()
ims = []
for i, key in enumerate(fields):
ax = axs[i]
data = slices_dict[key]
title = titles_dict[key]
if key.startswith("diff"):
norm = TwoSlopeNorm(
vmin=-np.log(1 + np.abs(np.min(data))),
vcenter=0,
vmax=np.log10(1 + np.abs(np.max(data))),
)
im = ax.imshow(np.sign(data) * np.log(1 + np.abs(data)), cmap="RdBu_r", norm=norm)
else:
im = ax.imshow(np.log10(2 + data), cmap=cmap)
ims.append((im, key))
ax.set_title(title, fontsize=fs_titles)
for spine in ax.spines.values():
spine.set_visible(False)
# Hide unused axes
for i in range(npanels, len(axs)):
axs[i].axis("off")
# Shared axes labels
for i, ax in enumerate(axs[:npanels]):
if i % ncols == 0:
ax.set_yticks([0, N // 2, N])
ax.set_yticklabels([f"{-L/2:.0f}", "0", f"{L/2:.0f}"], fontsize=fs)
ax.set_ylabel(r"Mpc/$h$", size=GLOBAL_FS_SMALL)
ax.set_xticks([0, N // 2, N])
ax.set_xticklabels([f"{-L/2:.0f}", "0", f"{L/2:.0f}"], fontsize=fs)
ax.set_xlabel(r"Mpc/$h$", size=GLOBAL_FS_SMALL)
# Colorbars
for ax, (im, key) in zip(axs[:npanels], ims):
divider = make_axes_locatable(ax)
cax = divider.append_axes("bottom", size="5%", pad=0.6)
cb = fig.colorbar(im, cax=cax, orientation="horizontal")
if key.startswith("diff"):
cb.set_label(
r"$\textrm{sgn}\left(\Delta\delta\right)\log_{10}(1 + |\Delta\delta|)$",
fontsize=fs,
)
else:
cb.set_label(r"$\log_{10}(2 + \delta)$", fontsize=fs)
cb.ax.tick_params(labelsize=fs)
cax.xaxis.set_ticks_position("bottom")
cax.xaxis.set_label_position("bottom")
fig.savefig(outdir + f"{figname}.png", bbox_inches="tight", dpi=300, transparent=True)
fig.savefig(outdir + f"{figname}.pdf", bbox_inches="tight", dpi=300)
plt.close(fig)
logger.info(f"Plotting fields done. Saved to {outdir}{figname}.png and {figname}.pdf")
logger.info("Computing and plotting power spectra...")
INDENT()
logger.info("Loading Fourier grid...")
G = read_FourierGrid(input_ss_file)
logger.info("Loading Fourier grid done.")
# Exit here with "0" for debugging purposes:
exit(0)
k = G.k_modes
AliasingCorr = False
logger.info("Computing power spectra...")
DELTA = read_field(simdir + "initial_density.h5")
Pk_INI, _ = get_autocorrelation(DELTA, G, AliasingCorr)
if lpt:
DELTA = read_field(simdir + "lpt_density.h5")
Pk_LPT, _ = get_autocorrelation(DELTA, G, AliasingCorr)
DELTA = read_field(simdir + f"pmref_nsteps{nsteps_pmref}_final_density_pm.h5")
Pk_PMref, _ = get_autocorrelation(DELTA, G, AliasingCorr)
DELTA = read_field(simdir + f"pm1_nsteps{nsteps_pm1}_final_density_pm.h5")
Pk_PM1, _ = get_autocorrelation(DELTA, G, AliasingCorr)
DELTA = read_field(simdir + f"pm2_nsteps{nsteps_pm2}_final_density_pm.h5")
Pk_PM2, _ = get_autocorrelation(DELTA, G, AliasingCorr)
if cola:
DELTA = read_field(simdir + f"cola_nsteps{nsteps_cola}_final_density_cola.h5")
Pk_COLA, _ = get_autocorrelation(DELTA, G, AliasingCorr)
DELTA = read_field(simdir + f"spm_nsteps{nsteps_spm}_final_density_spm.h5")
Pk_sPM, _ = get_autocorrelation(DELTA, G, AliasingCorr)
DELTA = read_field(simdir + f"p3m1_nsteps{nsteps_p3m1}_final_density_p3m.h5")
Pk_P3M1, _ = get_autocorrelation(DELTA, G, AliasingCorr)
DELTA = read_field(simdir + f"p3m2_nsteps{nsteps_p3m2}_final_density_p3m.h5")
Pk_P3M2, _ = get_autocorrelation(DELTA, G, AliasingCorr)
DELTA = read_field(simdir + f"p3m3_nsteps{nsteps_p3m3}_final_density_p3m.h5")
Pk_P3M3, _ = get_autocorrelation(DELTA, G, AliasingCorr)
logger.info("Computing power spectra done.")
if ref == "P3M1":
nsteps_reference = nsteps_p3m1
Pk_ref = Pk_P3M1
label_ref = f"Ref (P3M, $n_\\mathrm{{steps}}={nsteps_reference}$)"
elif ref == "PMref":
nsteps_reference = nsteps_pm1
Pk_ref = Pk_PMref
label_ref = f"Ref (PM, $n_\\mathrm{{steps}}={nsteps_reference}$)"
else:
raise ValueError("Invalid reference field. Choose 'P3M1' or 'PMref'.")
logger.info(f"Plotting power spectra...")
INDENT()
fig, ax = plt.subplots(figsize=(7, 4))
ax.set_xscale("log")
k = G.k_modes
kmin, kmax = k.min(), k.max()
logger.info(f"kmin = {kmin:.2e}, kmax = {kmax:.2e}")
log_pad = 0.02
log_k_min = np.log10(kmin)
log_k_max = np.log10(kmax)
log_range = log_k_max - log_k_min
xlim_min = 10 ** (log_k_min - log_pad * log_range)
xlim_max = 10 ** (log_k_max + log_pad * log_range)
plt.xlim(xlim_min, xlim_max)
# ax.set_ylim([0.2, 1.8])
dark_grey_bnd = 0.01
medium_grey_bnd = 0.05
light_grey_bnd = 0.1
ax.plot([kmin, kmax], [1, 1], color="black", linestyle="-", label=label_ref)
if lpt:
ax.plot(k, Pk_LPT / Pk_ref, label="2LPT", linestyle="--")
fields_to_plot = [
("PMref", Pk_PMref),
("PM1", Pk_PM1),
("PM2", Pk_PM2),
("sPM", Pk_sPM),
("P3M1", Pk_P3M1),
("P3M2", Pk_P3M2),
("P3M3", Pk_P3M3),
]
if cola:
fields_to_plot.append(("COLA", Pk_COLA))
# Remove the field corresponding to the reference:
fields_to_plot = [(field_name, Pk) for field_name, Pk in fields_to_plot if field_name != ref]
for field_name, Pk in fields_to_plot:
label = f"{field_name}, $n_\\mathrm{{steps}}={eval(f'nsteps_{field_name.lower()}')}$"
if field_name in ["PMref", "P3M1"]:
linestyle = "-"
zorder = 1
else:
linestyle = "--"
zorder = 2
ax.plot(k, Pk / Pk_ref, label=label, linestyle=linestyle)
ax.axhspan(1 - dark_grey_bnd, 1 + dark_grey_bnd, color="grey", alpha=0.3)
ax.axhspan(1 - medium_grey_bnd, 1 + medium_grey_bnd, color="grey", alpha=0.2)
ax.axhspan(1 - light_grey_bnd, 1 + light_grey_bnd, color="grey", alpha=0.1)
for i in range(1, len(k)):
ax.axvline(k[i], color="black", linestyle=":", linewidth=1, alpha=0.1, zorder=0)
ax.yaxis.set_major_locator(plt.MaxNLocator(6))
ax.yaxis.get_major_ticks()[0].label1.set_visible(False)
ax.set_xlabel("$k$ [$h/\\mathrm{Mpc}$]", fontsize=fs)
ax.set_ylabel("$P(k)/P_\\mathrm{ref}(k)$", fontsize=fs)
ax.tick_params(which="both", direction="in")
ax.tick_params(axis="both", which="major", labelsize=fs)
ax.tick_params(axis="both", which="minor", labelsize=fs)
# Characteristic vertical reference scales
nyquist = np.pi * N / L
nyquist_PM = np.pi * Npm / L
epsilon = 0.03 * L / Np
particle_length = 2 * epsilon
xs = 1.25 * L / Npm
xr = 4.5 * xs
particle_wavenumber = 2 * np.pi / particle_length # Too large to be shown
xs_inv = 2 * np.pi / xs
xr_inv = 2 * np.pi / xr
line1 = ax.axvline(
x=nyquist, color="black", linestyle="--", lw=2, label="Nyquist (density grid)", zorder=0
)
line1 = ax.axvline(
x=nyquist_PM, color="black", linestyle="-", lw=1, label="Nyquist (PM grid)", zorder=0
)
line3 = ax.axvline(
x=xs_inv, color="gray", linestyle="-.", lw=2, label=r"Split wavenumber $x_s$", zorder=0
)
line4 = ax.axvline(
x=xr_inv, color="gray", linestyle=":", lw=2, label=r"Short-range reach $x_r$", zorder=0
)
logger.info(f"Nyquist (density grid): {nyquist:.2f} h/Mpc")
logger.info(f"Nyquist (PM grid): {nyquist_PM:.2f} h/Mpc")
logger.info(f"Particle wavenumber: {particle_wavenumber:.2f} h/Mpc")
logger.info(f"Split wavenumber: {xs_inv:.2f} h/Mpc")
logger.info(f"Short-range reach: {xr_inv:.2f} h/Mpc")
handles, labels = plt.gca().get_legend_handles_labels()
# empty_patch = mpatches.Patch(color="none", label="")
# handles = [empty_patch, *handles]
# labels = ["", *labels]
plt.legend(
handles,
labels,
loc="upper center",
ncol=2,
bbox_to_anchor=(0.5, -0.2),
fontsize=fs,
frameon=False,
)
fig.savefig(
outdir + "power_spectra.png",
bbox_inches="tight",
dpi=300,
transparent=True,
)
fig.savefig(
outdir + "power_spectra.pdf",
bbox_inches="tight",
dpi=300,
)
plt.close(fig)
UNINDENT()
logger.info(
f"Power spectra plotted and saved to {outdir}power_spectra.png and power_spectra.pdf"
)
UNINDENT()
logger.info("Computing and plotting power spectra done.")
logger.info("All plots completed successfully.")

View file

@ -1,43 +1,139 @@
#!/bin/bash
# ----------------------------------------------------------------------
# Copyright (C) 2025 Tristan Hoellinger
# Distributed under the GNU General Public License v3.0 (GPLv3).
# See the LICENSE file in the root directory for details.
# SPDX-License-Identifier: GPL-3.0-or-later
# ----------------------------------------------------------------------
#SBATCH --job-name=cappel_L1024_Np512
#SBATCH -o /data70/hoellinger/WIP3M/cappel_L1024_Np512/log.log
#SBATCH -e /data70/hoellinger/WIP3M/cappel_L1024_Np512/err.err
#SBATCH --exclusive
#SBATCH -N 1 # Number of nodes (value or min-max)
#SBATCH -n 128 # The number of tasks (i.e. cores) per node
#SBATCH --time=24:00:00
# Author: Tristan Hoellinger
# Version: 0.1
# Date: 2025
# License: GPLv3
##SBATCH --exclusive
##SBATCH --mem=64G # Memory pool for all cores (see also --mem-per-cpu)
##SBATCH --array=0-10 # Size of the array
##SBATCH --constraint=i1 # Constraint e.g. specific node
eval "$(conda shell.bash hook)"
conda activate p3m
export OMP_NUM_THREADS=128
export OMP_NUM_THREADS=8
python $WIP3M_ROOT_PATH"src/wip3m/convergence_baseline_ts_parser.py" \
--run_id ts1/ \
--L 64 \
--N 64 \
--Np 64 \
--Npm 3600 \
--n_Tiles 32 \
--run_id cappel_L1024_Np512 \
--L 1024 \
--N 1024 \
--Np 512 \
--Npm 1024 \
--n_Tiles 128 \
--nsteps_pmref 200 \
--nsteps_pm1 100 \
--nsteps_pm2 20 \
--nsteps_cola 10 \
--nsteps_spm 200 \
--nsteps_p3m1 200 \
--nsteps_p3m2 100 \
--nsteps_p3m3 20 \
--tsd_pmref 0 \
--tsd_pm1 0 \
--tsd_pm2 0 \
--tsd_cola 0 \
--tsd_spm 0 \
--tsd_p3m1 0 \
--tsd_p3m2 0 \
--tsd_p3m3 0
--nsteps_pm2 50 \
--nsteps_cola 20 \
--nsteps_spm 400 \
--nsteps_p3m1 400 \
--nsteps_p3m2 300 \
--nsteps_p3m3 200
# python $WIP3M_ROOT_PATH"src/wip3m/convergence_baseline_ts_parser.py" \
# --run_id cappel_Np512 \
# --L 512 \
# --N 1024 \
# --Np 512 \
# --Npm 1024 \
# --n_Tiles 128 \
# --nsteps_pmref 200 \
# --nsteps_pm1 100 \
# --nsteps_pm2 50 \
# --nsteps_cola 20 \
# --nsteps_spm 400 \
# --nsteps_p3m1 400 \
# --nsteps_p3m2 300 \
# --nsteps_p3m3 200
# python $WIP3M_ROOT_PATH"src/wip3m/convergence_baseline_ts_parser.py" \
# --run_id cappel_loga_Np512 \
# --L 512 \
# --N 1024 \
# --Np 512 \
# --Npm 1024 \
# --n_Tiles 128 \
# --nsteps_pmref 200 \
# --nsteps_pm1 100 \
# --nsteps_pm2 50 \
# --nsteps_cola 20 \
# --nsteps_spm 400 \
# --nsteps_p3m1 400 \
# --nsteps_p3m2 300 \
# --nsteps_p3m3 200 \
# --tsd_pmref 1 \
# --tsd_pm1 1 \
# --tsd_pm2 1 \
# --tsd_cola 1 \
# --tsd_spm 1 \
# --tsd_p3m1 1 \
# --tsd_p3m2 1 \
# --tsd_p3m3 1
# python $WIP3M_ROOT_PATH"src/wip3m/convergence_baseline_ts_parser.py" \
# --run_id brabois_Np512_L1024 \
# --L 1024 \
# --N 1024 \
# --Np 512 \
# --Npm 1024 \
# --n_Tiles 128 \
# --nsteps_pmref 100 \
# --nsteps_pm1 50 \
# --nsteps_pm2 20 \
# --nsteps_cola 20 \
# --nsteps_spm 200 \
# --nsteps_p3m1 200 \
# --nsteps_p3m2 100 \
# --nsteps_p3m3 50
# python $WIP3M_ROOT_PATH"src/wip3m/convergence_baseline_ts_parser.py" \
# --run_id brabois_Np512 \
# --L 512 \
# --N 1024 \
# --Np 512 \
# --Npm 1024 \
# --n_Tiles 128 \
# --nsteps_pmref 100 \
# --nsteps_pm1 50 \
# --nsteps_pm2 20 \
# --nsteps_cola 20 \
# --nsteps_spm 200 \
# --nsteps_p3m1 200 \
# --nsteps_p3m2 100 \
# --nsteps_p3m3 50
# python $WIP3M_ROOT_PATH"src/wip3m/convergence_baseline_ts_parser.py" \
# --run_id brabois_Np256 \
# --L 512 \
# --N 512 \
# --Np 256 \
# --Npm 512 \
# --n_Tiles 64 \
# --nsteps_pmref 200 \
# --nsteps_pm1 100 \
# --nsteps_pm2 50 \
# --nsteps_cola 20 \
# --nsteps_spm 400 \
# --nsteps_p3m1 400 \
# --nsteps_p3m2 300 \
# --nsteps_p3m3 200
# python $WIP3M_ROOT_PATH"src/wip3m/convergence_baseline_ts_parser.py" \
# --run_id brabois_Np256_fine \
# --L 512 \
# --N 1024 \
# --Np 256 \
# --Npm 512 \
# --n_Tiles 64 \
# --nsteps_pmref 200 \
# --nsteps_pm1 100 \
# --nsteps_pm2 50 \
# --nsteps_cola 20 \
# --nsteps_spm 400 \
# --nsteps_p3m1 400 \
# --nsteps_p3m2 300 \
# --nsteps_p3m3 200
exit 0

View file

@ -0,0 +1,24 @@
#!/bin/bash
#SBATCH --job-name=cappel_L1024_Np512
#SBATCH -o /data70/hoellinger/WIP3M/cappel_L1024_Np512/plots.log
#SBATCH --exclusive
#SBATCH -N 1 # Number of nodes (value or min-max)
#SBATCH -n 128 # The number of tasks (i.e. cores) per node
#SBATCH --time=2:00:00
##SBATCH --exclusive
##SBATCH --mem=64G # Memory pool for all cores (see also --mem-per-cpu)
##SBATCH --array=0-10 # Size of the array
##SBATCH --constraint=i1 # Constraint e.g. specific node
conda activate p3m
export OMP_NUM_THREADS=64
python $WIP3M_ROOT_PATH"src/wip3m/plots_convergence_baseline_ts_parser.py" \
--run_id cappel_L1024_Np512 \
--outdir_suffix plots \
--lpt True \
--cola False
exit 0