Initial commit
This commit is contained in:
575
setup.sh
Executable file
575
setup.sh
Executable file
@@ -0,0 +1,575 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================================
|
||||
# setup.sh — Install Firedrake + icepack into a conda environment
|
||||
#
|
||||
# This script:
|
||||
# 1. Creates a conda environment with all native dependencies from conda-forge
|
||||
# 2. Sets environment variables so Firedrake finds conda's PETSc, MPI, etc.
|
||||
# 3. pip-installs Firedrake and icepack (skipping binary builds for
|
||||
# MPI-linked packages so they compile against conda's libraries)
|
||||
#
|
||||
# Usage:
|
||||
# bash setup.sh # env named "firedrake"
|
||||
# bash setup.sh my-env # env named "my-env"
|
||||
# bash setup.sh ./envs/firedrake # env at path ./envs/firedrake
|
||||
# FIREDRAKE_ENV=~/fd FIREDRAKE_REF=main bash setup.sh
|
||||
#
|
||||
# Prerequisites:
|
||||
# - conda or mamba installed
|
||||
# - internet access (to download packages)
|
||||
#
|
||||
# Tested on: Linux x86_64, macOS ARM64
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Configuration ──────────────────────────────────────────────────────────
|
||||
|
||||
# Environment name or path — accepts:
|
||||
# - A simple name like "firedrake" → conda env -n firedrake
|
||||
# - An absolute/relative path like "./envs/fd" → conda env -p ./envs/fd
|
||||
# Set via FIREDRAKE_ENV env var, first positional arg, or default "firedrake".
|
||||
ENV_TARGET="${FIREDRAKE_ENV:-${1:-firedrake}}"
|
||||
|
||||
# Detect if it's a path (contains / or starts with .)
|
||||
if [[ "$ENV_TARGET" == */* ]] || [[ "$ENV_TARGET" == .* ]]; then
|
||||
# Resolve to absolute path, creating parent dir if needed
|
||||
_parent="$(dirname "$ENV_TARGET")"
|
||||
mkdir -p "$_parent" 2>/dev/null || true
|
||||
ENV_TARGET="$(cd "$_parent" && pwd)/$(basename "$ENV_TARGET")"
|
||||
ENV_IS_PATH=true
|
||||
CONDA_ENV_FLAG="-p"
|
||||
else
|
||||
ENV_IS_PATH=false
|
||||
CONDA_ENV_FLAG="-n"
|
||||
fi
|
||||
ENV_DISPLAY="$ENV_TARGET"
|
||||
ENV_ACTIVATE="$ENV_TARGET"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Firedrake version: set to a release tag (e.g. "2025.10.2") or "main"
|
||||
FIREDRAKE_REF="${FIREDRAKE_REF:-${2:-2025.10.2}}"
|
||||
|
||||
# Prefer mamba if available (much faster solver)
|
||||
if command -v mamba &> /dev/null; then
|
||||
CONDA=mamba
|
||||
elif command -v conda &> /dev/null; then
|
||||
CONDA=conda
|
||||
else
|
||||
echo "ERROR: Neither conda nor mamba found. Please install one first."
|
||||
echo " See: https://docs.conda.io/en/latest/miniconda.html"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Colors ─────────────────────────────────────────────────────────────────
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
DIM='\033[2m'
|
||||
NC='\033[0m' # No color
|
||||
|
||||
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
fail() { echo -e "${RED}[FAIL]${NC} $*"; exit 1; }
|
||||
|
||||
# ── Live rolling tail ─────────────────────────────────────────────────────
|
||||
# Runs a command while showing a live, continuously-updating view of the
|
||||
# last N lines of its combined stdout/stderr. When the command finishes,
|
||||
# the rolling display is cleared and replaced with a summary.
|
||||
#
|
||||
# Usage: run_live <n_lines> <description> <command...>
|
||||
# Returns the exit code of the command.
|
||||
|
||||
TAIL_LINES=20
|
||||
|
||||
run_live() {
|
||||
local nlines="$1"; shift
|
||||
local desc="$1"; shift
|
||||
|
||||
local logfile
|
||||
logfile=$(mktemp /tmp/firedrake-install-XXXXXX.log)
|
||||
|
||||
echo -e "${DIM}─── ${desc} ───${NC}"
|
||||
|
||||
# Run the actual command in the background, directing all output to logfile
|
||||
"$@" > "$logfile" 2>&1 &
|
||||
local cmd_pid=$!
|
||||
|
||||
# Tail the logfile in real-time, showing last N lines with in-place rewriting
|
||||
python3 -u -c "
|
||||
import sys, os, time
|
||||
|
||||
nlines = int(sys.argv[1])
|
||||
path = sys.argv[2]
|
||||
pid = int(sys.argv[3])
|
||||
cols = int(os.environ.get('COLUMNS', 120))
|
||||
|
||||
buf = []
|
||||
shown = 0
|
||||
|
||||
def render():
|
||||
global shown
|
||||
# Move up to overwrite previous output
|
||||
if shown > 0:
|
||||
sys.stdout.write(f'\033[{shown}A')
|
||||
shown = 0
|
||||
for b in buf:
|
||||
sys.stdout.write(f'\033[2K {b}\n')
|
||||
shown += 1
|
||||
sys.stdout.flush()
|
||||
|
||||
def pid_alive(p):
|
||||
try:
|
||||
os.kill(p, 0)
|
||||
return True
|
||||
except ProcessLookupError:
|
||||
return False
|
||||
|
||||
with open(path, 'r') as f:
|
||||
while True:
|
||||
line = f.readline()
|
||||
if line:
|
||||
line = line.rstrip('\n')[:cols]
|
||||
buf.append(line)
|
||||
if len(buf) > nlines:
|
||||
buf = buf[-nlines:]
|
||||
render()
|
||||
else:
|
||||
# No new data — check if process is still running
|
||||
if not pid_alive(pid):
|
||||
# Drain any remaining lines
|
||||
for line in f:
|
||||
line = line.rstrip('\n')[:cols]
|
||||
buf.append(line)
|
||||
if len(buf) > nlines:
|
||||
buf = buf[-nlines:]
|
||||
render()
|
||||
break
|
||||
time.sleep(0.05)
|
||||
|
||||
# Clear the rolling display
|
||||
if shown > 0:
|
||||
sys.stdout.write(f'\033[{shown}A')
|
||||
for _ in range(shown):
|
||||
sys.stdout.write('\033[2K\n')
|
||||
sys.stdout.write(f'\033[{shown}A')
|
||||
sys.stdout.flush()
|
||||
" "$nlines" "$logfile" "$cmd_pid"
|
||||
|
||||
# Collect the exit code
|
||||
local exit_code=0
|
||||
wait "$cmd_pid" || exit_code=$?
|
||||
|
||||
if [[ $exit_code -ne 0 ]]; then
|
||||
warn "${desc} failed (exit code ${exit_code}). Full log: ${logfile}"
|
||||
echo -e "${DIM}─── Last 30 lines of ${logfile} ───${NC}"
|
||||
tail -30 "$logfile"
|
||||
echo -e "${DIM}────────────────────────────────────${NC}"
|
||||
else
|
||||
rm -f "$logfile"
|
||||
fi
|
||||
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
# ── Step 1: Create conda environment ──────────────────────────────────────
|
||||
|
||||
info "Creating conda environment '${ENV_DISPLAY}' from environment.yml ..."
|
||||
|
||||
# Check if environment already exists
|
||||
ENV_EXISTS=false
|
||||
if [[ "$ENV_IS_PATH" == true ]]; then
|
||||
[[ -d "${ENV_TARGET}/conda-meta" ]] && ENV_EXISTS=true
|
||||
else
|
||||
$CONDA env list 2>/dev/null | grep -q "^${ENV_TARGET} " && ENV_EXISTS=true
|
||||
fi
|
||||
|
||||
if [[ "$ENV_EXISTS" == true ]]; then
|
||||
warn "Environment '${ENV_DISPLAY}' already exists."
|
||||
read -p " Recreate it? [y/N] " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
$CONDA env remove ${CONDA_ENV_FLAG} "${ENV_TARGET}" -y
|
||||
else
|
||||
info "Reusing existing environment."
|
||||
fi
|
||||
fi
|
||||
|
||||
$CONDA env create ${CONDA_ENV_FLAG} "${ENV_TARGET}" -f "${SCRIPT_DIR}/environment.yml" || \
|
||||
$CONDA env update ${CONDA_ENV_FLAG} "${ENV_TARGET}" -f "${SCRIPT_DIR}/environment.yml"
|
||||
|
||||
ok "Conda environment created."
|
||||
|
||||
# ── Step 2: Activate and detect paths ─────────────────────────────────────
|
||||
|
||||
# We need to source conda's shell hook to use `conda activate` in a script
|
||||
eval "$(conda shell.bash hook)"
|
||||
conda activate "${ENV_ACTIVATE}"
|
||||
|
||||
CONDA_PREFIX="${CONDA_PREFIX}"
|
||||
info "CONDA_PREFIX = ${CONDA_PREFIX}"
|
||||
|
||||
# Detect platform
|
||||
OS="$(uname -s)"
|
||||
ARCH="$(uname -m)"
|
||||
info "Platform: ${OS} ${ARCH}"
|
||||
|
||||
if [[ "$OS" == "Darwin" ]]; then
|
||||
SOEXT="dylib"
|
||||
else
|
||||
SOEXT="so"
|
||||
fi
|
||||
|
||||
# ── Step 3: Verify conda packages ────────────────────────────────────────
|
||||
|
||||
info "Verifying critical conda packages ..."
|
||||
|
||||
# Check PETSc
|
||||
if [[ ! -f "${CONDA_PREFIX}/lib/libpetsc.${SOEXT}" ]]; then
|
||||
fail "PETSc shared library not found at ${CONDA_PREFIX}/lib/libpetsc.${SOEXT}"
|
||||
fi
|
||||
ok "PETSc found."
|
||||
|
||||
# Check MPI compiler wrappers
|
||||
if ! command -v mpicc &> /dev/null; then
|
||||
fail "mpicc not found. MPI compiler wrappers are missing."
|
||||
fi
|
||||
ok "MPI compilers found: $(which mpicc)"
|
||||
|
||||
# Check HDF5
|
||||
if [[ ! -f "${CONDA_PREFIX}/lib/libhdf5.${SOEXT}" ]]; then
|
||||
fail "HDF5 not found."
|
||||
fi
|
||||
ok "HDF5 found."
|
||||
|
||||
# ── Step 4: Set environment variables ─────────────────────────────────────
|
||||
|
||||
info "Setting environment variables for Firedrake ..."
|
||||
|
||||
# PETSc: conda installs in "prefix" mode (no PETSC_ARCH)
|
||||
export PETSC_DIR="${CONDA_PREFIX}"
|
||||
export PETSC_ARCH=""
|
||||
|
||||
# Compilers: use MPI wrappers from conda
|
||||
export CC=mpicc
|
||||
export CXX=mpicxx
|
||||
export FC=mpifort
|
||||
|
||||
# HDF5: tell h5py and Firedrake to use MPI-enabled HDF5
|
||||
export HDF5_MPI="ON"
|
||||
export HDF5_DIR="${CONDA_PREFIX}"
|
||||
|
||||
# Firedrake recommends single-threaded BLAS
|
||||
export OMP_NUM_THREADS=1
|
||||
|
||||
# PyOP2 JIT: conda's mpicc wraps x86_64-conda-linux-gnu-cc which PyOP2
|
||||
# doesn't recognise → falls back to generic compiler without -shared/-fPIC
|
||||
export PYOP2_CFLAGS="-fPIC -O2"
|
||||
export PYOP2_LDFLAGS="-shared -Wl,-rpath,${CONDA_PREFIX}/lib"
|
||||
|
||||
# Tell pip not to use cached wheels for MPI-linked packages
|
||||
# (they may be linked against the wrong MPI/PETSc)
|
||||
export PIP_NO_CACHE_DIR=1
|
||||
|
||||
info " PETSC_DIR = ${PETSC_DIR}"
|
||||
info " PETSC_ARCH = (empty — prefix install)"
|
||||
info " CC = ${CC}"
|
||||
info " HDF5_MPI = ${HDF5_MPI}"
|
||||
|
||||
# ── Step 5: Pip install Firedrake ecosystem ───────────────────────────────
|
||||
|
||||
info "Installing Firedrake and dependencies via pip ..."
|
||||
info "This may take several minutes (compiling C extensions) ..."
|
||||
|
||||
# Purge pip cache to avoid stale binaries linked against wrong libs
|
||||
pip cache purge 2>/dev/null || true
|
||||
|
||||
# Install packages that have C extensions and must link against conda's libs.
|
||||
# We use --no-binary to force compilation against the conda environment.
|
||||
# The order matters: dependencies first, then Firedrake, then icepack.
|
||||
|
||||
info " [1/5] Installing build dependencies ..."
|
||||
# These are needed as build backends / build-time deps by libsupermesh
|
||||
# and firedrake. We install them now so --no-build-isolation works
|
||||
# (which ensures C extensions link against conda's MPI/PETSc, not
|
||||
# some isolated copy).
|
||||
# Install ALL build backends that Firedrake's dependency tree uses.
|
||||
# --no-build-isolation means pip won't auto-install these, so we need
|
||||
# them all present before building anything.
|
||||
run_live $TAIL_LINES "Installing build backends" \
|
||||
pip install \
|
||||
setuptools wheel \
|
||||
scikit-build-core \
|
||||
meson-python meson ninja \
|
||||
hatchling hatch-vcs hatch-fancy-pypi-readme \
|
||||
flit-core \
|
||||
cython \
|
||||
pybind11 \
|
||||
petsctools
|
||||
ok "Build dependencies installed."
|
||||
|
||||
info " [2/5] Installing mpi4py, petsc4py, h5py (from source against conda libs) ..."
|
||||
# These should already be installed by conda, but if pip needs to reinstall
|
||||
# them (e.g. for Firedrake version constraints), force source builds.
|
||||
# Usually conda's versions are fine and pip will skip them.
|
||||
|
||||
info " [3/5] Installing libsupermesh ..."
|
||||
run_live $TAIL_LINES "libsupermesh" \
|
||||
pip install --no-build-isolation libsupermesh || {
|
||||
warn "libsupermesh pip install failed, trying from git ..."
|
||||
run_live $TAIL_LINES "libsupermesh (git)" \
|
||||
pip install --no-build-isolation \
|
||||
"libsupermesh @ git+https://github.com/firedrakeproject/libsupermesh.git"
|
||||
}
|
||||
ok "libsupermesh installed."
|
||||
|
||||
info " [4/5] Installing Firedrake (${FIREDRAKE_REF}) ..."
|
||||
# Firedrake's pyproject.toml lists dependencies like petsc4py, mpi4py, h5py
|
||||
# as git URLs. Conda already provides working versions of these, so we
|
||||
# generate a pip constraints file that pins them to the conda-installed
|
||||
# versions. This prevents pip from cloning & rebuilding petsc4py (which
|
||||
# fails on release tags due to a setuptools dry_run incompatibility).
|
||||
#
|
||||
# Clone first so the user can see git progress (pip hides it entirely).
|
||||
# Clones live in ~/.firedrake-conda/clones/ so they persist across
|
||||
# environment recreations (conda env remove + re-create).
|
||||
CLONE_DIR="${HOME}/.firedrake-conda/clones"
|
||||
mkdir -p "${CLONE_DIR}"
|
||||
|
||||
info " Cloning Firedrake (${FIREDRAKE_REF}) ..."
|
||||
if [[ -d "${CLONE_DIR}/firedrake" ]]; then
|
||||
info " (updating existing clone)"
|
||||
# Unshallow if needed (shallow clones can't switch tags)
|
||||
if [[ -f "${CLONE_DIR}/firedrake/.git/shallow" ]]; then
|
||||
git -C "${CLONE_DIR}/firedrake" fetch --progress --unshallow --tags origin 2>&1 || \
|
||||
git -C "${CLONE_DIR}/firedrake" fetch --progress --tags origin 2>&1
|
||||
else
|
||||
git -C "${CLONE_DIR}/firedrake" fetch --progress --tags origin 2>&1
|
||||
fi
|
||||
git -C "${CLONE_DIR}/firedrake" checkout "${FIREDRAKE_REF}" 2>&1
|
||||
if [[ "${FIREDRAKE_REF}" == "main" ]]; then
|
||||
git -C "${CLONE_DIR}/firedrake" reset --hard origin/main
|
||||
fi
|
||||
else
|
||||
# Full clone for tags; shallow clone for main
|
||||
if [[ "${FIREDRAKE_REF}" == "main" ]]; then
|
||||
git clone --progress --depth 1 --branch main \
|
||||
https://github.com/firedrakeproject/firedrake.git \
|
||||
"${CLONE_DIR}/firedrake" 2>&1
|
||||
else
|
||||
git clone --progress --branch "${FIREDRAKE_REF}" \
|
||||
https://github.com/firedrakeproject/firedrake.git \
|
||||
"${CLONE_DIR}/firedrake" 2>&1
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Generate pip constraints file ──────────────────────────────────────
|
||||
# Pin every conda-provided package so pip doesn't try to rebuild them
|
||||
# from git URLs in Firedrake's pyproject.toml.
|
||||
info " Generating pip constraints for conda packages ..."
|
||||
CONSTRAINTS="${CONDA_PREFIX}/constraints.txt"
|
||||
python3 - <<'PYEOF' > "${CONSTRAINTS}"
|
||||
import importlib.metadata, sys
|
||||
|
||||
# Packages that conda provides and pip must not rebuild
|
||||
CONDA_OWNS = [
|
||||
"petsc4py", "slepc4py", "mpi4py", "h5py", "numpy", "scipy",
|
||||
"sympy", "cython", "pybind11", "pkgconfig", "rtree",
|
||||
"scikit-build-core", "hatchling", "flit-core", "setuptools",
|
||||
]
|
||||
|
||||
for pkg in CONDA_OWNS:
|
||||
try:
|
||||
version = importlib.metadata.version(pkg)
|
||||
print(f"{pkg}=={version}")
|
||||
except importlib.metadata.PackageNotFoundError:
|
||||
pass
|
||||
PYEOF
|
||||
|
||||
info " Constraints file:"
|
||||
cat "${CONSTRAINTS}" | sed 's/^/ /'
|
||||
|
||||
# ── Patch pyproject.toml to remove conda-provided deps ─────────────────
|
||||
# Firedrake's release tags may list petsc4py, slepc4py, mpi4py, h5py as
|
||||
# git URL deps (@ git+https://...) or version pins. Pip can't satisfy
|
||||
# direct URL refs from conda, and tries to rebuild them — which fails
|
||||
# (e.g. petsc4py's setuptools dry_run bug). We strip these deps since
|
||||
# conda already provides them.
|
||||
info " Patching pyproject.toml to skip conda-provided deps ..."
|
||||
PYPROJECT="${CLONE_DIR}/firedrake/pyproject.toml"
|
||||
python3 - "${PYPROJECT}" <<'PYEOF'
|
||||
import re, sys
|
||||
|
||||
path = sys.argv[1]
|
||||
text = open(path).read()
|
||||
|
||||
# Packages conda provides — remove any line in dependencies[] that
|
||||
# starts with these names (handles version pins AND @ git+... URLs)
|
||||
CONDA_PKGS = ["petsc4py", "slepc4py", "mpi4py", "h5py"]
|
||||
|
||||
lines = text.split("\n")
|
||||
patched = []
|
||||
removed = []
|
||||
for line in lines:
|
||||
stripped = line.strip().strip('"').strip("'").strip(",").strip()
|
||||
skip = False
|
||||
for pkg in CONDA_PKGS:
|
||||
# Match "petsc4py...", " petsc4py...", '"petsc4py...', etc.
|
||||
if re.match(rf"^{re.escape(pkg)}(\s|>|<|=|@|;|$)", stripped):
|
||||
skip = True
|
||||
removed.append(stripped)
|
||||
break
|
||||
if not skip:
|
||||
patched.append(line)
|
||||
|
||||
if removed:
|
||||
open(path, "w").write("\n".join(patched))
|
||||
for r in removed:
|
||||
print(f" Removed: {r}")
|
||||
else:
|
||||
print(" (no conda-provided deps found to remove)")
|
||||
PYEOF
|
||||
|
||||
# ── pip install with constraints ───────────────────────────────────────
|
||||
info " pip install Firedrake (from local clone) ..."
|
||||
run_live $TAIL_LINES "Firedrake" \
|
||||
pip install --no-build-isolation -c "${CONSTRAINTS}" "${CLONE_DIR}/firedrake"
|
||||
ok "Firedrake installed."
|
||||
|
||||
info " [5/5] Installing icepack ..."
|
||||
info " Cloning icepack ..."
|
||||
if [[ -d "${CLONE_DIR}/icepack" ]]; then
|
||||
info " (updating existing clone)"
|
||||
git -C "${CLONE_DIR}/icepack" pull --progress 2>&1
|
||||
else
|
||||
git clone --progress --depth 1 \
|
||||
https://github.com/icepack/icepack.git \
|
||||
"${CLONE_DIR}/icepack" 2>&1
|
||||
fi
|
||||
|
||||
info " pip install icepack (from local clone) ..."
|
||||
run_live $TAIL_LINES "icepack" \
|
||||
pip install --no-build-isolation -c "${CONSTRAINTS}" "${CLONE_DIR}/icepack"
|
||||
ok "icepack installed."
|
||||
|
||||
# ── Step 6: Install activation script ────────────────────────────────────
|
||||
|
||||
info "Installing conda activation/deactivation scripts ..."
|
||||
|
||||
ACTIVATE_DIR="${CONDA_PREFIX}/etc/conda/activate.d"
|
||||
DEACTIVATE_DIR="${CONDA_PREFIX}/etc/conda/deactivate.d"
|
||||
mkdir -p "${ACTIVATE_DIR}" "${DEACTIVATE_DIR}"
|
||||
|
||||
cat > "${ACTIVATE_DIR}/firedrake-env-vars.sh" << 'ACTIVATE_EOF'
|
||||
#!/usr/bin/env bash
|
||||
# Set environment variables needed by Firedrake's JIT compiler.
|
||||
# These are automatically set on `conda activate`.
|
||||
|
||||
# Save previous values so we can restore on deactivate
|
||||
export _FIREDRAKE_OLD_CC="${CC:-}"
|
||||
export _FIREDRAKE_OLD_CXX="${CXX:-}"
|
||||
export _FIREDRAKE_OLD_FC="${FC:-}"
|
||||
export _FIREDRAKE_OLD_PETSC_DIR="${PETSC_DIR:-}"
|
||||
export _FIREDRAKE_OLD_PETSC_ARCH="${PETSC_ARCH:-}"
|
||||
export _FIREDRAKE_OLD_HDF5_MPI="${HDF5_MPI:-}"
|
||||
export _FIREDRAKE_OLD_HDF5_DIR="${HDF5_DIR:-}"
|
||||
export _FIREDRAKE_OLD_OMP_NUM_THREADS="${OMP_NUM_THREADS:-}"
|
||||
export _FIREDRAKE_OLD_PYOP2_CFLAGS="${PYOP2_CFLAGS:-}"
|
||||
export _FIREDRAKE_OLD_PYOP2_LDFLAGS="${PYOP2_LDFLAGS:-}"
|
||||
|
||||
# PETSc
|
||||
export PETSC_DIR="${CONDA_PREFIX}"
|
||||
export PETSC_ARCH=""
|
||||
|
||||
# Compilers — use MPI wrappers so JIT-compiled kernels link against
|
||||
# the correct MPI and can be loaded alongside PETSc.
|
||||
export CC=mpicc
|
||||
export CXX=mpicxx
|
||||
export FC=mpifort
|
||||
|
||||
# HDF5
|
||||
export HDF5_MPI="ON"
|
||||
export HDF5_DIR="${CONDA_PREFIX}"
|
||||
|
||||
# Firedrake recommends single-threaded BLAS to avoid oversubscription
|
||||
export OMP_NUM_THREADS=1
|
||||
|
||||
# PyOP2 JIT flags — conda's mpicc wraps x86_64-conda-linux-gnu-cc
|
||||
# which PyOP2 doesn't recognise, so it falls back to a generic compiler
|
||||
# class that omits -shared and -fPIC. These env vars inject the flags.
|
||||
export PYOP2_CFLAGS="-fPIC -O2"
|
||||
export PYOP2_LDFLAGS="-shared -Wl,-rpath,${CONDA_PREFIX}/lib"
|
||||
ACTIVATE_EOF
|
||||
|
||||
cat > "${DEACTIVATE_DIR}/firedrake-env-vars.sh" << 'DEACTIVATE_EOF'
|
||||
#!/usr/bin/env bash
|
||||
# Restore environment variables on `conda deactivate`.
|
||||
|
||||
export CC="${_FIREDRAKE_OLD_CC}"
|
||||
export CXX="${_FIREDRAKE_OLD_CXX}"
|
||||
export FC="${_FIREDRAKE_OLD_FC}"
|
||||
export PETSC_DIR="${_FIREDRAKE_OLD_PETSC_DIR}"
|
||||
export PETSC_ARCH="${_FIREDRAKE_OLD_PETSC_ARCH}"
|
||||
export HDF5_MPI="${_FIREDRAKE_OLD_HDF5_MPI}"
|
||||
export HDF5_DIR="${_FIREDRAKE_OLD_HDF5_DIR}"
|
||||
export OMP_NUM_THREADS="${_FIREDRAKE_OLD_OMP_NUM_THREADS}"
|
||||
export PYOP2_CFLAGS="${_FIREDRAKE_OLD_PYOP2_CFLAGS}"
|
||||
export PYOP2_LDFLAGS="${_FIREDRAKE_OLD_PYOP2_LDFLAGS}"
|
||||
|
||||
unset _FIREDRAKE_OLD_CC
|
||||
unset _FIREDRAKE_OLD_CXX
|
||||
unset _FIREDRAKE_OLD_FC
|
||||
unset _FIREDRAKE_OLD_PETSC_DIR
|
||||
unset _FIREDRAKE_OLD_PETSC_ARCH
|
||||
unset _FIREDRAKE_OLD_HDF5_MPI
|
||||
unset _FIREDRAKE_OLD_HDF5_DIR
|
||||
unset _FIREDRAKE_OLD_OMP_NUM_THREADS
|
||||
unset _FIREDRAKE_OLD_PYOP2_CFLAGS
|
||||
unset _FIREDRAKE_OLD_PYOP2_LDFLAGS
|
||||
|
||||
# Clean up empty vars
|
||||
[[ -z "$CC" ]] && unset CC
|
||||
[[ -z "$CXX" ]] && unset CXX
|
||||
[[ -z "$FC" ]] && unset FC
|
||||
[[ -z "$PETSC_DIR" ]] && unset PETSC_DIR
|
||||
[[ -z "$PETSC_ARCH" ]] && unset PETSC_ARCH
|
||||
[[ -z "$HDF5_MPI" ]] && unset HDF5_MPI
|
||||
[[ -z "$HDF5_DIR" ]] && unset HDF5_DIR
|
||||
[[ -z "$OMP_NUM_THREADS" ]] && unset OMP_NUM_THREADS
|
||||
[[ -z "$PYOP2_CFLAGS" ]] && unset PYOP2_CFLAGS
|
||||
[[ -z "$PYOP2_LDFLAGS" ]] && unset PYOP2_LDFLAGS
|
||||
DEACTIVATE_EOF
|
||||
|
||||
chmod +x "${ACTIVATE_DIR}/firedrake-env-vars.sh"
|
||||
chmod +x "${DEACTIVATE_DIR}/firedrake-env-vars.sh"
|
||||
|
||||
ok "Activation scripts installed."
|
||||
|
||||
# ── Step 7: Verify ────────────────────────────────────────────────────────
|
||||
|
||||
info "Running verification ..."
|
||||
|
||||
python "${SCRIPT_DIR}/verify.py" && ok "All checks passed!" || {
|
||||
warn "Some checks failed. See output above."
|
||||
warn "You may need to debug linking issues."
|
||||
warn "Try: conda activate ${ENV_ACTIVATE} && python verify.py"
|
||||
}
|
||||
|
||||
# ── Done ──────────────────────────────────────────────────────────────────
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
ok "Installation complete!"
|
||||
echo ""
|
||||
echo " To use Firedrake + icepack:"
|
||||
echo ""
|
||||
echo " conda activate ${ENV_ACTIVATE}"
|
||||
echo " python -c 'import firedrake; import icepack; print(\"Ready!\")'"
|
||||
echo ""
|
||||
echo " Environment variables (PETSC_DIR, CC, etc.) are set automatically"
|
||||
echo " when you activate the environment."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
Reference in New Issue
Block a user