Feature/6 cloud native tps services#2
Open
vidyuthdev wants to merge 3 commits into
Open
Conversation
- Add scripts/smoke_test.sh: plan creation, job polling, artifact download - Document smoke test steps after docker compose in README Made-with: Cursor
…routes. Implement Service 1 end-to-end with HU-to-density conversion models, axis-aligned resampling, contour rasterization, on-disk geometry persistence/cache, Orthanc adapter retrieval support, and /api/v1/geometry endpoints plus comprehensive unit and API tests. Made-with: Cursor
Service 1 — Geometry async + DICOM upload: - Celery task for async geometry builds + jobs endpoint + Postgres-backed job rows - POST /api/v1/uploads/dicom accepts ZIP, returns upload_id - PatientRef.upload_id wired into GeometryService._load - Validated end-to-end on TCIA LCTSC clinical patient Service 2 — Beam Model: - Pydantic models, machine_model loader, on-disk content-addressable cache - Proton spot generator + photon beamlet generator - BeamModelService orchestrator with 6-stage progress - Celery task + jobs table + Alembic migration - 5-endpoint REST surface (build / job / get / artifact / delete) Refactor: - _helpers.load_bdl / setup_calibration delegate to ProtonMachineModel Tests: 271 passing (259 prior + 12 new for uploads) Demos: demo/show_geometry.py (--upload flag) and demo/show_beam_model.py Docs: demo/README_DICOM.md (TCIA download walkthrough)
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.
Add Service 2 (Beam Model), async Geometry jobs, and DICOM upload
This PR ships two major features and one refactor, building on the Geometry Service foundation already on main. End-to-end validated against an anonymized clinical patient from TCIA's Lung CT Segmentation Challenge dataset.
Summary
Service 2 — Beam Model Service (new): turns a built geometry + a beam configuration into a deliverable proton/photon beam model, with full async + caching parity to Service 1.
Service 1 — DICOM upload + async jobs (new): clients can now POST a ZIP of DICOM files and receive an upload_id to use in geometry builds; geometry builds now dispatch via Celery with a polling endpoint.
_helpers refactor: BDL and MCsquare calibration loading now delegate to the new pluggable ProtonMachineModel, removing duplication across the proton workflows.
What's new
Service 2 — Beam Model Service
The next service in the planning pipeline. Takes a geometry_id + a BeamSetSpec + DeliveryParams + a Modality and produces a BeamModelResult plus a pickled OpenTPS plan artifact.
Models — Modality, BeamSetSpec, DeliveryParams, BeamModelBuildRequest with modality-filtered compute_cache_key(), FluenceElementSet, BeamModelResult, BeamModelJobStatus.
Pluggable machine model — ProtonMachineModel / PhotonMachineModel with lazy BDL + MCsquare calibration loaders. get_machine_model(modality, id) factory.
Modality builders — proton_spots.generate_proton_spots() drives OpenTPS ProtonPlanDesign.buildPlan(); photon_beamlets.generate_photon_beamlets() computes a beam's-eye-view beamlet grid for each PlanPhotonBeam.
Orchestrator — BeamModelService.build() with 6-stage progress callback (queued → loading_geometry → loading_machine_model → building_beams → computing_elements → persisting → done).
Persistence — atomic on-disk store under {artifact_dir}/beam_models/, content-addressable by SHA-256 of the modality-filtered request.
Async path — Celery task + beam_model_jobs table + Alembic migration c2f8d2a4b5e3.
REST surface — POST /beam-model/build (200 cache hit / 202 dispatch), GET /beam-model/jobs/{id}, GET /beam-model/{id}, GET /beam-model/{id}/artifact (streams pickled plan), DELETE /beam-model/{id}.
Same architectural patterns as Service 1, so reading one teaches you the other.
Service 1 — DICOM upload endpoint
Removes the dependency on the bundled OpenTPS data root for production use. Clients can now feed real DICOM directly.
POST /api/v1/uploads/dicom — accepts a single multipart file (ZIP), extracts safely under {upload_dir}/{upload_id}/ (zip-slip protected), walks the directory and returns CT-slice + RTSTRUCT counts via pydicom modality detection.
GET /uploads/{id}, DELETE /uploads/{id} for inspection and cleanup.
PatientRef.upload_id field — when set, GeometryService._load resolves to the upload directory and disk-loads from there. PACS and data-root paths remain as fallbacks. Cache key includes upload_id so two builds against the same upload dedup.
Settings — new RADIARCH_UPLOAD_DIR env var, defaults to {artifact_dir}/uploads.
Service 1 — async geometry jobs
Celery task build_geometry_job with progress callbacks mirrored to Postgres-backed geometry_jobs table.
GET /geometry/jobs/{id} for polling state, progress, stage, and final geometry_id.
Alembic migration b1e7c1f3a4f2.
API contract: POST /geometry/build returns 200 + full result on cache hit, 202 + job_id on cache miss.
Refactor — _helpers.py
setup_calibration() and load_bdl() are now thin shims delegating to ProtonMachineModel.from_default(). Public signatures unchanged — proton_basic, proton_robust, proton_optimized, photon_ccc workflows continue to work without modification. Single source of truth for machine calibration data.
Tests
271 passing (259 previously + 12 new for uploads), 4 skipped (existing slow-marked OpenTPS integration tests).
tests/test_api_uploads.py (10 tests) — happy path, error paths, geometry build with upload_id, stale upload_id handling, PatientRef validation
tests/test_api_beam_model.py (12 tests) — async dispatch, cache hit, jobs endpoint, retrieval, delete
tests/services/test_beam_model_* (50+ tests) — model invariants, machine model factory, persistence atomicity, modality builders, service orchestrator
Run with:
bashpytest tests/ -v
Live validation on clinical data
Validated end-to-end against LCTSC-Test-S1-101 from TCIA (anonymized clinical lung CT, 130 slices, 5 OARs):
frame_of_reference_uid correctly populated and linked CT ↔ RTSTRUCT
structure_index = {'Esophagus': 1, 'Heart': 2, 'Lung_L': 3, 'Lung_R': 4, 'SpinalCord': 5}
Real clinical grid (0.977, 0.977, 3.0) mm at (512, 512, 130)
First build: 7.2 s (full pipeline) — second build: 0.3 ms (cache hit, 21,655× speedup)
See demo/README_DICOM.md for the TCIA download walkthrough.
How to demo
bashsource src/.venv/bin/activate
Service 1 (geometry) on real DICOM
python demo/show_geometry.py --upload ~/lctsc_patient.zip --show
Service 2 (beam model) — both proton and photon paths
python demo/show_beam_model.py
Migrations
Two new Alembic revisions: b1e7c1f3a4f2 (geometry_jobs) and c2f8d2a4b5e3 (beam_model_jobs). Run alembic upgrade head after pulling.
Files
38 files changed, 5,412 insertions(+), 99 deletions(-)