diff --git a/CITATION.cff b/CITATION.cff index d0d8af3..19d6b12 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,8 +2,8 @@ cff-version: 1.2.0 message: "If you use this software, please cite the TRIMSP-NL Workbench publication and this software release." title: "TRIMSP-NL Workbench" type: software -version: 1.5.0 -date-released: 2026-08-28 +version: 1.6.0 +date-released: 2026-10-09 url: "http://musruser.psi.ch/TRIMSP-NL/" repository-code: "https://gitea.psi.ch/LMU/TRIMSP" license: GPL-3.0-or-later diff --git a/ToDo.txt b/ToDo.txt index e69de29..d749d54 100644 --- a/ToDo.txt +++ b/ToDo.txt @@ -0,0 +1,150 @@ +Seed handling and input hardening (deferred from the B-list fixes) +===================================================================== + +DONE - Validate ranSeed in the GUI and startSequence: it must be an integer in a + safe range (|v| <= 2^31-1, matching ISEED = IDINT(RI) in trimspNL.F). Enforced + in runSimulationSequence() (before any job is generated) AND in CreateInpFile() + (defense-in-depth), so a crafted share link or direct deck builder is blocked + with a "please change it" message. Non-numeric / NaN / non-integer / oversized + seeds no longer reach the engine. +DONE - Derive per-scan-point seeds deterministically from the base seed and the + scan index, so a multi-point run is reproducible in any finish order + (currently: initialSeed + iScan, only when the user set a valid seed). +DONE - Cap valEnergy magnitude at 60000 eV (positive incidence mode and the + Maxwellian transmission mode |E0| = temperature). Stopping fits (KDEE1=4) are + documented valid to ~50 keV for helium (meaning-of-params.txt:177-185), so 60 + keV is the model's fitted ceiling. Applied to scanned energies and the base + value in runSimulationSequence() and CreateInpFile(). +- Warn when a shared configuration fixed the RanSeed: every scan point then uses + the same generator, which can produce statistically misleading results. +DONE (advisory, not a hard cap) - Warn on the web when numberProj exceeds 25000 + per point. Measured cost model on a 3.2 GHz core, single-threaded engine: + runtime is linear in numberProj and linear in energy, and cost per projectile + is dominated by the number of nuclear collisions along the track, so the + expensive axis is the LIGHT projectiles, not the heavy ones. Projectile -> + Si, Maxwellian 60 keV, ZBL potential, ms/projectile: muon 3.04, H 3.30, + He 4.06 (worst), Li-8 3.91, Be-11 3.64, B-12 3.19, Mg-31 1.34, Ar 0.83. + Checked and found NOT to affect runtime: layer count (1..60 layers is flat, + the species lookup is a binary search), depth increment dz (0.1 A == 20 A), + and grazing incidence (ALPHA 89 deg is cheaper - the ion skims out). + NH=25000 at the worst case measured 98.6/99.5/100.1 s on the 1.3.2 binary and + 101.5/100.7/101.7 s on a fresh 1.4.0 build, i.e. ~3x margin under the 300 s + TRIMSP_CMD_TIMEOUT. Deliberately warning-only: the value is advisory, the run + still starts, and the user keeps the final say. Web-only, because the + standalone app has no watchdog. TRIMSP_WEB_PROJ_ADVISORY in TrimSPlib.js; + uses the max of the sweep when scanning numberProj. The dead GUI + ProjNumberLimit() (TrimSPlib.js:3516) was left alone - it clamps at 5000, + which is below the 50000-100000 used by the publication test configs, so + wiring it up as-is would have clamped the regression suite. +- Persisting the effective per-point seed: the seed already lives in each + archived *.inp (all three deck slots), so reproducibility does not require the + *.seq header change; extending *.seq would shift the 999(A20,1x) layer slots + and break getSequenceColumnInfo(). Surfacing it in run details instead is + deferred - not sure it is needed. + +Out-of-bounds recoil write in the sputtering path (parIRL >= 1) +============================================================= + +FIXED - the engine now refuses the divergent input instead of corrupting memory. + + Symptom, as originally reported: setting parIRL (the recoil/sputter output + level) to 1 crashed the engine with SIGSEGV, reproducibly 5/5 seeds, and end + to end through singleTrimSP.cgi as exitCode 139 / ok:false. parIRL=0 was never + affected. + + Root cause was NOT the KO dimension. The first diagnosis blamed + KO(600,MAXNL5,2) at trimspNL.F:114 (debug: "Index '601' of dimension 1 of + array 'ko' above upper bound of 600"), on the grounds that the other + recoil-list arrays are 2000 wide. Enlarging KO to 2000 does not help: with + KO(2000) the run still dies, because the cascade overruns all of them. + + The actual cause is a non-converging cascade driven by parERC = 0. The + primary knock-on test is `IF(DEN(IV).LE.ERC) GO TO 6`, so with a zero recoil + cutoff every atom that receives any energy at all is respawned as a recoil on + every cascade generation. The branching ratio exceeds one, so NREC1 and NREC2 + grow geometrically instead of draining: + + NREC2 = 81, 153, 269, 455, 773, 1317, ... (parERC=0, parIRL=1) + + and the list-2/list-1 loop at label 83 never terminates. Instrumented runs + show the block is never even entered when parIRL=0. parERC defaults to 0.0 + in the GUI (TrimSP.html:293) and tests/Si_1layer/Si_1layer_E19000.inp carries + 0.0, so the failing combination is the default one - no unusual user action + is required to hit it. + + Evidence that a real cutoff converges: forcing parERC to 0.02, 0.05 or 0.2 + makes parIRL=1 finish normally (exit 0), and the peak cascade depth is only + 103, well inside KO's 600. KO was therefore left at 600, and the resize was + reverted (it cost 7.7 MiB of .lbss and fixed nothing). + + Changes in fortran/trimspNL.F: + - After readGuiInput, reject parIRL>=1 with parERC<=0 using a clear message + and STOP 1, before any transport work. + - The `IF(NREC2.GT.2000)` guard now stops with a non-zero status instead of + falling through into the out-of-bounds write loop, so any future + non-convergent cascade fails cleanly rather than corrupting memory. + + The GUI now catches this before the engine is ever invoked: + - handleRecoilModeChange (wired to the parIRL widget) fills parERC with + TRIMSP_MIN_RECOIL_CUTOFF = 0.02 when recoil calculation is switched on with + a cutoff of 0, and says so in a transient warning. + - A pre-run check in runSimulationSequence refuses the run if the user then + insists on parIRL=1 with parERC=0, reports it in the status bar plus a + transient warning, and focuses the parERC field. The handler is deliberately + not wired to parERC, so retyping the cutoff is what "insisting" means and is + not silently overridden. This also covers shared-configuration links, which + assign widget values directly and so bypass the change handler. + - The parIRL tooltip documents the requirement. + + Verified: parERC=0/parIRL=1 -> exit 1 with the new message; parERC=0.02 with + parIRL=0 or 1 -> exit 0; parERC=0 with parIRL=0 -> exit 0 and byte-identical + .rge (and an identical .out apart from timestamps/elapsed/filenames) versus + the pre-fix binary. tests/cgi-tests.sh 18/18, plus + tests/recoil-cutoff-test.js (12 cases over the shipped cutoff code) for the + GUI behaviour above. + + Minimal repro: tests/Si_1layer/Si_1layer_E19000.inp with the last field of + line 2 changed from 0 to 1 (parERC is field 10 of line 1, still 0.0). + +Deferred follow-ups from the C-list CGI hardening +================================================= + +- The perl "Statement unlikely to be reached" warning at the exec/_exit(127) + child branch of run_bounded_command is intentional (matching muSRFlow) and + does not affect `perl -c`; tests run with -X to keep output clean. +- No global concurrency cap is implemented (by design, mirroring muSRFlow): + concurrent per-point engine runs are allowed; archive is serialized per run + with flock. Revisit if a runaway share link floods scan points. +- Auth (MUCH-need, muSRFlow parity): none is implemented, keeping the read-only + /tmp contract. If this CGI is ever exposed beyond a trusted LAN, gate it with + httpd authentication or a shared-token header. +- Restart integration (retch): the watchdog returns exit code 124 and the JSON + "log" carries the description; a deployment that wants automated retries can + key off exitCode === 124 and re-post the same .inp. +- A future client change could surface TRIMSP_TMPROOT behind a shared + configuration; the browser path regex is derived from the same env var, so + both sides stay consistent. + +Follow-ups from the parIRL / parERC fix +======================================= + +- startSim() switches to the Results tab and purges the plots BEFORE + runSimulationSequence() validates anything, so every refused run drops the + user on an empty Results tab with no explanation of why. This predates the + parIRL work and affects all the abort paths equally (energy cap, seed + validation, recoil cutoff, and the re-entrancy guard in startSequence). Fix + by validating in startSim() before the tab switch, or by moving the tab + switch after the first successful job is queued. +- The web deployment runs TRIMSP_BIN, which defaults to /usr/local/bin/trimspNL, + so the engine-side fix in trimspNL.F does not reach the live site until the + binary is rebuilt and installed. scripts/deploy-web.sh only rewrites the path + in the CGI; it does not build. The GUI-side fix works immediately, so a + deployment can briefly have the friendly GUI refusal in front of an engine + that still segfaults on a hand-written deck. +- Untracked scratch left in the tree, decide .gitignore vs delete: + SrTiO3_E2000.inp (2 keV / 1000-projectile benchmark deck at the repo root), + fortran/Makefile_org (Makefile backup), fortran/profile.txt and + fortran/profile_annotated.txt (gprof reports; .gitignore covers gmon.out and + fortran/trimspNL-profile but not these), and tests/pcg32-test/, + tests/ranlux-test/, tests/xoshiro-test/ (per-backend engine run output trees + from the RNG comparison, i.e. generated results rather than fixtures). diff --git a/TrimSP.html b/TrimSP.html index 7fc8566..7ff11db 100644 --- a/TrimSP.html +++ b/TrimSP.html @@ -395,12 +395,14 @@ No recoils are generated (i.e., no sputtering effects); used to speed up the calculation if only projectile ranges are of interest. -