xGFabric twin as a service: real workload + demo kit (supersedes #13, #14) - #16
Open
andre-merzky wants to merge 34 commits into
Open
xGFabric twin as a service: real workload + demo kit (supersedes #13, #14)#16andre-merzky wants to merge 34 commits into
andre-merzky wants to merge 34 commits into
Conversation
Same graph and the same selection story as twin.py -- Davis wind sensor, a field agent whose three surrogate architectures compete on profiler-predicted Pi runtime, a heatmap sink -- servicified: the twin lives in the ORBIT `dt` plugin, tasks run on a rhapsody endpoint, the sensor is an external channel publisher bound with `add_input`, the data plane is ORBIT. The service/* components ship by value and fake the physics at the seams twin.py already fakes (sensor records, tk_do_simulation); the real FNO/PINN/PCR training stacks are not imported. What stays real: the shared memoised simulation subtask across the three investigators, the profiler chain (an inline timed run of the shipped inference function), and pi-runtime-driven model selection. Verified end to end against a local broker + endpoint: twin running, per-window retraining visible in the probes, pcr (the cheapest architecture) winning selection, 28 heatmaps, 24 shared-sim cache hits, clean teardown. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
Same debugged constraints as the AmSC kit (dragon launcher, python >= 3.12.1, SLURM_EXPORT_ENV, PATH-by-name helpers, cert staging), with the xGFabric deltas: numpy/matplotlib for the by-value components, the endpoint registers as 'hpc' with the client's `remote` profile selecting it on dragon_v3, XGF_WORKSPACE on scratch -- and rhapsody pinned to fix/dragon-cancel-and-traceback on every tier, the branch carrying the idempotent-cancel fixes this demo surfaced (the orbit one runs broker-side). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
A home-quota casualty on the first remote attempt; the venv does not fit there. XGF_WORKSPACE already landed on scratch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
The main-based fix/dragon-cancel-and-traceback pin broke the endpoint: main's dragon backend passes task_logs= to Batch(), which the pinned dragonhpc 0.14.1 does not accept (Session init failed on endpoint: Batch.__init__() got an unexpected keyword argument 'task_logs'). Both tiers now pin fix/dragon-cancel-idempotent (e491cd2-based) -- the branch the AmSC demo proved on this same dragon, carrying the dragon cancel + traceback fixes. Trade-off: the orbit-backend cancel fix lives only on the main-based #91 branch, so a harmless KeyError-cancel line may appear on broker teardown. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
The twin declared only an inference engine, so every task -- retraining included -- shared one dashboard lane. twin_service now declares a learning engine (same endpoint, concurrent executor by default; env knobs DT_LEARNING_ENDPOINT/_BACKEND), and each SurrogateInvestigator's train task carries backend="learning" so it routes there and shows as its own lane; inference, profiling and selection stay on the inference engine. learn_backend threads agent -> investigator and defaults to None (no label) so the components still work on a single-engine session. Verified locally: rhapsody.<sid>.inference and rhapsody.<sid>.learning both register, training routes to learning, selection still converges on pcr, no errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
…des) Level A provisioning for the actual FNO/PINN/PCR trainings, which need TensorFlow. Rather than build it, setup-hpc-endpoint-real.sh clones Ben's cfdaai conda env (which carries the stack) and installs our runtime into the CLONE -- never his shared env -- checks out the tasks tree the profiler shells into, stages the Pi-predictor dataset, and fails fast if the env is not Python 3.12 (wire contract). run-hpc-endpoint.sh now serves both modes: DT_VENV selects the venv (default ve.demo for the faked demo, the conda clone for real), and XGF_DIR puts the tasks tree on PYTHONPATH. Broker and client are unchanged -- they stay TF-free. README documents the path and the two gating items still open: lazy TensorFlow imports in the investigator wrappers (so packaging on client/broker does not need TF) and cloudpickle parity between the conda clone and ve.demo. Those are code changes on the investigators, tracked separately. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
The sink renders on the endpoint and returns the PNG bytes inline (a downscaled ~16 KB image, well under the return-value cap -- the DT service has no file staging), then records it via runtime.record_output so it shows in the dashboard's Outputs panel as fields arrive. Still written to XGF_WORKSPACE on the endpoint too. Needs digitaltwin's record_output (digital.twins#38). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
Lazy imports so the real investigators package/instantiate on the
client and broker without TensorFlow (it loads only in the task bodies,
which run on the endpoint):
- the three surrogate wrappers (fno/pinn/pcr) defer `from .main
import ...` into their task bodies;
- each do_*/__init__.py replaces eager `from .main import *` with a
PEP 562 __getattr__, so importing a submodule no longer drags TF /
scikit-learn in, while `tasks.<pkg>.tk_*` still resolves on access
(wrapper.py's executable path, on the endpoint).
Verified: the three investigators + wind_agent + sink + profiler all
import TF-free (pyspot submodule + dotenv are the only remaining
client/broker deps, both light).
twin_service_real.py mirrors twin.py's graph with the real FNO/PINN/PCR
investigators, real profiler + Pi predictor, real sink, and the
external fake sensor -- experimental, the on-Perlmutter starting point.
setup-hpc-endpoint-real.sh inits the pyspot submodule; README documents
the client deps and the two open items (shared-filesystem topology,
cloudpickle parity).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
EndpointInvestigator.main_loop runs on the broker, but it appended new rows to data.csv that train_model (an executable task) reads on the endpoint -- different filesystems once broker and endpoint are split. The append moves into a function_task (whose body runs on the endpoint), so data.csv is produced and consumed on the same host. This is the one cross-host file hand-off that is fixable without staging. The rest of the profiler chain uses executable_tasks, whose bodies run on the engine (broker) to build the command while the command runs on the endpoint -- so the .pkl / .json inputs they write are broker-side and the commands read them endpoint-side. Bridging that is the ORBIT staging work (separate), not a filesystem assumption to fix here. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
…rocess The profiler cloudpickled the inference to a file and ran the standalone profiler.py under a fresh process group. Because an executable task's command-builder runs on the broker while its command runs on the endpoint, that .pkl was written broker-side and read endpoint-side -- a cross-host hand-off needing a shared filesystem. inproc.py runs the call in-process on the endpoint and reads the process's own counters (perf_counter, getrusage, psutil.io_counters), returning the profiler's exact six-key schema so data.csv and endpoint_trainer.py are unchanged. ProfilerInvestigator and EndpointInvestigator now profile via a function-task (endpoint-side, no file, no subprocess). Verified locally: real wall/cpu/mem on a dummy inference; components import clean. Trade-off (no process-group isolation, peak RSS not child PSS) is fine for a comparative fingerprint. PLAN-telemetry-profiling.md captures approach 2 -- read the endpoint's own per-task telemetry instead of re-running -- for later, plus the remaining executable-task input hand-offs in the Pi-predictor (train/eval) that want the same treatment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
- EndpointInvestigator.call_inference wrote inf.json in its executable-task command-builder (broker) while endpoint_eval.py reads it on the endpoint. A stage_inf function-task now writes it on the endpoint before the command runs -- the last cross-host file hand-off in the profiler chain. The datastore makedirs in __init__ (broker) is guarded; the endpoint tasks create it. - CUPS_Sink renders the heatmap to bytes and calls record_output so the real driver shows heatmaps in the dashboard Outputs panel (needs digital.twins#38), and imports matplotlib lazily so packaging on the client/broker stays light. Verified locally: all real-driver components import clean, TF-free and matplotlib-free at import. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
- setup-hpc-endpoint-real.sh checks out feature/dtaas-twin-real for the endpoint tasks tree -- the base branch lacks the in-process profiler, the inf.json staging fix and the output-emitting sink. - PLAYGROUND_DIR -> /pscratch/sd/m/merzky/xgf_playground (a real PM scratch path; the driver reads it from config.sh on the client and ships it to the components, which use it endpoint-side). Demo-specific values; to be generalised later. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
- setup-hpc-endpoint-real.sh force-reinstalls radical.orbit: a cloned conda env may already carry a stale one that pip treats as satisfied, so the endpoint script (radical-orbit-endpoint.py) never installs. - setup-client.sh installs the light client-side deps the real components import at packaging time (python-dotenv, numpy, pandas) and inits the pyspot submodule; TensorFlow/matplotlib stay lazy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
- run-hpc-endpoint.sh points RADICAL_ORBIT_LOG_FILE at scratch and sets the level to WARNING: the default ~/.radical/orbit/logs is on NERSC HOME, whose tiny quota the endpoint log (esp. at debug) blows out -- the registration itself succeeds but every log write then errors. - setup-hpc-endpoint-real.sh backfills orbit's own deps after the --no-deps script reinstall (fastapi/uvicorn/websockets), so a clone missing them still starts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
…elemetry Two regressions from the conda-clone endpoint: - pip install "$DT_DIR" (digitaltwin) ran after the rhapsody branch pin and pulled a different (main-based) rhapsody over it, reintroducing the Batch(task_logs=...) crash. rhapsody now installs LAST so the dragon-compatible branch wins. - the --no-deps rhapsody install skipped the telemetry extra's opentelemetry. A second, extras install (no --no-deps) backfills opentelemetry + dragonhpc without re-resolving the pinned stack. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
…pecific) So a plain re-run of setup + run does everything, no one-off commands: - setup-hpc-endpoint-real.sh seeds the Pi dataset at the RUNTIME datastore (PLAYGROUND_DIR/profiler/pi_profiler, where endpoint_trainer reads it), not just the tasks tree; PLAYGROUND_DIR is a script var matching config.sh. - run-hpc-endpoint.sh clears any old HOME orbit log (NERSC quota) on top of redirecting the log to scratch at WARNING. - setup-broker.sh backfills the telemetry extra's deps after the --no-deps rhapsody pin, mirroring the endpoint. Paths are NERSC/demo-specific for now, to be generalised later. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
Five self-contained scripts, each printing the demo banner and running all-that's-needed for its role (deploy fix + env + data dirs + run): Sep_04_broker.sh radical.3: install stack, pin dragon rhapsody, run broker Sep_04_endpoint_setup.sh PM login node: checkouts, cfdaai clone, runtime, seed data Sep_04_endpoint.sh PM allocation: env + launch endpoint under dragon Sep_04_sensor.sh client: publish davis-wind Sep_04_client.sh client: light deps + drive twin_service_real.py Args are only the broker IP (and 'hpc' endpoint name where relevant); everything else (SCRATCH paths, branch, PLAYGROUND_DIR, conda base env) is baked in for the demo. The endpoint is split because the allocation cannot pip-install; setup runs on a login node first. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
add_agent failed on the broker with ModuleNotFoundError: no module named 'utils_architecture'. WindFieldAgent references tasks.davis (and tasks.do_simulation, tasks.common.log_formatter), which import the repo-root utils_architecture; the curated register_user_modules list missed them, so they pickled by reference and the broker -- which has no xGFabric checkout -- could not import them. Sweep every already imported tasks.* module plus utils_architecture instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
The DTaaS service builds agents as cls(flow, *args, **engines); the required 'logger' positional made add_agent raise TypeError. Default it to the module logger. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
script_path was os.path.realpath(__file__) captured at import on the CLIENT; shipped by value, the executable/training commands then pointed at the laptop checkout (/home/merzky/projects/xgfabric/...) while running on Perlmutter. Invoke via 'python3 -m tasks.profiler.endpoint_*' instead -- the endpoint puts XGF_DIR on PYTHONPATH, so the path is resolved on whatever host runs the command. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
endpoint_trainer.py/endpoint_eval.py import xgboost; the cfdaai clone does not carry it. Install into the endpoint env at setup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
The real learners (FNO/PINN/PCR, Pi predictor) run on the default 'inference' engine, so the dashboard showed no learning lane. Add a compact WindTrendLearner that consumes the same sensor stream and routes its training to the 'learning' backend (the @function_task(backend=...) label the service demo's SurrogateInvestigator uses). It publishes to its own WIND_TREND dtype, so it never competes with the WIND_FIELD path feeding the sink. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
TF fell back to CPU because libcuda.so.1 was not on the task LD_LIBRARY_PATH. module load cudatoolkit + prepend /usr/lib64 (compute node driver) and the env's CUDA libs; log nvidia-smi -L so we can see whether the allocation actually has a GPU. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
No GPU allocation, so the real TF surrogates (~54s/inference on CPU) are too slow for a live demo. Default the demo client to twin_service.py -- fake SurrogateInvestigators (sleeps + numpy field synthesis, no TF or xgboost) that produce heatmaps in seconds and carry the learning lane (learn_backend='learning'). RUN_REAL=1 still selects twin_service_real.py for a GPU-backed run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
main_loop runs on the broker; it built the PNG path from the broker's home (XGF_WORKSPACE unset there -> ~/xgf_twin) and mkdir'd it there, then handed that broker path to render_heatmap, which runs on the endpoint and failed to write it (no such dir on Perlmutter). Resolve XGF_WORKSPACE and mkdir inside the task instead, and wrap the disk copy in try/except so it can never fail the field -- the dashboard copy rides inline regardless. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
…atch) install.sh installs digitaltwin by exact version string and skips when it is unchanged, so the broker kept pre-record_output code and the sink crashed with AttributeError: RuntimeAPI has no attribute record_output. Force-reinstall the checked-out devel tip after install.sh. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
Prepending /usr/lib64 to LD_LIBRARY_PATH can shadow the conda env's libs and break the dragon task launch -- the endpoint registers but its engine never comes up, so session init times out. Useless on the CPU/fake path anyway; make it opt-in. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
Keep the field-probe line as the heartbeat; the state dict is only used for the failed-state check now. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
Reflects reality after the demo cycle: runs end to end on the DTaaS stack; the shared-FS caveat is resolved (compute + hand-offs are endpoint-side); the TF path is GPU-bound so the demo defaults to the fake driver (RUN_REAL=1 selects this one). Points at the Sep_04 scripts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
Nothing to enable: the rhapsody plugin on the endpoint already records every task plus a resource poll (the [telemetry] extra) into telemetry-output/session.*.telemetry.jsonl. collect_reports.py points twin.py's existing report generators at those files -- waterfall, dependency wait, stage timers, swimlane, concurrency/resources, and the gantt render unmodified (verified against the service run's 8292 events). Known gap, documented: per-workflow gantt grouping needs asyncflow.workflow_id, which only workflow_scope() stamps -- an engine-side telemetry feature for the DT service, tracked in digital.twins. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Supersedes #13 (fake DTaaS twin) and #14 (telemetry reports) — both are fully contained here (#13 as ancestor, #14's
collect_reports.py+ README cherry-picked in), so they can be closed in favour of this PR.What's here
The xGFabric wind-field twin running as a DTaaS service on an ORBIT broker, with tasks on a rhapsody/dragon HPC endpoint. Two drivers share the same graph and components:
twin_service.py— fast fake-surrogate driver (sleeps + numpy field synthesis, no TF/xgboost). CPU-friendly, heatmaps in seconds; this is what the September demo runs.twin_service_real.py— the real workload (FNO/PINN/PCR surrogates, real profiler + Pi predictor). GPU-bound, so it's opt-in viaRUN_REAL=1.Shared pieces: streaming learning lane (retraining on its own
learningengine), profiler-predicted edge-runtime model selection, a heatmap sink that surfaces frames inline to the dashboard's Outputs panel, and the telemetry-report tool (service/collect_reports.py).Demo kit
demo/Sep_04_{broker,endpoint_setup,endpoint,sensor,client}.sh— one command per role (arg: broker IP, plushpcfor the endpoint). Everything else (SCRATCH paths, conda base env,PLAYGROUND_DIR, branch pins) is baked in — deliberately site/demo-specific for now.Notable fixes folded in
tasks.*subtree by value (broker has no xGFabric checkout)-m), not client__file__pathsRUN_GPU=1gate for the CUDA env (off by default; it can break the dragon launch on CPU)Not covered
Real TF timings need a GPU endpoint. rhapsody is pinned to the
fix/dragon-cancel-idempotentbranch (proper fix is rhapsody #91).🤖 Generated with Claude Code
https://claude.ai/code/session_016Npyz3Hbnwos12ESsdJ2YU