simjecture 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- conjecture_solver/__init__.py +10 -0
- conjecture_solver/__main__.py +4 -0
- conjecture_solver/action_handlers.py +459 -0
- conjecture_solver/adapters/__init__.py +7 -0
- conjecture_solver/adapters/base.py +96 -0
- conjecture_solver/adapters/fake.py +155 -0
- conjecture_solver/adapters/pic.py +196 -0
- conjecture_solver/adapters/warpx.py +1375 -0
- conjecture_solver/autonomous_research.py +1071 -0
- conjecture_solver/benchmarks/__init__.py +6 -0
- conjecture_solver/benchmarks/electrostatic_pic.py +547 -0
- conjecture_solver/benchmarks/kinetic_sufficiency.py +318 -0
- conjecture_solver/builtin_skills/warpx/SKILL.md +151 -0
- conjecture_solver/builtin_skills/warpx/agents/openai.yaml +4 -0
- conjecture_solver/builtin_skills/warpx/examples/implicit_em_smoke.py +74 -0
- conjecture_solver/builtin_skills/warpx/examples/minimal_smoke.py +54 -0
- conjecture_solver/builtin_skills/warpx/examples/openpmd_field_smoke.py +87 -0
- conjecture_solver/builtin_skills/warpx/manifest.json +11 -0
- conjecture_solver/builtin_skills/warpx/references/2d-xz-commissioning.md +145 -0
- conjecture_solver/builtin_skills/warpx/references/cpu-launch-tuning.md +54 -0
- conjecture_solver/builtin_skills/warpx/references/diagnostics.md +139 -0
- conjecture_solver/builtin_skills/warpx/references/gpu-launch-tuning.md +79 -0
- conjecture_solver/builtin_skills/warpx/references/local-cuda-deployment.md +87 -0
- conjecture_solver/builtin_skills/warpx/references/numerical-risks.md +32 -0
- conjecture_solver/builtin_skills/warpx/references/picmi-interface.md +118 -0
- conjecture_solver/builtin_skills/warpx/references/resource-scaling.md +24 -0
- conjecture_solver/builtin_skills/warpx/references/time-integration.md +58 -0
- conjecture_solver/builtin_skills/warpx/scripts/benchmark_cpu_threads.py +207 -0
- conjecture_solver/builtin_skills/warpx/scripts/benchmark_gpu.py +242 -0
- conjecture_solver/builtin_skills/warpx/scripts/bootstrap_local_cuda.sh +82 -0
- conjecture_solver/builtin_skills/warpx/scripts/probe_local_cuda.py +110 -0
- conjecture_solver/builtin_skills/warpx/scripts/reduced_energy_budget.py +195 -0
- conjecture_solver/builtin_skills/warpx/scripts/run_local_cuda.sh +39 -0
- conjecture_solver/campaign.py +406 -0
- conjecture_solver/cli.py +963 -0
- conjecture_solver/confirmation.py +287 -0
- conjecture_solver/control.py +234 -0
- conjecture_solver/deployment.py +874 -0
- conjecture_solver/discovery.py +68 -0
- conjecture_solver/domains/__init__.py +40 -0
- conjecture_solver/domains/base.py +157 -0
- conjecture_solver/domains/kinetic_sufficiency.py +521 -0
- conjecture_solver/ledger.py +238 -0
- conjecture_solver/lifecycle.py +131 -0
- conjecture_solver/literature.py +494 -0
- conjecture_solver/llm.py +228 -0
- conjecture_solver/models.py +279 -0
- conjecture_solver/mvp_agent.py +3212 -0
- conjecture_solver/mvp_claims.py +1279 -0
- conjecture_solver/mvp_control.py +322 -0
- conjecture_solver/mvp_guidance.py +172 -0
- conjecture_solver/mvp_launch.py +1296 -0
- conjecture_solver/mvp_monitor.py +1487 -0
- conjecture_solver/mvp_skills.py +538 -0
- conjecture_solver/orchestration.py +578 -0
- conjecture_solver/outbox.py +526 -0
- conjecture_solver/parameters.py +394 -0
- conjecture_solver/proposals.py +427 -0
- conjecture_solver/research_tools.py +278 -0
- conjecture_solver/schema_export.py +246 -0
- conjecture_solver/search.py +1047 -0
- conjecture_solver/semantics.py +146 -0
- conjecture_solver/tui/__init__.py +7 -0
- conjecture_solver/tui/app.py +202 -0
- conjecture_solver/tui/claim_views.py +213 -0
- conjecture_solver/tui/screens.py +1014 -0
- conjecture_solver/warpx_analysis.py +184 -0
- conjecture_solver/warpx_campaign.py +148 -0
- conjecture_solver/warpx_confirmation.py +617 -0
- simjecture-0.1.0.dist-info/METADATA +400 -0
- simjecture-0.1.0.dist-info/RECORD +75 -0
- simjecture-0.1.0.dist-info/WHEEL +4 -0
- simjecture-0.1.0.dist-info/entry_points.txt +4 -0
- simjecture-0.1.0.dist-info/licenses/LICENSE +202 -0
- simjecture-0.1.0.dist-info/licenses/NOTICE +6 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Reproduce the non-MPI 2D CUDA + openPMD WarpX runtime on a Linux/WSL host.
|
|
5
|
+
# The source tree is intentionally supplied by the caller: deployments must
|
|
6
|
+
# pin and audit the WarpX release rather than downloading an implicit branch.
|
|
7
|
+
|
|
8
|
+
usage() {
|
|
9
|
+
echo "usage: $0 --source WARPX_SOURCE [--jobs N] [--arch N]" >&2
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
source_tree=""
|
|
13
|
+
jobs=8
|
|
14
|
+
cuda_arch=""
|
|
15
|
+
while (($#)); do
|
|
16
|
+
case "$1" in
|
|
17
|
+
--source) source_tree="$2"; shift 2 ;;
|
|
18
|
+
--jobs) jobs="$2"; shift 2 ;;
|
|
19
|
+
--arch) cuda_arch="$2"; shift 2 ;;
|
|
20
|
+
*) usage; exit 2 ;;
|
|
21
|
+
esac
|
|
22
|
+
done
|
|
23
|
+
[[ -n "$source_tree" ]] || { usage; exit 2; }
|
|
24
|
+
source_tree="$(cd "$source_tree" && pwd)"
|
|
25
|
+
|
|
26
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
|
+
repo_root="$(cd "$script_dir/../../.." && pwd)"
|
|
28
|
+
cuda_root="$repo_root/.runtime/cuda-toolkit-12.4"
|
|
29
|
+
io_root="$repo_root/.runtime/warpx-cuda-openpmd-deps"
|
|
30
|
+
python_root="$repo_root/.runtime/warpx-cuda-openpmd"
|
|
31
|
+
|
|
32
|
+
for command in mamba python3.12 nvidia-smi; do
|
|
33
|
+
command -v "$command" >/dev/null || {
|
|
34
|
+
echo "missing deployment prerequisite: $command" >&2
|
|
35
|
+
exit 2
|
|
36
|
+
}
|
|
37
|
+
done
|
|
38
|
+
if [[ ! -e /dev/dxg && ! -e /dev/nvidia0 ]]; then
|
|
39
|
+
echo "no WSL or native NVIDIA device node found" >&2
|
|
40
|
+
exit 2
|
|
41
|
+
fi
|
|
42
|
+
if [[ -z "$cuda_arch" ]]; then
|
|
43
|
+
cuda_arch="$(nvidia-smi --query-gpu=compute_cap --format=csv,noheader | head -1 | tr -d '.')"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
mamba create -y -p "$cuda_root" -c conda-forge \
|
|
47
|
+
cuda-nvcc=12.4 cuda-cudart-dev=12.4 libcublas-dev=12.4 \
|
|
48
|
+
libcurand-dev=10.3.5.147 libcusparse-dev=12.3.1.170 \
|
|
49
|
+
cuda-profiler-api=12.4 cuda-nvtx-dev=12.4 \
|
|
50
|
+
gcc_linux-64=12.4 gxx_linux-64=12.4
|
|
51
|
+
mamba create -y -p "$io_root" -c conda-forge \
|
|
52
|
+
'hdf5=1.14.6=nompi_*' cmake ninja pkg-config
|
|
53
|
+
|
|
54
|
+
python3.12 -m venv --system-site-packages "$python_root"
|
|
55
|
+
"$python_root/bin/python" -m pip install --upgrade \
|
|
56
|
+
pip setuptools wheel packaging picmistandard==0.34.0 \
|
|
57
|
+
periodictable openpmd-api matplotlib numpy
|
|
58
|
+
|
|
59
|
+
# Keep Windows toolchains out of discovery under WSL. HDF5_ROOT and the
|
|
60
|
+
# non-MPI build prefix prevent a Windows/MPI HDF5 config from being selected.
|
|
61
|
+
export PATH="$cuda_root/bin:/usr/local/bin:/usr/bin:/bin:$io_root/bin"
|
|
62
|
+
export CUDA_PATH="$cuda_root"
|
|
63
|
+
export HDF5_ROOT="$io_root"
|
|
64
|
+
export CMAKE_PREFIX_PATH="$io_root"
|
|
65
|
+
export PKG_CONFIG_PATH="$io_root/lib/pkgconfig"
|
|
66
|
+
driver_path=""
|
|
67
|
+
[[ -d /usr/lib/wsl/lib ]] && driver_path="/usr/lib/wsl/lib:"
|
|
68
|
+
export LD_LIBRARY_PATH="${driver_path}$cuda_root/lib:$cuda_root/lib64:$io_root/lib"
|
|
69
|
+
export CPATH=/usr/include
|
|
70
|
+
export CC="$cuda_root/bin/x86_64-conda-linux-gnu-cc"
|
|
71
|
+
export CXX="$cuda_root/bin/x86_64-conda-linux-gnu-c++"
|
|
72
|
+
export CUDAHOSTCXX="$CXX"
|
|
73
|
+
export CUDACXX="$cuda_root/bin/nvcc"
|
|
74
|
+
export CMAKE_CUDA_ARCHITECTURES="$cuda_arch"
|
|
75
|
+
export WARPX_COMPUTE=CUDA WARPX_MPI=OFF WARPX_DIMS=2 WARPX_EB=OFF
|
|
76
|
+
export WARPX_OPENPMD=ON WARPX_OPENPMD_INTERNAL=ON
|
|
77
|
+
export WARPX_FFT=OFF WARPX_QED=OFF WARPX_QED_TABLE_GEN=OFF
|
|
78
|
+
export WARPX_PYTHON_IPO=OFF BUILD_PARALLEL="$jobs"
|
|
79
|
+
|
|
80
|
+
"$python_root/bin/python" -m pip install --no-build-isolation -v "$source_tree"
|
|
81
|
+
"$script_dir/run_local_cuda.sh" "$script_dir/probe_local_cuda.py" \
|
|
82
|
+
--require-openpmd
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Report whether the loaded WarpX runtime is usable for local CUDA work."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
import subprocess
|
|
9
|
+
import tempfile
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def hdf5_roundtrip(workdir: Path | None) -> bool:
|
|
16
|
+
"""Write and read one scalar through the openPMD HDF5 backend."""
|
|
17
|
+
import openpmd_api as io
|
|
18
|
+
|
|
19
|
+
def exercise(root: Path) -> bool:
|
|
20
|
+
path = root / "openpmd-probe.h5"
|
|
21
|
+
series = io.Series(str(path), io.Access.create)
|
|
22
|
+
component = series.iterations[0].meshes["probe"][
|
|
23
|
+
io.Mesh_Record_Component.SCALAR
|
|
24
|
+
]
|
|
25
|
+
component.reset_dataset(io.Dataset(np.dtype("float64"), [1]))
|
|
26
|
+
component.store_chunk(np.array([1.25]))
|
|
27
|
+
series.close()
|
|
28
|
+
|
|
29
|
+
readback = io.Series(str(path), io.Access.read_only)
|
|
30
|
+
loaded = readback.iterations[0].meshes["probe"][
|
|
31
|
+
io.Mesh_Record_Component.SCALAR
|
|
32
|
+
].load_chunk()
|
|
33
|
+
readback.flush()
|
|
34
|
+
value = float(loaded[0])
|
|
35
|
+
readback.close()
|
|
36
|
+
return value == 1.25
|
|
37
|
+
|
|
38
|
+
if workdir is not None:
|
|
39
|
+
workdir.mkdir(parents=True, exist_ok=True)
|
|
40
|
+
return exercise(workdir)
|
|
41
|
+
with tempfile.TemporaryDirectory(prefix="warpx-openpmd-probe-") as path:
|
|
42
|
+
return exercise(Path(path))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def main() -> int:
|
|
46
|
+
parser = argparse.ArgumentParser()
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"--require-openpmd",
|
|
49
|
+
action="store_true",
|
|
50
|
+
help="fail unless a temporary HDF5 openPMD series can be read back",
|
|
51
|
+
)
|
|
52
|
+
parser.add_argument("--workdir", type=Path)
|
|
53
|
+
args = parser.parse_args()
|
|
54
|
+
|
|
55
|
+
import amrex.space2d as amrex
|
|
56
|
+
import openpmd_api as io
|
|
57
|
+
|
|
58
|
+
smi = subprocess.run(
|
|
59
|
+
[
|
|
60
|
+
"nvidia-smi",
|
|
61
|
+
"--query-gpu=name,memory.total,compute_cap,driver_version",
|
|
62
|
+
"--format=csv,noheader,nounits",
|
|
63
|
+
],
|
|
64
|
+
check=True,
|
|
65
|
+
text=True,
|
|
66
|
+
capture_output=True,
|
|
67
|
+
).stdout.splitlines()[0]
|
|
68
|
+
name, memory_mib, compute_capability, driver = (
|
|
69
|
+
item.strip() for item in smi.split(",")
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
openpmd_hdf5 = bool(io.variants.get("hdf5", False))
|
|
73
|
+
openpmd_roundtrip = openpmd_hdf5 and hdf5_roundtrip(args.workdir)
|
|
74
|
+
result = {
|
|
75
|
+
"amrex": {
|
|
76
|
+
"gpu_backend": str(amrex.Config.gpu_backend),
|
|
77
|
+
"have_gpu": bool(amrex.Config.have_gpu),
|
|
78
|
+
"have_mpi": bool(amrex.Config.have_mpi),
|
|
79
|
+
"have_omp": bool(amrex.Config.have_omp),
|
|
80
|
+
},
|
|
81
|
+
"gpu": {
|
|
82
|
+
"name": name,
|
|
83
|
+
"memory_mib": int(memory_mib),
|
|
84
|
+
"compute_capability": compute_capability,
|
|
85
|
+
"driver": driver,
|
|
86
|
+
},
|
|
87
|
+
"openpmd_api": {
|
|
88
|
+
"version": io.__version__,
|
|
89
|
+
"hdf5": openpmd_hdf5,
|
|
90
|
+
"mpi": bool(io.variants.get("mpi", False)),
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
usable = result["amrex"]["have_gpu"] and (
|
|
94
|
+
str(result["amrex"]["gpu_backend"]).upper() == "CUDA"
|
|
95
|
+
)
|
|
96
|
+
result["checks"] = {
|
|
97
|
+
"cuda_warpx": usable,
|
|
98
|
+
"openpmd_hdf5_reader": openpmd_hdf5,
|
|
99
|
+
"openpmd_hdf5_roundtrip": openpmd_roundtrip,
|
|
100
|
+
}
|
|
101
|
+
print(json.dumps(result, indent=2, sort_keys=True))
|
|
102
|
+
if not usable:
|
|
103
|
+
return 1
|
|
104
|
+
if args.require_openpmd and not openpmd_roundtrip:
|
|
105
|
+
return 1
|
|
106
|
+
return 0
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Read a pinned single-level WarpX field-plus-particle energy budget safely."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import hashlib
|
|
8
|
+
import json
|
|
9
|
+
import math
|
|
10
|
+
import re
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
HEADER_TOKEN = re.compile(r"\[(\d+)\]([^\s]+)")
|
|
15
|
+
FIELD_TOTAL_LABEL = "total_lev0(J)"
|
|
16
|
+
PARTICLE_TOTAL_LABEL = "total(J)"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def sha256(path: Path) -> str:
|
|
20
|
+
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def read_table(path: Path) -> tuple[list[str], list[list[float]]]:
|
|
24
|
+
lines = path.read_text().splitlines()
|
|
25
|
+
if not lines or not lines[0].startswith("#"):
|
|
26
|
+
raise ValueError(f"missing WarpX reduced-diagnostic header: {path}")
|
|
27
|
+
indexed = {int(index): label for index, label in HEADER_TOKEN.findall(lines[0])}
|
|
28
|
+
if not indexed or sorted(indexed) != list(range(max(indexed) + 1)):
|
|
29
|
+
raise ValueError(f"non-contiguous reduced-diagnostic header: {path}")
|
|
30
|
+
labels = [indexed[index] for index in range(len(indexed))]
|
|
31
|
+
rows: list[list[float]] = []
|
|
32
|
+
for line_number, line in enumerate(lines[1:], start=2):
|
|
33
|
+
if not line.strip() or line.startswith("#"):
|
|
34
|
+
continue
|
|
35
|
+
try:
|
|
36
|
+
row = [float(value) for value in line.split()]
|
|
37
|
+
except ValueError as exc:
|
|
38
|
+
raise ValueError(f"non-numeric row {line_number}: {path}") from exc
|
|
39
|
+
if len(row) != len(labels):
|
|
40
|
+
raise ValueError(
|
|
41
|
+
f"row {line_number} has {len(row)} columns; header has "
|
|
42
|
+
f"{len(labels)}: {path}"
|
|
43
|
+
)
|
|
44
|
+
if not all(math.isfinite(value) for value in row):
|
|
45
|
+
raise ValueError(f"non-finite value in row {line_number}: {path}")
|
|
46
|
+
rows.append(row)
|
|
47
|
+
if not rows:
|
|
48
|
+
raise ValueError(f"empty reduced diagnostic: {path}")
|
|
49
|
+
return labels, rows
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def exact_column(labels: list[str], required: str, *, path: Path) -> int:
|
|
53
|
+
matches = [index for index, label in enumerate(labels) if label == required]
|
|
54
|
+
if len(matches) != 1:
|
|
55
|
+
raise ValueError(
|
|
56
|
+
f"expected exactly one {required!r} column in {path}; found {len(matches)}. "
|
|
57
|
+
"Do not guess or sum total and component columns."
|
|
58
|
+
)
|
|
59
|
+
return matches[0]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def relative_change(initial: float, final: float, *, name: str) -> float:
|
|
63
|
+
if initial == 0.0:
|
|
64
|
+
raise ValueError(f"cannot normalize {name}: initial value is zero")
|
|
65
|
+
return (final - initial) / abs(initial)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def energy_budget(
|
|
69
|
+
field_path: Path,
|
|
70
|
+
particle_path: Path,
|
|
71
|
+
*,
|
|
72
|
+
time_atol: float,
|
|
73
|
+
) -> dict[str, Any]:
|
|
74
|
+
field_labels, field_rows = read_table(field_path)
|
|
75
|
+
particle_labels, particle_rows = read_table(particle_path)
|
|
76
|
+
field_total_column = exact_column(
|
|
77
|
+
field_labels, FIELD_TOTAL_LABEL, path=field_path
|
|
78
|
+
)
|
|
79
|
+
particle_total_column = exact_column(
|
|
80
|
+
particle_labels, PARTICLE_TOTAL_LABEL, path=particle_path
|
|
81
|
+
)
|
|
82
|
+
if len(field_rows) != len(particle_rows):
|
|
83
|
+
raise ValueError("field and particle diagnostics have different row counts")
|
|
84
|
+
for index, (field_row, particle_row) in enumerate(
|
|
85
|
+
zip(field_rows, particle_rows, strict=True)
|
|
86
|
+
):
|
|
87
|
+
if field_row[0] != particle_row[0] or not math.isclose(
|
|
88
|
+
field_row[1],
|
|
89
|
+
particle_row[1],
|
|
90
|
+
rel_tol=0.0,
|
|
91
|
+
abs_tol=time_atol,
|
|
92
|
+
):
|
|
93
|
+
raise ValueError(
|
|
94
|
+
f"field and particle step/time coordinates differ at row {index}"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
field = [row[field_total_column] for row in field_rows]
|
|
98
|
+
particle = [row[particle_total_column] for row in particle_rows]
|
|
99
|
+
combined = [
|
|
100
|
+
field_value + particle_value
|
|
101
|
+
for field_value, particle_value in zip(field, particle, strict=True)
|
|
102
|
+
]
|
|
103
|
+
return {
|
|
104
|
+
"schema_version": "0.1.0",
|
|
105
|
+
"kind": "warpx_reduced_energy_budget",
|
|
106
|
+
"selection_policy": (
|
|
107
|
+
"Select exact non-overlapping total columns from realized headers; "
|
|
108
|
+
"never add a total column to its component or per-species columns."
|
|
109
|
+
),
|
|
110
|
+
"inputs": {
|
|
111
|
+
"field_path": str(field_path),
|
|
112
|
+
"field_sha256": sha256(field_path),
|
|
113
|
+
"particle_path": str(particle_path),
|
|
114
|
+
"particle_sha256": sha256(particle_path),
|
|
115
|
+
},
|
|
116
|
+
"selected_columns": {
|
|
117
|
+
"field": {
|
|
118
|
+
"index": field_total_column,
|
|
119
|
+
"label": field_labels[field_total_column],
|
|
120
|
+
},
|
|
121
|
+
"particle": {
|
|
122
|
+
"index": particle_total_column,
|
|
123
|
+
"label": particle_labels[particle_total_column],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
"ignored_columns": {
|
|
127
|
+
"field": field_labels[field_total_column + 1 :],
|
|
128
|
+
"particle": particle_labels[particle_total_column + 1 :],
|
|
129
|
+
},
|
|
130
|
+
"checks": {
|
|
131
|
+
"rows_aligned": True,
|
|
132
|
+
"all_values_finite": True,
|
|
133
|
+
"exact_field_total_label": True,
|
|
134
|
+
"exact_particle_total_label": True,
|
|
135
|
+
"non_overlapping_totals_selected": True,
|
|
136
|
+
},
|
|
137
|
+
"points": len(combined),
|
|
138
|
+
"initial": {
|
|
139
|
+
"field_J": field[0],
|
|
140
|
+
"particle_J": particle[0],
|
|
141
|
+
"combined_J": combined[0],
|
|
142
|
+
},
|
|
143
|
+
"final": {
|
|
144
|
+
"field_J": field[-1],
|
|
145
|
+
"particle_J": particle[-1],
|
|
146
|
+
"combined_J": combined[-1],
|
|
147
|
+
},
|
|
148
|
+
"relative_change": {
|
|
149
|
+
"field": relative_change(field[0], field[-1], name="field energy"),
|
|
150
|
+
"particle": relative_change(
|
|
151
|
+
particle[0], particle[-1], name="particle energy"
|
|
152
|
+
),
|
|
153
|
+
"combined": relative_change(
|
|
154
|
+
combined[0], combined[-1], name="combined energy"
|
|
155
|
+
),
|
|
156
|
+
},
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
161
|
+
parser = argparse.ArgumentParser(
|
|
162
|
+
description=(
|
|
163
|
+
"Compute a single-level WarpX field-plus-particle energy budget by "
|
|
164
|
+
"exact header identity."
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
parser.add_argument("field_energy", type=Path)
|
|
168
|
+
parser.add_argument("particle_energy", type=Path)
|
|
169
|
+
parser.add_argument("--output", type=Path)
|
|
170
|
+
parser.add_argument("--time-atol", type=float, default=1.0e-30)
|
|
171
|
+
return parser
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def main() -> int:
|
|
175
|
+
parser = build_parser()
|
|
176
|
+
args = parser.parse_args()
|
|
177
|
+
if args.time_atol < 0.0:
|
|
178
|
+
parser.error("--time-atol must be non-negative")
|
|
179
|
+
try:
|
|
180
|
+
result = energy_budget(
|
|
181
|
+
args.field_energy,
|
|
182
|
+
args.particle_energy,
|
|
183
|
+
time_atol=args.time_atol,
|
|
184
|
+
)
|
|
185
|
+
except (OSError, ValueError) as exc:
|
|
186
|
+
parser.error(str(exc))
|
|
187
|
+
encoded = json.dumps(result, indent=2, sort_keys=True) + "\n"
|
|
188
|
+
if args.output is not None:
|
|
189
|
+
args.output.write_text(encoded)
|
|
190
|
+
print(encoded, end="")
|
|
191
|
+
return 0
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
if __name__ == "__main__":
|
|
195
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Launch the project-local WarpX CUDA runtime under WSL. The WSL driver stub
|
|
5
|
+
# must precede Ubuntu's libcuda, and the source-built wheel bundles pyAMReX in
|
|
6
|
+
# a nested site-packages directory.
|
|
7
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
repo_root="$(cd "$script_dir/../../.." && pwd)"
|
|
9
|
+
cuda_root="$repo_root/.runtime/cuda-toolkit-12.4"
|
|
10
|
+
io_root="$repo_root/.runtime/warpx-cuda-openpmd-deps"
|
|
11
|
+
python_root="${WARPX_CUDA_RUNTIME:-$repo_root/.runtime/warpx-cuda-openpmd}"
|
|
12
|
+
if [[ ! -x "$python_root/bin/python" && -z "${WARPX_CUDA_RUNTIME:-}" ]]; then
|
|
13
|
+
python_root="$repo_root/.runtime/warpx-cuda"
|
|
14
|
+
fi
|
|
15
|
+
if [[ ! -x "$python_root/bin/python" ]]; then
|
|
16
|
+
echo "missing WarpX CUDA Python runtime: $python_root" >&2
|
|
17
|
+
exit 2
|
|
18
|
+
fi
|
|
19
|
+
python_site="$($python_root/bin/python -c \
|
|
20
|
+
"import sysconfig; print(sysconfig.get_paths()['purelib'])")"
|
|
21
|
+
bundled_site="$python_site/pywarpx/site-packages"
|
|
22
|
+
if [[ ! -x "$cuda_root/bin/nvcc" ]]; then
|
|
23
|
+
echo "missing CUDA 12.4 toolkit: $cuda_root" >&2
|
|
24
|
+
exit 2
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
export CUDA_PATH="$cuda_root"
|
|
28
|
+
driver_path=""
|
|
29
|
+
[[ -d /usr/lib/wsl/lib ]] && driver_path="/usr/lib/wsl/lib:"
|
|
30
|
+
io_path=""
|
|
31
|
+
[[ -d "$io_root/lib" ]] && io_path="$io_root/lib:"
|
|
32
|
+
export LD_LIBRARY_PATH="${driver_path}$cuda_root/lib:$cuda_root/lib64:${io_path}$python_site/pywarpx${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
|
33
|
+
export PYTHONPATH="$bundled_site:$python_site${PYTHONPATH:+:$PYTHONPATH}"
|
|
34
|
+
export OMP_NUM_THREADS=1
|
|
35
|
+
export OPENBLAS_NUM_THREADS=1
|
|
36
|
+
export MKL_NUM_THREADS=1
|
|
37
|
+
export NUMEXPR_NUM_THREADS=1
|
|
38
|
+
|
|
39
|
+
exec "$python_root/bin/python" "$@"
|