implemented COLA with P3M force evaluation and custom time stepping for P3M
This commit is contained in:
parent
7cb65744f3
commit
34594a2bf6
29 changed files with 69704 additions and 106986 deletions
File diff suppressed because one or more lines are too long
2758
notebooks/10_use_fit_P3M_limiter.ipynb
Normal file
2758
notebooks/10_use_fit_P3M_limiter.ipynb
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -436,7 +436,7 @@
|
|||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"display_name": "p3m",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
|
|
1262
notebooks/4_limiters_concept.ipynb
Normal file
1262
notebooks/4_limiters_concept.ipynb
Normal file
File diff suppressed because one or more lines are too long
10708
notebooks/5_limiters_tuning.ipynb
Normal file
10708
notebooks/5_limiters_tuning.ipynb
Normal file
File diff suppressed because one or more lines are too long
1268
notebooks/6_COLA_basics.ipynb
Normal file
1268
notebooks/6_COLA_basics.ipynb
Normal file
File diff suppressed because one or more lines are too long
5282
notebooks/7_limiters_tuning_COLA.ipynb
Normal file
5282
notebooks/7_limiters_tuning_COLA.ipynb
Normal file
File diff suppressed because one or more lines are too long
15821
notebooks/8_fit_P3M_limiter.ipynb
Normal file
15821
notebooks/8_fit_P3M_limiter.ipynb
Normal file
File diff suppressed because one or more lines are too long
354
notebooks/9_fit_P3M_limiter_external.ipynb
Normal file
354
notebooks/9_fit_P3M_limiter_external.ipynb
Normal file
File diff suppressed because one or more lines are too long
437
src/wip3m/compute_limiters_from_baseline_ts.py
Normal file
437
src/wip3m/compute_limiters_from_baseline_ts.py
Normal file
|
@ -0,0 +1,437 @@
|
|||
#!/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"
|
||||
|
||||
"""
|
||||
Compute time step limiters from baseline (linear / logarithmic) time
|
||||
step distribution.
|
||||
"""
|
||||
|
||||
# pyright: reportWildcardImportFromLibrary=false
|
||||
from os.path import isfile
|
||||
from pathlib import Path
|
||||
import numpy as np
|
||||
|
||||
from pysbmy.power import PowerSpectrum
|
||||
from pysbmy.fft import FourierGrid
|
||||
from pysbmy.timestepping import StandardTimeStepping
|
||||
|
||||
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.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="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=128.0, help="Box size in Mpc/h.")
|
||||
parser.add_argument("--N", type=int, default=256, help="Density grid size per dimension.")
|
||||
parser.add_argument("--Np", type=int, default=128, help="Number of particles per dimension.")
|
||||
parser.add_argument("--Npm", type=int, default=256, help="PM mesh resolution per dimension.")
|
||||
parser.add_argument(
|
||||
"--n_Tiles", type=int, default=32, help="Number of tiles per dimension for short-range PP."
|
||||
)
|
||||
parser.add_argument("--z_i", type=float, default=199.0, help="Initial redshift.")
|
||||
parser.add_argument("--z_f", type=float, default=0.0, help="Final redshift.")
|
||||
|
||||
# Timestep settings per method
|
||||
for scheme in ["pmref", "pm1", "pm2", "p3m1", "p3m2", "p3m3"]:
|
||||
parser.add_argument(
|
||||
f"--nsteps_{scheme}",
|
||||
type=int,
|
||||
default={
|
||||
"pmref": 300,
|
||||
"pm1": 200,
|
||||
"pm2": 100,
|
||||
"p3m1": 500,
|
||||
"p3m2": 400,
|
||||
"p3m3": 300,
|
||||
}[scheme],
|
||||
help=f"Number of timesteps for {scheme.upper()}.",
|
||||
)
|
||||
parser.add_argument(
|
||||
f"--tsd_{scheme}",
|
||||
type=int,
|
||||
default=1,
|
||||
help=f"TimeStepDistribution ID for {scheme.upper()}.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--nsteps_p3m4",
|
||||
type=int,
|
||||
help="Number of timesteps for P3M4.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tsd_p3m4",
|
||||
type=int,
|
||||
default=1,
|
||||
help="TimeStepDistribution ID for P3M4.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--nsteps_p3m5",
|
||||
type=int,
|
||||
help="Number of timesteps for P3M5.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tsd_p3m5",
|
||||
type=int,
|
||||
default=1,
|
||||
help="TimeStepDistribution ID for P3M5.",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
go_beyond_Nyquist_ss = True # for the summary statistics
|
||||
force = False
|
||||
force_hard = False
|
||||
|
||||
run_id = args.run_id
|
||||
L = args.L
|
||||
N = args.N
|
||||
Np = args.Np
|
||||
Npm = args.Npm
|
||||
n_Tiles = args.n_Tiles
|
||||
nsteps_pmref = args.nsteps_pmref
|
||||
nsteps_pm1 = args.nsteps_pm1
|
||||
nsteps_pm2 = args.nsteps_pm2
|
||||
nsteps_p3m1 = args.nsteps_p3m1
|
||||
nsteps_p3m2 = args.nsteps_p3m2
|
||||
nsteps_p3m3 = args.nsteps_p3m3
|
||||
tsd_pmref = args.tsd_pmref
|
||||
tsd_pm1 = args.tsd_pm1
|
||||
tsd_pm2 = args.tsd_pm2
|
||||
tsd_p3m1 = args.tsd_p3m1
|
||||
tsd_p3m2 = args.tsd_p3m2
|
||||
tsd_p3m3 = args.tsd_p3m3
|
||||
if args.nsteps_p3m4 is None:
|
||||
p3m4 = False
|
||||
else:
|
||||
p3m4 = True
|
||||
nsteps_p3m4 = args.nsteps_p3m4
|
||||
tsd_p3m4 = args.tsd_p3m4
|
||||
if args.nsteps_p3m5 is None:
|
||||
p3m5 = False
|
||||
else:
|
||||
p3m5 = True
|
||||
nsteps_p3m5 = args.nsteps_p3m5
|
||||
tsd_p3m5 = args.tsd_p3m5
|
||||
RedshiftLPT = args.z_i
|
||||
RedshiftFCs = args.z_f
|
||||
logger.info("Running convergence tests...")
|
||||
logger.info(f"Run ID: {run_id}")
|
||||
logger.info(f"Box size: {L} Mpc/h")
|
||||
logger.info(f"Density grid size: {N}^3")
|
||||
logger.info(f"Particles per dimension: {Np}^3")
|
||||
logger.info(f"PM grid size: {Npm}^3")
|
||||
logger.info(f"Number of tiles: {n_Tiles}^3")
|
||||
logger.info(f"Number of timesteps for PMREF: {nsteps_pmref}")
|
||||
logger.info(f"Number of timesteps for PM1: {nsteps_pm1}")
|
||||
logger.info(f"Number of timesteps for PM2: {nsteps_pm2}")
|
||||
logger.info(f"Number of timesteps for P3M1: {nsteps_p3m1}")
|
||||
logger.info(f"Number of timesteps for P3M2: {nsteps_p3m2}")
|
||||
logger.info(f"Number of timesteps for P3M3: {nsteps_p3m3}")
|
||||
if p3m4:
|
||||
logger.info(f"Number of timesteps for P3M4: {nsteps_p3m4}")
|
||||
if p3m5:
|
||||
logger.info(f"Number of timesteps for P3M5: {nsteps_p3m5}")
|
||||
INDENT()
|
||||
|
||||
corner = -L / 2.0
|
||||
ai = z2a(RedshiftLPT)
|
||||
af = z2a(RedshiftFCs)
|
||||
k_max = get_k_max(L, N) # k_max in h/Mpc
|
||||
cosmo = params_planck_kmax_missing.copy()
|
||||
cosmo["k_max"] = k_max
|
||||
|
||||
wd = workdir + run_id + "/"
|
||||
simdir = output_path + run_id + "/"
|
||||
logdir = simdir + "logs/"
|
||||
if force_hard:
|
||||
import shutil
|
||||
|
||||
if Path(simdir).exists():
|
||||
shutil.rmtree(simdir)
|
||||
if Path(wd).exists():
|
||||
shutil.rmtree(wd)
|
||||
Path(wd).mkdir(parents=True, exist_ok=True)
|
||||
Path(logdir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
input_white_noise_file = simdir + "input_white_noise.h5"
|
||||
input_seed_phase_file = simdir + "seed"
|
||||
ICs_path = simdir + "initial_density.h5"
|
||||
simpath = simdir
|
||||
|
||||
# Path to the input matter power spectrum (generated later)
|
||||
input_power_file = simdir + "input_power.h5"
|
||||
|
||||
sim_params = {
|
||||
"L": L,
|
||||
"N": N,
|
||||
"Np": Np,
|
||||
"Npm": Npm,
|
||||
"n_Tiles": n_Tiles,
|
||||
"RedshiftLPT": RedshiftLPT,
|
||||
"RedshiftFCs": RedshiftFCs,
|
||||
"nsteps_pmref": nsteps_pmref,
|
||||
"nsteps_pm1": nsteps_pm1,
|
||||
"nsteps_pm2": nsteps_pm2,
|
||||
"nsteps_p3m1": nsteps_p3m1,
|
||||
"nsteps_p3m2": nsteps_p3m2,
|
||||
"nsteps_p3m3": nsteps_p3m3,
|
||||
"ai": ai,
|
||||
"af": af,
|
||||
"cosmo": cosmo,
|
||||
"tsd_pmref": tsd_pmref,
|
||||
"tsd_pm1": tsd_pm1,
|
||||
"tsd_pm2": tsd_pm2,
|
||||
"tsd_p3m1": tsd_p3m1,
|
||||
"tsd_p3m2": tsd_p3m2,
|
||||
"tsd_p3m3": tsd_p3m3,
|
||||
}
|
||||
if p3m4:
|
||||
sim_params["nsteps_p3m4"] = nsteps_p3m4
|
||||
sim_params["tsd_p3m4"] = tsd_p3m4
|
||||
if p3m5:
|
||||
sim_params["nsteps_p3m5"] = nsteps_p3m5
|
||||
sim_params["tsd_p3m5"] = tsd_p3m5
|
||||
with open(wd + "sim_params.txt", "w") as f:
|
||||
f.write(f"{sim_params}\n")
|
||||
|
||||
logger.info("Setting up simulation parameters...")
|
||||
|
||||
common_params = {
|
||||
"Np": Np,
|
||||
"N": N,
|
||||
"L": L,
|
||||
"corner0": corner,
|
||||
"corner1": corner,
|
||||
"corner2": corner,
|
||||
"h": cosmo["h"],
|
||||
"Omega_m": cosmo["Omega_m"],
|
||||
"Omega_b": cosmo["Omega_b"],
|
||||
"n_s": cosmo["n_s"],
|
||||
"sigma8": cosmo["sigma8"],
|
||||
}
|
||||
|
||||
lpt_params = common_params.copy()
|
||||
lpt_params["method"] = "lpt"
|
||||
lpt_params["InputPowerSpectrum"] = input_power_file
|
||||
lpt_params["ICsMode"] = 1
|
||||
lpt_params["InputWhiteNoise"] = input_white_noise_file
|
||||
|
||||
common_params_num = common_params.copy()
|
||||
common_params_num["ai"] = ai
|
||||
common_params_num["af"] = af
|
||||
common_params_num["RedshiftLPT"] = RedshiftLPT
|
||||
common_params_num["RedshiftFCs"] = RedshiftFCs
|
||||
common_params_num["Npm"] = Npm
|
||||
common_params_num["RunForceDiagnostic"] = False
|
||||
common_params_num["nBinsForceDiagnostic"] = 20
|
||||
common_params_num["nPairsForceDiagnostic"] = 3
|
||||
common_params_num["maxTrialsForceDiagnostic"] = int(1e8)
|
||||
common_params_num["OutputForceDiagnostic"] = simdir + "force_diagnostic.txt"
|
||||
|
||||
pmref_params = common_params_num.copy()
|
||||
pmref_params["method"] = "pm"
|
||||
pmref_params["TimeStepDistribution"] = tsd_pmref
|
||||
pmref_params["nsteps"] = nsteps_pmref
|
||||
|
||||
pm1_params = common_params_num.copy()
|
||||
pm1_params["method"] = "pm"
|
||||
pm1_params["TimeStepDistribution"] = tsd_pm1
|
||||
pm1_params["nsteps"] = nsteps_pm1
|
||||
|
||||
pm2_params = common_params_num.copy()
|
||||
pm2_params["method"] = "pm"
|
||||
pm2_params["TimeStepDistribution"] = tsd_pm2
|
||||
pm2_params["nsteps"] = nsteps_pm2
|
||||
|
||||
p3m1_params = common_params_num.copy()
|
||||
p3m1_params["method"] = "p3m"
|
||||
p3m1_params["EvolutionMode"] = 4
|
||||
p3m1_params["TimeStepDistribution"] = tsd_p3m1
|
||||
p3m1_params["nsteps"] = nsteps_p3m1
|
||||
p3m1_params["n_Tiles"] = n_Tiles
|
||||
p3m1_params["PrintOutputTimestepsLog"] = True
|
||||
p3m1_params["OutputTimestepsLog"] = simdir + "p3m1_timestep_log.txt"
|
||||
|
||||
p3m2_params = common_params_num.copy()
|
||||
p3m2_params["method"] = "p3m"
|
||||
p3m2_params["EvolutionMode"] = 4
|
||||
p3m2_params["TimeStepDistribution"] = tsd_p3m2
|
||||
p3m2_params["nsteps"] = nsteps_p3m2
|
||||
p3m2_params["n_Tiles"] = n_Tiles
|
||||
p3m2_params["PrintOutputTimestepsLog"] = True
|
||||
p3m2_params["OutputTimestepsLog"] = simdir + "p3m2_timestep_log.txt"
|
||||
|
||||
p3m3_params = common_params_num.copy()
|
||||
p3m3_params["method"] = "p3m"
|
||||
p3m3_params["EvolutionMode"] = 4
|
||||
p3m3_params["TimeStepDistribution"] = tsd_p3m3
|
||||
p3m3_params["nsteps"] = nsteps_p3m3
|
||||
p3m3_params["n_Tiles"] = n_Tiles
|
||||
p3m3_params["PrintOutputTimestepsLog"] = True
|
||||
p3m3_params["OutputTimestepsLog"] = simdir + "p3m3_timestep_log.txt"
|
||||
|
||||
if p3m4:
|
||||
p3m4_params = common_params_num.copy()
|
||||
p3m4_params["method"] = "p3m"
|
||||
p3m4_params["EvolutionMode"] = 4
|
||||
p3m4_params["TimeStepDistribution"] = tsd_p3m4
|
||||
p3m4_params["nsteps"] = nsteps_p3m4
|
||||
p3m4_params["n_Tiles"] = n_Tiles
|
||||
p3m4_params["PrintOutputTimestepsLog"] = True
|
||||
p3m4_params["OutputTimestepsLog"] = simdir + "p3m4_timestep_log.txt"
|
||||
|
||||
if p3m5:
|
||||
p3m5_params = common_params_num.copy()
|
||||
p3m5_params["method"] = "p3m"
|
||||
p3m5_params["EvolutionMode"] = 4
|
||||
p3m5_params["TimeStepDistribution"] = tsd_p3m5
|
||||
p3m5_params["nsteps"] = nsteps_p3m5
|
||||
p3m5_params["n_Tiles"] = n_Tiles
|
||||
p3m5_params["PrintOutputTimestepsLog"] = True
|
||||
p3m5_params["OutputTimestepsLog"] = simdir + "p3m5_timestep_log.txt"
|
||||
|
||||
logger.info("Setting up simulation parameters done.")
|
||||
|
||||
logger.info("Generating simulation parameters...")
|
||||
INDENT()
|
||||
reset_plotting() # Default style for Simbelmynë
|
||||
generate_sim_params(lpt_params, ICs_path, wd, simdir, None, force)
|
||||
|
||||
all_sim_params = [
|
||||
("pmref", pmref_params),
|
||||
("pm1", pm1_params),
|
||||
("pm2", pm2_params),
|
||||
("p3m1", p3m1_params),
|
||||
("p3m2", p3m2_params),
|
||||
("p3m3", p3m3_params),
|
||||
]
|
||||
if p3m4:
|
||||
all_sim_params.append(("p3m4", p3m4_params))
|
||||
if p3m5:
|
||||
all_sim_params.append(("p3m5", p3m5_params))
|
||||
for name, parameters in all_sim_params:
|
||||
logger.info(
|
||||
f"Generating parameters for {name.upper()} with nsteps = {parameters['nsteps']}..."
|
||||
)
|
||||
file_ext = f"{name}_nsteps{parameters['nsteps']}"
|
||||
generate_sim_params(parameters, ICs_path, wd, simdir, file_ext, force)
|
||||
logger.info(f"Generating parameters for {name.upper()} done.")
|
||||
|
||||
UNINDENT()
|
||||
logger.info("Generating simulation parameters done.")
|
||||
|
||||
setup_plotting() # Reset plotting style for this project
|
||||
|
||||
logger.info("Generating white noise field...")
|
||||
generate_white_noise_Field(
|
||||
L=L,
|
||||
size=N,
|
||||
corner=corner,
|
||||
seedphase=BASELINE_SEEDPHASE,
|
||||
fname_whitenoise=input_white_noise_file,
|
||||
seedname_whitenoise=input_seed_phase_file,
|
||||
force_phase=force,
|
||||
)
|
||||
logger.info("Generating white noise field done.")
|
||||
|
||||
# If cosmo["WhichSpectrum"] == "class", then classy is required.
|
||||
if not isfile(input_power_file) or force:
|
||||
logger.info("Generating input power spectrum...")
|
||||
Pk = PowerSpectrum(L, L, L, N, N, N, cosmo_small_to_full_dict(cosmo))
|
||||
Pk.write(input_power_file)
|
||||
logger.info("Generating input power spectrum done.")
|
||||
|
||||
logger.info("Generating Fourier grid...")
|
||||
# k grid used to compute the final overdensity power spectrum
|
||||
Pinit = 100
|
||||
trim_threshold = 100 # Merge bins until this minimum number of modes per bin is reached
|
||||
log_kmin = np.log10(2 * np.pi / (np.sqrt(3) * L)) # Minimum non-zero k in h/Mpc
|
||||
if go_beyond_Nyquist_ss:
|
||||
k_max_ss = get_k_max(L, N)
|
||||
else:
|
||||
k_max_ss = get_k_max(L, N) / np.sqrt(3) # 1D Nyquist frequency
|
||||
Pbins_left_bnds = np.logspace(log_kmin, np.log10(k_max_ss), Pinit + 1, dtype=np.float32)
|
||||
Pbins_left_bnds = Pbins_left_bnds[:-1]
|
||||
input_ss_file = simdir + "input_ss_k_grid.h5"
|
||||
Gk = FourierGrid(
|
||||
L,
|
||||
L,
|
||||
L,
|
||||
N,
|
||||
N,
|
||||
N,
|
||||
k_modes=Pbins_left_bnds,
|
||||
kmax=k_max_ss,
|
||||
trim_bins=True,
|
||||
trim_threshold=trim_threshold,
|
||||
)
|
||||
Gk.write(input_ss_file)
|
||||
logger.info("Generating Fourier grid done.")
|
||||
|
||||
logger.info("Running simulations...")
|
||||
INDENT()
|
||||
logger.info("Running LPT simulation...")
|
||||
run_simulation("lpt", lpt_params, wd, logdir)
|
||||
logger.info("Running LPT simulation done.")
|
||||
|
||||
for name, parameters in all_sim_params:
|
||||
logger.info(f"Running {name.upper()} simulation...")
|
||||
run_simulation(name, parameters, wd, logdir)
|
||||
logger.info(f"Running {name.upper()} simulation done.")
|
||||
UNINDENT()
|
||||
logger.info("All simulations done.")
|
||||
|
||||
logger.info("Plotting time step diagnostics...")
|
||||
INDENT()
|
||||
|
||||
for name, parameters in all_sim_params:
|
||||
if name.startswith("p3m") or name.startswith("spm"):
|
||||
file_ext = f"{name}_nsteps{parameters['nsteps']}"
|
||||
method = parameters["method"]
|
||||
aiDrift = StandardTimeStepping.read(wd + file_ext + f"_ts_{method}.h5").aiDrift
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=parameters["OutputTimestepsLog"],
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=parameters["TimeStepDistribution"],
|
||||
nsteps=parameters["nsteps"],
|
||||
save_path=wd+"time_step_diagnostics.pdf"
|
||||
)
|
||||
UNINDENT()
|
||||
logger.info("Plotting time step diagnostics done.")
|
||||
|
||||
except OSError as e:
|
||||
logger.error("Directory or file access error: %s", str(e))
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.critical("An unexpected error occurred: %s", str(e))
|
||||
raise RuntimeError("Failed.") from e
|
||||
finally:
|
||||
UNINDENT()
|
||||
logger.info("Running convergence tests done.")
|
|
@ -22,6 +22,7 @@ import numpy as np
|
|||
|
||||
from pysbmy.power import PowerSpectrum
|
||||
from pysbmy.fft import FourierGrid
|
||||
from pysbmy.timestepping import StandardTimeStepping
|
||||
|
||||
from wip3m import *
|
||||
from wip3m.tools import get_k_max, generate_sim_params, generate_white_noise_Field, run_simulation
|
||||
|
@ -52,6 +53,8 @@ parser.add_argument("--Npm", type=int, default=64, help="PM mesh resolution per
|
|||
parser.add_argument(
|
||||
"--n_Tiles", type=int, default=8, help="Number of tiles per dimension for short-range PP."
|
||||
)
|
||||
parser.add_argument("--z_i", type=float, default=19.0, help="Initial redshift.")
|
||||
parser.add_argument("--z_f", type=float, default=0.0, help="Final redshift.")
|
||||
|
||||
# Timestep settings per method
|
||||
for scheme in ["pmref", "pm1", "pm2", "cola", "spm", "p3m1", "p3m2", "p3m3"]:
|
||||
|
@ -107,6 +110,8 @@ if __name__ == "__main__":
|
|||
tsd_p3m1 = args.tsd_p3m1
|
||||
tsd_p3m2 = args.tsd_p3m2
|
||||
tsd_p3m3 = args.tsd_p3m3
|
||||
RedshiftLPT = args.z_i
|
||||
RedshiftFCs = args.z_f
|
||||
logger.info("Running convergence tests...")
|
||||
logger.info(f"Run ID: {run_id}")
|
||||
logger.info(f"Box size: {L} Mpc/h")
|
||||
|
@ -125,8 +130,6 @@ if __name__ == "__main__":
|
|||
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
|
||||
|
@ -242,6 +245,8 @@ if __name__ == "__main__":
|
|||
spm_params["TimeStepDistribution"] = tsd_spm
|
||||
spm_params["nsteps"] = nsteps_spm
|
||||
spm_params["n_Tiles"] = n_Tiles
|
||||
spm_params["PrintOutputTimestepsLog"] = True
|
||||
spm_params["OutputTimestepsLog"] = simdir + "spm_timestep_log.txt"
|
||||
|
||||
p3m1_params = common_params_num.copy()
|
||||
p3m1_params["method"] = "p3m"
|
||||
|
@ -249,6 +254,8 @@ if __name__ == "__main__":
|
|||
p3m1_params["TimeStepDistribution"] = tsd_p3m1
|
||||
p3m1_params["nsteps"] = nsteps_p3m1
|
||||
p3m1_params["n_Tiles"] = n_Tiles
|
||||
p3m1_params["PrintOutputTimestepsLog"] = True
|
||||
p3m1_params["OutputTimestepsLog"] = simdir + "p3m1_timestep_log.txt"
|
||||
|
||||
p3m2_params = common_params_num.copy()
|
||||
p3m2_params["method"] = "p3m"
|
||||
|
@ -256,6 +263,8 @@ if __name__ == "__main__":
|
|||
p3m2_params["TimeStepDistribution"] = tsd_p3m2
|
||||
p3m2_params["nsteps"] = nsteps_p3m2
|
||||
p3m2_params["n_Tiles"] = n_Tiles
|
||||
p3m2_params["PrintOutputTimestepsLog"] = True
|
||||
p3m2_params["OutputTimestepsLog"] = simdir + "p3m2_timestep_log.txt"
|
||||
|
||||
p3m3_params = common_params_num.copy()
|
||||
p3m3_params["method"] = "p3m"
|
||||
|
@ -263,6 +272,8 @@ if __name__ == "__main__":
|
|||
p3m3_params["TimeStepDistribution"] = tsd_p3m3
|
||||
p3m3_params["nsteps"] = nsteps_p3m3
|
||||
p3m3_params["n_Tiles"] = n_Tiles
|
||||
p3m3_params["PrintOutputTimestepsLog"] = True
|
||||
p3m3_params["OutputTimestepsLog"] = simdir + "p3m3_timestep_log.txt"
|
||||
|
||||
logger.info("Setting up simulation parameters done.")
|
||||
|
||||
|
@ -281,12 +292,12 @@ if __name__ == "__main__":
|
|||
("p3m2", p3m2_params),
|
||||
("p3m3", p3m3_params),
|
||||
]
|
||||
for name, params in all_sim_params:
|
||||
for name, parameters in all_sim_params:
|
||||
logger.info(
|
||||
f"Generating parameters for {name.upper()} with nsteps = {params['nsteps']}..."
|
||||
f"Generating parameters for {name.upper()} with nsteps = {parameters['nsteps']}..."
|
||||
)
|
||||
file_ext = f"{name}_nsteps{params['nsteps']}"
|
||||
generate_sim_params(params, ICs_path, wd, simdir, file_ext, force)
|
||||
file_ext = f"{name}_nsteps{parameters['nsteps']}"
|
||||
generate_sim_params(parameters, ICs_path, wd, simdir, file_ext, force)
|
||||
logger.info(f"Generating parameters for {name.upper()} done.")
|
||||
|
||||
UNINDENT()
|
||||
|
@ -353,6 +364,24 @@ if __name__ == "__main__":
|
|||
UNINDENT()
|
||||
logger.info("All simulations done.")
|
||||
|
||||
logger.info("Plotting time step diagnostics...")
|
||||
INDENT()
|
||||
|
||||
for name, parameters in all_sim_params:
|
||||
if name.startswith("p3m") or name.startswith("spm"):
|
||||
file_ext = f"{name}_nsteps{parameters['nsteps']}"
|
||||
method = parameters["method"]
|
||||
aiDrift = StandardTimeStepping.read(wd + file_ext + f"_ts_{method}.h5").aiDrift
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=parameters["OutputTimestepsLog"],
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=parameters["TimeStepDistribution"],
|
||||
nsteps=parameters["nsteps"],
|
||||
save_path=wd+"time_step_diagnostics.pdf"
|
||||
)
|
||||
UNINDENT()
|
||||
logger.info("Plotting time step diagnostics done.")
|
||||
|
||||
except OSError as e:
|
||||
logger.error("Directory or file access error: %s", str(e))
|
||||
raise
|
||||
|
|
918
src/wip3m/convergence_cola_expl_parser.py
Normal file
918
src/wip3m/convergence_cola_expl_parser.py
Normal file
|
@ -0,0 +1,918 @@
|
|||
#!/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"
|
||||
|
||||
"""
|
||||
Perform time step convergence tests with custom time step distributions,
|
||||
using COLA with different force evaluations (PM, sPM, P3M).
|
||||
"""
|
||||
|
||||
# pyright: reportWildcardImportFromLibrary=false
|
||||
from os.path import isfile
|
||||
from pathlib import Path
|
||||
import numpy as np
|
||||
|
||||
from pysbmy.power import PowerSpectrum
|
||||
from pysbmy.fft import FourierGrid
|
||||
from pysbmy.timestepping import StandardTimeStepping, P3MTimeStepping
|
||||
|
||||
from wip3m import *
|
||||
from wip3m.tools import none_bool_str, 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.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="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("--z_i", type=float, default=199.0, help="Initial redshift.")
|
||||
parser.add_argument("--z_f", type=float, default=19.0, help="Final redshift.")
|
||||
parser.add_argument(
|
||||
"--plot_fields",
|
||||
type=none_bool_str,
|
||||
default=False,
|
||||
help="Whether to plot the fields or not.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lpt",
|
||||
type=none_bool_str,
|
||||
default=False,
|
||||
help="Whether to compute and plot the LPT power spectrum.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--scale_limiter",
|
||||
type=none_bool_str,
|
||||
default=False,
|
||||
help="Which limiter should be scaled by the scaling arguments? ",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--only_plot",
|
||||
type=none_bool_str,
|
||||
default=False,
|
||||
help="Do not re-run simulations. Warning: it is up to the user to ensure that the required files are present.",
|
||||
)
|
||||
|
||||
# Timestep settings per method
|
||||
for scheme in ["pmref", "pm1", "pm2", "spm", "p3m1", "p3m2", "p3m3"]:
|
||||
parser.add_argument(
|
||||
f"--scaling_{scheme}",
|
||||
type=float,
|
||||
default={
|
||||
"pmref": 1.,
|
||||
"pm1": 0.95,
|
||||
"pm2": 0.9,
|
||||
"spm": 1.,
|
||||
"p3m1": 1.,
|
||||
"p3m2": 0.95,
|
||||
"p3m3": 0.9,
|
||||
}[scheme],
|
||||
help=f"Relax the step limiter by this factor for {scheme.upper()}.",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
go_beyond_Nyquist_ss = True # for the summary statistics
|
||||
force = False
|
||||
force_hard = False
|
||||
only_plot = args.only_plot
|
||||
|
||||
run_id = args.run_id
|
||||
L = args.L
|
||||
N = args.N
|
||||
Np = args.Np
|
||||
Npm = args.Npm
|
||||
n_Tiles = args.n_Tiles
|
||||
scaling_pmref = args.scaling_pmref
|
||||
scaling_pm1 = args.scaling_pm1
|
||||
scaling_pm2 = args.scaling_pm2
|
||||
scaling_spm = args.scaling_spm
|
||||
scaling_p3m1 = args.scaling_p3m1
|
||||
scaling_p3m2 = args.scaling_p3m2
|
||||
scaling_p3m3 = args.scaling_p3m3
|
||||
name_scaled = args.scale_limiter
|
||||
if name_scaled is not False and name_scaled not in [
|
||||
"fac_dyn_custom",
|
||||
"da_max_early_custom",
|
||||
"fac_H_custom",
|
||||
"fac_bend",
|
||||
"sub_bend1",
|
||||
"sub_bend2",
|
||||
"da_max_late",
|
||||
]:
|
||||
raise ValueError(
|
||||
f"Invalid name_scaled: {name_scaled}. Must be one of the limiter names or False."
|
||||
)
|
||||
RedshiftLPT = args.z_i
|
||||
RedshiftFCs = args.z_f
|
||||
logger.info("Running convergence tests...")
|
||||
logger.info(f"Run ID: {run_id}")
|
||||
logger.info(f"Box size: {L} Mpc/h")
|
||||
logger.info(f"Density grid size: {N}^3")
|
||||
logger.info(f"Particles per dimension: {Np}^3")
|
||||
logger.info(f"PM grid size: {Npm}^3")
|
||||
logger.info(f"Number of tiles: {n_Tiles}^3")
|
||||
logger.info(f"Limiter to scale: {name_scaled if name_scaled else 'None'}")
|
||||
if name_scaled:
|
||||
logger.info(f"Limiter rescaling for PMREF: {scaling_pmref}")
|
||||
logger.info(f"Limiter rescaling for PM1: {scaling_pm1}")
|
||||
logger.info(f"Limiter rescaling for PM2: {scaling_pm2}")
|
||||
logger.info(f"Limiter rescaling for sPM: {scaling_spm}")
|
||||
logger.info(f"Limiter rescaling for P3M1: {scaling_p3m1}")
|
||||
logger.info(f"Limiter rescaling for P3M2: {scaling_p3m2}")
|
||||
logger.info(f"Limiter rescaling for P3M3: {scaling_p3m3}")
|
||||
INDENT()
|
||||
|
||||
lpt = args.lpt
|
||||
plot_fields = args.plot_fields
|
||||
|
||||
corner = -L / 2.0
|
||||
ai = z2a(RedshiftLPT)
|
||||
af = z2a(RedshiftFCs)
|
||||
k_max = get_k_max(L, N) # k_max in h/Mpc
|
||||
cosmo = params_planck_kmax_missing.copy()
|
||||
cosmo["k_max"] = k_max
|
||||
|
||||
wd = workdir + run_id + "/"
|
||||
simdir = output_path + run_id + "/"
|
||||
logdir = simdir + "logs/"
|
||||
outdir = simdir + "plots/"
|
||||
if force_hard:
|
||||
import shutil
|
||||
|
||||
if Path(simdir).exists():
|
||||
shutil.rmtree(simdir)
|
||||
if Path(wd).exists():
|
||||
shutil.rmtree(wd)
|
||||
Path(wd).mkdir(parents=True, exist_ok=True)
|
||||
Path(logdir).mkdir(parents=True, exist_ok=True)
|
||||
Path(outdir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
input_white_noise_file = simdir + "input_white_noise.h5"
|
||||
input_seed_phase_file = simdir + "seed"
|
||||
ICs_path = simdir + "initial_density.h5"
|
||||
simpath = simdir
|
||||
|
||||
# Path to the input matter power spectrum (generated later)
|
||||
input_power_file = simdir + "input_power.h5"
|
||||
|
||||
sim_params = {
|
||||
"L": L,
|
||||
"N": N,
|
||||
"Np": Np,
|
||||
"Npm": Npm,
|
||||
"n_Tiles": n_Tiles,
|
||||
"RedshiftLPT": RedshiftLPT,
|
||||
"RedshiftFCs": RedshiftFCs,
|
||||
"name_scaled": name_scaled,
|
||||
"scaling_pmref": scaling_pmref,
|
||||
"scaling_pm1": scaling_pm1,
|
||||
"scaling_pm2": scaling_pm2,
|
||||
"scaling_spm": scaling_spm,
|
||||
"scaling_p3m1": scaling_p3m1,
|
||||
"scaling_p3m2": scaling_p3m2,
|
||||
"scaling_p3m3": scaling_p3m3,
|
||||
}
|
||||
with open(wd + "sim_params.txt", "w") as f:
|
||||
f.write(f"{sim_params}\n")
|
||||
|
||||
logger.info("Setting up simulation parameters...")
|
||||
|
||||
common_params = {
|
||||
"Np": Np,
|
||||
"N": N,
|
||||
"L": L,
|
||||
"corner0": corner,
|
||||
"corner1": corner,
|
||||
"corner2": corner,
|
||||
"h": cosmo["h"],
|
||||
"Omega_m": cosmo["Omega_m"],
|
||||
"Omega_b": cosmo["Omega_b"],
|
||||
"n_s": cosmo["n_s"],
|
||||
"sigma8": cosmo["sigma8"],
|
||||
}
|
||||
|
||||
lpt_params = common_params.copy()
|
||||
lpt_params["method"] = "lpt"
|
||||
lpt_params["InputPowerSpectrum"] = input_power_file
|
||||
lpt_params["ICsMode"] = 1
|
||||
lpt_params["InputWhiteNoise"] = input_white_noise_file
|
||||
|
||||
fac_dyn_custom = DEFAULT_FAC_DYN_CUSTOM
|
||||
da_early = DEFAULT_DA_MAX_EARLY_CUSTOM
|
||||
fac_H_custom = DEFAULT_FAC_H_CUSTOM
|
||||
fac_bend = DEFAULT_FAC_BEND
|
||||
sub_bend1 = DEFAULT_SUB_BEND1_COLA
|
||||
sub_bend2 = DEFAULT_SUB_BEND2_COLA
|
||||
da_late = DEFAULT_DA_MAX_LATE_CUSTOM
|
||||
|
||||
limiters = {
|
||||
"fac_dyn_custom": fac_dyn_custom,
|
||||
"da_max_early_custom": da_early,
|
||||
"fac_H_custom": fac_H_custom,
|
||||
"fac_bend": fac_bend,
|
||||
"sub_bend1": sub_bend1,
|
||||
"sub_bend2": sub_bend2,
|
||||
"da_max_late": da_late,
|
||||
}
|
||||
|
||||
common_params_num = common_params.copy()
|
||||
common_params_num["ai"] = ai
|
||||
common_params_num["af"] = af
|
||||
common_params_num["RedshiftLPT"] = RedshiftLPT
|
||||
common_params_num["RedshiftFCs"] = RedshiftFCs
|
||||
common_params_num["Npm"] = Npm
|
||||
common_params_num["RunForceDiagnostic"] = False
|
||||
common_params_num["nBinsForceDiagnostic"] = 20
|
||||
common_params_num["nPairsForceDiagnostic"] = 3
|
||||
common_params_num["maxTrialsForceDiagnostic"] = int(1e8)
|
||||
common_params_num["OutputForceDiagnostic"] = simdir + "force_diagnostic.txt"
|
||||
common_params_num["TimeStepDistribution"] = 3 # Custom time step distribution
|
||||
common_params_num["cosmo_dict"] = cosmo
|
||||
common_params_num["fac_dyn_custom"] = limiters["fac_dyn_custom"]
|
||||
common_params_num["da_max_early_custom"] = limiters["da_max_early_custom"]
|
||||
common_params_num["fac_H_custom"] = limiters["fac_H_custom"]
|
||||
common_params_num["fac_bend"] = limiters["fac_bend"]
|
||||
common_params_num["sub_bend1"] = limiters["sub_bend1"]
|
||||
common_params_num["sub_bend2"] = limiters["sub_bend2"]
|
||||
common_params_num["da_max_late_custom"] = limiters["da_max_late"]
|
||||
|
||||
pmref_params = common_params_num.copy()
|
||||
pmref_params["method"] = "cola" # COLA+PM
|
||||
pmref_params["relax"] = scaling_pmref # plot unscaled values during the simulation
|
||||
if name_scaled:
|
||||
pmref_params[name_scaled] /= scaling_pmref
|
||||
|
||||
pm1_params = common_params_num.copy()
|
||||
pm1_params["method"] = "cola" # COLA+PM
|
||||
pm1_params["relax"] = scaling_pm1
|
||||
if name_scaled:
|
||||
pm1_params[name_scaled] /= scaling_pm1
|
||||
|
||||
pm2_params = common_params_num.copy()
|
||||
pm2_params["method"] = "cola" # COLA+PM
|
||||
pm2_params["relax"] = scaling_pm2
|
||||
if name_scaled:
|
||||
pm2_params[name_scaled] /= scaling_pm2
|
||||
|
||||
spm_params = common_params_num.copy()
|
||||
spm_params["method"] = "spm"
|
||||
spm_params["EvolutionMode"] = 6 # COLA+sPM
|
||||
spm_params["n_Tiles"] = n_Tiles
|
||||
spm_params["PrintOutputTimestepsLog"] = True
|
||||
spm_params["OutputTimestepsLog"] = simdir + "colaspm_timestep_log.txt"
|
||||
spm_params["relax"] = scaling_spm
|
||||
if name_scaled:
|
||||
spm_params[name_scaled] /= scaling_spm
|
||||
|
||||
p3m1_params = common_params_num.copy()
|
||||
p3m1_params["method"] = "p3m"
|
||||
p3m1_params["EvolutionMode"] = 7 # COLA+P3M
|
||||
p3m1_params["n_Tiles"] = n_Tiles
|
||||
p3m1_params["PrintOutputTimestepsLog"] = True
|
||||
p3m1_params["OutputTimestepsLog"] = simdir + "colap3m1_timestep_log.txt"
|
||||
p3m1_params["relax"] = scaling_p3m1
|
||||
if name_scaled:
|
||||
p3m1_params[name_scaled] /= scaling_p3m1
|
||||
|
||||
p3m2_params = common_params_num.copy()
|
||||
p3m2_params["method"] = "p3m"
|
||||
p3m2_params["EvolutionMode"] = 7 # COLA+P3M
|
||||
p3m2_params["n_Tiles"] = n_Tiles
|
||||
p3m2_params["PrintOutputTimestepsLog"] = True
|
||||
p3m2_params["OutputTimestepsLog"] = simdir + "colap3m2_timestep_log.txt"
|
||||
p3m2_params["relax"] = scaling_p3m2
|
||||
if name_scaled:
|
||||
p3m2_params[name_scaled] /= scaling_p3m2
|
||||
|
||||
p3m3_params = common_params_num.copy()
|
||||
p3m3_params["method"] = "p3m"
|
||||
p3m3_params["EvolutionMode"] = 7 # COLA+P3M
|
||||
p3m3_params["n_Tiles"] = n_Tiles
|
||||
p3m3_params["PrintOutputTimestepsLog"] = True
|
||||
p3m3_params["OutputTimestepsLog"] = simdir + "colap3m3_timestep_log.txt"
|
||||
p3m3_params["relax"] = scaling_p3m3
|
||||
if name_scaled:
|
||||
p3m3_params[name_scaled] /= scaling_p3m3
|
||||
|
||||
logger.info("Setting up simulation parameters done.")
|
||||
|
||||
if not only_plot:
|
||||
logger.info("Generating simulation parameters...")
|
||||
INDENT()
|
||||
reset_plotting() # Default style for Simbelmynë
|
||||
generate_sim_params(lpt_params, ICs_path, wd, simdir, None, force)
|
||||
|
||||
all_sim_params = [
|
||||
("pmref", pmref_params),
|
||||
("pm1", pm1_params),
|
||||
("pm2", pm2_params),
|
||||
("spm", spm_params),
|
||||
("p3m1", p3m1_params),
|
||||
("p3m2", p3m2_params),
|
||||
("p3m3", p3m3_params),
|
||||
]
|
||||
for name, parameters in all_sim_params:
|
||||
logger.info(
|
||||
f"Generating parameters for {name.upper()} with relax = {parameters['relax']}..."
|
||||
)
|
||||
file_ext = f"{name}_relax{str(parameters['relax']).replace('.', '_')}"
|
||||
generate_sim_params(parameters, ICs_path, wd, simdir, file_ext, force)
|
||||
logger.info(f"Generating parameters for {name.upper()} done.")
|
||||
|
||||
UNINDENT()
|
||||
logger.info("Generating simulation parameters done.")
|
||||
|
||||
setup_plotting() # Reset plotting style for this project
|
||||
|
||||
logger.info("Generating white noise field...")
|
||||
generate_white_noise_Field(
|
||||
L=L,
|
||||
size=N,
|
||||
corner=corner,
|
||||
seedphase=BASELINE_SEEDPHASE,
|
||||
fname_whitenoise=input_white_noise_file,
|
||||
seedname_whitenoise=input_seed_phase_file,
|
||||
force_phase=force,
|
||||
)
|
||||
logger.info("Generating white noise field done.")
|
||||
|
||||
# If cosmo["WhichSpectrum"] == "class", then classy is required.
|
||||
if not isfile(input_power_file) or force:
|
||||
logger.info("Generating input power spectrum...")
|
||||
Pk = PowerSpectrum(L, L, L, N, N, N, cosmo_small_to_full_dict(cosmo))
|
||||
Pk.write(input_power_file)
|
||||
logger.info("Generating input power spectrum done.")
|
||||
|
||||
logger.info("Generating Fourier grid...")
|
||||
# k grid used to compute the final overdensity power spectrum
|
||||
Pinit = 100
|
||||
trim_threshold = 100 # Merge bins until this minimum number of modes per bin is reached
|
||||
log_kmin = np.log10(2 * np.pi / (np.sqrt(3) * L)) # Minimum non-zero k in h/Mpc
|
||||
if go_beyond_Nyquist_ss:
|
||||
k_max_ss = get_k_max(L, N)
|
||||
else:
|
||||
k_max_ss = get_k_max(L, N) / np.sqrt(3) # 1D Nyquist frequency
|
||||
Pbins_left_bnds = np.logspace(log_kmin, np.log10(k_max_ss), Pinit + 1, dtype=np.float32)
|
||||
Pbins_left_bnds = Pbins_left_bnds[:-1]
|
||||
input_ss_file = simdir + "input_ss_k_grid.h5"
|
||||
Gk = FourierGrid(
|
||||
L,
|
||||
L,
|
||||
L,
|
||||
N,
|
||||
N,
|
||||
N,
|
||||
k_modes=Pbins_left_bnds,
|
||||
kmax=k_max_ss,
|
||||
trim_bins=True,
|
||||
trim_threshold=trim_threshold,
|
||||
)
|
||||
Gk.write(input_ss_file)
|
||||
logger.info("Generating Fourier grid done.")
|
||||
|
||||
logger.info("Running simulations...")
|
||||
INDENT()
|
||||
logger.info("Running LPT simulation...")
|
||||
run_simulation("lpt", lpt_params, wd, logdir)
|
||||
logger.info("Running LPT simulation done.")
|
||||
|
||||
for name, parameters in all_sim_params:
|
||||
logger.info(f"Running {name.upper()} simulation...")
|
||||
run_simulation(name, parameters, wd, logdir)
|
||||
logger.info(f"Running {name.upper()} simulation done.")
|
||||
UNINDENT()
|
||||
logger.info("All simulations done.")
|
||||
|
||||
logger.info("Plotting time step diagnostics...")
|
||||
INDENT()
|
||||
|
||||
nsteps_dict = {}
|
||||
for name, parameters in all_sim_params:
|
||||
TimeStepDistribution = parameters["TimeStepDistribution"]
|
||||
file_ext = f"{name}_relax{str(parameters['relax']).replace('.', '_')}"
|
||||
method = parameters["method"]
|
||||
TSpath = wd + file_ext + f"_ts_{method}.h5" if file_ext else wd + f"ts_{method}.h5"
|
||||
if TimeStepDistribution in [0, 1, 2]:
|
||||
TS = StandardTimeStepping.read(TSpath)
|
||||
aiDrift = TS.aiDrift
|
||||
nsteps = TS.nsteps
|
||||
elif TimeStepDistribution == 3:
|
||||
TS = P3MTimeStepping.read(TSpath)
|
||||
aiDrift = TS.aiDrift
|
||||
nsteps = TS.nsteps
|
||||
else:
|
||||
raise ValueError(f"Invalid TimeStepDistribution: {TimeStepDistribution}")
|
||||
nsteps_dict[name] = nsteps
|
||||
if name.startswith("p3m") or name.startswith("spm"):
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=parameters["OutputTimestepsLog"],
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=TimeStepDistribution,
|
||||
nsteps=nsteps,
|
||||
ymin=2e-3,
|
||||
exact=False,
|
||||
save_path=outdir + f"time_step_diagnostics_{name}.pdf",
|
||||
)
|
||||
plot_custom_timestepping_diagnostics(
|
||||
log_path=parameters["OutputTimestepsLog"],
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=TimeStepDistribution,
|
||||
nsteps=nsteps,
|
||||
ymin=2e-3,
|
||||
fac_hubble=fac_H_custom,
|
||||
fac_bend=fac_bend,
|
||||
da_max_early=da_early,
|
||||
da_max_late=DEFAULT_DA_MAX_LATE_CUSTOM,
|
||||
save_path=outdir + f"time_step_diagnostics_custom_{name}.pdf",
|
||||
)
|
||||
|
||||
UNINDENT()
|
||||
logger.info("Plotting time step diagnostics done.")
|
||||
|
||||
logger.info("Computing and plotting power spectra...")
|
||||
INDENT()
|
||||
|
||||
from pysbmy.fft import read_FourierGrid
|
||||
from pysbmy.field import read_field
|
||||
from pysbmy.correlations import get_autocorrelation
|
||||
|
||||
logger.info("Loading Fourier grid...")
|
||||
G = read_FourierGrid(input_ss_file)
|
||||
logger.info("Loading Fourier grid done.")
|
||||
|
||||
k = G.k_modes
|
||||
AliasingCorr = False
|
||||
slice_ijk = (N // 2, slice(None), slice(None)) # for plot_fields
|
||||
|
||||
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_relax{str(pmref_params['relax']).replace('.', '_')}_final_density_cola.h5")
|
||||
if plot_fields:
|
||||
delta_pmref = DELTA.data[slice_ijk]
|
||||
Pk_PMref, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"pm1_relax{str(pm1_params['relax']).replace('.', '_')}_final_density_cola.h5")
|
||||
if plot_fields:
|
||||
delta_pm1 = DELTA.data[slice_ijk]
|
||||
Pk_PM1, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"pm2_relax{str(pm2_params['relax']).replace('.', '_')}_final_density_cola.h5")
|
||||
if plot_fields:
|
||||
delta_pm2 = DELTA.data[slice_ijk]
|
||||
Pk_PM2, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"spm_relax{str(spm_params['relax']).replace('.', '_')}_final_density_spm.h5")
|
||||
if plot_fields:
|
||||
delta_spm = DELTA.data[slice_ijk]
|
||||
Pk_sPM, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"p3m1_relax{str(p3m1_params['relax']).replace('.', '_')}_final_density_p3m.h5")
|
||||
if plot_fields:
|
||||
delta_p3m1 = DELTA.data[slice_ijk]
|
||||
Pk_P3M1, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"p3m2_relax{str(p3m2_params['relax']).replace('.', '_')}_final_density_p3m.h5")
|
||||
if plot_fields:
|
||||
delta_p3m2 = DELTA.data[slice_ijk]
|
||||
Pk_P3M2, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"p3m3_relax{str(p3m3_params['relax']).replace('.', '_')}_final_density_p3m.h5")
|
||||
if plot_fields:
|
||||
delta_p3m3 = DELTA.data[slice_ijk]
|
||||
Pk_P3M3, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
logger.info("Computing power spectra done.")
|
||||
|
||||
ref = "P3M1"
|
||||
|
||||
scaling_reference = scaling_p3m1
|
||||
Pk_ref = Pk_P3M1
|
||||
label_ref = f"P3M, scaling={scaling_reference}, ns={nsteps_dict['p3m1']}"
|
||||
|
||||
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),
|
||||
]
|
||||
# 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}, scaling={eval(f'scaling_{field_name.lower()}')}, ns={nsteps_dict[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)
|
||||
ax.axhspan(1 - dark_grey_bnd, 1 + dark_grey_bnd, color="grey", alpha=0.3)
|
||||
ax.axhline(1 - medium_grey_bnd, color="grey", linestyle="-", linewidth=1)
|
||||
ax.axhline(1 + medium_grey_bnd, color="grey", linestyle="-", linewidth=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
|
||||
if nyquist <= xlim_max:
|
||||
line1 = ax.axvline(
|
||||
x=nyquist, color="black", linestyle="--", lw=2, label="Nyquist (density grid)", zorder=0
|
||||
)
|
||||
if nyquist_PM <= xlim_max:
|
||||
line2 = ax.axvline(
|
||||
x=nyquist_PM, color="black", linestyle="-", lw=1, label="Nyquist (PM grid)", zorder=0
|
||||
)
|
||||
if xs_inv <= xlim_max:
|
||||
line3 = ax.axvline(
|
||||
x=xs_inv, color="gray", linestyle="-.", lw=2, label=r"Split wavenumber $x_s$", zorder=0
|
||||
)
|
||||
if xr_inv <= xlim_max:
|
||||
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("Plotting power spectra done.")
|
||||
|
||||
logger.info(f"Plotting P3M 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)
|
||||
fields_to_plot = [
|
||||
("P3M1", Pk_P3M1),
|
||||
("P3M2", Pk_P3M2),
|
||||
("P3M3", Pk_P3M3),
|
||||
]
|
||||
# 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}, scaling={eval(f'scaling_{field_name.lower()}')}, ns={nsteps_dict[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.axhline(1 - medium_grey_bnd, color="grey", linestyle="-", linewidth=1)
|
||||
ax.axhline(1 + medium_grey_bnd, color="grey", linestyle="-", linewidth=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
|
||||
if nyquist <= xlim_max:
|
||||
line1 = ax.axvline(
|
||||
x=nyquist, color="black", linestyle="--", lw=2, label="Nyquist (density grid)", zorder=0
|
||||
)
|
||||
if nyquist_PM <= xlim_max:
|
||||
line2 = ax.axvline(
|
||||
x=nyquist_PM, color="black", linestyle="-", lw=1, label="Nyquist (PM grid)", zorder=0
|
||||
)
|
||||
if xs_inv <= xlim_max:
|
||||
line3 = ax.axvline(
|
||||
x=xs_inv, color="gray", linestyle="-.", lw=2, label=r"Split wavenumber $x_s$", zorder=0
|
||||
)
|
||||
if xr_inv <= xlim_max:
|
||||
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()
|
||||
plt.legend(
|
||||
handles,
|
||||
labels,
|
||||
loc="upper center",
|
||||
ncol=2,
|
||||
bbox_to_anchor=(0.5, -0.2),
|
||||
fontsize=fs,
|
||||
frameon=False,
|
||||
)
|
||||
fig.savefig(
|
||||
outdir + "p3m_power_spectra.png",
|
||||
bbox_inches="tight",
|
||||
dpi=300,
|
||||
transparent=True,
|
||||
)
|
||||
fig.savefig(
|
||||
outdir + "p3m_power_spectra.pdf",
|
||||
bbox_inches="tight",
|
||||
dpi=300,
|
||||
)
|
||||
plt.close(fig)
|
||||
UNINDENT()
|
||||
logger.info(
|
||||
f"P3M power spectra plotted and saved to {outdir}p3m_power_spectra.png and power_spectra.pdf"
|
||||
)
|
||||
logger.info("Pplotting P3M power spectra done.")
|
||||
UNINDENT()
|
||||
|
||||
logger.info("Computing and plotting power spectra done.")
|
||||
|
||||
if plot_fields:
|
||||
logger.info("Reading lpt field...")
|
||||
delta_lpt = read_field(simdir + "lpt_density.h5").data[slice_ijk]
|
||||
logger.info("Reading lpt field done.")
|
||||
|
||||
diff_pm1_pmref = delta_pm1 - delta_pmref
|
||||
diff_pm2_pmref = delta_pm2 - delta_pmref
|
||||
diff_p3m1_pmref = delta_p3m1 - delta_pmref
|
||||
diff_p3m2_p3m1 = delta_p3m2 - delta_p3m1
|
||||
diff_p3m3_p3m1 = delta_p3m3 - delta_p3m1
|
||||
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_p3m1",
|
||||
"diff_p3m3_p3m1",
|
||||
] # fields to plot
|
||||
ncols = 6 # Max panels per row
|
||||
|
||||
figname = "_".join(fields)
|
||||
slices_dict = {
|
||||
"lpt": delta_lpt,
|
||||
"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_p3m1": diff_p3m2_p3m1,
|
||||
"diff_p3m3_p3m1": diff_p3m3_p3m1,
|
||||
"diff_p3m1_spm": diff_p3m1_spm,
|
||||
}
|
||||
titles_dict = {
|
||||
"lpt": "LPT",
|
||||
"pmref": f"PMref relax={scaling_pmref}",
|
||||
"pm1": f"PM1 relax={scaling_pm1}",
|
||||
"pm2": f"PM2 relax={scaling_pm2}",
|
||||
"spm": f"sPM relax={scaling_spm}",
|
||||
"p3m1": f"P3M1 relax={scaling_p3m1}",
|
||||
"p3m2": f"P3M2 relax={scaling_p3m2}",
|
||||
"p3m3": f"P3M3 relax={scaling_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_p3m1": r"$\delta_{\rm P3M2}-\delta_{\rm P3M1}$",
|
||||
"diff_p3m3_p3m1": r"$\delta_{\rm P3M3}-\delta_{\rm P3M1}$",
|
||||
"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"):
|
||||
vmin = -np.log(1 + np.abs(np.min(data)))
|
||||
vmax = np.log10(1 + np.abs(np.max(data)))
|
||||
if vmax > 0 > vmin:
|
||||
norm = TwoSlopeNorm(vmin=vmin, vcenter=0, vmax=vmax)
|
||||
im = ax.imshow(
|
||||
np.sign(data) * np.log(1 + np.abs(data)), cmap="RdBu_r", norm=norm
|
||||
)
|
||||
else:
|
||||
im = ax.imshow(np.log10(1 + np.abs(data)), cmap="RdBu_r")
|
||||
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("All plots completed successfully.")
|
||||
|
||||
except OSError as e:
|
||||
logger.error("Directory or file access error: %s", str(e))
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.critical("An unexpected error occurred: %s", str(e))
|
||||
raise RuntimeError("Failed.") from e
|
||||
finally:
|
||||
UNINDENT()
|
||||
logger.info("Running convergence tests done.")
|
718
src/wip3m/convergence_cola_p3m_only_expl.py
Normal file
718
src/wip3m/convergence_cola_p3m_only_expl.py
Normal file
|
@ -0,0 +1,718 @@
|
|||
#!/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"
|
||||
|
||||
"""
|
||||
Perform time step convergence tests with custom time step distributions,
|
||||
using COLA with different force evaluations (PM, sPM, P3M).
|
||||
"""
|
||||
|
||||
# pyright: reportWildcardImportFromLibrary=false
|
||||
from os.path import isfile
|
||||
from pathlib import Path
|
||||
import numpy as np
|
||||
|
||||
from pysbmy.power import PowerSpectrum
|
||||
from pysbmy.fft import FourierGrid
|
||||
from pysbmy.timestepping import StandardTimeStepping, P3MTimeStepping
|
||||
|
||||
from wip3m import *
|
||||
from wip3m.tools import none_bool_str, 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.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="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("--z_i", type=float, default=199.0, help="Initial redshift.")
|
||||
parser.add_argument("--z_f", type=float, default=19.0, help="Final redshift.")
|
||||
parser.add_argument(
|
||||
"--plot_fields",
|
||||
type=none_bool_str,
|
||||
default=False,
|
||||
help="Whether to plot the fields or not.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lpt",
|
||||
type=none_bool_str,
|
||||
default=False,
|
||||
help="Whether to compute and plot the LPT power spectrum.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--scale_limiter",
|
||||
type=none_bool_str,
|
||||
default=False,
|
||||
help="Which limiter should be scaled by the scaling arguments? ",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--use_p3m_fit",
|
||||
type=none_bool_str,
|
||||
default=False,
|
||||
help="Whether to use the fitted P3M limiter or the bend limiter.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--only_plot",
|
||||
type=none_bool_str,
|
||||
default=False,
|
||||
help="Do not re-run simulations. Warning: it is up to the user to ensure that the required files are present.",
|
||||
)
|
||||
|
||||
# Timestep settings per method
|
||||
for scheme in ["p3m1", "p3m2", "p3m3"]:
|
||||
parser.add_argument(
|
||||
f"--scaling_{scheme}",
|
||||
type=float,
|
||||
default={
|
||||
"p3m1": 1.,
|
||||
"p3m2": 0.95,
|
||||
"p3m3": 0.9,
|
||||
}[scheme],
|
||||
help=f"Relax the step limiter by this factor for {scheme.upper()}.",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
go_beyond_Nyquist_ss = True # for the summary statistics
|
||||
force = False
|
||||
force_hard = False
|
||||
only_plot = args.only_plot
|
||||
|
||||
run_id = args.run_id
|
||||
L = args.L
|
||||
N = args.N
|
||||
Np = args.Np
|
||||
Npm = args.Npm
|
||||
n_Tiles = args.n_Tiles
|
||||
scaling_p3m1 = args.scaling_p3m1
|
||||
scaling_p3m2 = args.scaling_p3m2
|
||||
scaling_p3m3 = args.scaling_p3m3
|
||||
name_scaled = args.scale_limiter
|
||||
use_p3m_fit = args.use_p3m_fit
|
||||
if name_scaled is not False and name_scaled not in [
|
||||
"fac_dyn_custom",
|
||||
"da_max_early_custom",
|
||||
"fac_H_custom",
|
||||
"fac_bend",
|
||||
"sub_bend1",
|
||||
"sub_bend2",
|
||||
"fac_p3m_fit",
|
||||
"da_max_late",
|
||||
]:
|
||||
raise ValueError(
|
||||
f"Invalid name_scaled: {name_scaled}. Must be one of the limiter names or False."
|
||||
)
|
||||
RedshiftLPT = args.z_i
|
||||
RedshiftFCs = args.z_f
|
||||
logger.info("Running convergence tests...")
|
||||
logger.info(f"Run ID: {run_id}")
|
||||
logger.info(f"Box size: {L} Mpc/h")
|
||||
logger.info(f"Density grid size: {N}^3")
|
||||
logger.info(f"Particles per dimension: {Np}^3")
|
||||
logger.info(f"PM grid size: {Npm}^3")
|
||||
logger.info(f"Number of tiles: {n_Tiles}^3")
|
||||
logger.info(f"Limiter to scale: {name_scaled if name_scaled else 'None'}")
|
||||
if name_scaled:
|
||||
logger.info(f"Limiter rescaling for P3M1: {scaling_p3m1}")
|
||||
logger.info(f"Limiter rescaling for P3M2: {scaling_p3m2}")
|
||||
logger.info(f"Limiter rescaling for P3M3: {scaling_p3m3}")
|
||||
INDENT()
|
||||
|
||||
lpt = args.lpt
|
||||
plot_fields = args.plot_fields
|
||||
|
||||
corner = -L / 2.0
|
||||
ai = z2a(RedshiftLPT)
|
||||
af = z2a(RedshiftFCs)
|
||||
k_max = get_k_max(L, N) # k_max in h/Mpc
|
||||
cosmo = params_planck_kmax_missing.copy()
|
||||
cosmo["k_max"] = k_max
|
||||
|
||||
wd = workdir + run_id + "/"
|
||||
simdir = output_path + run_id + "/"
|
||||
logdir = simdir + "logs/"
|
||||
outdir = simdir + "plots/"
|
||||
if force_hard:
|
||||
import shutil
|
||||
|
||||
if Path(simdir).exists():
|
||||
shutil.rmtree(simdir)
|
||||
if Path(wd).exists():
|
||||
shutil.rmtree(wd)
|
||||
Path(wd).mkdir(parents=True, exist_ok=True)
|
||||
Path(logdir).mkdir(parents=True, exist_ok=True)
|
||||
Path(outdir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
input_white_noise_file = simdir + "input_white_noise.h5"
|
||||
input_seed_phase_file = simdir + "seed"
|
||||
ICs_path = simdir + "initial_density.h5"
|
||||
simpath = simdir
|
||||
|
||||
# Path to the input matter power spectrum (generated later)
|
||||
input_power_file = simdir + "input_power.h5"
|
||||
input_ss_file = simdir + "input_ss_k_grid.h5"
|
||||
|
||||
sim_params = {
|
||||
"L": L,
|
||||
"N": N,
|
||||
"Np": Np,
|
||||
"Npm": Npm,
|
||||
"n_Tiles": n_Tiles,
|
||||
"RedshiftLPT": RedshiftLPT,
|
||||
"RedshiftFCs": RedshiftFCs,
|
||||
"name_scaled": name_scaled,
|
||||
"scaling_p3m1": scaling_p3m1,
|
||||
"scaling_p3m2": scaling_p3m2,
|
||||
"scaling_p3m3": scaling_p3m3,
|
||||
}
|
||||
with open(wd + "sim_params.txt", "w") as f:
|
||||
f.write(f"{sim_params}\n")
|
||||
|
||||
logger.info("Setting up simulation parameters...")
|
||||
|
||||
common_params = {
|
||||
"Np": Np,
|
||||
"N": N,
|
||||
"L": L,
|
||||
"corner0": corner,
|
||||
"corner1": corner,
|
||||
"corner2": corner,
|
||||
"h": cosmo["h"],
|
||||
"Omega_m": cosmo["Omega_m"],
|
||||
"Omega_b": cosmo["Omega_b"],
|
||||
"n_s": cosmo["n_s"],
|
||||
"sigma8": cosmo["sigma8"],
|
||||
}
|
||||
|
||||
lpt_params = common_params.copy()
|
||||
lpt_params["method"] = "lpt"
|
||||
lpt_params["InputPowerSpectrum"] = input_power_file
|
||||
lpt_params["ICsMode"] = 1
|
||||
lpt_params["InputWhiteNoise"] = input_white_noise_file
|
||||
|
||||
fac_dyn_custom = DEFAULT_FAC_DYN_CUSTOM
|
||||
da_early = DEFAULT_DA_MAX_EARLY_CUSTOM
|
||||
fac_H_custom = DEFAULT_FAC_H_CUSTOM
|
||||
fac_bend = DEFAULT_FAC_BEND
|
||||
sub_bend1 = DEFAULT_SUB_BEND1_COLA
|
||||
sub_bend2 = DEFAULT_SUB_BEND2_COLA
|
||||
fac_p3m_fit = DEFAULT_FAC_P3M_FIT
|
||||
da_late = DEFAULT_DA_MAX_LATE_CUSTOM
|
||||
|
||||
limiters = {
|
||||
"fac_dyn_custom": fac_dyn_custom,
|
||||
"da_max_early_custom": da_early,
|
||||
"fac_H_custom": fac_H_custom,
|
||||
"fac_bend": fac_bend,
|
||||
"sub_bend1": sub_bend1,
|
||||
"sub_bend2": sub_bend2,
|
||||
"fac_p3m_fit": fac_p3m_fit,
|
||||
"da_max_late": da_late,
|
||||
}
|
||||
|
||||
if use_p3m_fit:
|
||||
if L == 2 * Np:
|
||||
p3m_fit_coeffs = P3M_FIT_COEFFS_DEFAULT_2NP
|
||||
elif L == Np:
|
||||
p3m_fit_coeffs = P3M_FIT_COEFFS_DEFAULT_NP
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Invalid box size L={L}. Must be either 2*Np={2*Np} or Np={Np} when using the fitted P3M limiter."
|
||||
)
|
||||
else:
|
||||
p3m_fit_coeffs = None
|
||||
|
||||
common_params_num = common_params.copy()
|
||||
common_params_num["ai"] = ai
|
||||
common_params_num["af"] = af
|
||||
common_params_num["RedshiftLPT"] = RedshiftLPT
|
||||
common_params_num["RedshiftFCs"] = RedshiftFCs
|
||||
common_params_num["Npm"] = Npm
|
||||
common_params_num["RunForceDiagnostic"] = False
|
||||
common_params_num["nBinsForceDiagnostic"] = 20
|
||||
common_params_num["nPairsForceDiagnostic"] = 3
|
||||
common_params_num["maxTrialsForceDiagnostic"] = int(1e8)
|
||||
common_params_num["OutputForceDiagnostic"] = simdir + "force_diagnostic.txt"
|
||||
common_params_num["TimeStepDistribution"] = 3 # Custom time step distribution
|
||||
common_params_num["cosmo_dict"] = cosmo
|
||||
common_params_num["fac_dyn_custom"] = limiters["fac_dyn_custom"]
|
||||
common_params_num["da_max_early_custom"] = limiters["da_max_early_custom"]
|
||||
common_params_num["fac_H_custom"] = limiters["fac_H_custom"]
|
||||
common_params_num["fac_bend"] = limiters["fac_bend"]
|
||||
common_params_num["sub_bend1"] = limiters["sub_bend1"]
|
||||
common_params_num["sub_bend2"] = limiters["sub_bend2"]
|
||||
common_params_num["fac_p3m_fit"] = limiters["fac_p3m_fit"]
|
||||
common_params_num["da_max_late_custom"] = limiters["da_max_late"]
|
||||
common_params_num["p3m_fit_coeffs"] = p3m_fit_coeffs
|
||||
common_params_num["use_p3m_fit"] = use_p3m_fit
|
||||
|
||||
p3m1_params = common_params_num.copy()
|
||||
p3m1_params["method"] = "p3m"
|
||||
p3m1_params["EvolutionMode"] = 7 # COLA+P3M
|
||||
p3m1_params["n_Tiles"] = n_Tiles
|
||||
p3m1_params["PrintOutputTimestepsLog"] = True
|
||||
p3m1_params["OutputTimestepsLog"] = simdir + "colap3m1_timestep_log.txt"
|
||||
p3m1_params["relax"] = scaling_p3m1
|
||||
if name_scaled:
|
||||
p3m1_params[name_scaled] /= scaling_p3m1
|
||||
|
||||
p3m2_params = common_params_num.copy()
|
||||
p3m2_params["method"] = "p3m"
|
||||
p3m2_params["EvolutionMode"] = 7 # COLA+P3M
|
||||
p3m2_params["n_Tiles"] = n_Tiles
|
||||
p3m2_params["PrintOutputTimestepsLog"] = True
|
||||
p3m2_params["OutputTimestepsLog"] = simdir + "colap3m2_timestep_log.txt"
|
||||
p3m2_params["relax"] = scaling_p3m2
|
||||
if name_scaled:
|
||||
p3m2_params[name_scaled] /= scaling_p3m2
|
||||
|
||||
p3m3_params = common_params_num.copy()
|
||||
p3m3_params["method"] = "p3m"
|
||||
p3m3_params["EvolutionMode"] = 7 # COLA+P3M
|
||||
p3m3_params["n_Tiles"] = n_Tiles
|
||||
p3m3_params["PrintOutputTimestepsLog"] = True
|
||||
p3m3_params["OutputTimestepsLog"] = simdir + "colap3m3_timestep_log.txt"
|
||||
p3m3_params["relax"] = scaling_p3m3
|
||||
if name_scaled:
|
||||
p3m3_params[name_scaled] /= scaling_p3m3
|
||||
|
||||
all_sim_params = [
|
||||
("p3m1", p3m1_params),
|
||||
("p3m2", p3m2_params),
|
||||
("p3m3", p3m3_params),
|
||||
]
|
||||
|
||||
logger.info("Setting up simulation parameters done.")
|
||||
|
||||
if not only_plot:
|
||||
logger.info("Generating simulation parameters...")
|
||||
INDENT()
|
||||
reset_plotting() # Default style for Simbelmynë
|
||||
generate_sim_params(lpt_params, ICs_path, wd, simdir, None, force)
|
||||
|
||||
for name, parameters in all_sim_params:
|
||||
logger.info(
|
||||
f"Generating parameters for {name.upper()} with relax = {parameters['relax']}..."
|
||||
)
|
||||
file_ext = f"{name}_relax{str(parameters['relax']).replace('.', '_')}"
|
||||
generate_sim_params(parameters, ICs_path, wd, simdir, file_ext, force)
|
||||
logger.info(f"Generating parameters for {name.upper()} done.")
|
||||
|
||||
UNINDENT()
|
||||
logger.info("Generating simulation parameters done.")
|
||||
|
||||
setup_plotting() # Reset plotting style for this project
|
||||
|
||||
logger.info("Generating white noise field...")
|
||||
generate_white_noise_Field(
|
||||
L=L,
|
||||
size=N,
|
||||
corner=corner,
|
||||
seedphase=BASELINE_SEEDPHASE,
|
||||
fname_whitenoise=input_white_noise_file,
|
||||
seedname_whitenoise=input_seed_phase_file,
|
||||
force_phase=force,
|
||||
)
|
||||
logger.info("Generating white noise field done.")
|
||||
|
||||
# If cosmo["WhichSpectrum"] == "class", then classy is required.
|
||||
if not isfile(input_power_file) or force:
|
||||
logger.info("Generating input power spectrum...")
|
||||
Pk = PowerSpectrum(L, L, L, N, N, N, cosmo_small_to_full_dict(cosmo))
|
||||
Pk.write(input_power_file)
|
||||
logger.info("Generating input power spectrum done.")
|
||||
|
||||
logger.info("Generating Fourier grid...")
|
||||
# k grid used to compute the final overdensity power spectrum
|
||||
Pinit = 100
|
||||
trim_threshold = 100 # Merge bins until this minimum number of modes per bin is reached
|
||||
log_kmin = np.log10(2 * np.pi / (np.sqrt(3) * L)) # Minimum non-zero k in h/Mpc
|
||||
if go_beyond_Nyquist_ss:
|
||||
k_max_ss = get_k_max(L, N)
|
||||
else:
|
||||
k_max_ss = get_k_max(L, N) / np.sqrt(3) # 1D Nyquist frequency
|
||||
Pbins_left_bnds = np.logspace(log_kmin, np.log10(k_max_ss), Pinit + 1, dtype=np.float32)
|
||||
Pbins_left_bnds = Pbins_left_bnds[:-1]
|
||||
Gk = FourierGrid(
|
||||
L,
|
||||
L,
|
||||
L,
|
||||
N,
|
||||
N,
|
||||
N,
|
||||
k_modes=Pbins_left_bnds,
|
||||
kmax=k_max_ss,
|
||||
trim_bins=True,
|
||||
trim_threshold=trim_threshold,
|
||||
)
|
||||
Gk.write(input_ss_file)
|
||||
logger.info("Generating Fourier grid done.")
|
||||
|
||||
logger.info("Running simulations...")
|
||||
INDENT()
|
||||
logger.info("Running LPT simulation...")
|
||||
run_simulation("lpt", lpt_params, wd, logdir)
|
||||
logger.info("Running LPT simulation done.")
|
||||
|
||||
for name, parameters in all_sim_params:
|
||||
logger.info(f"Running {name.upper()} simulation...")
|
||||
run_simulation(name, parameters, wd, logdir)
|
||||
logger.info(f"Running {name.upper()} simulation done.")
|
||||
UNINDENT()
|
||||
logger.info("All simulations done.")
|
||||
|
||||
logger.info("Plotting time step diagnostics...")
|
||||
INDENT()
|
||||
|
||||
nsteps_dict = {}
|
||||
for name, parameters in all_sim_params:
|
||||
TimeStepDistribution = parameters["TimeStepDistribution"]
|
||||
file_ext = f"{name}_relax{str(parameters['relax']).replace('.', '_')}"
|
||||
method = parameters["method"]
|
||||
TSpath = wd + file_ext + f"_ts_{method}.h5" if file_ext else wd + f"ts_{method}.h5"
|
||||
if TimeStepDistribution in [0, 1, 2]:
|
||||
TS = StandardTimeStepping.read(TSpath)
|
||||
aiDrift = TS.aiDrift
|
||||
nsteps = TS.nsteps
|
||||
elif TimeStepDistribution == 3:
|
||||
TS = P3MTimeStepping.read(TSpath)
|
||||
aiDrift = TS.aiDrift
|
||||
nsteps = TS.nsteps
|
||||
else:
|
||||
raise ValueError(f"Invalid TimeStepDistribution: {TimeStepDistribution}")
|
||||
nsteps_dict[name] = nsteps
|
||||
if name.startswith("p3m") or name.startswith("spm"):
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=parameters["OutputTimestepsLog"],
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=TimeStepDistribution,
|
||||
nsteps=nsteps,
|
||||
ymin=3e-3,
|
||||
exact=False,
|
||||
save_path=outdir + f"time_step_diagnostics_{name}.pdf",
|
||||
)
|
||||
plot_custom_timestepping_diagnostics(
|
||||
log_path=parameters["OutputTimestepsLog"],
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=TimeStepDistribution,
|
||||
nsteps=nsteps,
|
||||
ymin=3e-3,
|
||||
fac_hubble=fac_H_custom,
|
||||
fac_bend=fac_bend,
|
||||
plot_bend=not use_p3m_fit,
|
||||
da_max_early=da_early,
|
||||
da_max_late=DEFAULT_DA_MAX_LATE_CUSTOM,
|
||||
save_path=outdir + f"time_step_diagnostics_custom_{name}.pdf",
|
||||
)
|
||||
|
||||
UNINDENT()
|
||||
logger.info("Plotting time step diagnostics done.")
|
||||
|
||||
logger.info("Computing and plotting power spectra...")
|
||||
INDENT()
|
||||
|
||||
from pysbmy.fft import read_FourierGrid
|
||||
from pysbmy.field import read_field
|
||||
from pysbmy.correlations import get_autocorrelation
|
||||
|
||||
logger.info("Loading Fourier grid...")
|
||||
G = read_FourierGrid(input_ss_file)
|
||||
logger.info("Loading Fourier grid done.")
|
||||
|
||||
k = G.k_modes
|
||||
AliasingCorr = False
|
||||
slice_ijk = (N // 2, slice(None), slice(None)) # for plot_fields
|
||||
|
||||
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"p3m1_relax{str(p3m1_params['relax']).replace('.', '_')}_final_density_p3m.h5")
|
||||
if plot_fields:
|
||||
delta_p3m1 = DELTA.data[slice_ijk]
|
||||
Pk_P3M1, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"p3m2_relax{str(p3m2_params['relax']).replace('.', '_')}_final_density_p3m.h5")
|
||||
if plot_fields:
|
||||
delta_p3m2 = DELTA.data[slice_ijk]
|
||||
Pk_P3M2, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"p3m3_relax{str(p3m3_params['relax']).replace('.', '_')}_final_density_p3m.h5")
|
||||
if plot_fields:
|
||||
delta_p3m3 = DELTA.data[slice_ijk]
|
||||
Pk_P3M3, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
logger.info("Computing power spectra done.")
|
||||
|
||||
ref = "P3M1"
|
||||
|
||||
scaling_reference = scaling_p3m1
|
||||
Pk_ref = Pk_P3M1
|
||||
label_ref = f"P3M, scaling={scaling_reference}, ns={nsteps_dict['p3m1']}"
|
||||
|
||||
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)
|
||||
fields_to_plot = [
|
||||
("P3M1", Pk_P3M1),
|
||||
("P3M2", Pk_P3M2),
|
||||
("P3M3", Pk_P3M3),
|
||||
]
|
||||
# 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}, scaling={eval(f'scaling_{field_name.lower()}')}, ns={nsteps_dict[field_name.lower()]}"
|
||||
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.axhline(1 - medium_grey_bnd, color="grey", linestyle="-", linewidth=1)
|
||||
ax.axhline(1 + medium_grey_bnd, color="grey", linestyle="-", linewidth=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
|
||||
if nyquist <= xlim_max:
|
||||
line1 = ax.axvline(
|
||||
x=nyquist, color="black", linestyle="--", lw=2, label="Nyquist (density grid)", zorder=0
|
||||
)
|
||||
if nyquist_PM <= xlim_max:
|
||||
line2 = ax.axvline(
|
||||
x=nyquist_PM, color="black", linestyle="-", lw=1, label="Nyquist (PM grid)", zorder=0
|
||||
)
|
||||
if xs_inv <= xlim_max:
|
||||
line3 = ax.axvline(
|
||||
x=xs_inv, color="gray", linestyle="-.", lw=2, label=r"Split wavenumber $x_s$", zorder=0
|
||||
)
|
||||
if xr_inv <= xlim_max:
|
||||
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()
|
||||
plt.legend(
|
||||
handles,
|
||||
labels,
|
||||
loc="upper center",
|
||||
ncol=2,
|
||||
bbox_to_anchor=(0.5, -0.2),
|
||||
fontsize=fs,
|
||||
frameon=False,
|
||||
)
|
||||
fig.savefig(
|
||||
outdir + "p3m_power_spectra.png",
|
||||
bbox_inches="tight",
|
||||
dpi=300,
|
||||
transparent=True,
|
||||
)
|
||||
fig.savefig(
|
||||
outdir + "p3m_power_spectra.pdf",
|
||||
bbox_inches="tight",
|
||||
dpi=300,
|
||||
)
|
||||
plt.close(fig)
|
||||
UNINDENT()
|
||||
logger.info(
|
||||
f"P3M power spectra plotted and saved to {outdir}p3m_power_spectra.png and power_spectra.pdf"
|
||||
)
|
||||
logger.info("Pplotting P3M power spectra done.")
|
||||
UNINDENT()
|
||||
|
||||
logger.info("Computing and plotting power spectra done.")
|
||||
|
||||
if plot_fields:
|
||||
logger.info("Reading lpt field...")
|
||||
delta_lpt = read_field(simdir + "lpt_density.h5").data[slice_ijk]
|
||||
logger.info("Reading lpt field done.")
|
||||
|
||||
diff_p3m2_p3m1 = delta_p3m2 - delta_p3m1
|
||||
diff_p3m3_p3m1 = delta_p3m3 - delta_p3m1
|
||||
|
||||
fields = [
|
||||
"p3m1",
|
||||
"p3m2",
|
||||
"p3m3",
|
||||
"diff_p3m2_p3m1",
|
||||
"diff_p3m3_p3m1",
|
||||
] # fields to plot
|
||||
ncols = 6 # Max panels per row
|
||||
|
||||
figname = "_".join(fields)
|
||||
slices_dict = {
|
||||
"lpt": delta_lpt,
|
||||
"p3m1": delta_p3m1,
|
||||
"p3m2": delta_p3m2,
|
||||
"p3m3": delta_p3m3,
|
||||
"diff_p3m2_p3m1": diff_p3m2_p3m1,
|
||||
"diff_p3m3_p3m1": diff_p3m3_p3m1,
|
||||
}
|
||||
titles_dict = {
|
||||
"lpt": "LPT",
|
||||
"p3m1": f"P3M1 relax={scaling_p3m1}",
|
||||
"p3m2": f"P3M2 relax={scaling_p3m2}",
|
||||
"p3m3": f"P3M3 relax={scaling_p3m3}",
|
||||
"diff_p3m2_p3m1": r"$\delta_{\rm P3M2}-\delta_{\rm P3M1}$",
|
||||
"diff_p3m3_p3m1": r"$\delta_{\rm P3M3}-\delta_{\rm P3M1}$",
|
||||
"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"):
|
||||
vmin = -np.log(1 + np.abs(np.min(data)))
|
||||
vmax = np.log10(1 + np.abs(np.max(data)))
|
||||
if vmax > 0 > vmin:
|
||||
norm = TwoSlopeNorm(vmin=vmin, vcenter=0, vmax=vmax)
|
||||
im = ax.imshow(
|
||||
np.sign(data) * np.log(1 + np.abs(data)), cmap="RdBu_r", norm=norm
|
||||
)
|
||||
else:
|
||||
im = ax.imshow(np.log10(1 + np.abs(data)), cmap="RdBu_r")
|
||||
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("All plots completed successfully.")
|
||||
|
||||
except OSError as e:
|
||||
logger.error("Directory or file access error: %s", str(e))
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.critical("An unexpected error occurred: %s", str(e))
|
||||
raise RuntimeError("Failed.") from e
|
||||
finally:
|
||||
UNINDENT()
|
||||
logger.info("Running convergence tests done.")
|
907
src/wip3m/convergence_custom_ts_expl_parser.py
Normal file
907
src/wip3m/convergence_custom_ts_expl_parser.py
Normal file
|
@ -0,0 +1,907 @@
|
|||
#!/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"
|
||||
|
||||
"""
|
||||
Perform time step convergence tests with custom time step distributions
|
||||
"""
|
||||
|
||||
# pyright: reportWildcardImportFromLibrary=false
|
||||
from os.path import isfile
|
||||
from pathlib import Path
|
||||
import numpy as np
|
||||
|
||||
from pysbmy.power import PowerSpectrum
|
||||
from pysbmy.fft import FourierGrid
|
||||
from pysbmy.timestepping import StandardTimeStepping, P3MTimeStepping
|
||||
|
||||
from wip3m import *
|
||||
from wip3m.tools import none_bool_str, 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.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="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("--z_i", type=float, default=199.0, help="Initial redshift.")
|
||||
parser.add_argument("--z_f", type=float, default=19.0, help="Final redshift.")
|
||||
parser.add_argument(
|
||||
"--plot_fields",
|
||||
type=none_bool_str,
|
||||
default=False,
|
||||
help="Whether to plot the fields or not.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lpt",
|
||||
type=none_bool_str,
|
||||
default=False,
|
||||
help="Whether to compute and plot the LPT power spectrum.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--scale_limiter",
|
||||
type=none_bool_str,
|
||||
default=False,
|
||||
help="Which limiter should be scaled by the scaling arguments? ",
|
||||
)
|
||||
|
||||
# Timestep settings per method
|
||||
for scheme in ["pmref", "pm1", "pm2", "spm", "p3m1", "p3m2", "p3m3"]:
|
||||
parser.add_argument(
|
||||
f"--scaling_{scheme}",
|
||||
type=float,
|
||||
default={
|
||||
"pmref": 1.,
|
||||
"pm1": 0.95,
|
||||
"pm2": 0.9,
|
||||
"spm": 1.,
|
||||
"p3m1": 1.,
|
||||
"p3m2": 0.95,
|
||||
"p3m3": 0.9,
|
||||
}[scheme],
|
||||
help=f"Relax the step limiter by this factor for {scheme.upper()}.",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
go_beyond_Nyquist_ss = True # for the summary statistics
|
||||
force = False
|
||||
force_hard = False
|
||||
|
||||
run_id = args.run_id
|
||||
L = args.L
|
||||
N = args.N
|
||||
Np = args.Np
|
||||
Npm = args.Npm
|
||||
n_Tiles = args.n_Tiles
|
||||
scaling_pmref = args.scaling_pmref
|
||||
scaling_pm1 = args.scaling_pm1
|
||||
scaling_pm2 = args.scaling_pm2
|
||||
scaling_spm = args.scaling_spm
|
||||
scaling_p3m1 = args.scaling_p3m1
|
||||
scaling_p3m2 = args.scaling_p3m2
|
||||
scaling_p3m3 = args.scaling_p3m3
|
||||
name_scaled = args.scale_limiter
|
||||
if name_scaled is not False and name_scaled not in [
|
||||
"fac_dyn_custom",
|
||||
"da_max_early_custom",
|
||||
"fac_H_custom",
|
||||
"fac_bend",
|
||||
"sub_bend1",
|
||||
"sub_bend2",
|
||||
"da_max_late",
|
||||
]:
|
||||
raise ValueError(
|
||||
f"Invalid name_scaled: {name_scaled}. Must be one of the limiter names or False."
|
||||
)
|
||||
RedshiftLPT = args.z_i
|
||||
RedshiftFCs = args.z_f
|
||||
logger.info("Running convergence tests...")
|
||||
logger.info(f"Run ID: {run_id}")
|
||||
logger.info(f"Box size: {L} Mpc/h")
|
||||
logger.info(f"Density grid size: {N}^3")
|
||||
logger.info(f"Particles per dimension: {Np}^3")
|
||||
logger.info(f"PM grid size: {Npm}^3")
|
||||
logger.info(f"Number of tiles: {n_Tiles}^3")
|
||||
logger.info(f"Limiter to scale: {name_scaled if name_scaled else 'None'}")
|
||||
if name_scaled:
|
||||
logger.info(f"Limiter rescaling for PMREF: {scaling_pmref}")
|
||||
logger.info(f"Limiter rescaling for PM1: {scaling_pm1}")
|
||||
logger.info(f"Limiter rescaling for PM2: {scaling_pm2}")
|
||||
logger.info(f"Limiter rescaling for sPM: {scaling_spm}")
|
||||
logger.info(f"Limiter rescaling for P3M1: {scaling_p3m1}")
|
||||
logger.info(f"Limiter rescaling for P3M2: {scaling_p3m2}")
|
||||
logger.info(f"Limiter rescaling for P3M3: {scaling_p3m3}")
|
||||
INDENT()
|
||||
|
||||
lpt = args.lpt
|
||||
plot_fields = args.plot_fields
|
||||
|
||||
corner = -L / 2.0
|
||||
ai = z2a(RedshiftLPT)
|
||||
af = z2a(RedshiftFCs)
|
||||
k_max = get_k_max(L, N) # k_max in h/Mpc
|
||||
cosmo = params_planck_kmax_missing.copy()
|
||||
cosmo["k_max"] = k_max
|
||||
|
||||
wd = workdir + run_id + "/"
|
||||
simdir = output_path + run_id + "/"
|
||||
logdir = simdir + "logs/"
|
||||
outdir = simdir + "plots/"
|
||||
if force_hard:
|
||||
import shutil
|
||||
|
||||
if Path(simdir).exists():
|
||||
shutil.rmtree(simdir)
|
||||
if Path(wd).exists():
|
||||
shutil.rmtree(wd)
|
||||
Path(wd).mkdir(parents=True, exist_ok=True)
|
||||
Path(logdir).mkdir(parents=True, exist_ok=True)
|
||||
Path(outdir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
input_white_noise_file = simdir + "input_white_noise.h5"
|
||||
input_seed_phase_file = simdir + "seed"
|
||||
ICs_path = simdir + "initial_density.h5"
|
||||
simpath = simdir
|
||||
|
||||
# Path to the input matter power spectrum (generated later)
|
||||
input_power_file = simdir + "input_power.h5"
|
||||
|
||||
sim_params = {
|
||||
"L": L,
|
||||
"N": N,
|
||||
"Np": Np,
|
||||
"Npm": Npm,
|
||||
"n_Tiles": n_Tiles,
|
||||
"RedshiftLPT": RedshiftLPT,
|
||||
"RedshiftFCs": RedshiftFCs,
|
||||
"name_scaled": name_scaled,
|
||||
"scaling_pmref": scaling_pmref,
|
||||
"scaling_pm1": scaling_pm1,
|
||||
"scaling_pm2": scaling_pm2,
|
||||
"scaling_spm": scaling_spm,
|
||||
"scaling_p3m1": scaling_p3m1,
|
||||
"scaling_p3m2": scaling_p3m2,
|
||||
"scaling_p3m3": scaling_p3m3,
|
||||
}
|
||||
with open(wd + "sim_params.txt", "w") as f:
|
||||
f.write(f"{sim_params}\n")
|
||||
|
||||
logger.info("Setting up simulation parameters...")
|
||||
|
||||
common_params = {
|
||||
"Np": Np,
|
||||
"N": N,
|
||||
"L": L,
|
||||
"corner0": corner,
|
||||
"corner1": corner,
|
||||
"corner2": corner,
|
||||
"h": cosmo["h"],
|
||||
"Omega_m": cosmo["Omega_m"],
|
||||
"Omega_b": cosmo["Omega_b"],
|
||||
"n_s": cosmo["n_s"],
|
||||
"sigma8": cosmo["sigma8"],
|
||||
}
|
||||
|
||||
lpt_params = common_params.copy()
|
||||
lpt_params["method"] = "lpt"
|
||||
lpt_params["InputPowerSpectrum"] = input_power_file
|
||||
lpt_params["ICsMode"] = 1
|
||||
lpt_params["InputWhiteNoise"] = input_white_noise_file
|
||||
|
||||
fac_dyn_custom = DEFAULT_FAC_DYN_CUSTOM
|
||||
da_early = DEFAULT_DA_MAX_EARLY_CUSTOM
|
||||
fac_H_custom = DEFAULT_FAC_H_CUSTOM
|
||||
fac_bend = DEFAULT_FAC_BEND
|
||||
sub_bend1 = DEFAULT_SUB_BEND1
|
||||
sub_bend2 = DEFAULT_SUB_BEND2
|
||||
da_late = DEFAULT_DA_MAX_LATE_CUSTOM
|
||||
|
||||
limiters = {
|
||||
"fac_dyn_custom": fac_dyn_custom,
|
||||
"da_max_early_custom": da_early,
|
||||
"fac_H_custom": fac_H_custom,
|
||||
"fac_bend": fac_bend,
|
||||
"sub_bend1": sub_bend1,
|
||||
"sub_bend2": sub_bend2,
|
||||
"da_max_late": da_late,
|
||||
}
|
||||
|
||||
common_params_num = common_params.copy()
|
||||
common_params_num["ai"] = ai
|
||||
common_params_num["af"] = af
|
||||
common_params_num["RedshiftLPT"] = RedshiftLPT
|
||||
common_params_num["RedshiftFCs"] = RedshiftFCs
|
||||
common_params_num["Npm"] = Npm
|
||||
common_params_num["RunForceDiagnostic"] = False
|
||||
common_params_num["nBinsForceDiagnostic"] = 20
|
||||
common_params_num["nPairsForceDiagnostic"] = 3
|
||||
common_params_num["maxTrialsForceDiagnostic"] = int(1e8)
|
||||
common_params_num["OutputForceDiagnostic"] = simdir + "force_diagnostic.txt"
|
||||
common_params_num["TimeStepDistribution"] = 3 # Custom time step distribution
|
||||
common_params_num["cosmo_dict"] = cosmo
|
||||
common_params_num["fac_dyn_custom"] = limiters["fac_dyn_custom"]
|
||||
common_params_num["da_max_early_custom"] = limiters["da_max_early_custom"]
|
||||
common_params_num["fac_H_custom"] = limiters["fac_H_custom"]
|
||||
common_params_num["fac_bend"] = limiters["fac_bend"]
|
||||
common_params_num["sub_bend1"] = limiters["sub_bend1"]
|
||||
common_params_num["sub_bend2"] = limiters["sub_bend2"]
|
||||
common_params_num["da_max_late_custom"] = limiters["da_max_late"]
|
||||
|
||||
pmref_params = common_params_num.copy()
|
||||
pmref_params["method"] = "pm"
|
||||
pmref_params["relax"] = scaling_pmref # plot unscaled values during the simulation
|
||||
if name_scaled:
|
||||
pmref_params[name_scaled] /= scaling_pmref
|
||||
|
||||
pm1_params = common_params_num.copy()
|
||||
pm1_params["method"] = "pm"
|
||||
pm1_params["relax"] = scaling_pm1
|
||||
if name_scaled:
|
||||
pm1_params[name_scaled] /= scaling_pm1
|
||||
|
||||
pm2_params = common_params_num.copy()
|
||||
pm2_params["method"] = "pm"
|
||||
pm2_params["relax"] = scaling_pm2
|
||||
if name_scaled:
|
||||
pm2_params[name_scaled] /= scaling_pm2
|
||||
|
||||
spm_params = common_params_num.copy()
|
||||
spm_params["method"] = "spm"
|
||||
spm_params["EvolutionMode"] = 5
|
||||
spm_params["n_Tiles"] = n_Tiles
|
||||
spm_params["PrintOutputTimestepsLog"] = True
|
||||
spm_params["OutputTimestepsLog"] = simdir + "spm_timestep_log.txt"
|
||||
spm_params["relax"] = scaling_spm
|
||||
if name_scaled:
|
||||
spm_params[name_scaled] /= scaling_spm
|
||||
|
||||
p3m1_params = common_params_num.copy()
|
||||
p3m1_params["method"] = "p3m"
|
||||
p3m1_params["EvolutionMode"] = 4
|
||||
p3m1_params["n_Tiles"] = n_Tiles
|
||||
p3m1_params["PrintOutputTimestepsLog"] = True
|
||||
p3m1_params["OutputTimestepsLog"] = simdir + "p3m1_timestep_log.txt"
|
||||
p3m1_params["relax"] = scaling_p3m1
|
||||
if name_scaled:
|
||||
p3m1_params[name_scaled] /= scaling_p3m1
|
||||
|
||||
p3m2_params = common_params_num.copy()
|
||||
p3m2_params["method"] = "p3m"
|
||||
p3m2_params["EvolutionMode"] = 4
|
||||
p3m2_params["n_Tiles"] = n_Tiles
|
||||
p3m2_params["PrintOutputTimestepsLog"] = True
|
||||
p3m2_params["OutputTimestepsLog"] = simdir + "p3m2_timestep_log.txt"
|
||||
p3m2_params["relax"] = scaling_p3m2
|
||||
if name_scaled:
|
||||
p3m2_params[name_scaled] /= scaling_p3m2
|
||||
|
||||
p3m3_params = common_params_num.copy()
|
||||
p3m3_params["method"] = "p3m"
|
||||
p3m3_params["EvolutionMode"] = 4
|
||||
p3m3_params["n_Tiles"] = n_Tiles
|
||||
p3m3_params["PrintOutputTimestepsLog"] = True
|
||||
p3m3_params["OutputTimestepsLog"] = simdir + "p3m3_timestep_log.txt"
|
||||
p3m3_params["relax"] = scaling_p3m3
|
||||
if name_scaled:
|
||||
p3m3_params[name_scaled] /= scaling_p3m3
|
||||
|
||||
logger.info("Setting up simulation parameters done.")
|
||||
|
||||
logger.info("Generating simulation parameters...")
|
||||
INDENT()
|
||||
reset_plotting() # Default style for Simbelmynë
|
||||
generate_sim_params(lpt_params, ICs_path, wd, simdir, None, force)
|
||||
|
||||
all_sim_params = [
|
||||
("pmref", pmref_params),
|
||||
("pm1", pm1_params),
|
||||
("pm2", pm2_params),
|
||||
("spm", spm_params),
|
||||
("p3m1", p3m1_params),
|
||||
("p3m2", p3m2_params),
|
||||
("p3m3", p3m3_params),
|
||||
]
|
||||
for name, parameters in all_sim_params:
|
||||
logger.info(
|
||||
f"Generating parameters for {name.upper()} with relax = {parameters['relax']}..."
|
||||
)
|
||||
file_ext = f"{name}_relax{str(parameters['relax']).replace('.', '_')}"
|
||||
generate_sim_params(parameters, ICs_path, wd, simdir, file_ext, force)
|
||||
logger.info(f"Generating parameters for {name.upper()} done.")
|
||||
|
||||
UNINDENT()
|
||||
logger.info("Generating simulation parameters done.")
|
||||
|
||||
setup_plotting() # Reset plotting style for this project
|
||||
|
||||
logger.info("Generating white noise field...")
|
||||
generate_white_noise_Field(
|
||||
L=L,
|
||||
size=N,
|
||||
corner=corner,
|
||||
seedphase=BASELINE_SEEDPHASE,
|
||||
fname_whitenoise=input_white_noise_file,
|
||||
seedname_whitenoise=input_seed_phase_file,
|
||||
force_phase=force,
|
||||
)
|
||||
logger.info("Generating white noise field done.")
|
||||
|
||||
# If cosmo["WhichSpectrum"] == "class", then classy is required.
|
||||
if not isfile(input_power_file) or force:
|
||||
logger.info("Generating input power spectrum...")
|
||||
Pk = PowerSpectrum(L, L, L, N, N, N, cosmo_small_to_full_dict(cosmo))
|
||||
Pk.write(input_power_file)
|
||||
logger.info("Generating input power spectrum done.")
|
||||
|
||||
logger.info("Generating Fourier grid...")
|
||||
# k grid used to compute the final overdensity power spectrum
|
||||
Pinit = 100
|
||||
trim_threshold = 100 # Merge bins until this minimum number of modes per bin is reached
|
||||
log_kmin = np.log10(2 * np.pi / (np.sqrt(3) * L)) # Minimum non-zero k in h/Mpc
|
||||
if go_beyond_Nyquist_ss:
|
||||
k_max_ss = get_k_max(L, N)
|
||||
else:
|
||||
k_max_ss = get_k_max(L, N) / np.sqrt(3) # 1D Nyquist frequency
|
||||
Pbins_left_bnds = np.logspace(log_kmin, np.log10(k_max_ss), Pinit + 1, dtype=np.float32)
|
||||
Pbins_left_bnds = Pbins_left_bnds[:-1]
|
||||
input_ss_file = simdir + "input_ss_k_grid.h5"
|
||||
Gk = FourierGrid(
|
||||
L,
|
||||
L,
|
||||
L,
|
||||
N,
|
||||
N,
|
||||
N,
|
||||
k_modes=Pbins_left_bnds,
|
||||
kmax=k_max_ss,
|
||||
trim_bins=True,
|
||||
trim_threshold=trim_threshold,
|
||||
)
|
||||
Gk.write(input_ss_file)
|
||||
logger.info("Generating Fourier grid done.")
|
||||
|
||||
logger.info("Running simulations...")
|
||||
INDENT()
|
||||
logger.info("Running LPT simulation...")
|
||||
run_simulation("lpt", lpt_params, wd, logdir)
|
||||
logger.info("Running LPT simulation done.")
|
||||
|
||||
for name, parameters in all_sim_params:
|
||||
logger.info(f"Running {name.upper()} simulation...")
|
||||
run_simulation(name, parameters, wd, logdir)
|
||||
logger.info(f"Running {name.upper()} simulation done.")
|
||||
UNINDENT()
|
||||
logger.info("All simulations done.")
|
||||
|
||||
logger.info("Plotting time step diagnostics...")
|
||||
INDENT()
|
||||
|
||||
nsteps_dict = {}
|
||||
for name, parameters in all_sim_params:
|
||||
TimeStepDistribution = parameters["TimeStepDistribution"]
|
||||
file_ext = f"{name}_relax{str(parameters['relax']).replace('.', '_')}"
|
||||
method = parameters["method"]
|
||||
TSpath = wd + file_ext + f"_ts_{method}.h5" if file_ext else wd + f"ts_{method}.h5"
|
||||
if TimeStepDistribution in [0, 1, 2]:
|
||||
TS = StandardTimeStepping.read(TSpath)
|
||||
aiDrift = TS.aiDrift
|
||||
nsteps = TS.nsteps
|
||||
elif TimeStepDistribution == 3:
|
||||
TS = P3MTimeStepping.read(TSpath)
|
||||
aiDrift = TS.aiDrift
|
||||
nsteps = TS.nsteps
|
||||
else:
|
||||
raise ValueError(f"Invalid TimeStepDistribution: {TimeStepDistribution}")
|
||||
nsteps_dict[name] = nsteps
|
||||
if name.startswith("p3m") or name.startswith("spm"):
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=parameters["OutputTimestepsLog"],
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=TimeStepDistribution,
|
||||
nsteps=nsteps,
|
||||
exact=False,
|
||||
save_path=outdir + f"time_step_diagnostics_{name}.pdf",
|
||||
)
|
||||
plot_custom_timestepping_diagnostics(
|
||||
log_path=parameters["OutputTimestepsLog"],
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=TimeStepDistribution,
|
||||
nsteps=nsteps,
|
||||
ymin=8e-3,
|
||||
fac_hubble=fac_H_custom,
|
||||
fac_bend=fac_bend,
|
||||
da_max_early=da_early,
|
||||
save_path=outdir + f"time_step_diagnostics_custom_{name}.pdf",
|
||||
)
|
||||
|
||||
UNINDENT()
|
||||
logger.info("Plotting time step diagnostics done.")
|
||||
|
||||
logger.info("Computing and plotting power spectra...")
|
||||
INDENT()
|
||||
|
||||
from pysbmy.fft import read_FourierGrid
|
||||
from pysbmy.field import read_field
|
||||
from pysbmy.correlations import get_autocorrelation
|
||||
|
||||
logger.info("Loading Fourier grid...")
|
||||
G = read_FourierGrid(input_ss_file)
|
||||
logger.info("Loading Fourier grid done.")
|
||||
|
||||
k = G.k_modes
|
||||
AliasingCorr = False
|
||||
slice_ijk = (N // 2, slice(None), slice(None)) # for plot_fields
|
||||
|
||||
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_relax{str(pmref_params['relax']).replace('.', '_')}_final_density_pm.h5")
|
||||
if plot_fields:
|
||||
delta_pmref = DELTA.data[slice_ijk]
|
||||
Pk_PMref, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"pm1_relax{str(pm1_params['relax']).replace('.', '_')}_final_density_pm.h5")
|
||||
if plot_fields:
|
||||
delta_pm1 = DELTA.data[slice_ijk]
|
||||
Pk_PM1, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"pm2_relax{str(pm2_params['relax']).replace('.', '_')}_final_density_pm.h5")
|
||||
if plot_fields:
|
||||
delta_pm2 = DELTA.data[slice_ijk]
|
||||
Pk_PM2, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"spm_relax{str(spm_params['relax']).replace('.', '_')}_final_density_spm.h5")
|
||||
if plot_fields:
|
||||
delta_spm = DELTA.data[slice_ijk]
|
||||
Pk_sPM, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"p3m1_relax{str(p3m1_params['relax']).replace('.', '_')}_final_density_p3m.h5")
|
||||
if plot_fields:
|
||||
delta_p3m1 = DELTA.data[slice_ijk]
|
||||
Pk_P3M1, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"p3m2_relax{str(p3m2_params['relax']).replace('.', '_')}_final_density_p3m.h5")
|
||||
if plot_fields:
|
||||
delta_p3m2 = DELTA.data[slice_ijk]
|
||||
Pk_P3M2, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
DELTA = read_field(simdir + f"p3m3_relax{str(p3m3_params['relax']).replace('.', '_')}_final_density_p3m.h5")
|
||||
if plot_fields:
|
||||
delta_p3m3 = DELTA.data[slice_ijk]
|
||||
Pk_P3M3, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
logger.info("Computing power spectra done.")
|
||||
|
||||
ref = "P3M1"
|
||||
|
||||
scaling_reference = scaling_p3m1
|
||||
Pk_ref = Pk_P3M1
|
||||
label_ref = f"P3M, scaling={scaling_reference}, ns={nsteps_dict['p3m1']}"
|
||||
|
||||
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),
|
||||
]
|
||||
# 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}, scaling={eval(f'scaling_{field_name.lower()}')}, ns={nsteps_dict[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)
|
||||
ax.axhspan(1 - dark_grey_bnd, 1 + dark_grey_bnd, color="grey", alpha=0.3)
|
||||
ax.axhline(1 - medium_grey_bnd, color="grey", linestyle="-", linewidth=1)
|
||||
ax.axhline(1 + medium_grey_bnd, color="grey", linestyle="-", linewidth=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
|
||||
if nyquist <= xlim_max:
|
||||
line1 = ax.axvline(
|
||||
x=nyquist, color="black", linestyle="--", lw=2, label="Nyquist (density grid)", zorder=0
|
||||
)
|
||||
if nyquist_PM <= xlim_max:
|
||||
line2 = ax.axvline(
|
||||
x=nyquist_PM, color="black", linestyle="-", lw=1, label="Nyquist (PM grid)", zorder=0
|
||||
)
|
||||
if xs_inv <= xlim_max:
|
||||
line3 = ax.axvline(
|
||||
x=xs_inv, color="gray", linestyle="-.", lw=2, label=r"Split wavenumber $x_s$", zorder=0
|
||||
)
|
||||
if xr_inv <= xlim_max:
|
||||
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("Plotting power spectra done.")
|
||||
|
||||
logger.info(f"Plotting P3M 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)
|
||||
fields_to_plot = [
|
||||
("P3M1", Pk_P3M1),
|
||||
("P3M2", Pk_P3M2),
|
||||
("P3M3", Pk_P3M3),
|
||||
]
|
||||
# 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}, scaling={eval(f'scaling_{field_name.lower()}')}, ns={nsteps_dict[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.axhline(1 - medium_grey_bnd, color="grey", linestyle="-", linewidth=1)
|
||||
ax.axhline(1 + medium_grey_bnd, color="grey", linestyle="-", linewidth=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
|
||||
if nyquist <= xlim_max:
|
||||
line1 = ax.axvline(
|
||||
x=nyquist, color="black", linestyle="--", lw=2, label="Nyquist (density grid)", zorder=0
|
||||
)
|
||||
if nyquist_PM <= xlim_max:
|
||||
line2 = ax.axvline(
|
||||
x=nyquist_PM, color="black", linestyle="-", lw=1, label="Nyquist (PM grid)", zorder=0
|
||||
)
|
||||
if xs_inv <= xlim_max:
|
||||
line3 = ax.axvline(
|
||||
x=xs_inv, color="gray", linestyle="-.", lw=2, label=r"Split wavenumber $x_s$", zorder=0
|
||||
)
|
||||
if xr_inv <= xlim_max:
|
||||
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()
|
||||
plt.legend(
|
||||
handles,
|
||||
labels,
|
||||
loc="upper center",
|
||||
ncol=2,
|
||||
bbox_to_anchor=(0.5, -0.2),
|
||||
fontsize=fs,
|
||||
frameon=False,
|
||||
)
|
||||
fig.savefig(
|
||||
outdir + "p3m_power_spectra.png",
|
||||
bbox_inches="tight",
|
||||
dpi=300,
|
||||
transparent=True,
|
||||
)
|
||||
fig.savefig(
|
||||
outdir + "p3m_power_spectra.pdf",
|
||||
bbox_inches="tight",
|
||||
dpi=300,
|
||||
)
|
||||
plt.close(fig)
|
||||
UNINDENT()
|
||||
logger.info(
|
||||
f"P3M power spectra plotted and saved to {outdir}p3m_power_spectra.png and power_spectra.pdf"
|
||||
)
|
||||
logger.info("Pplotting P3M power spectra done.")
|
||||
UNINDENT()
|
||||
|
||||
logger.info("Computing and plotting power spectra done.")
|
||||
|
||||
if plot_fields:
|
||||
logger.info("Reading lpt field...")
|
||||
delta_lpt = read_field(simdir + "lpt_density.h5").data[slice_ijk]
|
||||
logger.info("Reading lpt field done.")
|
||||
|
||||
diff_pm1_pmref = delta_pm1 - delta_pmref
|
||||
diff_pm2_pmref = delta_pm2 - delta_pmref
|
||||
diff_p3m1_pmref = delta_p3m1 - delta_pmref
|
||||
diff_p3m2_p3m1 = delta_p3m2 - delta_p3m1
|
||||
diff_p3m3_p3m1 = delta_p3m3 - delta_p3m1
|
||||
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_p3m1",
|
||||
"diff_p3m3_p3m1",
|
||||
] # fields to plot
|
||||
ncols = 6 # Max panels per row
|
||||
|
||||
figname = "_".join(fields)
|
||||
slices_dict = {
|
||||
"lpt": delta_lpt,
|
||||
"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_p3m1": diff_p3m2_p3m1,
|
||||
"diff_p3m3_p3m1": diff_p3m3_p3m1,
|
||||
"diff_p3m1_spm": diff_p3m1_spm,
|
||||
}
|
||||
titles_dict = {
|
||||
"lpt": "LPT",
|
||||
"pmref": f"PMref relax={scaling_pmref}",
|
||||
"pm1": f"PM1 relax={scaling_pm1}",
|
||||
"pm2": f"PM2 relax={scaling_pm2}",
|
||||
"spm": f"sPM relax={scaling_spm}",
|
||||
"p3m1": f"P3M1 relax={scaling_p3m1}",
|
||||
"p3m2": f"P3M2 relax={scaling_p3m2}",
|
||||
"p3m3": f"P3M3 relax={scaling_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_p3m1": r"$\delta_{\rm P3M2}-\delta_{\rm P3M1}$",
|
||||
"diff_p3m3_p3m1": r"$\delta_{\rm P3M3}-\delta_{\rm P3M1}$",
|
||||
"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"):
|
||||
vmin = -np.log(1 + np.abs(np.min(data)))
|
||||
vmax = np.log10(1 + np.abs(np.max(data)))
|
||||
if vmax > 0 > vmin:
|
||||
norm = TwoSlopeNorm(vmin=vmin, vcenter=0, vmax=vmax)
|
||||
im = ax.imshow(
|
||||
np.sign(data) * np.log(1 + np.abs(data)), cmap="RdBu_r", norm=norm
|
||||
)
|
||||
else:
|
||||
im = ax.imshow(np.log10(1 + np.abs(data)), cmap="RdBu_r")
|
||||
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("All plots completed successfully.")
|
||||
|
||||
except OSError as e:
|
||||
logger.error("Directory or file access error: %s", str(e))
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.critical("An unexpected error occurred: %s", str(e))
|
||||
raise RuntimeError("Failed.") from e
|
||||
finally:
|
||||
UNINDENT()
|
||||
logger.info("Running convergence tests done.")
|
|
@ -72,6 +72,25 @@ params_planck_kmax_missing = {
|
|||
"WhichSpectrum": WHICH_SPECTRUM,
|
||||
}
|
||||
|
||||
Omega_b_CONCEPT = 0.049
|
||||
Omega_cdm_CONCEPT = 0.27
|
||||
Omega_m_CONCEPT = Omega_b_CONCEPT + Omega_cdm_CONCEPT
|
||||
params_CONCEPT_kmax_missing = {
|
||||
"h": 0.67,
|
||||
"Omega_r": 0.0,
|
||||
"Omega_q": 1.0 - Omega_m_CONCEPT,
|
||||
"Omega_b": Omega_b_CONCEPT,
|
||||
"Omega_m": Omega_m_CONCEPT,
|
||||
"m_ncdm": 0.0,
|
||||
"Omega_k": 0.0,
|
||||
"tau_reio": TAU_REIO,
|
||||
"n_s": 0.96,
|
||||
"sigma8": 0.8102,
|
||||
"w0_fld": -1.0,
|
||||
"wa_fld": 0.0,
|
||||
"WhichSpectrum": WHICH_SPECTRUM,
|
||||
}
|
||||
|
||||
|
||||
def z2a(z):
|
||||
return 1.0 / (1 + z)
|
||||
|
@ -109,5 +128,27 @@ def cosmo_small_to_full_dict(cosmo_min):
|
|||
}
|
||||
return cosmo_full
|
||||
|
||||
# Default parameters time step limiters
|
||||
# Mostly for development and testing purposes.
|
||||
# For real P3M+COLA runs starting at redshift z=19, only
|
||||
# DEFAULT_FAC_DYN_CUSTOM_COLA, DEFAULT_FAC_H_CUSTOM_COLA,
|
||||
# DEFAULT_FAC_P3M_FIT, DEFAULT_DA_MAX_LATE_CUSTOM and
|
||||
# P3M_FIT_COEFFS_DEFAULT_{1,2}NP are relevant.
|
||||
DEFAULT_FAC_DYN_CUSTOM = 0.0154
|
||||
DEFAULT_FAC_DYN_CUSTOM_COLA = 0.03
|
||||
DEFAULT_DA_MAX_EARLY_CUSTOM = 0.0013
|
||||
DEFAULT_FAC_H_CUSTOM = 0.03
|
||||
DEFAULT_FAC_H_CUSTOM_COLA = 0.06
|
||||
DEFAULT_FAC_BEND = 0.04375
|
||||
DEFAULT_SUB_BEND1 = 0.012
|
||||
DEFAULT_SUB_BEND2 = 0.007
|
||||
DEFAULT_SUB_BEND1_COLA = 0.012
|
||||
DEFAULT_SUB_BEND2_COLA = 0.014
|
||||
DEFAULT_FAC_P3M_FIT = 1.0
|
||||
DEFAULT_DA_MAX_LATE_CUSTOM = 0.02
|
||||
P3M_FIT_COEFFS_DEFAULT_2NP = [-4.41928363, 0.24380666, 1.92034984, 1.21626721, 0.81694801, 0.20590599] # fitted P3M limiter for L=2Np
|
||||
P3M_FIT_COEFFS_DEFAULT_NP = [-5.14695921, 0.35234941, 2.08397701, 1.2447759, 0.61695249, 0.11401126] # fitted P3M limiter for L=Np
|
||||
# TODO: add polynomial coefficients for more L/Np values
|
||||
|
||||
# pyright: reportUnsupportedDunderAll=false
|
||||
__all__ = [k for k in globals() if not k.startswith("_")]
|
||||
|
|
531
src/wip3m/plot_limiters_from_baseline_ts.py
Normal file
531
src/wip3m/plot_limiters_from_baseline_ts.py
Normal file
|
@ -0,0 +1,531 @@
|
|||
#!/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 pysbmy.timestepping import StandardTimeStepping
|
||||
|
||||
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(
|
||||
"--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
|
||||
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_p3m1 = sim_params["nsteps_p3m1"]
|
||||
nsteps_p3m2 = sim_params["nsteps_p3m2"]
|
||||
nsteps_p3m3 = sim_params["nsteps_p3m3"]
|
||||
tsd_p3m1 = sim_params["tsd_p3m1"]
|
||||
tsd_p3m2 = sim_params["tsd_p3m2"]
|
||||
tsd_p3m3 = sim_params["tsd_p3m3"]
|
||||
p3m4 = "nsteps_p3m4" in sim_params
|
||||
p3m5 = "nsteps_p3m5" in sim_params
|
||||
if p3m4:
|
||||
nsteps_p3m4 = sim_params["nsteps_p3m4"]
|
||||
tsd_p3m4 = sim_params["tsd_p3m4"]
|
||||
if p3m5:
|
||||
nsteps_p3m5 = sim_params["nsteps_p3m5"]
|
||||
tsd_p3m5 = sim_params["tsd_p3m5"]
|
||||
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_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
|
||||
]
|
||||
if p3m4:
|
||||
DELTA_P3M4 = read_field(simdir + f"p3m4_nsteps{nsteps_p3m4}_final_density_p3m.h5").data[
|
||||
slice_ijk
|
||||
]
|
||||
if p3m5:
|
||||
DELTA_P3M5 = read_field(simdir + f"p3m5_nsteps{nsteps_p3m5}_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_pmref = DELTA_P3M2 - DELTA_PMREF
|
||||
diff_p3m3_pmref = DELTA_P3M3 - DELTA_PMREF
|
||||
if p3m4:
|
||||
diff_p3m4_pmref = DELTA_P3M4 - DELTA_PMREF
|
||||
if p3m5:
|
||||
diff_p3m5_pmref = DELTA_P3M5 - DELTA_PMREF
|
||||
|
||||
fields = [
|
||||
"pmref",
|
||||
"pm1",
|
||||
"pm2",
|
||||
"p3m1",
|
||||
"p3m2",
|
||||
"p3m3",
|
||||
"diff_pm1_pmref",
|
||||
"diff_pm2_pmref",
|
||||
"diff_p3m1_pmref",
|
||||
"diff_p3m2_pmref",
|
||||
"diff_p3m3_pmref",
|
||||
] # fields to plot
|
||||
if p3m4:
|
||||
fields.append("p3m4")
|
||||
fields.append("diff_p3m4_pmref")
|
||||
if p3m5:
|
||||
fields.append("p3m5")
|
||||
fields.append("diff_p3m5_pmref")
|
||||
ncols = 6 # Max panels per row
|
||||
|
||||
figname = "_".join(fields)
|
||||
slices_dict = {
|
||||
"lpt": DELTA_LPT,
|
||||
"pmref": DELTA_PMREF,
|
||||
"pm1": DELTA_PM1,
|
||||
"pm2": DELTA_PM2,
|
||||
"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_pmref": diff_p3m2_pmref,
|
||||
"diff_p3m3_pmref": diff_p3m3_pmref,
|
||||
}
|
||||
if p3m4:
|
||||
slices_dict["p3m4"] = DELTA_P3M4
|
||||
slices_dict["diff_p3m4_pmref"] = diff_p3m4_pmref
|
||||
if p3m5:
|
||||
slices_dict["p3m5"] = DELTA_P3M5
|
||||
slices_dict["diff_p3m5_pmref"] = diff_p3m5_pmref
|
||||
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}$",
|
||||
"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_pmref": r"$\delta_{\rm P3M2}-\delta_{\rm PMref}$",
|
||||
"diff_p3m3_pmref": r"$\delta_{\rm P3M3}-\delta_{\rm PMref}$",
|
||||
}
|
||||
if p3m4:
|
||||
slices_dict["p3m4"] = DELTA_P3M4
|
||||
titles_dict["p3m4"] = f"P3M4 $n_\\mathrm{{steps}}={nsteps_p3m4}$"
|
||||
slices_dict["diff_p3m4_pmref"] = diff_p3m4_pmref
|
||||
titles_dict["diff_p3m4_pmref"] = r"$\delta_{\rm P3M4}-\delta_{\rm PMref}$"
|
||||
if p3m5:
|
||||
slices_dict["p3m5"] = DELTA_P3M5
|
||||
titles_dict["p3m5"] = f"P3M5 $n_\\mathrm{{steps}}={nsteps_p3m5}$"
|
||||
slices_dict["diff_p3m5_pmref"] = diff_p3m5_pmref
|
||||
titles_dict["diff_p3m5_pmref"] = r"$\delta_{\rm P3M5}-\delta_{\rm PMref}$"
|
||||
|
||||
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.")
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
if p3m4:
|
||||
DELTA = read_field(simdir + f"p3m4_nsteps{nsteps_p3m4}_final_density_p3m.h5")
|
||||
Pk_P3M4, _ = get_autocorrelation(DELTA, G, AliasingCorr)
|
||||
|
||||
if p3m5:
|
||||
DELTA = read_field(simdir + f"p3m5_nsteps{nsteps_p3m5}_final_density_p3m.h5")
|
||||
Pk_P3M5, _ = 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("Plotting time step diagnostics...")
|
||||
INDENT()
|
||||
|
||||
file_ext = f"p3m1_nsteps{nsteps_p3m1}"
|
||||
aiDrift = StandardTimeStepping.read(wd + file_ext + f"_ts_p3m.h5").aiDrift
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=simdir + "p3m1_timestep_log.txt",
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=tsd_p3m1,
|
||||
nsteps=nsteps_p3m1,
|
||||
save_path=outdir + "time_step_diagnostics_p3m1.pdf",
|
||||
)
|
||||
|
||||
file_ext = f"p3m2_nsteps{nsteps_p3m2}"
|
||||
aiDrift = StandardTimeStepping.read(wd + file_ext + f"_ts_p3m.h5").aiDrift
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=simdir + "p3m2_timestep_log.txt",
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=tsd_p3m2,
|
||||
nsteps=nsteps_p3m2,
|
||||
save_path=outdir + "time_step_diagnostics_p3m2.pdf",
|
||||
)
|
||||
|
||||
file_ext = f"p3m3_nsteps{nsteps_p3m3}"
|
||||
aiDrift = StandardTimeStepping.read(wd + file_ext + f"_ts_p3m.h5").aiDrift
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=simdir + "p3m3_timestep_log.txt",
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=tsd_p3m3,
|
||||
nsteps=nsteps_p3m3,
|
||||
save_path=outdir + "time_step_diagnostics_p3m3.pdf",
|
||||
)
|
||||
|
||||
if p3m4:
|
||||
file_ext = f"p3m4_nsteps{nsteps_p3m4}"
|
||||
aiDrift = StandardTimeStepping.read(wd + file_ext + f"_ts_p3m.h5").aiDrift
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=simdir + "p3m4_timestep_log.txt",
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=tsd_p3m4,
|
||||
nsteps=nsteps_p3m4,
|
||||
save_path=outdir + "time_step_diagnostics_p3m4.pdf",
|
||||
)
|
||||
|
||||
if p3m5:
|
||||
file_ext = f"p3m5_nsteps{nsteps_p3m5}"
|
||||
aiDrift = StandardTimeStepping.read(wd + file_ext + f"_ts_p3m.h5").aiDrift
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=simdir + "p3m5_timestep_log.txt",
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=tsd_p3m5,
|
||||
nsteps=nsteps_p3m5,
|
||||
save_path=outdir + "time_step_diagnostics_p3m5.pdf",
|
||||
)
|
||||
|
||||
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),
|
||||
("P3M1", Pk_P3M1),
|
||||
("P3M2", Pk_P3M2),
|
||||
("P3M3", Pk_P3M3),
|
||||
]
|
||||
if p3m4:
|
||||
fields_to_plot.append(("P3M4", Pk_P3M4))
|
||||
if p3m5:
|
||||
fields_to_plot.append(("P3M5", Pk_P3M5))
|
||||
# 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
|
||||
if nyquist <= xlim_max:
|
||||
line1 = ax.axvline(
|
||||
x=nyquist, color="black", linestyle="--", lw=2, label="Nyquist (density grid)", zorder=0
|
||||
)
|
||||
if nyquist_PM <= xlim_max:
|
||||
line2 = ax.axvline(
|
||||
x=nyquist_PM, color="black", linestyle="-", lw=1, label="Nyquist (PM grid)", zorder=0
|
||||
)
|
||||
if xs_inv <= xlim_max:
|
||||
line3 = ax.axvline(
|
||||
x=xs_inv, color="gray", linestyle="-.", lw=2, label=r"Split wavenumber $x_s$", zorder=0
|
||||
)
|
||||
if xr_inv <= xlim_max:
|
||||
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.")
|
|
@ -17,12 +17,17 @@ import numpy as np
|
|||
import matplotlib.pyplot as plt
|
||||
import matplotlib.patches as mpatches
|
||||
from matplotlib.colors import TwoSlopeNorm
|
||||
from matplotlib.gridspec import GridSpec
|
||||
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
||||
import cmocean.cm as cm
|
||||
import plotly.graph_objects as go
|
||||
|
||||
from wip3m.plot_params import * # type: ignore
|
||||
|
||||
from wip3m.logger import getCustomLogger, INDENT, UNINDENT
|
||||
|
||||
logger = getCustomLogger(__name__)
|
||||
|
||||
# Configure global plotting settings
|
||||
setup_plotting()
|
||||
|
||||
|
@ -263,7 +268,7 @@ def plot_force_distance(r, fmag, f_max=1e-1, a=None, title=None, save_path=None)
|
|||
r = np.linspace(np.min(rs), np.max(rs), 300)
|
||||
f_th = inverse_square(r, a)
|
||||
ax.plot(r, f_th, "r-", label=r"$\propto 1/r^2$")
|
||||
|
||||
|
||||
ax.set_xscale("log")
|
||||
ax.set_yscale("log")
|
||||
ax.set_xlabel(r"Distance $r$ [Mpc/$h$]")
|
||||
|
@ -275,7 +280,7 @@ def plot_force_distance(r, fmag, f_max=1e-1, a=None, title=None, save_path=None)
|
|||
|
||||
plt.tight_layout()
|
||||
if save_path:
|
||||
plt.savefig(save_path)
|
||||
plt.savefig(save_path, dpi=300)
|
||||
print(f"Figure saved to: {save_path}")
|
||||
plt.show()
|
||||
|
||||
|
@ -369,4 +374,295 @@ def plot_force_distance_comparison(rr, ff, ll, L, Np, Npm, ss=None, a=None, titl
|
|||
if save_path:
|
||||
plt.savefig(save_path)
|
||||
print(f"Figure saved to: {save_path}")
|
||||
plt.show()
|
||||
plt.show()
|
||||
|
||||
def plot_timestepping_diagnostics(log_path, aiDrift=None, TimeStepDistribution=None, nsteps=None, ymin=2e-3, save_path=None, exact=True, show=True):
|
||||
"""
|
||||
Plot and compare timestep limiters from log file, as given by the
|
||||
`compute_time_step` function in p3m.c.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
log_path : str
|
||||
Path to the timestep log file.
|
||||
aiDrift : array_like or None
|
||||
Reference array of scale factors.
|
||||
TimeStepDistribution : int or None
|
||||
Baseline timestep distribution mode (0, 1, or 2).
|
||||
nsteps : int or None
|
||||
Total number of baseline steps (used for labelling).
|
||||
save_path : str or None
|
||||
If provided, path to save the figure.
|
||||
show : bool, optional
|
||||
Whether to display the plot (default: True).
|
||||
"""
|
||||
logger.info(f"Plotting timestep limiters from {log_path}...")
|
||||
INDENT()
|
||||
|
||||
a, da, da_max, da_H_approx, da_H_exact, da_dyn_approx, da_dyn_exact, da_p3m, da_late = np.loadtxt(log_path, delimiter=",", unpack=True, skiprows=0)
|
||||
|
||||
if aiDrift is not None:
|
||||
assert np.allclose(a, aiDrift), "aiDrift does not match the time steps in the log file."
|
||||
|
||||
fig, ax = plt.subplots(figsize=(5.5, 4))
|
||||
ax.set_ylim(ymin, 0.35)
|
||||
ax.set_xlim(np.min(a), np.max(a))
|
||||
|
||||
# ax.loglog(a, np.ones_like(a)*1/50 / a, label=r"$\Delta a=1/50$")
|
||||
ax.loglog(a, da_H_approx / a, marker="o", markersize=4, linestyle="-", label=r"$\Delta a/a\approx 0.031$ (Hubble, approx)")
|
||||
ax.loglog(a, da_dyn_approx / a, marker="o", markersize=4, linestyle="-", label=r"$\Delta a\propto aH(a)\left(G\rho\right)^{-1/2}$ (Dynamical, approx)")
|
||||
ax.loglog(a, da_p3m / a, marker="o", markersize=4, linestyle="-", label=r"$\Delta t\propto x_s/\sqrt{\langle v^2\rangle}$ (P3M, untuned)")
|
||||
if exact:
|
||||
ax.loglog(a, da_dyn_exact/a, marker="o", markersize=4, linestyle="-", label=r"$\Delta t=\eta_\textrm{d}\left(G\rho\right)^{-1/2}$ (Dynamical, exact)")
|
||||
ax.loglog(a, da_H_exact / a, marker="x", markersize=4, linestyle="-", label=r"$\Delta t=0.031/H(t)$ (Hubble, exact)")
|
||||
ax.loglog(a, da_late / a, marker="o", markersize=4, linestyle="-", label=r"$\Delta a_\textrm{max}=0.05$ (late)")
|
||||
ls = "dotted" if TimeStepDistribution in [1, 3] else ""
|
||||
ax.loglog(a, da_max / a, color="black", marker="o", markersize=4, linestyle=ls, label="CONCEPT global step")
|
||||
ax.loglog(a, da / a, marker="x", markersize=4, linestyle="", label=(
|
||||
rf"$\Delta a=1/{nsteps}$ (baseline)" if TimeStepDistribution == 0 else
|
||||
f"Baseline ({nsteps} log-spaced steps)" if TimeStepDistribution == 1 else
|
||||
f"Actual {nsteps} steps"))
|
||||
|
||||
ax.set_xlabel(r"Scale factor $a$", fontsize=12)
|
||||
ax.set_ylabel(r"$\Delta a/a$", fontsize=12)
|
||||
ax.xaxis.grid(True, which="both", linestyle=":", linewidth=0.6)
|
||||
ax.tick_params(labelsize=10)
|
||||
fig.subplots_adjust(bottom=0.37)
|
||||
fig.legend(fontsize=10, loc='lower center', ncol=2, frameon=False)
|
||||
|
||||
if save_path:
|
||||
plt.savefig(save_path)
|
||||
logger.info(f"Figure saved to: {save_path}")
|
||||
UNINDENT()
|
||||
logger.info(f"Plotting timestep limiters from {log_path} done.")
|
||||
if show:
|
||||
plt.show()
|
||||
# plt.close(fig)
|
||||
|
||||
|
||||
def plot_custom_timestepping_diagnostics(
|
||||
log_path,
|
||||
aiDrift=None,
|
||||
TimeStepDistribution=None,
|
||||
nsteps=None,
|
||||
fac_hubble=None,
|
||||
fac_bend=None,
|
||||
fac_p3m_fit=None,
|
||||
da_max_early=None,
|
||||
da_max_late=None,
|
||||
plot_bend=True,
|
||||
ymin=2e-3,
|
||||
ymax=0.35,
|
||||
save_path=None,
|
||||
show=True,
|
||||
):
|
||||
"""
|
||||
Plot and compare timestep limiters from log file, as given by the
|
||||
`compute_time_step` function in p3m.c.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
log_path : str
|
||||
Path to the timestep log file.
|
||||
aiDrift : array_like or None
|
||||
Reference array of scale factors.
|
||||
TimeStepDistribution : int or None
|
||||
Baseline timestep distribution mode (0, 1, or 2).
|
||||
nsteps : int or None
|
||||
Total number of baseline steps (used for labelling).
|
||||
fac_hubble : float, optional
|
||||
Factor for the Hubble timestep limiter (default: None).
|
||||
fac_bend : float, optional
|
||||
Factor for the bend timestep limiter (default: None).
|
||||
fac_p3m_fit : float, optional
|
||||
Factor for the P3M fit timestep limiter (default: None).
|
||||
da_max_early : float, optional
|
||||
Maximum timestep allowed for early stages (default: None).
|
||||
da_max_late : float, optional
|
||||
Maximum timestep allowed for late stages (default: None).
|
||||
plot_bend : bool, optional
|
||||
Whether to plot the bend limiter (default: True).
|
||||
ymin : float, optional
|
||||
Minimum y-axis limit for the plot (default: 5e-3).
|
||||
save_path : str or None
|
||||
If provided, path to save the figure.
|
||||
show : bool, optional
|
||||
Whether to display the plot (default: True).
|
||||
"""
|
||||
log_path_custom = log_path[:-4] + "_custom.txt"
|
||||
logger.info(f"Plotting timestep limiters from {log_path} and {log_path_custom}...")
|
||||
INDENT()
|
||||
a, da, da_max, da_dyn, da_bend, da_p3m, da_p3m_fit, da_hubble, da_hubble_lenient, da_late = np.loadtxt(log_path_custom, delimiter=",", unpack=True, skiprows=0)
|
||||
|
||||
if aiDrift is not None:
|
||||
assert np.allclose(a, aiDrift), "aiDrift does not match the time steps in the log file."
|
||||
|
||||
fig, ax = plt.subplots(figsize=(5.5, 4))
|
||||
ax.set_ylim(ymin, ymax)
|
||||
ax.set_xlim(np.min(a), np.max(a))
|
||||
bottom_pad = 0.37
|
||||
|
||||
if fac_hubble is not None:
|
||||
lab = rf"$\Delta a/a= {fac_hubble:.3f}$ (Hubble)"
|
||||
else:
|
||||
lab = r"$\Delta a/a$ constant (Hubble)"
|
||||
ax.loglog(a, da_hubble / a, marker="o", markersize=4, linestyle="-", label=lab)
|
||||
if da_max_early is not None:
|
||||
lab = rf"Hubble, lenient (early $\Delta a_\textrm{{max}}={da_max_early:.3f}$)"
|
||||
else:
|
||||
lab = "Hubble, lenient"
|
||||
ax.loglog(a, da_hubble_lenient / a, marker="o", markersize=4, linestyle="-", label=lab)
|
||||
ax.loglog(a, da_dyn / a, marker="o", markersize=4, linestyle="-", label=r"$\Delta a\propto aH(a)\left(G\rho\right)^{-1/2}$ (Dynamical)")
|
||||
if plot_bend:
|
||||
if fac_bend is not None:
|
||||
lab = rf"Bend-approx. P3M ($\eta_\textrm{{P}}={fac_bend:.5f}$)"
|
||||
else:
|
||||
lab = "Bend-approx. P3M"
|
||||
ax.loglog(a, da_bend / a, marker="o", markersize=4, linestyle="-", label=lab)
|
||||
if fac_p3m_fit is not None:
|
||||
lab = rf"P3M fit ($\eta_\textrm{{Pf}}={fac_p3m_fit:.3f}$)"
|
||||
ax.loglog(a, da_p3m_fit / a, marker="o", markersize=4, linestyle="-", label=lab)
|
||||
bottom_pad += 0.06
|
||||
# _, _, _, _, _, _, _, da_p3m_concept, _ = np.loadtxt(log_path, delimiter=",", unpack=True, skiprows=0)
|
||||
# ax.loglog(a, da_p3m_concept / a, marker="o", markersize=4, linestyle="-", label=r"$\Delta t\propto x_s/\sqrt{\langle v^2\rangle}$ (P3M, untuned)")
|
||||
ax.loglog(a, da_p3m / a, marker="o", markersize=4, linestyle="-", label=r"$\Delta t\propto x_s/\sqrt{\langle v^2\rangle}$ (P3M)")
|
||||
if da_max_late is not None:
|
||||
lab = rf"$\Delta a_\textrm{{max}}={da_max_late:.3f}$ (late)"
|
||||
else:
|
||||
lab = r"$\Delta a_\textrm{max}$ (late)"
|
||||
ax.loglog(a, da_late / a, marker="o", markersize=4, linestyle="-", label=lab)
|
||||
ls = "dotted" if TimeStepDistribution in [1, 3] else ""
|
||||
ax.loglog(a, da_max / a, color="black", marker="o", markersize=4, linestyle=ls, label="Max time step allowed (custom)")
|
||||
ax.loglog(a, da / a, marker=".", markersize=4, linestyle="", label=(
|
||||
rf"$\Delta a=1/{nsteps}$ (baseline)" if TimeStepDistribution == 0 else
|
||||
f"Baseline ({nsteps} log-spaced steps)" if TimeStepDistribution == 1 else
|
||||
f"Actual {nsteps} steps"))
|
||||
|
||||
ax.set_xlabel(r"Scale factor $a$", fontsize=12)
|
||||
ax.set_ylabel(r"$\Delta a/a$", fontsize=12)
|
||||
ax.xaxis.grid(True, which="both", linestyle=":", linewidth=0.6)
|
||||
ax.tick_params(labelsize=10)
|
||||
fig.subplots_adjust(bottom=bottom_pad)
|
||||
fig.legend(fontsize=10, loc='lower center', ncol=2, frameon=False)
|
||||
|
||||
if save_path:
|
||||
plt.savefig(save_path, dpi=300)
|
||||
logger.info(f"Figure saved to: {save_path}")
|
||||
UNINDENT()
|
||||
logger.info(f"Plotting timestep limiters from {log_path} and {log_path_custom} done.")
|
||||
if show:
|
||||
plt.show()
|
||||
# plt.close(fig)
|
||||
|
||||
|
||||
def log_poly_model(log_a, *coeffs):
|
||||
"""Polynomial model. log(y) = c0 + c1*log(a) + c2*log(a)^2 + ..."""
|
||||
return sum(c * log_a**i for i, c in enumerate(coeffs))
|
||||
|
||||
|
||||
def fit_p3m(a, da_p3m, degree=3, amin=1e-1):
|
||||
"""
|
||||
Fit log(da_p3m / a) = poly(log(a)) for a > 1e-1.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
a : ndarray
|
||||
Scale factor.
|
||||
da_p3m : ndarray
|
||||
P3M limiter values.
|
||||
degree : int
|
||||
Polynomial degree.
|
||||
|
||||
Returns
|
||||
-------
|
||||
coeffs : ndarray
|
||||
Polynomial coefficients [c0, ..., c_degree].
|
||||
"""
|
||||
from scipy.optimize import curve_fit
|
||||
|
||||
mask = a > amin
|
||||
if not np.any(mask):
|
||||
raise ValueError(f"No values of 'a' greater than {amin} found.")
|
||||
log_a = np.log(a[mask])
|
||||
log_y = np.log(da_p3m[mask] / a[mask])
|
||||
if degree == 3:
|
||||
initial_guess = [-3.0, 0.2, 2.5, 0.6]
|
||||
elif degree == 5:
|
||||
initial_guess = [-3.5, -0.3, -0.5, -4.0, -3.0, -0.5]
|
||||
else:
|
||||
initial_guess = np.ones(degree + 1)
|
||||
coeffs, _ = curve_fit(log_poly_model, log_a, log_y, p0=initial_guess)
|
||||
return coeffs
|
||||
|
||||
|
||||
def evaluate_polynomial_log(coeffs, a):
|
||||
log_a = np.log(a)
|
||||
return np.exp(log_poly_model(log_a, *coeffs))
|
||||
|
||||
|
||||
def plot_p3m(a, da_p3m, coeffs, amin_fit=1e-1, amin_plot=5e-2):
|
||||
"""
|
||||
Plot da_p3m/a, fit, and residuals.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
a : ndarray
|
||||
Scale factor values.
|
||||
da_p3m : ndarray
|
||||
P3M limiter values.
|
||||
coeffs : ndarray
|
||||
Polynomial coefficients.
|
||||
amin_fit : float
|
||||
Minimum a value used in the fit (for legend).
|
||||
amin_plot: float
|
||||
Minimum a value to include in plot.
|
||||
"""
|
||||
mask = a > amin_plot # Only plot for a > amin_plot
|
||||
a = a[mask]
|
||||
da_p3m = da_p3m[mask]
|
||||
ratio = da_p3m / a
|
||||
a_dense = np.logspace(np.log10(a.min()), np.log10(a.max()), 500)
|
||||
fit_vals = evaluate_polynomial_log(coeffs, a_dense)
|
||||
fit_interp = evaluate_polynomial_log(coeffs, a)
|
||||
# ratio = da_p3m/a
|
||||
# ratio_fit = exp(fit(log(da_p3m)/log(a))) and we're interested in
|
||||
# the residuals, res, of the da_p3m fit, that is,
|
||||
# res = log(da_p3m) - log_da_p3m_fit
|
||||
# = log(da_p3m) - log(exp(log_da_p3m_fit))
|
||||
# = log(da_p3m) - log(a*fit_interp)
|
||||
# = log(da_p3m / (a*fit_interp))
|
||||
# = log(da_p3m/a / fit_interp)
|
||||
# = log(ratio / fit_interp)
|
||||
# res_exp = ratio / fit_interp
|
||||
residuals = ratio / fit_interp
|
||||
|
||||
fig = plt.figure(figsize=(6, 3.5))
|
||||
gs = GridSpec(2, 1, height_ratios=[3, 1], hspace=0.05)
|
||||
ax1 = fig.add_subplot(gs[0])
|
||||
ax2 = fig.add_subplot(gs[1], sharex=ax1)
|
||||
|
||||
# P3M limiter and fit
|
||||
ax1.plot(a, ratio, ".", label=r"$\Delta t \propto x_s/\sqrt{\langle v^2 \rangle}$", alpha=0.7)
|
||||
ax1.plot(a_dense, fit_vals, "-", label=rf"Fit for $a > {amin_fit}$", lw=1.8)
|
||||
ax1.set_yscale("log")
|
||||
ax1.set_xscale("log")
|
||||
ax1.set_ylabel(r"$\Delta a/a$")
|
||||
ax1.legend(loc="upper right", frameon=False)
|
||||
ax1.tick_params(which="both", direction="in", top=True, right=True)
|
||||
ax1.grid(True, which="major", ls=":", lw=0.5)
|
||||
|
||||
# Residuals
|
||||
ax2.plot(a, residuals, "o", color="grey", alpha=0.6, markersize=3)
|
||||
ax2.axhline(1.0, color="black", lw=1, ls="--")
|
||||
ax2.set_ylabel("res", labelpad=2)
|
||||
ax2.set_xlabel(r"Scale factor $a$")
|
||||
ax2.set_xscale("log")
|
||||
ax2.set_yscale("log")
|
||||
|
||||
ax2.tick_params(which="both", direction="in", top=True, right=True)
|
||||
ax2.grid(True, which="major", ls=":", lw=0.5)
|
||||
ax1.tick_params(labelbottom=False)
|
||||
ax2.get_yaxis().set_major_formatter(plt.FuncFormatter(lambda y, _: f"{y:g}"))
|
||||
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
|
|
@ -20,6 +20,7 @@ import numpy as np
|
|||
from pysbmy.fft import read_FourierGrid
|
||||
from pysbmy.field import read_field
|
||||
from pysbmy.correlations import get_autocorrelation
|
||||
from pysbmy.timestepping import StandardTimeStepping
|
||||
|
||||
from wip3m import *
|
||||
from wip3m.tools import none_bool_str
|
||||
|
@ -109,6 +110,10 @@ if __name__ == "__main__":
|
|||
nsteps_p3m1 = sim_params["nsteps_p3m1"]
|
||||
nsteps_p3m2 = sim_params["nsteps_p3m2"]
|
||||
nsteps_p3m3 = sim_params["nsteps_p3m3"]
|
||||
tsd_spm = sim_params["tsd_spm"]
|
||||
tsd_p3m1 = sim_params["tsd_p3m1"]
|
||||
tsd_p3m2 = sim_params["tsd_p3m2"]
|
||||
tsd_p3m3 = sim_params["tsd_p3m3"]
|
||||
logger.info(f"Reading simulation parameters from {wd}sim_params.txt done.")
|
||||
|
||||
if plot_fields:
|
||||
|
@ -275,9 +280,6 @@ if __name__ == "__main__":
|
|||
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
|
||||
|
||||
|
@ -326,6 +328,49 @@ if __name__ == "__main__":
|
|||
else:
|
||||
raise ValueError("Invalid reference field. Choose 'P3M1' or 'PMref'.")
|
||||
|
||||
logger.info("Plotting time step diagnostics...")
|
||||
INDENT()
|
||||
|
||||
file_ext = f"spm_nsteps{nsteps_spm}"
|
||||
aiDrift = StandardTimeStepping.read(wd + file_ext + f"_ts_spm.h5").aiDrift
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=simdir + "spm_timestep_log.txt",
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=tsd_spm,
|
||||
nsteps=nsteps_spm,
|
||||
save_path=outdir + "time_step_diagnostics_spm.pdf",
|
||||
)
|
||||
|
||||
file_ext = f"p3m1_nsteps{nsteps_p3m1}"
|
||||
aiDrift = StandardTimeStepping.read(wd + file_ext + f"_ts_p3m.h5").aiDrift
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=simdir + "p3m1_timestep_log.txt",
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=tsd_p3m1,
|
||||
nsteps=nsteps_p3m1,
|
||||
save_path=outdir + "time_step_diagnostics_p3m1.pdf",
|
||||
)
|
||||
|
||||
file_ext = f"p3m2_nsteps{nsteps_p3m2}"
|
||||
aiDrift = StandardTimeStepping.read(wd + file_ext + f"_ts_p3m.h5").aiDrift
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=simdir + "p3m2_timestep_log.txt",
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=tsd_p3m2,
|
||||
nsteps=nsteps_p3m2,
|
||||
save_path=outdir + "time_step_diagnostics_p3m2.pdf",
|
||||
)
|
||||
|
||||
file_ext = f"p3m3_nsteps{nsteps_p3m3}"
|
||||
aiDrift = StandardTimeStepping.read(wd + file_ext + f"_ts_p3m.h5").aiDrift
|
||||
plot_timestepping_diagnostics(
|
||||
log_path=simdir + "p3m3_timestep_log.txt",
|
||||
aiDrift=aiDrift,
|
||||
TimeStepDistribution=tsd_p3m3,
|
||||
nsteps=nsteps_p3m3,
|
||||
save_path=outdir + "time_step_diagnostics_p3m3.pdf",
|
||||
)
|
||||
|
||||
logger.info(f"Plotting power spectra...")
|
||||
INDENT()
|
||||
fig, ax = plt.subplots(figsize=(7, 4))
|
||||
|
@ -397,18 +442,22 @@ if __name__ == "__main__":
|
|||
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
|
||||
)
|
||||
if nyquist <= xlim_max:
|
||||
line1 = ax.axvline(
|
||||
x=nyquist, color="black", linestyle="--", lw=2, label="Nyquist (density grid)", zorder=0
|
||||
)
|
||||
if nyquist_PM <= xlim_max:
|
||||
line2 = ax.axvline(
|
||||
x=nyquist_PM, color="black", linestyle="-", lw=1, label="Nyquist (PM grid)", zorder=0
|
||||
)
|
||||
if xs_inv <= xlim_max:
|
||||
line3 = ax.axvline(
|
||||
x=xs_inv, color="gray", linestyle="-.", lw=2, label=r"Split wavenumber $x_s$", zorder=0
|
||||
)
|
||||
if xr_inv <= xlim_max:
|
||||
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")
|
||||
|
|
|
@ -19,6 +19,7 @@ import os
|
|||
import gc
|
||||
import numpy as np
|
||||
|
||||
from wip3m import *
|
||||
from wip3m.logger import getCustomLogger
|
||||
|
||||
logger = getCustomLogger(__name__)
|
||||
|
@ -98,6 +99,8 @@ def generate_sim_params(params_dict, ICs, workdir, outdir, file_ext=None, force=
|
|||
Directory where to store the simulation outputs.
|
||||
file_ext : str, optional
|
||||
Prefix for the output files.
|
||||
force : bool, optional
|
||||
If True, force regeneration of the parameter file.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
@ -107,7 +110,7 @@ def generate_sim_params(params_dict, ICs, workdir, outdir, file_ext=None, force=
|
|||
"""
|
||||
from os.path import isfile
|
||||
from pysbmy import param_file
|
||||
from pysbmy.timestepping import StandardTimeStepping
|
||||
from pysbmy.timestepping import StandardTimeStepping, P3MTimeStepping
|
||||
|
||||
method = params_dict["method"]
|
||||
path = workdir + file_ext + "_" if file_ext else workdir
|
||||
|
@ -129,16 +132,51 @@ def generate_sim_params(params_dict, ICs, workdir, outdir, file_ext=None, force=
|
|||
|
||||
# Generate the time-stepping distribution
|
||||
if method in ["pm", "cola", "spm", "p3m"]:
|
||||
TimeStepDistribution = params_dict["TimeStepDistribution"]
|
||||
|
||||
fac_dyn_custom = params_dict.get("fac_dyn_custom", DEFAULT_FAC_DYN_CUSTOM)
|
||||
da_max_early_custom = params_dict.get("da_max_early_custom", DEFAULT_DA_MAX_EARLY_CUSTOM)
|
||||
fac_H_custom = params_dict.get("fac_H_custom", DEFAULT_FAC_H_CUSTOM)
|
||||
fac_bend = params_dict.get("fac_bend", DEFAULT_FAC_BEND)
|
||||
sub_bend1 = params_dict.get("sub_bend1", DEFAULT_SUB_BEND1)
|
||||
sub_bend2 = params_dict.get("sub_bend2", DEFAULT_SUB_BEND2)
|
||||
da_max_late_custom = params_dict.get("da_max_late_custom", DEFAULT_DA_MAX_LATE_CUSTOM)
|
||||
|
||||
ts_filename = path + "ts_" + method + ".h5"
|
||||
logger.info("Time-stepping distribution file: %s", ts_filename)
|
||||
if not isfile(ts_filename) or force:
|
||||
TimeStepDistribution = params_dict["TimeStepDistribution"]
|
||||
ai = params_dict["ai"]
|
||||
af = params_dict["af"]
|
||||
nsteps = params_dict["nsteps"]
|
||||
snapshots = np.full((nsteps), False)
|
||||
TS = StandardTimeStepping(ai, af, snapshots, TimeStepDistribution)
|
||||
if TimeStepDistribution in [0, 1, 2]:
|
||||
ai = params_dict["ai"]
|
||||
af = params_dict["af"]
|
||||
nsteps = params_dict["nsteps"]
|
||||
snapshots = np.full((nsteps), False)
|
||||
TS = StandardTimeStepping(ai, af, snapshots, TimeStepDistribution)
|
||||
elif TimeStepDistribution == 3:
|
||||
ai = params_dict["ai"]
|
||||
af = params_dict["af"]
|
||||
p3m_fit_coeffs = params_dict.get("p3m_fit_coeffs", None)
|
||||
use_p3m_fit = params_dict.get("use_p3m_fit", False)
|
||||
fac_p3m_fit = params_dict.get("fac_p3m_fit", DEFAULT_FAC_P3M_FIT)
|
||||
TS = P3MTimeStepping(
|
||||
ai=params_dict["ai"],
|
||||
af=params_dict["af"],
|
||||
cosmo=params_dict["cosmo_dict"],
|
||||
fac_dynamical=fac_dyn_custom,
|
||||
delta_a_max_early=da_max_early_custom,
|
||||
fac_hubble=fac_H_custom,
|
||||
fac_bend=fac_bend,
|
||||
sub_bend1=sub_bend1,
|
||||
sub_bend2=sub_bend2,
|
||||
delta_a_max_late=da_max_late_custom,
|
||||
p3m_fit_coeffs=p3m_fit_coeffs,
|
||||
use_p3m_fit=use_p3m_fit,
|
||||
fac_p3m_fit=fac_p3m_fit,
|
||||
forces_every=1,
|
||||
)
|
||||
else:
|
||||
raise ValueError("Invalid TimeStepDistribution: {}".format(TimeStepDistribution))
|
||||
TS.write(ts_filename)
|
||||
logger.info("TS.ai = %f, TS.af = %f, TS.nsteps = %d", TS.ai, TS.af, TS.nsteps)
|
||||
else:
|
||||
logger.info("Time-stepping distribution file already exists: %s", ts_filename)
|
||||
StandardTimeStepping.read(ts_filename).plot(savepath=path + "ts_" + method + ".png")
|
||||
|
@ -277,6 +315,13 @@ def generate_sim_params(params_dict, ICs, workdir, outdir, file_ext=None, force=
|
|||
wa_fld=0.0,
|
||||
)
|
||||
elif params_dict["method"] == "p3m" or params_dict["method"] == "spm":
|
||||
if params_dict["EvolutionMode"] == 6 or params_dict["EvolutionMode"] == 7:
|
||||
## Special case for COLA ##
|
||||
sub_bend1_diagnostic = params_dict.get("sub_bend1", DEFAULT_SUB_BEND1_COLA)
|
||||
sub_bend2_diagnostic = params_dict.get("sub_bend2", DEFAULT_SUB_BEND2_COLA)
|
||||
else:
|
||||
sub_bend1_diagnostic = params_dict.get("sub_bend1", DEFAULT_SUB_BEND1)
|
||||
sub_bend2_diagnostic = params_dict.get("sub_bend2", DEFAULT_SUB_BEND2)
|
||||
S = param_file(
|
||||
# Basic setup:
|
||||
Particles=Particles,
|
||||
|
@ -306,11 +351,22 @@ def generate_sim_params(params_dict, ICs, workdir, outdir, file_ext=None, force=
|
|||
WriteFinalDensity=1,
|
||||
OutputFinalDensity=simpath + "final_density_{}.h5".format(method),
|
||||
n_Tiles=params_dict["n_Tiles"],
|
||||
RunForceDiagnostic=params_dict["RunForceDiagnostic"],
|
||||
nPairsForceDiagnostic=params_dict["nPairsForceDiagnostic"],
|
||||
nBinsForceDiagnostic=params_dict["nBinsForceDiagnostic"],
|
||||
maxTrialsForceDiagnostic=params_dict["maxTrialsForceDiagnostic"],
|
||||
OutputForceDiagnostic=params_dict["OutputForceDiagnostic"],
|
||||
RunForceDiagnostic=params_dict.get("RunForceDiagnostic", False),
|
||||
nPairsForceDiagnostic=params_dict.get("nPairsForceDiagnostic", 0),
|
||||
nBinsForceDiagnostic=params_dict.get("nBinsForceDiagnostic", 0),
|
||||
maxTrialsForceDiagnostic=params_dict.get("maxTrialsForceDiagnostic", 0),
|
||||
OutputForceDiagnostic=params_dict.get("OutputForceDiagnostic", ""),
|
||||
PrintOutputTimestepsLog=params_dict.get("PrintOutputTimestepsLog", False),
|
||||
OutputTimestepsLog=params_dict.get("OutputTimestepsLog", ""),
|
||||
fac_dyn_custom=params_dict.get("fac_dyn_custom", DEFAULT_FAC_DYN_CUSTOM),
|
||||
fac_H_custom=params_dict.get("fac_H_custom", DEFAULT_FAC_H_CUSTOM),
|
||||
fac_bend=params_dict.get("fac_bend", DEFAULT_FAC_BEND),
|
||||
sub_bend1=params_dict.get("sub_bend1", sub_bend1_diagnostic),
|
||||
sub_bend2=params_dict.get("sub_bend2", sub_bend2_diagnostic),
|
||||
fac_p3m_fit=params_dict.get("fac_p3m_fit", DEFAULT_FAC_P3M_FIT),
|
||||
use_p3m_fit=params_dict.get("use_p3m_fit", False),
|
||||
da_max_early_custom=params_dict.get("da_max_early_custom", DEFAULT_DA_MAX_EARLY_CUSTOM),
|
||||
da_max_late_custom=params_dict.get("da_max_late_custom", DEFAULT_DA_MAX_LATE_CUSTOM),
|
||||
#############################
|
||||
## Cosmological parameters ##
|
||||
#############################
|
||||
|
@ -428,12 +484,19 @@ def run_simulation(name, params, wd, logdir):
|
|||
from wip3m.low_level import stderr_redirector
|
||||
from pysbmy import pySbmy
|
||||
|
||||
file_ext = f"{name}_nsteps{params['nsteps']}" if params.get("nsteps") is not None else None
|
||||
if params.get("nsteps") is not None:
|
||||
file_ext = f"{name}_nsteps{params['nsteps']}"
|
||||
elif params.get("relax") is not None:
|
||||
file_ext = f"{name}_relax{str(params['relax']).replace('.', '_')}"
|
||||
elif params.get("file_ext") is not None:
|
||||
file_ext = params["file_ext"]
|
||||
else:
|
||||
file_ext = None
|
||||
method = params["method"]
|
||||
path = wd + file_ext + "_" if file_ext else wd
|
||||
sbmy_path = joinstrs([path, "example_", method, ".sbmy"])
|
||||
log_path = joinstrs([logdir, file_ext, method, ".txt"])
|
||||
|
||||
|
||||
f = BytesIO()
|
||||
with stderr_redirector(f):
|
||||
pySbmy(sbmy_path, log_path)
|
||||
|
|
86
submit/actual_p3m_limiter_cola.sh
Normal file
86
submit/actual_p3m_limiter_cola.sh
Normal file
|
@ -0,0 +1,86 @@
|
|||
#!/bin/bash
|
||||
#SBATCH --job-name=cola_2_1_05_L64_N64_Np64_v4
|
||||
#SBATCH --output=/data70/hoellinger/WIP3M/cola_2_1_05_L64_N64_Np64_v4/log.log
|
||||
#SBATCH --error=/data70/hoellinger/WIP3M/cola_2_1_05_L64_N64_Np64_v4/err.err
|
||||
#SBATCH --nodes=1 # Number of nodes (value or min-max)
|
||||
#SBATCH --ntasks=64 # The number of tasks (i.e. cores) per node
|
||||
#SBATCH --partition=comp,pscomp # Partition name
|
||||
#SBATCH --time=12:00:00
|
||||
|
||||
##SBATCH --exclusive
|
||||
##SBATCH --nodelist=i26 # Node name
|
||||
##SBATCH --mem=64G # Memory pool for all cores (see also --mem-per-cpu)
|
||||
##SBATCH --array=0-10 # Size of the array
|
||||
##SBATCH --constraint=? # Constraint e.g. specific node type
|
||||
|
||||
conda activate p3m
|
||||
|
||||
export OMP_NUM_THREADS=16
|
||||
python $WIP3M_ROOT_PATH"src/wip3m/convergence_cola_p3m_only_expl.py" \
|
||||
--run_id cola_2_1_05_L64_N64_Np64_v4 \
|
||||
--L 64 \
|
||||
--N 64 \
|
||||
--Np 64 \
|
||||
--Npm 128 \
|
||||
--n_Tiles 16 \
|
||||
--z_i 19.0 \
|
||||
--z_f 0.0 \
|
||||
--plot_fields True \
|
||||
--scale_limiter "fac_p3m_fit" \
|
||||
--use_p3m_fit True \
|
||||
--scaling_p3m1 2.0 \
|
||||
--scaling_p3m2 1.0 \
|
||||
--scaling_p3m3 0.5
|
||||
|
||||
# export OMP_NUM_THREADS=32
|
||||
# python $WIP3M_ROOT_PATH"src/wip3m/convergence_cola_p3m_only_expl.py" \
|
||||
# --run_id cola_2_1_05_L128_N128_Np128 \
|
||||
# --L 128 \
|
||||
# --N 128 \
|
||||
# --Np 128 \
|
||||
# --Npm 256 \
|
||||
# --n_Tiles 32 \
|
||||
# --z_i 19.0 \
|
||||
# --z_f 0.0 \
|
||||
# --plot_fields True \
|
||||
# --scale_limiter "fac_p3m_fit" \
|
||||
# --use_p3m_fit True \
|
||||
# --scaling_p3m1 2.0 \
|
||||
# --scaling_p3m2 1.0 \
|
||||
# --scaling_p3m3 0.5
|
||||
|
||||
# export OMP_NUM_THREADS=64
|
||||
# python $WIP3M_ROOT_PATH"src/wip3m/convergence_cola_p3m_only_expl.py" \
|
||||
# --run_id cola_432_L1024_N512_Np512 \
|
||||
# --L 1024 \
|
||||
# --N 512 \
|
||||
# --Np 512 \
|
||||
# --Npm 1024 \
|
||||
# --n_Tiles 128 \
|
||||
# --z_i 19.0 \
|
||||
# --z_f 0.0 \
|
||||
# --plot_fields True \
|
||||
# --scale_limiter "fac_p3m_fit" \
|
||||
# --use_p3m_fit True \
|
||||
# --scaling_p3m1 4.0 \
|
||||
# --scaling_p3m2 3.0 \
|
||||
# --scaling_p3m3 2.0
|
||||
|
||||
# export OMP_NUM_THREADS=64
|
||||
# python $WIP3M_ROOT_PATH"src/wip3m/convergence_cola_p3m_only_expl.py" \
|
||||
# --run_id cola_432_L128_N256_Np128 \
|
||||
# --L 128 \
|
||||
# --N 256 \
|
||||
# --Np 128 \
|
||||
# --Npm 256 \
|
||||
# --n_Tiles 32 \
|
||||
# --z_i 19.0 \
|
||||
# --z_f 0.0 \
|
||||
# --plot_fields True \
|
||||
# --scale_limiter "fac_p3m_fit" \
|
||||
# --use_p3m_fit True \
|
||||
# --scaling_p3m1 4.0 \
|
||||
# --scaling_p3m2 3.0 \
|
||||
# --scaling_p3m3 2.0
|
||||
|
||||
exit 0
|
38
submit/colalim.sh
Normal file
38
submit/colalim.sh
Normal file
|
@ -0,0 +1,38 @@
|
|||
#!/bin/bash
|
||||
#SBATCH --job-name=cola_zi19_zf0L512_N512_Np512
|
||||
#SBATCH --output=/data70/hoellinger/WIP3M/cola_zi19_zf0L512_N512_Np512/log.log
|
||||
#SBATCH --error=/data70/hoellinger/WIP3M/cola_zi19_zf0L512_N512_Np512/err.err
|
||||
#SBATCH --nodes=1 # Number of nodes (value or min-max)
|
||||
#SBATCH --ntasks=128 # The number of tasks (i.e. cores) per node
|
||||
#SBATCH --partition=comp,pscomp # Partition name
|
||||
#SBATCH --time=48:00:00
|
||||
|
||||
##SBATCH --exclusive
|
||||
##SBATCH --nodelist=i26 # Node name
|
||||
##SBATCH --mem=64G # Memory pool for all cores (see also --mem-per-cpu)
|
||||
##SBATCH --array=0-10 # Size of the array
|
||||
##SBATCH --constraint=? # Constraint e.g. specific node type
|
||||
|
||||
conda activate p3m
|
||||
|
||||
export OMP_NUM_THREADS=64
|
||||
python $WIP3M_ROOT_PATH"src/wip3m/convergence_cola_expl_parser.py" \
|
||||
--run_id cola_zi19_zf0L512_N512_Np512 \
|
||||
--L 512 \
|
||||
--N 512 \
|
||||
--Np 512 \
|
||||
--Npm 1024 \
|
||||
--n_Tiles 128 \
|
||||
--z_i 19.0 \
|
||||
--z_f 0.0 \
|
||||
--plot_fields True \
|
||||
--scale_limiter "fac_bend" \
|
||||
--scaling_pmref 1.2 \
|
||||
--scaling_pm1 1.0 \
|
||||
--scaling_pm2 0.8 \
|
||||
--scaling_spm 1.2 \
|
||||
--scaling_p3m1 1.2 \
|
||||
--scaling_p3m2 1.0 \
|
||||
--scaling_p3m3 0.8
|
||||
|
||||
exit 0
|
|
@ -1,139 +1,45 @@
|
|||
#!/bin/bash
|
||||
#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 --job-name=obz99_zf19_ts0_L256_N1024_Np512
|
||||
#SBATCH --output=/data70/hoellinger/WIP3M/obz99_zf19_ts0_L256_N1024_Np512/log.log
|
||||
#SBATCH --error=/data70/hoellinger/WIP3M/obz99_zf19_ts0_L256_N1024_Np512/err.err
|
||||
#SBATCH --nodes=1 # Number of nodes (value or min-max)
|
||||
#SBATCH --ntasks=128 # The number of tasks (i.e. cores) per node
|
||||
#SBATCH --partition=comp,pscomp # Partition name
|
||||
#SBATCH --time=24:00:00
|
||||
|
||||
##SBATCH --exclusive
|
||||
##SBATCH --nodelist=i26 # Node name
|
||||
##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
|
||||
##SBATCH --constraint=? # Constraint e.g. specific node type
|
||||
|
||||
conda activate p3m
|
||||
|
||||
export OMP_NUM_THREADS=128
|
||||
|
||||
python $WIP3M_ROOT_PATH"src/wip3m/convergence_baseline_ts_parser.py" \
|
||||
--run_id cappel_L1024_Np512 \
|
||||
--L 1024 \
|
||||
export OMP_NUM_THREADS=64
|
||||
python $WIP3M_ROOT_PATH"src/wip3m/compute_limiters_from_baseline_ts.py" \
|
||||
--run_id obz99_zf19_ts0_L256_N1024_Np512\
|
||||
--L 256 \
|
||||
--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_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
|
||||
--z_i 99.0 \
|
||||
--z_f 19.0 \
|
||||
--nsteps_pmref 50 \
|
||||
--nsteps_pm1 10 \
|
||||
--nsteps_pm2 5 \
|
||||
--nsteps_p3m1 50 \
|
||||
--nsteps_p3m2 20 \
|
||||
--nsteps_p3m3 10 \
|
||||
--nsteps_p3m4 5 \
|
||||
--nsteps_p3m5 3 \
|
||||
--tsd_pmref 0 \
|
||||
--tsd_pm1 0 \
|
||||
--tsd_pm2 0 \
|
||||
--tsd_p3m1 0 \
|
||||
--tsd_p3m2 0 \
|
||||
--tsd_p3m3 0 \
|
||||
--tsd_p3m4 0 \
|
||||
--tsd_p3m5 0
|
||||
|
||||
exit 0
|
38
submit/custom_convergence_ts_expl.sh
Normal file
38
submit/custom_convergence_ts_expl.sh
Normal file
|
@ -0,0 +1,38 @@
|
|||
#!/bin/bash
|
||||
#SBATCH --job-name=s2_tiny_zi199_zf0_L64_N256_Np128
|
||||
#SBATCH --output=/data70/hoellinger/WIP3M/s2_tiny_zi199_zf0_L64_N256_Np128/log.log
|
||||
#SBATCH --error=/data70/hoellinger/WIP3M/s2_tiny_zi199_zf0_L64_N256_Np128/err.err
|
||||
#SBATCH --nodes=1 # Number of nodes (value or min-max)
|
||||
#SBATCH --ntasks=128 # The number of tasks (i.e. cores) per node
|
||||
#SBATCH --partition=comp,pscomp # Partition name
|
||||
#SBATCH --time=24:00:00
|
||||
|
||||
##SBATCH --exclusive
|
||||
##SBATCH --nodelist=i26 # Node name
|
||||
##SBATCH --mem=64G # Memory pool for all cores (see also --mem-per-cpu)
|
||||
##SBATCH --array=0-10 # Size of the array
|
||||
##SBATCH --constraint=? # Constraint e.g. specific node type
|
||||
|
||||
conda activate p3m
|
||||
|
||||
export OMP_NUM_THREADS=64
|
||||
python $WIP3M_ROOT_PATH"src/wip3m/convergence_custom_ts_expl_parser.py" \
|
||||
--run_id s2_tiny_zi199_zf0_L64_N256_Np128 \
|
||||
--L 64 \
|
||||
--N 256 \
|
||||
--Np 128 \
|
||||
--Npm 256 \
|
||||
--n_Tiles 32 \
|
||||
--z_i 199.0 \
|
||||
--z_f 0.0 \
|
||||
--plot_fields True \
|
||||
--scale_limiter "fac_H_custom" \
|
||||
--scaling_pmref 2.0 \
|
||||
--scaling_pm1 1.5 \
|
||||
--scaling_pm2 1.2 \
|
||||
--scaling_spm 2.0 \
|
||||
--scaling_p3m1 2.0 \
|
||||
--scaling_p3m2 1.5 \
|
||||
--scaling_p3m3 1.2
|
||||
|
||||
exit 0
|
38
submit/limiters.sh
Normal file
38
submit/limiters.sh
Normal file
|
@ -0,0 +1,38 @@
|
|||
#!/bin/bash
|
||||
#SBATCH --job-name=s4_tiny_zi13_5_zf0_L128_N256_Np128
|
||||
#SBATCH --output=/data70/hoellinger/WIP3M/s4_tiny_zi13_5_zf0_L128_N256_Np128/log.log
|
||||
#SBATCH --error=/data70/hoellinger/WIP3M/s4_tiny_zi13_5_zf0_L128_N256_Np128/err.err
|
||||
#SBATCH --nodes=1 # Number of nodes (value or min-max)
|
||||
#SBATCH --ntasks=64 # The number of tasks (i.e. cores) per node
|
||||
#SBATCH --partition=comp,pscomp # Partition name
|
||||
#SBATCH --time=24:00:00
|
||||
|
||||
##SBATCH --exclusive
|
||||
##SBATCH --nodelist=i26 # Node name
|
||||
##SBATCH --mem=64G # Memory pool for all cores (see also --mem-per-cpu)
|
||||
##SBATCH --array=0-10 # Size of the array
|
||||
##SBATCH --constraint=? # Constraint e.g. specific node type
|
||||
|
||||
conda activate p3m
|
||||
|
||||
export OMP_NUM_THREADS=64
|
||||
python $WIP3M_ROOT_PATH"src/wip3m/convergence_custom_ts_expl_parser.py" \
|
||||
--run_id s4_tiny_zi13_5_zf0_L128_N256_Np128 \
|
||||
--L 128 \
|
||||
--N 256 \
|
||||
--Np 128 \
|
||||
--Npm 256 \
|
||||
--n_Tiles 32 \
|
||||
--z_i 13.5 \
|
||||
--z_f 0.0 \
|
||||
--plot_fields True \
|
||||
--scale_limiter "fac_bend" \
|
||||
--scaling_pmref 4.0 \
|
||||
--scaling_pm1 3.0 \
|
||||
--scaling_pm2 2.0 \
|
||||
--scaling_spm 4.0 \
|
||||
--scaling_p3m1 4.0 \
|
||||
--scaling_p3m2 3.0 \
|
||||
--scaling_p3m3 2.0
|
||||
|
||||
exit 0
|
50
submit/only_cola_p3m.sh
Normal file
50
submit/only_cola_p3m.sh
Normal file
|
@ -0,0 +1,50 @@
|
|||
#!/bin/bash
|
||||
#SBATCH --job-name=colazf08_2_08_07_L512_N1024_Np512
|
||||
#SBATCH --output=/data70/hoellinger/WIP3M/colazf08_2_08_07_L512_N1024_Np512/log.log
|
||||
#SBATCH --error=/data70/hoellinger/WIP3M/colazf08_2_08_07_L512_N1024_Np512/err.err
|
||||
#SBATCH --nodes=1 # Number of nodes (value or min-max)
|
||||
#SBATCH --ntasks=128 # The number of tasks (i.e. cores) per node
|
||||
#SBATCH --partition=comp,pscomp # Partition name
|
||||
#SBATCH --time=24:00:00
|
||||
|
||||
##SBATCH --exclusive
|
||||
##SBATCH --nodelist=i26 # Node name
|
||||
##SBATCH --mem=64G # Memory pool for all cores (see also --mem-per-cpu)
|
||||
##SBATCH --array=0-10 # Size of the array
|
||||
##SBATCH --constraint=? # Constraint e.g. specific node type
|
||||
|
||||
conda activate p3m
|
||||
|
||||
# export OMP_NUM_THREADS=64
|
||||
# python $WIP3M_ROOT_PATH"src/wip3m/convergence_cola_p3m_only_expl.py" \
|
||||
# --run_id cola_H_L128_N128_Np128 \
|
||||
# --L 128 \
|
||||
# --N 128 \
|
||||
# --Np 128 \
|
||||
# --Npm 256 \
|
||||
# --n_Tiles 32 \
|
||||
# --z_i 19.0 \
|
||||
# --z_f 0.0 \
|
||||
# --plot_fields True \
|
||||
# --scale_limiter "fac_H_custom" \
|
||||
# --scaling_p3m1 1.0 \
|
||||
# --scaling_p3m2 0.6 \
|
||||
# --scaling_p3m3 0.3
|
||||
|
||||
export OMP_NUM_THREADS=64
|
||||
python $WIP3M_ROOT_PATH"src/wip3m/convergence_cola_p3m_only_expl.py" \
|
||||
--run_id colazf08_2_08_07_L512_N1024_Np512 \
|
||||
--L 512 \
|
||||
--N 1024 \
|
||||
--Np 512 \
|
||||
--Npm 1024 \
|
||||
--n_Tiles 128 \
|
||||
--z_i 19.0 \
|
||||
--z_f 0.8 \
|
||||
--plot_fields True \
|
||||
--scale_limiter "fac_bend" \
|
||||
--scaling_p3m1 2.0 \
|
||||
--scaling_p3m2 0.8 \
|
||||
--scaling_p3m3 0.7
|
||||
|
||||
exit 0
|
23
submit/plot_limiters_from_baseline_ts.sh
Normal file
23
submit/plot_limiters_from_baseline_ts.sh
Normal file
|
@ -0,0 +1,23 @@
|
|||
#!/bin/bash
|
||||
#SBATCH --job-name=obz99_zf19_L128_N512_Np256
|
||||
#SBATCH -o /data70/hoellinger/WIP3M/obz99_zf19_L128_N512_Np256/plots.log
|
||||
#SBATCH -N 1 # Number of nodes (value or min-max)
|
||||
#SBATCH -n 128 # The number of tasks (i.e. cores) per node
|
||||
#SBATCH --partition=comp,pscomp # Partition name
|
||||
#SBATCH --time=12: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=? # Constraint e.g. specific node type
|
||||
|
||||
conda activate p3m
|
||||
|
||||
export OMP_NUM_THREADS=64
|
||||
|
||||
python $WIP3M_ROOT_PATH"src/wip3m/plot_limiters_from_baseline_ts.py" \
|
||||
--run_id obz99_zf19_L128_N512_Np256 \
|
||||
--outdir_suffix plots \
|
||||
--lpt False
|
||||
|
||||
exit 0
|
|
@ -1,24 +1,24 @@
|
|||
#!/bin/bash
|
||||
#SBATCH --job-name=cappel_L1024_Np512
|
||||
#SBATCH -o /data70/hoellinger/WIP3M/cappel_L1024_Np512/plots.log
|
||||
#SBATCH --exclusive
|
||||
#SBATCH --job-name=ob_tsd1_L512_N1024_Np512
|
||||
#SBATCH -o /data70/hoellinger/WIP3M/ob_tsd1_L512_N1024_Np512/plots.log
|
||||
#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 --partition=comp,pscomp # Partition name
|
||||
#SBATCH --time=12: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
|
||||
##SBATCH --constraint=? # Constraint e.g. specific node type
|
||||
|
||||
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 \
|
||||
--run_id ob_tsd1_L512_N1024_Np512 \
|
||||
--outdir_suffix plots \
|
||||
--lpt True \
|
||||
--cola False
|
||||
--lpt False \
|
||||
--cola True
|
||||
|
||||
exit 0
|
Loading…
Add table
Add a link
Reference in a new issue