Density correction v37: pooled n=484 refit (supersedes v33) (#49) #163
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| jobs: | |
| fortran-syntax: | |
| name: Fortran syntax check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install gfortran | |
| run: sudo apt-get update && sudo apt-get install -y gfortran | |
| - name: Check base module files compile | |
| run: | | |
| PASS=0 | |
| FAIL=0 | |
| # Check critical base files that have been modernized | |
| # Note: CONTRL.f90 excluded because it INCLUDEs variant headers | |
| for f in src-converted/base/main.f90 \ | |
| src-converted/base/myopen.f90 \ | |
| src-converted/base/volkey.f90 \ | |
| src-converted/base/algevl.f90 \ | |
| src-converted/base/evldx.f90 \ | |
| src-converted/base/errgro.f90; do | |
| if [ -f "$f" ]; then | |
| if gfortran -c -fsyntax-only -ffree-form -fPIC "$f" 2>/dev/null; then | |
| echo "[OK] $f" | |
| PASS=$((PASS+1)) | |
| else | |
| echo "[FAIL] $f" | |
| FAIL=$((FAIL+1)) | |
| fi | |
| fi | |
| done | |
| echo "" | |
| echo "Results: $PASS passed, $FAIL failed" | |
| [ "$FAIL" -eq 0 ] || exit 1 | |
| build-ne-variant: | |
| name: Build FVS-NE shared library | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install gfortran | |
| run: sudo apt-get update && sudo apt-get install -y gfortran | |
| - name: Build NE variant | |
| run: | | |
| bash deployment/scripts/build_fvs_libraries.sh \ | |
| src-converted lib ne | |
| echo "" | |
| echo "Build output:" | |
| ls -lh lib/FVS*.so 2>/dev/null || echo "No .so files produced" | |
| - name: Verify NE library symbols | |
| run: | | |
| SO=lib/FVSne.so | |
| if [ ! -f "$SO" ]; then | |
| echo "FAIL: FVSne.so not found" | |
| exit 1 | |
| fi | |
| SIZE=$(stat -c%s "$SO") | |
| echo "FVSne.so size: $SIZE bytes" | |
| if [ "$SIZE" -lt 1000000 ]; then | |
| echo "FAIL: Library suspiciously small (<1MB)" | |
| exit 1 | |
| fi | |
| # Check API symbols that fvs2py and rFVS use to drive simulations | |
| MISSING=0 | |
| for sym in fvssetcmdline_ fvssummary_ fvsdimsizes_ fvstreeattr_ volinit_; do | |
| if nm "$SO" 2>/dev/null | grep -q " [Tt] ${sym}"; then | |
| echo "[OK] $sym" | |
| else | |
| echo "[WARN] $sym not found" | |
| MISSING=$((MISSING+1)) | |
| fi | |
| done | |
| # fvssetcmdline_ and fvssummary_ are essential API entry points | |
| for sym in fvssetcmdline_ fvssummary_; do | |
| nm "$SO" 2>/dev/null | grep -q " [Tt] ${sym}" || { | |
| echo "FAIL: critical API symbol $sym missing" | |
| exit 1 | |
| } | |
| done | |
| echo "" | |
| TOTAL=$(nm "$SO" 2>/dev/null | grep -c " T ") | |
| echo "Total exported text symbols: $TOTAL" | |
| - name: Python ctypes load test | |
| run: | | |
| python3 << 'PYEOF' | |
| import ctypes, ctypes.util, os, sys | |
| so_path = os.path.abspath('lib/FVSne.so') | |
| print(f'Loading {so_path} via ctypes...') | |
| # FVS .so files have some unresolved internal symbols (e.g., debug_mod | |
| # from NVEL) that are harmless at runtime but cause RTLD_NOW to fail | |
| # on some Python/glibc combinations. Use RTLD_LAZY (0x1 on Linux). | |
| RTLD_LAZY = 0x1 | |
| try: | |
| lib = ctypes.CDLL(so_path, mode=RTLD_LAZY) | |
| except OSError: | |
| # Fallback: try default mode (works on some Python versions) | |
| try: | |
| lib = ctypes.CDLL(so_path) | |
| except OSError as e: | |
| print(f'FAIL: Could not load library: {e}') | |
| sys.exit(1) | |
| print(f' Loaded successfully: {lib._name}') | |
| # Verify key API functions are callable | |
| api_funcs = ['fvssetcmdline_', 'fvssummary_', 'fvsdimsizes_'] | |
| for fname in api_funcs: | |
| try: | |
| fn = getattr(lib, fname) | |
| print(f' [OK] {fname} -> {fn}') | |
| except AttributeError: | |
| print(f' [WARN] {fname} not found') | |
| # Verify fvssetcmdline_ is present (critical for any simulation) | |
| try: | |
| _ = lib.fvssetcmdline_ | |
| print() | |
| print('PASS: FVSne.so loads and API entry points are accessible') | |
| except AttributeError: | |
| print('FAIL: fvssetcmdline_ missing from loaded library') | |
| sys.exit(1) | |
| PYEOF | |
| build-executables: | |
| name: Build FVS executables (NE, LS) | |
| needs: build-ne-variant | |
| runs-on: ubuntu-latest | |
| # Exe builds may fail on CI due to gfortran version differences in | |
| # F77->F90 conversion handling. This job is informational: the | |
| # .so library build (above) is the gate that must pass. | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install gfortran | |
| run: sudo apt-get update && sudo apt-get install -y gfortran | |
| - name: Build shared libraries (needed for fallback linking) | |
| run: bash deployment/scripts/build_fvs_libraries.sh . lib ne ls | |
| - name: Build executables (NE + LS) | |
| run: | | |
| echo "gfortran version: $(gfortran --version | head -1)" | |
| ls -lh lib/FVS*.so 2>/dev/null || echo "No .so files yet" | |
| echo "" | |
| bash deployment/scripts/build_fvs_executables.sh . lib ne ls || true | |
| echo "" | |
| ls -lh lib/FVS* 2>/dev/null || echo "No executables found" | |
| echo "" | |
| for var in ne ls; do | |
| EXE="lib/FVS${var}" | |
| if [ -f "$EXE" ]; then | |
| SIZE=$(stat -c%s "$EXE") | |
| echo "FVS${var}: $SIZE bytes" | |
| else | |
| echo "FVS${var}: not built (gfortran version may differ from local)" | |
| fi | |
| done | |
| - name: Smoke test (banner prints and exits) | |
| run: | | |
| export LD_LIBRARY_PATH="$(pwd)/lib:${LD_LIBRARY_PATH:-}" | |
| for var in ne ls; do | |
| EXE="./lib/FVS${var}" | |
| if [ ! -f "$EXE" ]; then | |
| echo "SKIP: FVS${var} not available" | |
| continue | |
| fi | |
| OUTPUT=$(echo "" | timeout 10 "$EXE" 2>&1 || true) | |
| echo "=== FVS${var} ===" | |
| echo "$OUTPUT" | |
| if echo "$OUTPUT" | grep -q "FVS VARIANT"; then | |
| echo "PASS: FVS${var} banner detected" | |
| else | |
| echo "WARN: FVS${var} banner not found" | |
| fi | |
| done | |
| nvel-audit: | |
| name: NVEL coefficient audit | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Run NVEL upstream audit | |
| run: | | |
| python3 scripts/audit_nvel_upstream.py --verbose \ | |
| --allow-differ r8clist.inc | |
| lint: | |
| name: Lint scripts | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Shellcheck deployment scripts | |
| run: | | |
| ERRORS=0 | |
| for f in deployment/scripts/*.sh; do | |
| echo "Checking $f..." | |
| shellcheck --severity=error "$f" || ERRORS=$((ERRORS+1)) | |
| done | |
| # Report but don't fail on shellcheck warnings for now | |
| echo "Scripts with errors: $ERRORS" | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install ruff | |
| run: pip install ruff | |
| - name: Lint Python code | |
| run: | | |
| ruff check calibration/python/ deployment/fvs2py/ config/ \ | |
| --select=E,F,W --ignore=E501 || true |