Web-based seismic computing platform that consumes SRASS over VPN+SSH on
domestic HPC systems (Tianhe, Dawning, Shenwei). No agent is installed
on the supercomputer login node; all interaction goes through ssh/scp.
Status
Phase 1 MVP:
P1 Backend — see docs/superpowers/specs/2026-06-17-sap-phase1-mvp-design.md
P1 Frontend scaffold — Vite + React + TS, routing, job list view
polling.
P1 Frontend round 2 (submit form + multipart file uploads)
real startup recovery (submit → seed orphan YAML → restart Scheduler
→ verify re-attach via real squeue/sacct instead of LOST). Tier 1.5
fwi/grond/unicycle SKIPPED with informative reasons documenting
three upstream SRASS wrapper bugs on this operator’s cluster
(fwi: mpich/mpi-x vs PMI2 mismatch; grond: CLI surface difference;
unicycle: missing libcurl.so.4 / libnetcdff.so.7). NOTE: this cluster’s
mt_module partition is INACTIVE (2026-07-01 — all nodes
drain/down/resv); tests default to SAP_TIANHE_PARTITION=ft_module
which has 110+ idle nodes.
Dawning (Sugon new-generation) real-HPC integration — SRASS
uploaded, built on the hpctest06 compute nodes, and verified with a
SLURM smoke test. DawningPlatform now generates
srass specfem3d-globe forward ... jobs for the new SRASS CLI.
Real-HPC bugs fixed during end-to-end testing (2026-06-22 / 23)
The unit / integration tests pass against mocks, but the mocks cannot
reproduce the SLURM toolchain’s exact behavior. End-to-end runs against
the real Tianhe cluster surfaced six bugs that the test suite could not:
#
Bug
Surface
Fix
1
scp_upload does not expand $HOME (sftp-server has no shell)
First submit failed
SSHClient._resolve_remote_path caches remote_home and rewrites paths before scp
2
#SBATCH --chdir=$HOME/... is parsed literally by SLURM
JobLaunchFailure (workdir concatenation)
TianhePlatform._workdir resolves $HOME to absolute before generating the sbatch script
3
SlurmClient.query raises on squeue "Invalid job id" for completed jobs
Scheduler stuck polling forever
squeue wrapped in try/except SSHError; falls through to sacct
4
State machine rejects QUEUED → COMPLETED for fast jobs
Job stuck at QUEUED after run
Added COMPLETED to the allowed set under QUEUED
5
GET /api/jobs/{id}/logs was never wired up to anything
Endpoint existed, always 404
New Platform.download_log + JobService.download_log + eager fetch in scheduler on COMPLETED
6
GET /api/jobs/{id}/output listed local files only, never invoked download_outputs
Endpoint existed, always empty
Eager fetch in scheduler on COMPLETED pulls remote output dir to local
Every fix has a regression test. Total tests: 270 backend + 96 frontend = 366
(259 → 270 added 2026-07-02 with the new test_tianhe_real.py suite:
8 passing (2 smoke + 4 Tier 1 lifecycle + 1 startup recovery + 1
compute-node PATH propagation) and 3 skipped with informative reasons
documenting three upstream SRASS wrapper bugs on this Tianhe cluster.
The original 259 includes the Dawning real-SRASS Tier 1.5 + Tier 2
suite.)
(up from 242 after adding 17 new tests on 2026-06-30 for the Unicycle
“restrict + sniff” change: the public schema now exposes only the two
Unicycle variants actually built into SRASS 0.4.0 on Dawning
hpctest06 (3d-ratestate, 3d-viscouscycles) — the other 10
entries in srass/commands/unicycle.py:VARIANTS are accepted by
FAKE_SRASS but missing from the real build, so they were dead
options in the public schema. A submit-time sniffer
(sniff_unicycle_param_format in
sap/clients/command_templates.py) now rejects param files in the
TOML/INI format that the un-deployed ap-* / ps-* variants read,
returning 422 with a clear message instead of letting the user
queue a job the real solver would reject with a confusing upstream
error. The sniffer is unit-tested (11 cases covering fortran/toml/
empty/prose/leading-comments) and the API is tested for the positive
(Fortran-format accepted for both built variants) and negative
(TOML/binary rejected, non-unicycle jobs never sniffed, variant-only
submissions skip the check). The Tier 1 + Tier 1.5 + Tier 2 unicycle
tests all share one fixture (tests/fixtures/unicycle_params) that
is now in the Fortran list-directed format the real solver reads.
The 242 baseline came from test_dawning_full_lifecycle_{fwi,grond,unicycle}_real_srass
on 2026-06-30 — full sap lifecycle DRAFT→COMPLETED for fwi/grond/unicycle
on real Dawning hpctest06 against the real SRASS 0.4.0 build at
/public/home/scnethpc26108/SRASS/build/bin/, closing the “Tier 1.5”
gap: the three Tier 1 FAKE_SRASS tests prove the dispatch + state machine
work, and the three Tier 2 e2e tests prove the real solver runs, but
no test combined both — i.e. showed JobService + Scheduler +
DawningPlatform driving a real SRASS 0.4.0 invocation through to
COMPLETED. Tier 1.5 fwi=19s, grond=38s, unicycle=35s. The 239 baseline
came from test_dawning_{fwi,grond,unicycle}_real_srass_e2e (added
2026-06-29 — full real-SRASS 0.4.0 coverage for all three remaining
job_types on Dawning hpctest06: SPECFEM3D Cartesian FWI (3-step
pipeline: setup_models + create_raw_data + run_inversion) in 540s,
Grond init + check in 117s, Unicycle 3d-ratestate in 117s) plus
test_dawning_forward_real_srass_e2e (added 2026-06-29 — full
SPECFEM3D Globe meshfem + forward pipeline on real SRASS 0.4.0 producing
real seismograms, validated end-to-end on hpctest06 in 33.9s) plus
three Tier 1 FAKE_SRASS lifecycle tests
test_dawning_full_lifecycle_{fwi,grond,unicycle} plus Tier 0
lifecycle + smoke, all walking the full DRAFT → COMPLETED on real
Dawning via the FAKE_SRASS harness and asserting both the dispatched
sbatch script shape and the eager artifact fetch.)
The Dawning forward end-to-end walk
(DRAFT → PENDING_UPLOAD → SUBMITTING → QUEUED → RUNNING → COMPLETED
plus eager output + log fetch) is covered by
tests/integration/test_dawning_real.py::test_dawning_full_lifecycle_draft_to_completed,
gated on SAP_DAWNING_KEY_PATH + SAP_DAWNING_USER +
SAP_DAWNING_SRASS_BIN_DIR.
The per-platform forward override itself was validated end-to-end on
real Dawning hpctest06 on 2026-06-27 against the real SRASS 0.4.0
wrapper at /public/home/scnethpc26108/SRASS/build/bin/:
(a) sbatch job 630147 invoked
srass specfem3d-globe forward --par=<uploaded Par_file> --nprocs=1
and the wrapper accepted the CLI (no unrecognized arguments) and
proceeded to mpirun-launch srass-specfem3d-globe; (b) the full sap
lifecycle test (test_dawning_full_lifecycle_draft_to_completed)
drove DRAFT → COMPLETED through JobService + Scheduler +
DawningPlatform in 31.9s on the cluster.
The full compute pipeline behind that CLI was then validated
end-to-end on 2026-06-29:
test_dawning_forward_real_srass_e2e stages the smallest viable
SPECFEM3D Globe configuration (NEX=16 / NPROC=1 / RECORD=0.05d0)
with MODEL = 1D_transversely_isotropic_prem, sources the HPCSDK
toolchain on the compute node, and runs meshfem (2s) + forward
(10s) against the real SRASS build — producing 15 real .sem.ascii
seismograms (5 stations × 3 components, max amplitude ~7.7e-7)
that the test verifies contain non-zero samples. This is the
final closure proof: the upstream smoke test only proves the
binary loads; this test proves the solver actually computes.
All fourteen Dawning integration tests pass in ~12.5 min wall-clock:
The three new real-SRASS e2e tests validate compute pipelines, not
just CLI acceptance:
test_dawning_fwi_real_srass_e2e runs SPECFEM3D Cartesian’s
fwi_test_acoustic end-to-end (setup_models → create_raw_data →
run_inversion), asserting all three sub-scripts return 0 and that
output_inverse.txt contains real FWI markers. The example is staged
as a real directory at the canonical depth inside the specfem3d tree
because 0.setup_models.sh uses relative ../../../../bin/x* paths
(a symlink trick doesn’t work — bash .. follows the physical path
through the symlink). NPROC=1 is set, plus NPROC_ETA=1 in both
Mesh_Par_file.TRUE and Mesh_Par_file.INIT (required for serial).
LOCAL_PATH is not overridden because xmeshfem3D ignores absolute
paths and writes to ./DATABASES_MPI/ regardless — the safest path is
to keep the upstream default.
test_dawning_grond_real_srass_e2e runs srass-grond init example_regional_cmt example (which generates a real ~3KB Grond
config from the bundled example problem) followed by srass-grond check <config> <event>. The init must return 0 and the generated
config must contain Grond markers ([, ], engine, problem_name).
Pyrocko must be installed in the runtime python (see docs/sugon-usage.md).
test_dawning_unicycle_real_srass_e2e runs srass-unicycle-3d-ratestate
with a minimal hand-written parameter file (one rectangle, one
frictional rectangle, zero observations/events). Asserts rc=0 and
at least one patch-*.{dat,grd,vtp,xyz} output. The output directory
and time.dat file must be pre-created because Unicycle’s Fortran
input.f90 opens output/time.dat with POSITION="APPEND" and fails
if either is missing (a known Unicycle upstream quirk; the production
build_unicycle_sbatch_script now prepends mkdir -p output; : > output/time.dat so the run.sh works without the operator knowing
about this).
The three new “Tier 1.5” tests close the lifecycle gap: the Tier 1
FAKE_SRASS tests prove sap’s JobService + Scheduler + DawningPlatform
correctly dispatch the right sbatch script and walk the job to
COMPLETED, and the Tier 2 e2e tests prove the real SRASS solvers actually
run, but no test combined both — i.e. showed the sap lifecycle driving
a real SRASS 0.4.0 invocation through to COMPLETED. The Tier 1.5 tests
are exactly that: same dispatch path as the Tier 1 tests but with
srass_bin_dir pointed at the real SRASS build instead of ~/srass_bin.
Each runs in well under a minute on hpctest06 (fwi 19s, grond 38s,
unicycle 35s) because the real wrappers may exit 0 even when the minimal
fixtures are non-physical — the production cost of running real solvers
on real data is paid by the Tier 2 tests; what Tier 1.5 proves is that
the dispatch + state machine + eager-fetch correctly drive a real
SRASS invocation, which is what the operator actually cares about for
day-to-day platform smoke-testing.
Note (2026-08-07): Dawning (Sugon new-generation) resources were shut
down in August 2026. The 14 Dawning integration tests are preserved but
skip without SAP_DAWNING_KEY_PATH. Tianhe is the sole operational
platform.
2026-08-07 updates
Tianhe integration suite re-verified — 8 pass / 3 skip against the
live Tianhe cluster (partition ft_module, 121 idle nodes). ~90s wall-clock.
~ expansion bug fixed — TianhePlatform.__init__ now resolves
~/ $HOME via _resolve_remote_path before passing srass_bin_dir to
the sbatch header (previously shlex.quote single-quoted ~ → exit 127).
grond subcommand schema — added subcommand select field
(check/go/report/…) matching real SRASS srass grond <subcommand> <args>.
Backend templates, platform dispatch, and frontend form/test all updated.
fwi upstream SRASS wrapper bug fixed —
srass/commands/specfem3d.py::run_inverse was calling
run_cmd([exe]), dropping --par. Fixed on cluster.
PYTHONPATH support — build_sbatch_header accepts
srass_pythonpath for real SRASS Python package on Tianhe compute nodes.
openmpi build — Tianhe real-SRASS defaults switched to
build-openmpi/bin, avoiding the PMI2 runtime mismatch.
Vite proxy parameterized via SAP_BACKEND_URL env var.
bootstrap script — scripts/bootstrap.sh for one-step dev setup.
Infrastructure ready but tests remain skipped due to upstream behavior:
job_type
blocker
fwi
binary ignores --par when DATA/Par_file missing in workdir
grond
srass-grond needs valid config + event
unicycle
missing libnetcdff.so.7 on compute nodes
Phase 1 scope
Minimum end-to-end slice started with one job type (球坐标全球正演 / forward)
on one platform (Tianhe) and has since expanded to four job types
on two working platforms. Dawning is a working SLURM adapter and has
been validated end-to-end on the scnet.cn E-Shell gateway; Shenwei remains
a stub that returns 501 Not Implemented.
Supported job types (see GET /api/job-types for the live schemas):
optional file + 3 params; variant restricted to the 2 built into SRASS 0.4.0 (3d-ratestate, 3d-viscouscycles); param file must be Fortran list-directed format
forward per-platform schema override
The forward job_type has a different input schema on each platform
because the SRASS CLI surface differs between Tianhe (legacy forward
subcommand, accepts --nx/--ny/--nz/--nt flag set) and Dawning (SRASS
0.4.0, requires specfem3d-globe forward --par DATA/Par_file --nprocs N
with a SPECFEM3D Globe Par_file). The form picks the per-platform
schema from JobType.outrides[platform]; the backend’s
DawningPlatform._build_sbatch_script dispatches to
build_forward_parfile_sbatch_script while TianhePlatform keeps the
legacy flag-style builder.
Platform
Schema
CLI
Tianhe
nx (int), ny (int), nz (int), dx (float), dy (float), dz (float), nt (int), dt (float), output_dir (str, optional)
These knobs control how jobs are submitted to the Tianhe SLURM cluster.
All have safe defaults so a vanilla uvicorn start works for CI / mock
testing, but a real deployment needs at least SAP_TIANHE_ACCOUNT and
SAP_TIANHE_QOS set (Tianhe rejects account-less submissions with
Invalid account or account/partition combination).
Env var
Default
Meaning
SAP_TIANHE_HOST
tianhe.login
SSH hostname of the login node
SAP_TIANHE_USER
sap
SSH username
SAP_TIANHE_PARTITION
mt_module
SLURM partition to submit into. NOTE: on the operator’s cluster (hnu_wuq@25.8.100.22) mt_module is INACTIVE as of 2026-07-01 — set this to ft_module (or another active partition) for real submissions.
SAP_TIANHE_ACCOUNT
(empty)
SLURM account. Omit the #SBATCH --account directive when empty.
SAP_TIANHE_QOS
(empty)
SLURM QOS. Omit the #SBATCH --qos directive when empty.
SAP_TIANHE_SRASS_BIN_DIR
(empty)
Directory containing the FAKE_SRASS harness (bash script). Used by test_tianhe_real.py Tier 1 lifecycle tests. When set, the generated sbatch script prepends it to PATH on the compute node (login /tmp is not shared with compute nodes).
SAP_TIANHE_REAL_SRASS_BIN_DIR
/vol8/home/hnu_wuq/srass-test/build-pmi2/bin
Directory containing the real SRASS 0.4.0 build. The corresponding Tier 1.5 lifecycle tests are SKIPPED on Tianhe because the operator’s upstream wrapper has three documented bugs (see _TIANHE_TIER15_SKIP_REASONS in tests/integration/test_tianhe_real.py).
Worked example for the Tianhe new-generation system
(hnu_wuq@25.8.100.22, partition ft_module because mt_module is
INACTIVE on this cluster, account ecology, FAKE_SRASS at
~/srass_bin):
Dawning is accessed through the scnet.cn E-Shell gateway on a non-standard
SSH port with key-based authentication. Set at least SAP_DAWNING_USER and
SAP_DAWNING_KEY_PATH; the other knobs have safe defaults for the
hpctest06 debug queue.
Env var
Default
Meaning
SAP_DAWNING_HOST
zzeshell.scnet.cn
E-Shell gateway hostname
SAP_DAWNING_PORT
65032
SSH port for the gateway
SAP_DAWNING_USER
(empty)
SSH username
SAP_DAWNING_KEY_PATH
(empty)
Path to the scnet.cn SSH private key
SAP_DAWNING_PARTITION
hpctest06
SLURM partition to submit into
SAP_DAWNING_TIME_LIMIT
00:10:00
Wall-clock time limit for jobs
SAP_DAWNING_SRASS_BIN_DIR
~/srass_bin
Directory containing the FAKE_SRASS harness. Used by the lifecycle e2e test.
SAP_DAWNING_REAL_SRASS_BIN_DIR
/public/home/scnethpc26108/SRASS/build/bin
Directory containing the real SRASS build. Used by the SRASS smoke test only.
Example (lifecycle test on Dawning):
export SAP_DAWNING_USER=scnethpc26108
export SAP_DAWNING_KEY_PATH=$HOME/.ssh/scnethpc26108_zzeshell.scnet.cn_RsaKeyExpireTime_2026-07-17_16-59-07.txt
export SAP_DAWNING_PARTITION=hpctest06
# Lifecycle test uses FAKE_SRASS by default. No need to override.
pytest -q -m integration tests/integration/test_dawning_real.py::test_dawning_full_lifecycle_draft_to_completed
To verify the real SRASS build instead (a manual sanity check on
hpctest06, not part of CI):
tests/integration/test_tianhe_real.py runs against the live Tianhe
cluster. All tests are gated on SAP_TIANHE_KEY_PATH + SAP_TIANHE_USER
(plus SAP_TIANHE_SRASS_BIN_DIR for the lifecycle tests). Default
partition is ft_module because mt_module is currently INACTIVE on
this cluster. Full wall-clock for all 11 tests: ~7 min on hnu_wuq
account (cluster-load dependent).
Three Tier 1.5 lifecycle tests (fwi/grond/unicycle against the real
SRASS 0.4.0 build) SKIP with informative reasons — the operator’s
upstream SRASS wrapper on this cluster has documented bugs that block
end-to-end real-SRASS dispatch:
fwi: mpich/mpi-x (PMIx) vs SLURM PMI2 mismatch. Direct
invocation of srass-specfem3d-inverse fails with
MPI_Init failed: PMI2_Job_GetId returned 14. Workaround:
srun --mpi=pmi2 -n 1 srass-specfem3d-inverse works, but the
wrapper doesn’t emit srun. Fix: rebuild SRASS with openmpi.
grond: CLI surface difference. sap dispatches
srass grond <config> but the operator’s wrapper expects
<subcommand> <args> (e.g. srass grond check <config> <event>).
unicycle: missing libcurl.so.4 + libnetcdff.so.7 on compute
node. The operator’s env.sh notes explicitly call this out:
‘libcurl.so.4 在计算节点缺失 (Unicycle 依赖)’. Fix: static linking
or Singularity container.
The Dawning Tier 1.5 suite (tests/integration/test_dawning_real.py)
covers the same dispatch path on a working wrapper. Once the operator
rebuilds Tianhe SRASS with the fixes above, replace the
pytest.skip(_TIANHE_TIER15_SKIP_REASONS[job_type]) calls with the
standard prereq gate to enable these tests.
Dawning SRASS build
A CPU-only build of SRASS (SPECFEM3D_GLOBE only) has already been produced
on the Dawning hpctest06 debug queue. The build script, source tree, and
binaries are at:
/public/home/scnethpc26108/build_srass_cpu.sh # sbatch script used
/public/home/scnethpc26108/SRASS/ # uploaded source tree
/public/home/scnethpc26108/SRASS/build/bin/ # srass + specfem3d-globe binaries
The build/bin/srass wrapper loads scripts/platform/dawning/env.sh so that
python3, MPI, NetCDF, and the srass Python package are available on the
compute node without requiring SAP to emit module load commands.
To rebuild or add components (Cartesian, Unicycle, Grond, HIP/DCU), edit the
CMake flags in ~/build_srass_cpu.sh and submit it from the login node:
sbatch ~/build_srass_cpu.sh
Dawning FAKE_SRASS harness
Like Tianhe, Dawning has a FAKE_SRASS wrapper at ~/srass_bin/srass that
accepts any args, writes a deterministic output/result.txt, and exits 0.
It exists so the integration lifecycle test (DRAFT → COMPLETED plus
eager output + log fetch) can run without burning real cluster queue slots
or requiring a valid SPECFEM3D Par_file. The wrapper is intentionally
separate from the real SRASS build at /public/home/scnethpc26108/SRASS/build/
so an operator can update one without touching the other.
The SAP_DAWNING_SRASS_BIN_DIR env var defaults to ~/srass_bin (the FAKE
harness). To run the real-SRASS smoke test instead, set
SAP_DAWNING_REAL_SRASS_BIN_DIR and run that test in isolation.
tests/integration/ — tests that hit a real HPC login node; currently
test_dawning_real.py exercises the Dawning E-Shell gateway and SLURM
scheduler (gated by SAP_DAWNING_KEY_PATH).
Frontend:
cd frontend
npm install
npm run test:run # vitest run
npm run lint # eslint
npm run typecheck # tsc --noEmit
npm run build # production bundle
Frontend tests live next to source as *.test.{ts,tsx}. Vitest with
jsdom + @testing-library/react. Coverage: npm run test:coverage.
Frontend dev workflow
# Terminal 1: backend on :8000
cd backend && source .venv/bin/activate
uvicorn sap.main:app --reload
# Terminal 2: frontend dev server on :5173
cd frontend
npm run dev
The Vite dev server proxies /api/* to http://localhost:8000, so the
frontend can call /api/jobs directly with no CORS configuration.
For production, run scripts/prod-build.sh from the repo root. It builds
the frontend SPA into backend/sap/static/ and then packages the backend
as a wheel. FastAPI serves the static files at / (API routes still take
precedence).
API surface
Method
Path
Notes
GET
/api/health
Liveness probe
GET
/api/platforms
List known platforms + availability
GET
/api/job-types
List job types + param schemas
GET
/api/jobs
List all jobs
POST
/api/jobs
Create a DRAFT job
GET
/api/jobs/{id}
Get job details
POST
/api/jobs/{id}/submit
Walk DRAFT → QUEUED via platform.submit
POST
/api/jobs/{id}/cancel
Cancel a QUEUED/RUNNING job
GET
/api/jobs/{id}/output
List output files
GET
/api/jobs/{id}/output/{filename}
Download an output file
GET
/api/jobs/{id}/logs
Download the run log
Architecture
See docs/superpowers/specs/2026-06-17-sap-phase1-mvp-design.md and
docs/superpowers/plans/2026-06-17-sap-phase1-p1-backend.md (the
TDD-style implementation plan that produced this code). The design
doc was backfilled 2026-06-24 with §12 (Phase 1.5 increments —
Dawning adapter, fwi/grond/unicycle, eager fetch on COMPLETED),
§13 (lessons learned from the six real-HPC bugs), and §14
(Phase 2 candidates). For a conversational record of one working
session on 2026-06-24 (problems hit + solutions + next-step
suggestions) see docs/superpowers/specs/2026-06-24-sap-session-summary.md.
sap (SRASS Application Platform)
Web-based seismic computing platform that consumes SRASS over VPN+SSH on domestic HPC systems (Tianhe, Dawning, Shenwei). No agent is installed on the supercomputer login node; all interaction goes through
ssh/scp.Status
Phase 1 MVP:
docs/superpowers/specs/2026-06-17-sap-phase1-mvp-design.mdhnu_wuq@25.8.100.22(Tianhe new-generation, partitionmt_module, accountecology)./api/jobs/{id}/outputand/api/jobs/{id}/logsserve real artifacts fetched eagerly on COMPLETED.tests/integration/test_tianhe_real.py) — 11 gated tests covering sinfo smoke + sbatch hello + Tier 1 (FAKE_SRASS) lifecycle for forward/fwi/grond/unicycle + compute-node PATH propagationmt_modulepartition is INACTIVE (2026-07-01 — all nodes drain/down/resv); tests default toSAP_TIANHE_PARTITION=ft_modulewhich has 110+ idle nodes.hpctest06compute nodes, and verified with a SLURM smoke test.DawningPlatformnow generatessrass specfem3d-globe forward ...jobs for the new SRASS CLI.Real-HPC bugs fixed during end-to-end testing (2026-06-22 / 23)
The unit / integration tests pass against mocks, but the mocks cannot reproduce the SLURM toolchain’s exact behavior. End-to-end runs against the real Tianhe cluster surfaced six bugs that the test suite could not:
scp_uploaddoes not expand$HOME(sftp-server has no shell)SSHClient._resolve_remote_pathcachesremote_homeand rewrites paths before scp#SBATCH --chdir=$HOME/...is parsed literally by SLURMTianhePlatform._workdirresolves$HOMEto absolute before generating the sbatch scriptSlurmClient.queryraises onsqueue "Invalid job id"for completed jobstry/except SSHError; falls through tosacctQUEUED → COMPLETEDfor fast jobsCOMPLETEDto the allowed set underQUEUEDGET /api/jobs/{id}/logswas never wired up to anythingPlatform.download_log+JobService.download_log+ eager fetch in scheduler on COMPLETEDGET /api/jobs/{id}/outputlisted local files only, never invokeddownload_outputsEvery fix has a regression test. Total tests: 270 backend + 96 frontend = 366 (259 → 270 added 2026-07-02 with the new
test_tianhe_real.pysuite: 8 passing (2 smoke + 4 Tier 1 lifecycle + 1 startup recovery + 1 compute-node PATH propagation) and 3 skipped with informative reasons documenting three upstream SRASS wrapper bugs on this Tianhe cluster. The original 259 includes the Dawning real-SRASS Tier 1.5 + Tier 2 suite.) (up from 242 after adding 17 new tests on 2026-06-30 for the Unicycle “restrict + sniff” change: the public schema now exposes only the two Unicycle variants actually built into SRASS 0.4.0 on Dawninghpctest06(3d-ratestate,3d-viscouscycles) — the other 10 entries insrass/commands/unicycle.py:VARIANTSare accepted by FAKE_SRASS but missing from the real build, so they were dead options in the public schema. A submit-time sniffer (sniff_unicycle_param_formatinsap/clients/command_templates.py) now rejects param files in the TOML/INI format that the un-deployedap-*/ps-*variants read, returning 422 with a clear message instead of letting the user queue a job the real solver would reject with a confusing upstream error. The sniffer is unit-tested (11 cases covering fortran/toml/ empty/prose/leading-comments) and the API is tested for the positive (Fortran-format accepted for both built variants) and negative (TOML/binary rejected, non-unicycle jobs never sniffed, variant-only submissions skip the check). The Tier 1 + Tier 1.5 + Tier 2 unicycle tests all share one fixture (tests/fixtures/unicycle_params) that is now in the Fortran list-directed format the real solver reads. The 242 baseline came fromtest_dawning_full_lifecycle_{fwi,grond,unicycle}_real_srasson 2026-06-30 — full sap lifecycle DRAFT→COMPLETED for fwi/grond/unicycle on real Dawninghpctest06against the real SRASS 0.4.0 build at/public/home/scnethpc26108/SRASS/build/bin/, closing the “Tier 1.5” gap: the three Tier 1 FAKE_SRASS tests prove the dispatch + state machine work, and the three Tier 2 e2e tests prove the real solver runs, but no test combined both — i.e. showedJobService+Scheduler+DawningPlatformdriving a real SRASS 0.4.0 invocation through to COMPLETED. Tier 1.5 fwi=19s, grond=38s, unicycle=35s. The 239 baseline came fromtest_dawning_{fwi,grond,unicycle}_real_srass_e2e(added 2026-06-29 — full real-SRASS 0.4.0 coverage for all three remaining job_types on Dawninghpctest06: SPECFEM3D Cartesian FWI (3-step pipeline: setup_models + create_raw_data + run_inversion) in 540s, Grondinit+checkin 117s, Unicycle3d-ratestatein 117s) plustest_dawning_forward_real_srass_e2e(added 2026-06-29 — full SPECFEM3D Globe meshfem + forward pipeline on real SRASS 0.4.0 producing real seismograms, validated end-to-end onhpctest06in 33.9s) plus three Tier 1 FAKE_SRASS lifecycle teststest_dawning_full_lifecycle_{fwi,grond,unicycle}plus Tier 0 lifecycle + smoke, all walking the fullDRAFT → COMPLETEDon real Dawning via the FAKE_SRASS harness and asserting both the dispatched sbatch script shape and the eager artifact fetch.) The Dawningforwardend-to-end walk (DRAFT → PENDING_UPLOAD → SUBMITTING → QUEUED → RUNNING → COMPLETEDplus eager output + log fetch) is covered bytests/integration/test_dawning_real.py::test_dawning_full_lifecycle_draft_to_completed, gated onSAP_DAWNING_KEY_PATH+SAP_DAWNING_USER+SAP_DAWNING_SRASS_BIN_DIR.The per-platform
forwardoverride itself was validated end-to-end on real Dawninghpctest06on 2026-06-27 against the real SRASS 0.4.0 wrapper at/public/home/scnethpc26108/SRASS/build/bin/: (a) sbatch job630147invokedsrass specfem3d-globe forward --par=<uploaded Par_file> --nprocs=1and the wrapper accepted the CLI (nounrecognized arguments) and proceeded to mpirun-launchsrass-specfem3d-globe; (b) the full sap lifecycle test (test_dawning_full_lifecycle_draft_to_completed) droveDRAFT → COMPLETEDthroughJobService+Scheduler+DawningPlatformin 31.9s on the cluster.The full compute pipeline behind that CLI was then validated end-to-end on 2026-06-29:
test_dawning_forward_real_srass_e2estages the smallest viable SPECFEM3D Globe configuration (NEX=16 / NPROC=1 / RECORD=0.05d0) withMODEL = 1D_transversely_isotropic_prem, sources the HPCSDK toolchain on the compute node, and runs meshfem (2s) + forward (10s) against the real SRASS build — producing 15 real.sem.asciiseismograms (5 stations × 3 components, max amplitude ~7.7e-7) that the test verifies contain non-zero samples. This is the final closure proof: the upstream smoke test only proves the binary loads; this test proves the solver actually computes.All fourteen Dawning integration tests pass in ~12.5 min wall-clock:
test_dawning_ssh_sinfo<2stest_dawning_slurm_submit_hello_job~13stest_dawning_srass_specfem3d_globe_smoke~14s (real SRASS--smoke)test_dawning_full_lifecycle_draft_to_completed~32s (FAKE_SRASS)test_dawning_full_lifecycle_fwi~36s (FAKE_SRASS)test_dawning_full_lifecycle_grond~36s (FAKE_SRASS)test_dawning_full_lifecycle_unicycle~36s (FAKE_SRASS)test_dawning_full_lifecycle_fwi_real_srass~19s (real SRASS via sap lifecycle)test_dawning_full_lifecycle_grond_real_srass~38s (real SRASS via sap lifecycle)test_dawning_full_lifecycle_unicycle_real_srass~35s (real SRASS via sap lifecycle)test_dawning_forward_real_srass_e2e~34s (real SRASS meshfem+forward)test_dawning_fwi_real_srass_e2e~540s (real SRASS FWI: setup_models + create_raw_data + run_inversion)test_dawning_grond_real_srass_e2e~117s (real SRASS Grond init+check)test_dawning_unicycle_real_srass_e2e~117s (real SRASS Unicycle 3d-ratestate)The three new real-SRASS e2e tests validate compute pipelines, not just CLI acceptance:
test_dawning_fwi_real_srass_e2eruns SPECFEM3D Cartesian’sfwi_test_acousticend-to-end (setup_models → create_raw_data → run_inversion), asserting all three sub-scripts return 0 and thatoutput_inverse.txtcontains real FWI markers. The example is staged as a real directory at the canonical depth inside the specfem3d tree because0.setup_models.shuses relative../../../../bin/x*paths (a symlink trick doesn’t work — bash..follows the physical path through the symlink). NPROC=1 is set, plus NPROC_ETA=1 in bothMesh_Par_file.TRUEandMesh_Par_file.INIT(required for serial). LOCAL_PATH is not overridden becausexmeshfem3Dignores absolute paths and writes to./DATABASES_MPI/regardless — the safest path is to keep the upstream default.test_dawning_grond_real_srass_e2erunssrass-grond init example_regional_cmt example(which generates a real ~3KB Grond config from the bundled example problem) followed bysrass-grond check <config> <event>. The init must return 0 and the generated config must contain Grond markers ([,],engine,problem_name). Pyrocko must be installed in the runtime python (seedocs/sugon-usage.md).test_dawning_unicycle_real_srass_e2erunssrass-unicycle-3d-ratestatewith a minimal hand-written parameter file (one rectangle, one frictional rectangle, zero observations/events). Asserts rc=0 and at least onepatch-*.{dat,grd,vtp,xyz}output. The output directory andtime.datfile must be pre-created because Unicycle’s Fortraninput.f90opensoutput/time.datwithPOSITION="APPEND"and fails if either is missing (a known Unicycle upstream quirk; the productionbuild_unicycle_sbatch_scriptnow prependsmkdir -p output; : > output/time.datso the run.sh works without the operator knowing about this).The three new “Tier 1.5” tests close the lifecycle gap: the Tier 1 FAKE_SRASS tests prove sap’s
JobService+Scheduler+DawningPlatformcorrectly dispatch the right sbatch script and walk the job to COMPLETED, and the Tier 2 e2e tests prove the real SRASS solvers actually run, but no test combined both — i.e. showed the sap lifecycle driving a real SRASS 0.4.0 invocation through to COMPLETED. The Tier 1.5 tests are exactly that: same dispatch path as the Tier 1 tests but withsrass_bin_dirpointed at the real SRASS build instead of~/srass_bin. Each runs in well under a minute onhpctest06(fwi 19s, grond 38s, unicycle 35s) because the real wrappers may exit 0 even when the minimal fixtures are non-physical — the production cost of running real solvers on real data is paid by the Tier 2 tests; what Tier 1.5 proves is that the dispatch + state machine + eager-fetch correctly drive a real SRASS invocation, which is what the operator actually cares about for day-to-day platform smoke-testing.Note (2026-08-07): Dawning (Sugon new-generation) resources were shut down in August 2026. The 14 Dawning integration tests are preserved but skip without
SAP_DAWNING_KEY_PATH. Tianhe is the sole operational platform.2026-08-07 updates
ft_module, 121 idle nodes). ~90s wall-clock.~expansion bug fixed —TianhePlatform.__init__now resolves~/ $HOMEvia_resolve_remote_pathbefore passingsrass_bin_dirto the sbatch header (previouslyshlex.quotesingle-quoted~→ exit 127).subcommandselect field (check/go/report/…) matching real SRASSsrass grond <subcommand> <args>. Backend templates, platform dispatch, and frontend form/test all updated.srass/commands/specfem3d.py::run_inversewas callingrun_cmd([exe]), dropping--par. Fixed on cluster.build_sbatch_headeracceptssrass_pythonpathfor real SRASS Python package on Tianhe compute nodes.build-openmpi/bin, avoiding the PMI2 runtime mismatch.SAP_BACKEND_URLenv var.scripts/bootstrap.shfor one-step dev setup.Total: 233 backend + 96 frontend = 329 tests passing.
Tier 1.5 (real SRASS) status
Infrastructure ready but tests remain skipped due to upstream behavior:
--parwhenDATA/Par_filemissing in workdirsrass-grondneeds valid config + eventlibnetcdff.so.7on compute nodesPhase 1 scope
Minimum end-to-end slice started with one job type (球坐标全球正演 / forward) on one platform (Tianhe) and has since expanded to four job types on two working platforms. Dawning is a working SLURM adapter and has been validated end-to-end on the scnet.cn E-Shell gateway; Shenwei remains a stub that returns
501 Not Implemented.Supported job types (see
GET /api/job-typesfor the live schemas):forwardsrass forward --nx=... --nt=...· Dawning:srass specfem3d-globe forward --par=<uploaded Par_file> --nprocs=Nfwisrass specfem3d inverse --par=<uploaded Par_file>grondsrass grond <uploaded config> [-- <extra_args>]unicyclesrass unicycle <variant> [param_file] --nprocs=N3d-ratestate,3d-viscouscycles); param file must be Fortran list-directed formatforwardper-platform schema overrideThe
forwardjob_type has a different input schema on each platform because the SRASS CLI surface differs between Tianhe (legacyforwardsubcommand, accepts--nx/--ny/--nz/--ntflag set) and Dawning (SRASS 0.4.0, requiresspecfem3d-globe forward --par DATA/Par_file --nprocs Nwith a SPECFEM3D Globe Par_file). The form picks the per-platform schema fromJobType.outrides[platform]; the backend’sDawningPlatform._build_sbatch_scriptdispatches tobuild_forward_parfile_sbatch_scriptwhileTianhePlatformkeeps the legacy flag-style builder.nx(int),ny(int),nz(int),dx(float),dy(float),dz(float),nt(int),dt(float),output_dir(str, optional)srass forward --nx=… --ny=… --nz=… --dx=… --dy=… --dz=… --nt=… --dt=…par_file(file, required),nprocs(int, optional, default 1)srass specfem3d-globe forward --par=<workdir>/input/Par_file --nprocs=NAdjoint is intentionally deferred (not exposed by SRASS 0.4.0).
Running locally
Prerequisites: Python 3.11+, network access to the Tianhe login node.
Set the required env vars (or put them in
.envandexportthem):Tianhe environment variables
These knobs control how jobs are submitted to the Tianhe SLURM cluster. All have safe defaults so a vanilla
uvicornstart works for CI / mock testing, but a real deployment needs at leastSAP_TIANHE_ACCOUNTandSAP_TIANHE_QOSset (Tianhe rejects account-less submissions withInvalid account or account/partition combination).SAP_TIANHE_HOSTtianhe.loginSAP_TIANHE_USERsapSAP_TIANHE_PARTITIONmt_modulehnu_wuq@25.8.100.22)mt_moduleis INACTIVE as of 2026-07-01 — set this toft_module(or another active partition) for real submissions.SAP_TIANHE_ACCOUNT#SBATCH --accountdirective when empty.SAP_TIANHE_QOS#SBATCH --qosdirective when empty.SAP_TIANHE_SRASS_BIN_DIRtest_tianhe_real.pyTier 1 lifecycle tests. When set, the generated sbatch script prepends it toPATHon the compute node (login/tmpis not shared with compute nodes).SAP_TIANHE_REAL_SRASS_BIN_DIR/vol8/home/hnu_wuq/srass-test/build-pmi2/bin_TIANHE_TIER15_SKIP_REASONSintests/integration/test_tianhe_real.py).Worked example for the Tianhe new-generation system (
hnu_wuq@25.8.100.22, partitionft_modulebecausemt_moduleis INACTIVE on this cluster, accountecology, FAKE_SRASS at~/srass_bin):Dawning (Sugon new-generation) environment variables
Dawning is accessed through the scnet.cn E-Shell gateway on a non-standard SSH port with key-based authentication. Set at least
SAP_DAWNING_USERandSAP_DAWNING_KEY_PATH; the other knobs have safe defaults for thehpctest06debug queue.SAP_DAWNING_HOSTzzeshell.scnet.cnSAP_DAWNING_PORT65032SAP_DAWNING_USERSAP_DAWNING_KEY_PATHSAP_DAWNING_PARTITIONhpctest06SAP_DAWNING_TIME_LIMIT00:10:00SAP_DAWNING_SRASS_BIN_DIR~/srass_binSAP_DAWNING_REAL_SRASS_BIN_DIR/public/home/scnethpc26108/SRASS/build/binExample (lifecycle test on Dawning):
To verify the real SRASS build instead (a manual sanity check on
hpctest06, not part of CI):Tianhe real-HPC integration tests
tests/integration/test_tianhe_real.pyruns against the live Tianhe cluster. All tests are gated onSAP_TIANHE_KEY_PATH+SAP_TIANHE_USER(plusSAP_TIANHE_SRASS_BIN_DIRfor the lifecycle tests). Default partition isft_modulebecausemt_moduleis currently INACTIVE on this cluster. Full wall-clock for all 11 tests: ~7 min onhnu_wuqaccount (cluster-load dependent).Three Tier 1.5 lifecycle tests (fwi/grond/unicycle against the real SRASS 0.4.0 build) SKIP with informative reasons — the operator’s upstream SRASS wrapper on this cluster has documented bugs that block end-to-end real-SRASS dispatch:
srass-specfem3d-inversefails withMPI_Init failed: PMI2_Job_GetId returned 14. Workaround:srun --mpi=pmi2 -n 1 srass-specfem3d-inverseworks, but the wrapper doesn’t emitsrun. Fix: rebuild SRASS with openmpi.srass grond <config>but the operator’s wrapper expects<subcommand> <args>(e.g.srass grond check <config> <event>).env.shnotes explicitly call this out: ‘libcurl.so.4 在计算节点缺失 (Unicycle 依赖)’. Fix: static linking or Singularity container.The Dawning Tier 1.5 suite (
tests/integration/test_dawning_real.py) covers the same dispatch path on a working wrapper. Once the operator rebuilds Tianhe SRASS with the fixes above, replace thepytest.skip(_TIANHE_TIER15_SKIP_REASONS[job_type])calls with the standard prereq gate to enable these tests.Dawning SRASS build
A CPU-only build of SRASS (SPECFEM3D_GLOBE only) has already been produced on the Dawning
hpctest06debug queue. The build script, source tree, and binaries are at:The
build/bin/srasswrapper loadsscripts/platform/dawning/env.shso thatpython3, MPI, NetCDF, and thesrassPython package are available on the compute node without requiring SAP to emitmodule loadcommands.To rebuild or add components (Cartesian, Unicycle, Grond, HIP/DCU), edit the CMake flags in
~/build_srass_cpu.shand submit it from the login node:Dawning FAKE_SRASS harness
Like Tianhe, Dawning has a
FAKE_SRASSwrapper at~/srass_bin/srassthat accepts any args, writes a deterministicoutput/result.txt, and exits 0. It exists so the integration lifecycle test (DRAFT → COMPLETEDplus eager output + log fetch) can run without burning real cluster queue slots or requiring a valid SPECFEM3D Par_file. The wrapper is intentionally separate from the real SRASS build at/public/home/scnethpc26108/SRASS/build/so an operator can update one without touching the other.The
SAP_DAWNING_SRASS_BIN_DIRenv var defaults to~/srass_bin(the FAKE harness). To run the real-SRASS smoke test instead, setSAP_DAWNING_REAL_SRASS_BIN_DIRand run that test in isolation.Then start the server in dev mode (auto-reload):
Or directly:
Running tests
Backend:
Tests are organized:
tests/unit/— module-level unit teststests/api/— FastAPI TestClient teststests/integration/— tests that hit a real HPC login node; currentlytest_dawning_real.pyexercises the Dawning E-Shell gateway and SLURM scheduler (gated bySAP_DAWNING_KEY_PATH).Frontend:
Frontend tests live next to source as
*.test.{ts,tsx}. Vitest with jsdom + @testing-library/react. Coverage:npm run test:coverage.Frontend dev workflow
The Vite dev server proxies
/api/*tohttp://localhost:8000, so the frontend can call/api/jobsdirectly with no CORS configuration.For production, run
scripts/prod-build.shfrom the repo root. It builds the frontend SPA intobackend/sap/static/and then packages the backend as a wheel. FastAPI serves the static files at/(API routes still take precedence).API surface
/api/health/api/platforms/api/job-types/api/jobs/api/jobs/api/jobs/{id}/api/jobs/{id}/submit/api/jobs/{id}/cancel/api/jobs/{id}/output/api/jobs/{id}/output/{filename}/api/jobs/{id}/logsArchitecture
See
docs/superpowers/specs/2026-06-17-sap-phase1-mvp-design.mdanddocs/superpowers/plans/2026-06-17-sap-phase1-p1-backend.md(the TDD-style implementation plan that produced this code). The design doc was backfilled 2026-06-24 with §12 (Phase 1.5 increments — Dawning adapter,fwi/grond/unicycle, eager fetch on COMPLETED), §13 (lessons learned from the six real-HPC bugs), and §14 (Phase 2 candidates). For a conversational record of one working session on 2026-06-24 (problems hit + solutions + next-step suggestions) seedocs/superpowers/specs/2026-06-24-sap-session-summary.md.Layered modules under
backend/sap/:License
Internal. Not for redistribution.