Skip to content

Modify path and add a CI/CD workflow. #1

Modify path and add a CI/CD workflow.

Modify path and add a CI/CD workflow. #1

Workflow file for this run

name: interface
# 仅支持手动触发,不自动运行
on:
workflow_dispatch:
jobs:
wannier-interface:
name: "wannier interface — ${{ matrix.name }}"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- name: basic (LCAO)
script: example_basic.py
prefix: Bi2Se3_basic
basis: lcao
- name: pw (Plane Wave)
script: example_pw.py
prefix: Bi2Se3_pw
basis: pw
- name: advance (LCAO)
script: example_advance.py
prefix: Bi2Se3_advanced
basis: lcao
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install abacusw90
run: |
python -m pip install --upgrade pip
pip install -e .
- name: Confirm no wannier90.x in environment
run: |
if command -v wannier90.x 2>/dev/null; then
echo "::error::wannier90.x found — CI must test WITHOUT external binaries"
exit 1
fi
echo "OK: no wannier90.x (dryrun mode)"
# ================================================================
# Shared mock + patch (covers all three scripts)
# ================================================================
- name: Create mock data & patch script
run: python3 << 'PYEOF'
import os, re, textwrap

Check failure on line 55 in .github/workflows/interface.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/interface.yml

Invalid workflow file

You have an error in your yaml syntax on line 55
script = os.environ["SCRIPT"]
prefix = os.environ["PREFIX"]
# ── 1. Patch the example script ────────────────────────────
with open(script) as f:
src = f.read()
# Enable dry run
src = src.replace("DRY_RUN = False", "DRY_RUN = True")
# Skip step0 — requires ABACUS binary
src = re.sub(
r"\s*job\.step0_run_scf\([^)]*\)",
" # [CI:skip] step0_run_scf — no ABACUS in this environment",
src,
flags=re.DOTALL,
)
# Skip step1 — requires wannier90.x binary
src = src.replace(
"job.step1_generate_wannier_win()",
"# [CI:skip] step1_generate_wannier_win — no wannier90.x in this environment",
)
with open(script, "w") as f:
f.write(src)
print(f"[patch] {script}")
# ── 2. Create work directories ─────────────────────────────
for d in [f"{prefix}/scf", f"{prefix}/wannier"]:
os.makedirs(d, exist_ok=True)
# ── 3. Mock STRU (scf/ + wannier/) ────────────────────────
stru = textwrap.dedent("""\
ATOMIC_SPECIES
Bi 208.980 Bi.upf
Se 78.960 Se.upf
LATTICE_CONSTANT
1.0
LATTICE_VECTORS
-2.069 -3.583614 0.0
2.069 -3.583614 0.0
0.000 2.389075 9.546667
ATOMIC_POSITIONS
Direct
Bi
0.399 0.399 0.697 1 1 1
0.601 0.601 0.303 1 1 1
Se
0.000 0.000 0.500 1 1 1
0.206 0.206 0.118 1 1 1
0.794 0.794 0.882 1 1 1
""")
for d in [f"{prefix}/scf", f"{prefix}/wannier"]:
p = os.path.join(d, "STRU")
with open(p, "w") as f:
f.write(stru)
print(f"[mock] {p}")
# ── 4. Mock wannier90.nnkp ────────────────────────────────
nbands = 20
nk = 64 # 4 × 4 × 4
nntot = 14
lines = [
f" {nbands}", "! num_bands",
f" {nk}", "! num_kpts",
" 4 4 4", "! mp_grid",
]
for ix in range(4):
for iy in range(4):
for iz in range(4):
lines.append(f" {ix/4:.15f} {iy/4:.15f} {iz/4:.15f}")
lines.append(f" {nntot}")
lines.append("! nntot")
for ik in range(nk):
lines.append(f" {nntot}")
for ib in range(nntot):
lines.append(f" {(ik + ib) % nk + 1}")
lines.append(f" {nntot}")
for ib in range(nntot):
lines.append(" 0 0 0")
lines += [
f" {nbands}", "! num_exclude_bands",
" 30", "! num_wann",
" 6", "! num_proj",
]
for proj in ["Bi : pz","Bi : px","Bi : py",
"Se : pz","Se : px","Se : py"]:
lines.append(f" {proj}")
nnkp = os.path.join(prefix, "wannier", "wannier90.nnkp")
with open(nnkp, "w") as f:
f.write("\n".join(lines) + "\n")
print(f"[mock] {nnkp} ({len(lines)} lines)")
# ── 5. Mock PP & ORB files (unified path for all 3 scripts) ──
orb_dir = "tests/PP_ORB/for_interface/Wannier90_interface"
os.makedirs(orb_dir, exist_ok=True)
for name in ["Bi.upf", "Se.upf", "Bi.orb", "Se.orb"]:
p = os.path.join(orb_dir, name)
with open(p, "w") as f:
f.write("# mock for CI dryrun\n")
print(f"[mock] {p}")
print("\n[done] all mocks ready")
PYEOF
env:
SCRIPT: ${{ matrix.script }}
PREFIX: ${{ matrix.prefix }}
# ================================================================
# Run
# ================================================================
- name: Run ${{ matrix.script }} — dryrun
run: python ${{ matrix.script }} 2>&1 | tee run.log
# ================================================================
# Validate
# ================================================================
- name: Check dryrun completion banner
run: |
if grep -q "DRY RUN COMPLETE" run.log; then
echo " [OK] dryrun banner found"
else
echo " [FAIL] 'DRY RUN COMPLETE' not found — script may have crashed"
exit 1
fi
- name: Validate generated files (${{ matrix.name }})
run: |
W="${{ matrix.prefix }}/wannier"
ERR=0
check() {
if [ -f "$1" ]; then
echo " [OK] $1 ($(wc -c < "$1") bytes)"
else
echo " [FAIL] $1 (not found)"
ERR=1
fi
}
echo "=========================================="
echo " NSCF input files (from step2)"
echo "=========================================="
check "$W/INPUT"
check "$W/KPT"
check "$W/STRU"
# ── INPUT content checks ──────────────────────
if [ -f "$W/INPUT" ]; then
echo ""
echo "--- INPUT keyword checks ---"
for key in "calculation" "towannier90" "nspin"; do
if grep -qi "$key" "$W/INPUT"; then
echo " [OK] INPUT contains '$key'"
else
echo " [FAIL] INPUT missing '$key'"
ERR=1
fi
done
# gamma_only is FORBIDDEN
if grep -qi "gamma_only" "$W/INPUT"; then
echo " [FAIL] INPUT contains 'gamma_only' — forbidden!"
ERR=1
else
echo " [OK] INPUT does NOT contain 'gamma_only'"
fi
fi
# ── Basis-specific checks ─────────────────────
echo ""
echo "--- Basis-specific checks (${{ matrix.basis }}) ---"
if [ "${{ matrix.basis }}" = "pw" ]; then
if grep -qi "orbital_file\|orbital_dir" "$W/INPUT"; then
echo " [FAIL] PW INPUT contains orbital_file/orbital_dir"
ERR=1
else
echo " [OK] PW INPUT does NOT contain orbital_file/orbital_dir"
fi
else
if grep -qi "orbital_file\|orbital_dir" "$W/INPUT"; then
echo " [OK] LCAO INPUT contains orbital_file/orbital_dir"
else
echo " [WARN] LCAO INPUT missing orbital references (may be in STRU)"
fi
fi
# ── KPT sanity ────────────────────────────────
if [ -f "$W/KPT" ]; then
echo ""
klines=$(wc -l < "$W/KPT")
if [ "$klines" -gt 5 ]; then
echo " [OK] KPT has $klines lines (reasonable)"
else
echo " [FAIL] KPT too short ($klines lines)"
ERR=1
fi
fi
echo ""
if [ "$ERR" -ne 0 ]; then
echo "=========================================="
echo " VALIDATION FAILED"
echo "=========================================="
exit 1
fi
echo "=========================================="
echo " ALL CHECKS PASSED (${{ matrix.name }})"
echo "=========================================="
# ================================================================
# Debug (always runs, even on failure)
# ================================================================
- name: Show full script output
if: always()
run: cat run.log
- name: Show generated INPUT
if: always()
run: |
f="${{ matrix.prefix }}/wannier/INPUT"
if [ -f "$f" ]; then echo "=== $f ==="; cat "$f"; else echo "$f not found"; fi
- name: Show generated KPT (first 20 lines)
if: always()
run: |
f="${{ matrix.prefix }}/wannier/KPT"
if [ -f "$f" ]; then echo "=== $f (first 20 lines) ==="; head -20 "$f"; else echo "$f not found"; fi
- name: List all generated files
if: always()
run: find ${{ matrix.prefix }} -type f | sort