PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
bootstrap_install.sh
Go to the documentation of this file.
1#!/usr/bin/env bash
2set -euo pipefail
3
4SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
6
7INSTALL_PETSC=0
8SKIP_SYSTEM_DEPS=0
9USE_VENV=1
10WITH_PLOTTING=0
11INSTALL_SHELL_HOOK=0
12PETSC_VERSION="3.20.3"
13PETSC_PREFIX="${HOME}/software"
14PETSC_ARCH="arch-linux-c-debug"
15PYTHON_BIN=""
16VENV_DIR="${REPO_ROOT}/.picurv-venv"
17SHELL_RC="${HOME}/.bashrc"
18
19usage() {
20 cat <<'EOF'
21Usage: scripts/bootstrap_install.sh [options]
22
23Automates local PICurv setup:
241) installs base system dependencies (Debian/Ubuntu),
252) creates a managed Python virtual environment by default,
263) optionally installs PETSc with DMSwarm,
274) verifies PETSc + DMSwarm visibility,
285) builds PICurv binaries.
29
30Options:
31 --install-petsc Build/install PETSc from source.
32 --petsc-version <ver> PETSc version tag (default: 3.20.3).
33 --petsc-prefix <dir> Parent directory for PETSc source/build.
34 --petsc-arch <arch> PETSc arch name (default: arch-linux-c-debug).
35 --python-bin <path> Python interpreter to seed the venv or use directly.
36 --venv-dir <dir> Managed venv path (default: .picurv-venv).
37 --no-venv Use the selected Python directly.
38 --with-plotting Also install matplotlib for sweep plot generation.
39 --install-shell-hook Add an idempotent source line to ~/.bashrc.
40 --shell-rc <path> Shell startup file for --install-shell-hook.
41 --skip-system-deps Skip apt package installation.
42 -h, --help Show this help.
43
44Examples:
45 scripts/bootstrap_install.sh
46 scripts/bootstrap_install.sh --install-petsc
47 scripts/bootstrap_install.sh --install-shell-hook
48 scripts/bootstrap_install.sh --no-venv
49EOF
50}
51
52log() {
53 printf '[INFO] %s\n' "$*"
54}
55
56die() {
57 printf '[ERROR] %s\n' "$*" >&2
58 exit 1
59}
60
61require_cmd() {
62 command -v "$1" >/dev/null 2>&1 || die "Required command '$1' is missing."
63}
64
65absolute_path() {
66 local path="$1"
67 if [[ "${path}" = /* ]]; then
68 printf '%s\n' "${path}"
69 else
70 printf '%s/%s\n' "${REPO_ROOT}" "${path}"
71 fi
72}
73
74python_version_label() {
75 "$1" -c 'import sys; print(".".join(str(part) for part in sys.version_info[:3]))'
76}
77
78python_is_supported_seed() {
79 "$1" -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)'
80}
81
82python_runtime_library_dirs() {
83 local python_bin="$1"
84 local resolved_python
85
86 resolved_python="$(command -v "${python_bin}" || printf '%s' "${python_bin}")"
87 "${python_bin}" - <<'PY'
88import os
89import sysconfig
90
91for key in ("LIBDIR", "LIBPL"):
92 value = sysconfig.get_config_var(key)
93 if value and os.path.isdir(value):
94 print(os.path.realpath(value))
95PY
96 if command -v ldd >/dev/null 2>&1 && [[ -x "${resolved_python}" ]]; then
97 ldd "${resolved_python}" 2>/dev/null | awk '
98 /=> \// { path = $3 }
99 /^[[:space:]]*\// { path = $1 }
100 path != "" {
101 sub(/\/[^\/]+$/, "", path)
102 print path
103 path = ""
104 }
105 '
106 fi
107}
108
109write_python_env_file() {
110 local python_bin="$1"
111 local env_file="${REPO_ROOT}/.picurv-python-env"
112 local runtime_dirs
113
114 runtime_dirs="$(
115 python_runtime_library_dirs "${python_bin}" \
116 | awk '$0 !~ "^/(lib|lib64|usr/lib|usr/lib64)(/|$)"' \
117 | awk 'NF && !seen[$0]++' \
118 | paste -sd ':' -
119 )"
120 {
121 printf '# Generated by scripts/bootstrap_install.sh. Do not commit.\n'
122 printf 'PICURV_PYTHON_LD_LIBRARY_PATH=%q\n' "${runtime_dirs}"
123 } > "${env_file}"
124}
125
126install_shell_hook() {
127 local rc_path="$1"
128 local hook_path="${REPO_ROOT}/etc/picurv.sh"
129 local begin_marker="# >>> PICurv shell setup >>>"
130 local end_marker="# <<< PICurv shell setup <<<"
131 local tmp_path
132
133 mkdir -p "$(dirname "${rc_path}")"
134 touch "${rc_path}"
135 tmp_path="$(mktemp "${rc_path}.picurv.XXXXXX")"
136 awk -v begin="${begin_marker}" -v end="${end_marker}" '
137 $0 == begin {skip = 1; next}
138 $0 == end {skip = 0; next}
139 skip != 1 {print}
140 ' "${rc_path}" > "${tmp_path}"
141 {
142 printf '%s\n' "${begin_marker}"
143 printf 'source %q\n' "${hook_path}"
144 printf '%s\n' "${end_marker}"
145 } >> "${tmp_path}"
146 mv "${tmp_path}" "${rc_path}"
147}
148
149while [[ $# -gt 0 ]]; do
150 case "$1" in
151 --install-petsc) INSTALL_PETSC=1; shift ;;
152 --skip-system-deps) SKIP_SYSTEM_DEPS=1; shift ;;
153 --petsc-version) PETSC_VERSION="${2:?missing value for --petsc-version}"; shift 2 ;;
154 --petsc-prefix) PETSC_PREFIX="${2:?missing value for --petsc-prefix}"; shift 2 ;;
155 --petsc-arch) PETSC_ARCH="${2:?missing value for --petsc-arch}"; shift 2 ;;
156 --python-bin) PYTHON_BIN="${2:?missing value for --python-bin}"; shift 2 ;;
157 --venv-dir) VENV_DIR="$(absolute_path "${2:?missing value for --venv-dir}")"; shift 2 ;;
158 --no-venv) USE_VENV=0; shift ;;
159 --with-plotting) WITH_PLOTTING=1; shift ;;
160 --install-shell-hook) INSTALL_SHELL_HOOK=1; shift ;;
161 --shell-rc) SHELL_RC="$(absolute_path "${2:?missing value for --shell-rc}")"; shift 2 ;;
162 -h|--help) usage; exit 0 ;;
163 *) die "Unknown option: $1" ;;
164 esac
165done
166
167if [[ -z "${PYTHON_BIN}" ]]; then
168 if command -v python3.12 >/dev/null 2>&1; then
169 PYTHON_BIN="python3.12"
170 elif command -v python3.11 >/dev/null 2>&1; then
171 PYTHON_BIN="python3.11"
172 elif command -v python3.10 >/dev/null 2>&1; then
173 PYTHON_BIN="python3.10"
174 else
175 PYTHON_BIN="python3"
176 fi
177fi
178
179require_cmd "${PYTHON_BIN}"
180require_cmd git
181
182if [[ "${SKIP_SYSTEM_DEPS}" -eq 0 ]]; then
183 require_cmd apt-get
184 log "Installing base system dependencies (Debian/Ubuntu)..."
185 sudo apt-get update
186 sudo apt-get install -y \
187 build-essential gfortran mpich git make pkg-config \
188 libx11-dev python3 python3-pip python3-venv
189fi
190
191PYTHON_VERSION="$(python_version_label "${PYTHON_BIN}")"
192log "Selected Python seed: ${PYTHON_BIN} (${PYTHON_VERSION})"
193
194if [[ "${USE_VENV}" -eq 1 ]]; then
195 if ! python_is_supported_seed "${PYTHON_BIN}"; then
196 die "Managed venv installs require Python 3.10+; selected ${PYTHON_BIN} is ${PYTHON_VERSION}. Load a newer Python module, pass --python-bin, or use --no-venv."
197 fi
198 log "Creating/updating managed Python environment at ${VENV_DIR}..."
199 "${PYTHON_BIN}" -m venv "${VENV_DIR}"
200 CLI_PYTHON="${VENV_DIR}/bin/python"
201 env -u PYTHONPATH PYTHONNOUSERSITE=1 "${CLI_PYTHON}" -m pip install --upgrade pip
202 PYTHON_PACKAGES=(pyyaml numpy)
203 if [[ "${WITH_PLOTTING}" -eq 1 ]]; then
204 PYTHON_PACKAGES+=(matplotlib)
205 fi
206 log "Installing Python dependencies into managed venv: ${PYTHON_PACKAGES[*]}"
207 env -u PYTHONPATH PYTHONNOUSERSITE=1 "${CLI_PYTHON}" -m pip install "${PYTHON_PACKAGES[@]}"
208else
209 CLI_PYTHON="${PYTHON_BIN}"
210 log "Installing Python dependencies with site-managed Python: ${CLI_PYTHON}"
211 "${CLI_PYTHON}" -m pip install --upgrade pip
212 PYTHON_PACKAGES=(pyyaml numpy)
213 if [[ "${WITH_PLOTTING}" -eq 1 ]]; then
214 PYTHON_PACKAGES+=(matplotlib)
215 fi
216 "${CLI_PYTHON}" -m pip install "${PYTHON_PACKAGES[@]}"
217fi
218
219printf '%s\n' "$(command -v "${CLI_PYTHON}" || printf '%s' "${CLI_PYTHON}")" > "${REPO_ROOT}/.picurv-python"
220write_python_env_file "${CLI_PYTHON}"
221
222if [[ "${INSTALL_PETSC}" -eq 1 ]]; then
223 require_cmd mpicc
224 PETSC_SRC="${PETSC_PREFIX}/petsc-${PETSC_VERSION}"
225 mkdir -p "${PETSC_PREFIX}"
226
227 if [[ ! -d "${PETSC_SRC}" ]]; then
228 log "Cloning PETSc v${PETSC_VERSION} into ${PETSC_SRC}..."
229 git clone -b "v${PETSC_VERSION}" https://gitlab.com/petsc/petsc.git "${PETSC_SRC}"
230 else
231 log "Using existing PETSc source at ${PETSC_SRC}."
232 fi
233
234 log "Configuring PETSc with DMSwarm support..."
235 pushd "${PETSC_SRC}" >/dev/null
236 ./configure --PETSC_ARCH="${PETSC_ARCH}" \
237 --with-cc=mpicc --with-cxx=mpicxx --with-fc=mpif90 \
238 --download-fblaslapack --download-metis --download-parmetis \
239 --with-dmswarm=1 --with-debugging=1
240 make all
241 make check
242 popd >/dev/null
243
244 export PETSC_DIR="${PETSC_SRC}"
245 export PETSC_ARCH="${PETSC_ARCH}"
246fi
247
248if [[ -z "${PETSC_DIR:-}" ]]; then
249 die "PETSC_DIR must be set (or use --install-petsc)."
250fi
251
252PETSC_DMSWARM_HEADER="${PETSC_DIR}/include/petscdmswarm.h"
253PETSC_CONF_CANDIDATES=("${PETSC_DIR}/include/petscconf.h")
254if [[ -n "${PETSC_ARCH:-}" ]]; then
255 PETSC_CONF_CANDIDATES=("${PETSC_DIR}/${PETSC_ARCH}/include/petscconf.h" "${PETSC_CONF_CANDIDATES[@]}")
256fi
257PETSC_CONF=""
258for candidate in "${PETSC_CONF_CANDIDATES[@]}"; do
259 if [[ -f "${candidate}" ]]; then
260 PETSC_CONF="${candidate}"
261 break
262 fi
263done
264[[ -n "${PETSC_CONF}" ]] || die "PETSc config not found. Checked: ${PETSC_CONF_CANDIDATES[*]}"
265[[ -f "${PETSC_DMSWARM_HEADER}" ]] || die "DMSwarm header not found: ${PETSC_DMSWARM_HEADER}"
266
267log "Verified PETSc config (${PETSC_CONF}) and DMSwarm header."
268
269log "Building PICurv binaries..."
270cd "${REPO_ROOT}"
271"${CLI_PYTHON}" ./scripts/picurv build
272
273[[ -x "${REPO_ROOT}/bin/simulator" ]] || die "Missing binary: bin/simulator"
274[[ -x "${REPO_ROOT}/bin/postprocessor" ]] || die "Missing binary: bin/postprocessor"
275[[ -x "${REPO_ROOT}/bin/picurv" ]] || die "Missing binary: bin/picurv"
276
277log "Bootstrap complete."
278log "PICurv Python: ${CLI_PYTHON}"
279if [[ "${INSTALL_SHELL_HOOK}" -eq 1 ]]; then
280 install_shell_hook "${SHELL_RC}"
281 log "Installed PICurv shell hook in ${SHELL_RC}."
282 log "Reload with: source ${SHELL_RC}"
283else
284 log "To add picurv to your PATH, run:"
285 log " echo 'source ${REPO_ROOT}/etc/picurv.sh' >> ~/.bashrc && source ~/.bashrc"
286 log "Or rerun bootstrap with: --install-shell-hook"
287fi
288log "Then verify with: picurv --help"