Mission + analyse du code original

Mission et analyse du code original

Mission

Porter le code de simulation gamma-imaging de Radience, écrit pour NVIDIA OptiX SDK 4.0.2 (sortie 2017), vers la dernière version NVIDIA OptiX 9.x (sortie 2024-2025), en exploitant l'occasion pour poser les fondations d'une intégration durable dans la chaîne de calcul scientifique de Radience.

Le projet est un portage architectural, pas une modernisation incrémentale (cf. docs/adr/ADR-001-vocabulaire-portage-radience.md). OptiX 7 (2019) a complètement refondu l'API : disparition du wrapper C++ optix::Context, passage à l'API C OptixDeviceContext + OptixModule + OptixProgramGroup + OptixPipeline + OptixShaderBindingTable. Le saut OptiX 4 vers 9 traverse cette frontière d'API sans pouvoir être incrémental.

Cadre contractuel

Trois choses que ce livrable doit prouver

Issue de la délibération delib-20260504-1de1, le contrat à J+45 (mid-point) puis J+90 (final) doit prouver trois choses :

  1. Compréhension — un wiki literate qui rend explicites les bits du système (pinhole geometry, bicouche W/Fe, RNG xorshift, 6 processus EM, 18 constantes hard-codées tracées vers leurs sources), lisible en 2-4 h par un nouvel ingénieur.
  2. Mesure falsifiable — 4-5 niveaux de tests numériques (L0-L4) avec seuils pré-déclarés et conditions de réfutation explicites, parcourus par une chaîne d'autorité de signature à 4 signataires (Emmanuel auto vers red-team agent vers Agustin vers François).
  3. Honnêteté sur les bornes — section "Bornes" en première page de chaque artefact nommant ce qui n'a pas pu être prouvé : robustesse production, conformité réglementaire éventuelle, bit-exactitude image, fidélité totale sans entretien Agustin.

L'observable principal de Radience n'est pas le binaire OptiX 9 fonctionnel (sous-produit nécessaire) — c'est le signal de fiabilité opérationnelle qui qualifie l'orchestration agentique comme partenaire. Le portage est l'instrument de cette évaluation, pas son objet.

Bornes (à nommer dès la première page)

Suivant la discipline carnot :


Analyse du code original

Origine et licence

Le code reçu d'Agustin Lifschitz (cofondateur Radience) le 2026-05-04 est un fork du sample NVIDIA OptiX SDK 4.0.2 optixMeshViewer (Copyright 2016 NVIDIA), licence BSD-style — pas de souci IP sur la base, IP custom uniquement sur les modifications Radience.

Fichier sourceRôleTailleDate dernière mod
optixMeshViewer.cppHost program — entrypoint OptiX13 Ko (438 LOC)2026-02-27 (récent)
pinhole_camera.cuRaygen kernel — logique gamma-imaging custom35 Ko (1012 LOC)2026-02-27 (récent)
parallelogram.cuHit kernel — intersection parallélogramme3,4 Ko2017-09 (intact)
constantbg.cuMiss kernel — background uniforme1,8 Ko2016-11 (intact)
commonStructs.hStructures C/CUDA partagées1,9 Ko2017-04
helpers.hHelpers utilitaires9,5 Ko2016-11
random.hRNG xorshift32 + Wang hash1,2 Ko2017-05

Source-of-truth : gamma_gpu.tgz.gz SHA256 eea6ee773bc0630b504780c086b7e0114b95a3a21ab6f5423f9dba80533ccf99, BLAKE3 96156b0566870dce766e394b82ee9a05b311308f7b9b2b956a46c37410a9d214, ancrage OpenTimestamps 4 calendriers (Bitcoin pending) le 2026-05-04. Cf. poc/inbound/2026-05-04-gamma-gpu-agustin/RECEIVED.md.

Architecture algorithmique

Le programme raygen pinhole_camera() (pinhole_camera.cu:900-956) est inhabituel pour un sample OptiX. Au lieu de tracer un rayon par lancement et accumuler la radiance, il simule une cascade de particules dans une boucle while sur le launch index. C'est un Monte Carlo de transport, pas un path tracer.

RT_PROGRAM pinhole_camera()              // pinhole_camera.cu:900-956
├─ initializeRandomState (seed = i*100 + iter)
├─ X = source position (uniform sur ±0.05 mm × ±0.05 mm × z=500)
├─ P = direction (uniform sur ±0.25 cône, P.z = -sqrt(1-...))
├─ ray1[0..9], data[0..9]                // pile de 10 particules max
├─ data[0].x = 10 * U[0,1)               // énergie initiale ∈ [0, 10) MeV
├─ data[0].y = 0                         // type : 0 = gamma
└─ while (j >= 0) {
     Advance_part(&j, &k, ray1, data, &state);
     if (ii++ > 100) break;              // garde-fou anti-boucle infinie
   }

Advance_part est une DFS sur l'arbre de cascade électromagnétique :

C'est exactement comme PENELOPE et Geant4 le font côté CPU, mais ici déroulé par thread GPU.

Six processus physiques modélisés

Audit knuth :

#ProcessusCode (pinhole_camera.cu:)Cible matérielleSource documentaire revendiquée
1Bremsstrahlung (section efficace)L194-239Tungstène (Z=74, voldens=6.32e19)Seltzer-Berger fit (sb_W, sb_Fe)
2Bremsstrahlung (énergie \(\gamma\) émise)L253-322TungstèneGeant4 + Seltzer-Berger
3Rayleigh scatteringL347-361, L830-884Fer (Z=26, voldens=8.486e19)Geant4.9.5 (Livermore polarized)
4Compton scatteringL362-415, L529-609FerKlein-Nishina (Geant4)
5PhotoélectriqueL417-443, L611-670FerBiggs & Lighthill SAND87-0070, Sauter-Gavrila K-shell
6Production de pairesL446-497, L672-828FerBethe-Heitler + Coulomb correction (L. Urban / Geant3)

Quatre anomalies de modélisation détectées

Issu de l'audit knuth — à préserver textuellement par le portage, signaler dans la wiki, interroger Agustin :

  1. Bicouche W/Fe implicite jamais documentée — le matériau cible diffère selon le type de particule (électrons vers W, gammas vers Fe). Si intentionnel, modélise probablement une géométrie bicouche (anode tungstène générant le Bremsstrahlung, écran/fantôme fer pour la propagation gamma) — cohérent avec gammagraphie industrielle. Mais nulle part documenté. Point critique : si on "nettoie" en mettant un seul matériau par mégarde, toute la simulation casse silencieusement.
  2. Branche basse-énergie Brems jamais implémentéeif (Energy > 0.5 || 3==3) (L277). La condition 3==3 est toujours vraie vers le test sur Energy > 0.5 est désactivé. Le commentaire L276 dit « below 500 keV vers To do it (from Penelope) ». TODO devenu silencieux. Un re-lecteur OptiX 9 qui "nettoie" cette ligne casse silencieusement la simulation.
  3. Pile tronquée à 10ray1[10], data[10] (L923-925), mais une cascade de pair-production peut empiler 2 secondaires d'un coup ((*j1)+=2, L822). Le test if(*j1<nr-2) (L814) protège, mais quand il échoue le code ne fait que printf("Pair couldn't be created!\n") et continue vers biais silencieux : aux hautes énergies où la pair-production est fréquente, on tronque la cascade.
  4. Boucle bornée à 100 itérationsif(ii++ > 100) break; (L950). Pour un photon de 10 MeV qui rebondit Compton de nombreuses fois, 100 itérations peuvent être atteintes vers cascade tronquée silencieusement. Biais de queue (haute multiplicité vers sous-estimation). À exposer comme paramètre dans le portage.

Bug critique côté host

Hors de pinhole_camera.cu, dans optixMeshViewer.cpp :

Le main() alloue double lanex[2100][2100][8] sur la pile (~280 Mo). Sur Linux avec ulimit par défaut (8 Mo), ce programme seg-fault sans le montrer clairement.

C'est le bug n°1 à corriger d'emblée dans le portage Rust (allocation Vec<f64> heap), indépendamment d'OptiX. À mentionner à Agustin — il connaît peut-être déjà le contournement (probablement ulimit -s unlimited dans son environnement de production).

RNG : découverte critique pour la stratégie de validation

Le RNG dans random.h:13-19 est xorshift32 (Marsaglia 2003) + wang_hash déterministe — pas curand, malgré l'#include <curand.h> à pinhole_camera.cu:30 (legacy non utilisé, à confirmer). Seed = i*100 + iter (pinhole_camera.cu:910).

Conséquence : la non-bit-exactitude n'est pas condamnée d'avance comme on le craindrait avec curand. Un test L1 mono-thread bit-exact entre OptiX 4 et OptiX 9 est défendable comme falsifier le plus fort, AVANT de replier sur L2 statistique.

Position adoptée (cf. docs/adr/ADR-06-rng-provenance.md, ADR-09) : tenter L1 mono-thread comme test interne ; le livrable s'engage uniquement sur convergence statistique (L2 SSIM \(\geq\) 0.95, L3 conservation d'énergie, L4 reproductibilité intra-version).

Données livrées et données manquantes

FichierTailleRôleStatut
ferrure_brute.obj26 MoMesh 3D principal (pièce industrielle)
ferrure_brute.dat21 MoDonnées associées (géométrie pré-calculée ou volumes)✓ — usage à clarifier
IQI_Sinus.obj1,1 MoPhantom calibration gamma-imaging (Image Quality Indicator)
piramide.obj174 octetsMesh test minimal (pyramide debug)
spectre.dat23 KoSpectre énergétique de la source gamma
gammas.dat0 octetsVraisemblablement input ou outputvide — à clarifier avec Agustin (oubli d'envoi, placeholder, ou généré au runtime ?)
f_pri.jpg, f_tot.jpgSorties simulation (flux primaire + flux total)✓ — référence visuelle
ima.jpg, ima_zoom.jpgImages de l'objet imagé

Manque critique : un dump binaire brut des output_buffer et extra_buffer du run de référence. Les JPG sont des post-process gnuplot 8-bit compressés ; pour la validation numérique, on a besoin du buffer flottant brut. Question à Agustin (cf. fleet M1).

Diagnostic technique — version OptiX source

Confirmé : OptiX SDK 4.0.2 (NVIDIA, sortie 2017) — chemin extrait du CMakeCache.txt : NVIDIA-OptiX-SDK-4.0.2-linux64/SDK/optixMeshViewer.

API utilisée :

Pattern objet : Context::create(), Buffer::create(), Geometry::create(), Material::create(), Program::create(), Acceleration::create("Trbvh").

Fuite mineure mais irréversible

CMakeCache.txt contient /home/lifschitz/Downloads/optix/NVIDIA-OptiX-SDK-4.0.2-linux64/SDK/optixMeshViewer — chemin du user Linux d'Agustin. PII mineure mais irréversible (impossible de "non-recevoir"). Ne pas propager hors-galaxie radience. Documenté dans la chronique d'inbound (RECEIVED.md ligne 73).


Estimation effort (synthèse de la délibération)

HypothèseEffortConditions
Migration OptiX 4 vers 9 d'un sample standard (1 raygen simple + 1-2 hit programs)3-4 jDev expérimenté OptiX 7+, machine Linux+GPU prête
Avec version pro-modernisée comme baseline4-6 jRéduit de 30-40 % le temps + supprime le reverse-engineering
Sans version pro-modernisée6-8 jReverse-engineering inclus
Sans entretien-walk-through Agustin+5-15 jCoût de re-spécifier le savoir tacite

Borne basse incompressible (smoke L0 = PNG OptiX 9 non-vide) : 2-4 j avec setup environnement préparé.

Compteur NDA aujourd'hui : J+4 sur 90, soit 4.4 % du budget consommé sur de la pure latence administrative (compteur démarré à signature, pas à réception code).


Sections suivantes (à rédiger)

SectionTitreStatut
02Choix de modernisation (récap ADRs 001-08, vocabulaire, hexagonal, FFI, validation)⏳ pending
03Bibliographie OptiX / gamma-imaging / Monte Carlo (12 refs Zotero)⏳ pending
04Wiki-doc structurée (8 dossiers TAOCP-style)⏳ pending
05Stratégie de validation L0-L4 + chaîne d'autorité S1-S4⏳ pending
06Résultats du portage (tests numériques, performance, anomalies découvertes)⏳ post-implémentation
07Bornes atteintes vs bornes non atteintes (honnêteté > complétude)⏳ post-implémentation

Cross-références