#!/usr/bin/env bash # ============================================================================ # diagnose.sh — Diagnose linking and configuration issues # # Run this if verify.py fails. It prints detailed information about # the environment that helps debug library conflicts. # # Usage: # conda activate firedrake # bash diagnose.sh # ============================================================================ set -uo pipefail echo "═══════════════════════════════════════════════════════════════" echo "Firedrake conda environment diagnostics" echo "═══════════════════════════════════════════════════════════════" echo "" echo "── Platform ─────────────────────────────────────────────────" echo "OS: $(uname -s)" echo "Arch: $(uname -m)" echo "Kernel: $(uname -r)" if [[ "$(uname -s)" == "Linux" ]]; then echo "Distro: $(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 | tr -d '"')" echo "glibc: $(ldd --version 2>&1 | head -1)" fi echo "" echo "── Conda ────────────────────────────────────────────────────" echo "conda: $(conda --version 2>/dev/null || echo 'not found')" echo "mamba: $(mamba --version 2>/dev/null | head -1 || echo 'not found')" echo "CONDA_PREFIX: ${CONDA_PREFIX:-not set}" echo "Active env: ${CONDA_DEFAULT_ENV:-none}" echo "" echo "── Key environment variables ────────────────────────────────" for var in PETSC_DIR PETSC_ARCH CC CXX FC HDF5_MPI HDF5_DIR \ PYOP2_CFLAGS PYOP2_LDFLAGS OMP_NUM_THREADS \ LD_LIBRARY_PATH DYLD_LIBRARY_PATH PATH; do val="${!var:-}" # Truncate long PATH values if [[ ${#val} -gt 120 ]]; then val="${val:0:120}..." fi printf " %-22s %s\n" "$var" "$val" done echo "" echo "── Compilers ────────────────────────────────────────────────" for cmd in cc gcc mpicc mpicxx mpifort; do loc=$(which "$cmd" 2>/dev/null || echo "not found") printf " %-10s %s\n" "$cmd" "$loc" done echo "" echo "── MPI ────────────────────────────────────────────────────────" if command -v mpicc &> /dev/null; then echo "mpicc -show:" mpicc -show 2>/dev/null || mpicc --showme 2>/dev/null || echo " (could not determine)" fi if command -v mpiexec &> /dev/null; then mpiexec --version 2>&1 | head -3 fi echo "" echo "── Shared libraries in CONDA_PREFIX/lib ─────────────────────" if [[ -n "${CONDA_PREFIX:-}" ]]; then for lib in libpetsc libslepc libmpi libhdf5 libopenblas libsuperlu_dist \ libdmumps libscotch libmetis libparmetis; do found=$(ls "${CONDA_PREFIX}/lib/${lib}"* 2>/dev/null | head -1) if [[ -n "$found" ]]; then printf " %-25s %s\n" "$lib" "$(basename "$found")" else printf " %-25s %s\n" "$lib" "NOT FOUND" fi done fi echo "" echo "── Python packages ──────────────────────────────────────────" python3 -c " import sys print(f' Python: {sys.version}') print(f' Prefix: {sys.prefix}') print() pkgs = [ 'numpy', 'mpi4py', 'petsc4py', 'slepc4py', 'h5py', 'ufl', 'FIAT', 'finat', 'tsfc', 'loopy', 'pyop2', 'firedrake', 'icepack', ] for name in pkgs: try: mod = __import__(name) ver = getattr(mod, '__version__', '?') loc = getattr(mod, '__file__', '?') # Shorten path loc = loc.replace(sys.prefix, '\$PREFIX') print(f' {name:15s} {ver:20s} {loc}') except ImportError as e: print(f' {name:15s} IMPORT FAILED: {e}') " echo "" echo "── petsc4py linking ─────────────────────────────────────────" if [[ "$(uname -s)" == "Linux" ]]; then PETSC4PY_SO=$(python3 -c "import petsc4py; print(petsc4py.__file__)" 2>/dev/null) if [[ -n "$PETSC4PY_SO" ]]; then PETSC4PY_DIR=$(dirname "$PETSC4PY_SO") SO_FILE=$(find "$PETSC4PY_DIR" -name "PETSc*.so" 2>/dev/null | head -1) if [[ -n "$SO_FILE" ]]; then echo " petsc4py .so: $SO_FILE" echo " Links against:" LD_LIBRARY_PATH="${CONDA_PREFIX}/lib:${LD_LIBRARY_PATH:-}" ldd "$SO_FILE" 2>/dev/null | grep -E "petsc|mpi|hdf5|blas|lapack" | sed 's/^/ /' fi fi elif [[ "$(uname -s)" == "Darwin" ]]; then PETSC4PY_SO=$(python3 -c "import petsc4py; print(petsc4py.__file__)" 2>/dev/null) if [[ -n "$PETSC4PY_SO" ]]; then PETSC4PY_DIR=$(dirname "$PETSC4PY_SO") SO_FILE=$(find "$PETSC4PY_DIR" -name "PETSc*.so" -o -name "PETSc*.dylib" 2>/dev/null | head -1) if [[ -n "$SO_FILE" ]]; then echo " petsc4py .so: $SO_FILE" echo " Links against:" otool -L "$SO_FILE" 2>/dev/null | grep -E "petsc|mpi|hdf5|blas|lapack" | sed 's/^/ /' fi fi fi echo "" echo "── JIT compilation test ─────────────────────────────────────" python3 -c " import os, tempfile, subprocess, sys cc = os.environ.get('CC', 'mpicc') prefix = os.environ.get('CONDA_PREFIX', '') # Write a minimal C file that links against PETSc src = ''' #include int main(int argc, char **argv) { PetscInitialize(&argc, &argv, NULL, NULL); PetscPrintf(PETSC_COMM_WORLD, \"PETSc JIT test OK\\\\n\"); PetscFinalize(); return 0; } ''' with tempfile.NamedTemporaryFile(suffix='.c', mode='w', delete=False) as f: f.write(src) src_path = f.name out_path = src_path.replace('.c', '') # Build command similar to what PyOP2 would use cmd = [ cc, src_path, '-o', out_path, f'-I{prefix}/include', f'-L{prefix}/lib', '-lpetsc', f'-Wl,-rpath,{prefix}/lib', ] print(f' Compile command: {\" \".join(cmd)}') result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode == 0: print(' ✓ Compilation succeeded') run = subprocess.run([out_path], capture_output=True, text=True) if run.returncode == 0: print(f' ✓ Execution succeeded: {run.stdout.strip()}') else: print(f' ✗ Execution failed: {run.stderr.strip()}') else: print(f' ✗ Compilation failed:') print(f' stdout: {result.stdout.strip()}') print(f' stderr: {result.stderr.strip()}') os.unlink(src_path) try: os.unlink(out_path) except: pass " 2>&1 echo "" echo "═══════════════════════════════════════════════════════════════" echo "Done. If issues persist, share this output when seeking help." echo "═══════════════════════════════════════════════════════════════"