Repository navigation
Conversation
The det <= 0 branch (surface parallel to the z-axis, e.g. at the waist of an ellipsoid or for a cylinder) divided the y-component by r^2 instead of r, giving a non-unit, non-radial normal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
reflectNeutronConic() applied a hard-coded Gaussian slope error of 1 urad (meridional) and 250 urad (sagittal). These are now ConicSurf fields (figerr_m, figerr_s), set by the make*() functions to the previous values (CONIC_SIGMA_M/S) and exposed as sigma_m/sigma_s on Conics_EH/HE/PH/PP with unchanged defaults, i.e. results are unchanged by default. With both set to 0 the rotation and its two randnorm() draws are skipped. Dead debug output is removed. Mirror_conic and the test instruments pass the parameters through; Mirror_conic defaults to no figure error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
reflectNeutronConic() absorbed neutrons with grazing angle above m*theta_c(Ni), ignoring the Qc parameter and cutting the tanh tail of StdReflecFunc. The reflectivity is now given by StdReflecFunc alone, and neutrons with R=0 are absorbed (as in Mirror.comp). The unused cut-off in reflectNeutronFlat() is removed as well. Small numerical change: slightly more intensity just above m*Qc. Mirror_conic conic=1 now agrees exactly with Mirror(transmit=0). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With sourceDist unset (0), the mirror spacing was calculated by get_r_at_z0() for a source at the mirror centre, since the default sourceDist=LStart was applied only afterwards. Also allocate with sizeof(double) instead of sizeof(double_t). Affects NMO as well (inherits INITIALIZE). Bundled instruments set sourceDist explicitly and are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Re-recorded (1e6 rays, seed 1) after the conic.h reflectivity fixes of #2789 and this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
For review process: conic.h reflectivity fixes: summary reportCovers issue mccode-dev/McCode#2787 and three PRs:
All changes are McStas-only ( SummaryThe ConicTracer reflection code in One more defect was found during verification: spurious re-hits of the surface a Work donePR #2789 (merged)
PR #2793, branch
|
| Commit | Change | Numerical effect |
|---|---|---|
b9a3c9c66 |
getNormConic: degenerate (det ≤ 0) branch returned -y/r² instead of -y/r. |
None for bundled examples (bit-identical) |
212539892 |
Figure error made configurable: ConicSurf.figerr_m/figerr_s, default CONIC_SIGMA_M = 1e-6, CONIC_SIGMA_S = 250e-6 rad (the old hard-coded values); exposed as sigma_m/sigma_s on Conics_* (same defaults) and Mirror_conic (default 0). Setting both to 0 skips the rotation and its RNG draws. Debug output removed. |
None at default values (bit-identical) |
84ea859dd |
reflectNeutronConic: hard cut-off at m·θc(Ni) removed; reflectivity now comes from StdReflecFunc alone (R = 0 → absorbed, as in Mirror). Unused gc removed from reflectNeutronFlat. |
Small: slightly more intensity just above m·Qc |
33ea7b96d |
Dead if (ref < 0) printf("this happens?") branch removed from reflectNeutronFlat. |
None |
a9d899e7b |
FlatEllipse_finite_mirror (and NMO, which inherits its INITIALIZE): sourceDist default (= LStart) is now applied before the mirror spacing is computed; sizeof(double_t) → sizeof(double). |
Only when sourceDist is left unset |
70973d07d |
Test_Conics_pairs: %Example reference values re-recorded. |
n/a |
Minimal-change design of the figure-error control
- Two
doublefields onConicSurf. The threemake*()constructors set them from
overridable macros, so a#define CONIC_SIGMA_M ...before includingconic.h
also works. - The components set the fields after building the scene with a 4-line loop over
s.c[].
No function signatures changed. - Field names (
figerr_*) differ from the parameter names (sigma_*) because component
parameters become macros inside the component code. - The doc strings state that the values are RMS slope errors, so the reflected ray
deviates by twice the value.
Verification
All runs used McStas 3.9.2 (conda), macOS arm64, clang, CPU only, 1e6 rays, --seed 1,
a single MPI process, and the branch's conic.h/components copied into the run directory.
Test_Mirror_conic, θ = 1°, m = 2, 1–11 Å; reflected intensity (transmitted for flat in brackets):
| Setup | main before #2789 | main now | this branch |
|---|---|---|---|
Mirror transmit=1 (reference) |
8.982e-7 (7.018e-7) | same | same |
| conic.h FlatSurf | 1.568e-6 (1.6e-8) | 8.982e-7 (7.018e-7) | 8.982e-7 (7.018e-7) |
Mirror transmit=0 (reference) |
8.976e-7 | same | same |
| conic.h ConicSurf | – | 8.831e-7 | 8.976e-7 (identical to Mirror) |
Other checks:
- Same pattern at θ = 0.5°, m = 4.
Test_Mirror_FlatEllipse:FlatEllipse_finite_mirroris identical toMirroronmainand on the branch.- Figure-error refactor is bit-identical. A scratch build of the branch with the old cut-off
temporarily re-inserted reproducesmainexactly on all 12Test_Conics_pairsexamples. - The cut-off removal changes
Test_Conics_pairsby about 1e-5 relative. The new references
are 1–2.5% below the old stored values; that shift comes from the q fix in conic.h: fix reflectivity in reflectNeutronFlat/Conic (#2787) + Mirror_conic tests #2789. sourceDist: with it left unset inTest_FlatEllipse_finite_mirror,maingives
nonsensical mirror radii (0.51, 1.65, 2.53, 3.29 m, …). The branch gives 0.207, 0.169,
0.138, 0.113, 0.093 m, identical to settingsourceDist = LStartexplicitly.Test_NMOandTest_FlatEllipse_finite_mirrorwith defaults are bit-identical tomain.WOFSANS width=0.15: unchanged by conic.h follow-up: figure-error control, StdReflecFunc-only reflectivity, normal and sourceDist fixes #2793 and conic.h: avoid spurious re-hit (and absorption) on the surface just reflected from #2794 (PSD_10 = 3.50e9 ± 0.57e9, stored reference 4e9).- Every intermediate commit builds and runs.
clang -Wall -Wextra: no new warnings; one fewer (unusedgc).
Not tested: Linux (gcc/clang), Windows (MSVC), OpenACC/GPU, MPI with more than one
process.
Spurious re-hit of the departure surface (#2794)
Finding. After a reflection the neutron sits on the surface it left, so that surface's
quadric has a root at t ≈ 0. Round-off in the constant term C, divided by the small linear
term B of a grazing ray, can turn this into a small positive root (seen: 1e-18 … 5e-7 s,
i.e. up to about 0.5 mm). traceSingleNeutron then "hits" the same surface again from the
outside, and reflectNeutronConic absorbs the neutron (vn > 0). solveQuad's 1e-11
cut-off does not catch these cases.
Impact (Test_Conics_pairs OPTIC=1, focal monitor widened to 2 m; counts of doubly reflected neutrons):
| Case | Spurious absorptions before → after | Double reflections before → after |
|---|---|---|
| OPTIC=1, σ = 0 | 12 809 → 0 | 13 198 → 26 007 |
| OPTIC=1, σm = 1 µrad, σs = 0 | 3 043 → 0 | 22 947 → 25 990 |
| OPTIC=1, default σ | 1 408 → 0 | 24 582 → 25 990 |
| OPTIC=2 (PH), default σ | 3 139 → 0 | 9 788 → 12 927 |
| OPTIC=4 (PP), default σ | 66 → 0 | 25 942 → 26 008 |
- Before the fix, about 6% of the focused intensity was lost at default settings (EH/HE) and
about 25% for the PH pair. - Without figure error about half was lost, so results depended strongly on the figure
error for unphysical reasons: 1 µrad should not matter.
Fix. For the surface hit in the previous step, the next intersection is computed from
the exact factorisation (C = 0), so the other root is t = −B/A. This is done in the new
getTimeOfNextCollisionConic/Flat(), which traceSingleNeutron uses for the last surface
hit. traceNeutronConic/Flat() now take the collision time from traceSingleNeutron
instead of recomputing it; nothing else calls them. All other surfaces give bit-identical
results.
- Numerical change:
Test_Conics_pairsreferences go up by +6% (EH/HE), +33% (PH),
+0.2% (PP) and 0% (fs=8 fi=6cases); they are updated in the PR. - Alternative considered and rejected: a small post-reflection nudge. The spurious
roots lie 1e-9 to 1e-4 m from the reflection point (mostly 1e-7 to 1e-5 m), far above
round-off. A nudge removes them only once it reaches 1e-4 to 1e-3 m (OPTIC=1 with no figure
error: 12 809 spurious absorptions at no nudge, 4 896 at 1 µm, 475 at 10 µm, 28 at 0.1 mm,
0 at 1 mm). That length is comparable to the scale of the mirror geometry and can skip
genuine hits. At 1 mm it already loses intensity relative to the exact-root fix
(7.604e-13 vs 7.620e-13). The exact-root approach needs no length scale. - The FlatSurf part is preventive.
Test_NMOandTest_FlatEllipse_finite_mirror(also with
a silicon substrate,mirror_width=0.0005,mb=0/4),Test_Mirror_conic,
Test_Mirror_FlatEllipseandWOFSANS width=0.15are all bit-identical to before.
Next steps
- Review and merge conic.h follow-up: figure-error control, StdReflecFunc-only reflectivity, normal and sourceDist fixes #2793, then retarget conic.h: avoid spurious re-hit (and absorption) on the surface just reflected from #2794 to
mainand merge it. - Watch CI on Linux and Windows: only macOS/clang/CPU was tested locally.
- Consider mentioning the changed Wolter-optic intensities (conic.h: fix reflectivity in reflectNeutronFlat/Conic (#2787) + Mirror_conic tests #2789, conic.h: avoid spurious re-hit (and absorption) on the surface just reflected from #2794) in the release notes.
Follow-up to #2789 (issue #2787). This handles the open items listed there. McStas only.
Changes
getNormConic-y/r²instead of-y/r.ConicSurf.figerr_m/figerr_s(defaultsCONIC_SIGMA_M/S), exposed assigma_m/sigma_sonConics_EH/HE/PH/PPwith unchanged defaults. Setting both to 0 switches the figure error off. Debug code removed.reflectNeutronConic: the hard cut-off at m·θc(Ni) (which ignoredQc) is removed, soStdReflecFuncalone sets R. Neutrons with R = 0 are absorbed, as inMirror.Test_Conics_pairsref < 0branch removed inreflectNeutronFlat.sourceDistFlatEllipse_finite_mirror/NMO: the defaultsourceDist = LStartis now applied before the mirror spacing is computed (previously the radii were nonsense when it was unset).sizeof(double_t)→sizeof(double).sourceDistis unset; all bundled instruments set itTest_Conics_pairs%Examplevalues re-recorded; they now include the 1–2.5% shift from #2789.The change is small: removing the cut-off adds a little intensity just above m·Qc. Everything else is bit-identical at default parameters. To check this, a build with the old cut-off temporarily re-inserted reproduced
mainexactly on all 12Test_Conics_pairsexamples.Tested
--seed 1, single process, using McStas 3.9.2 with this branch'sconic.h/components:Test_Mirror_conic: conic.h flat and conic shell now both matchMirrorexactly.Test_Mirror_FlatEllipse,Test_Conics_pairs(all examples, plussigma_*variations).Test_NMOandTest_FlatEllipse_finite_mirror: bit-identical tomain.-Wall -Wextra: no new warnings.WOFSANS.Known remaining issue (separate PR to follow)
Right after a reflection,
traceSingleNeutroncan find a spurious re-hit of the same surface 1e-9 to 1e-4 m away.reflectNeutronConicthen absorbs the neutron as if it had hit the shell from outside. At default settings this loses about 6% of the focused intensity inTest_Conics_pairsOPTIC=1. It is also why results depend strongly on the figure error: with exact normals about half the intensity is lost.🤖 Generated with Claude Code