1 line
156 KiB
HTML
1 line
156 KiB
HTML
<!DOCTYPE html> <html lang=en data-content_root="./"> <meta charset=utf-8 /> <meta name=viewport content="width=device-width, initial-scale=1.0" /><meta name=viewport content="width=device-width, initial-scale=1" /> <meta name=viewport content="width=device-width,initial-scale=1"> <meta http-equiv=x-ua-compatible content="ie=edge"> <meta name="lang:clipboard.copy" content="Copy to clipboard"> <meta name="lang:clipboard.copied" content="Copied to clipboard"> <meta name="lang:search.language" content=en > <meta name="lang:search.pipeline.stopwords" content=True > <meta name="lang:search.pipeline.trimmer" content=True > <meta name="lang:search.result.none" content="No matching documents"> <meta name="lang:search.result.one" content="1 matching document"> <meta name="lang:search.result.other" content="# matching documents"> <meta name="lang:search.tokenizer" content="[\s\-]+"> <link href="https://fonts.gstatic.com/" rel=preconnect crossorigin> <link href="https://fonts.googleapis.com/css?family=Roboto+Mono:400,500,700|Roboto:300,400,400i,700&display=fallback" rel=stylesheet > <style> body, input { font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif } code, kbd, pre { font-family: "Roboto Mono", "Courier New", Courier, monospace } </style> <link rel=stylesheet href="_static/stylesheets/application.css"/> <link rel=stylesheet href="_static/stylesheets/application-palette.css"/> <link rel=stylesheet href="_static/stylesheets/application-fixes.css"/> <link rel=stylesheet href="_static/fonts/material-icons.css"/> <meta name=theme-color content="#3f51b5"> <script src="_static/javascripts/modernizr.js"></script> <title>Data analysis: integration, scaling and merging (§8–§12) — Jungfraujoch 1.0.0-rc.174 documentation</title> <link rel=stylesheet type="text/css" href="_static/pygments.css?v=83e35b93" /> <link rel=stylesheet type="text/css" href="_static/material.css?v=79c92029" /> <script src="_static/documentation_options.js?v=3dccfb20"></script> <script src="_static/doctools.js?v=9bcbadda"></script> <script src="_static/sphinx_highlight.js?v=dc90522c"></script> <script>window.MathJax = {"tex": {"tags": "ams", "inlineMath": [["\\(", "\\)"], ["$", "$"]], "displayMath": [["\\[", "\\]"], ["$$", "$$"]], "processEscapes": true}, "options": {"processHtmlClass": "tex2jax_process|mathjax_process|math|output_area"}}</script> <script defer=defer src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script> <link rel=icon href="_static/jfjoch.png"/> <link rel=index title=Index href=genindex.html /> <link rel=search title=Search href=search.html /> <link rel=next title="Data analysis: space group and validation (§13–§14)" href=CPU_DATA_ANALYSIS_DECISIONS.html /> <link rel=prev title="Data analysis: indexing and geometry (§4–§7)" href=CPU_DATA_ANALYSIS_INDEXING.html /> <body dir=ltr data-md-color-primary=indigo data-md-color-accent=lime> <svg class=md-svg > <defs data-children-count=0 > <svg xmlns="http://www.w3.org/2000/svg" width=500 height=500 viewBox="0 0 500 500" id=__gitlab ><path fill=currentColor d="M93.667 473.347l90.684-279.097H2.983l90.684 279.097z" transform="translate(156.198 1.16)"/><path fill=currentColor d="M221.333 473.345L130.649 194.25H3.557l217.776 279.095z" transform="translate(28.531 1.16)" opacity=.7 /><path fill=currentColor d="M32 195.155L4.441 279.97a18.773 18.773 0 0 0 6.821 20.99l238.514 173.29L32 195.155z" transform="translate(.089 .256)" opacity=.5 /><path fill=currentColor d="M2.667-84.844h127.092L75.14-252.942c-2.811-8.649-15.047-8.649-17.856 0L2.667-84.844z" transform="translate(29.422 280.256)"/><path fill=currentColor d="M2.667 473.345L93.351 194.25h127.092L2.667 473.345z" transform="translate(247.198 1.16)" opacity=.7 /><path fill=currentColor d="M221.334 195.155l27.559 84.815a18.772 18.772 0 0 1-6.821 20.99L3.557 474.25l217.777-279.095z" transform="translate(246.307 .256)" opacity=.5 /><path fill=currentColor d="M130.667-84.844H3.575l54.618-168.098c2.811-8.649 15.047-8.649 17.856 0l54.618 168.098z" transform="translate(336.974 280.256)"/></svg> </defs> </svg> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer > <input class=md-toggle data-md-toggle=search type=checkbox id=__search > <label class=md-overlay data-md-component=overlay for=__drawer ></label> <a href="#CPU_DATA_ANALYSIS_INTEGRATION" tabindex=1 class=md-skip > Skip to content </a> <header class=md-header data-md-component=header > <nav class="md-header-nav md-grid"> <div class="md-flex navheader"> <div class="md-flex__cell md-flex__cell--shrink"> <a href=index.html title="Jungfraujoch 1.0.0-rc.174 documentation" class="md-header-nav__button md-logo"> <i class=md-icon ></i> </a> </div> <div class="md-flex__cell md-flex__cell--shrink"> <label class="md-icon md-icon--menu md-header-nav__button" for=__drawer ></label> </div> <div class="md-flex__cell md-flex__cell--stretch"> <div class="md-flex__ellipsis md-header-nav__title" data-md-component=title > <span class=md-header-nav__topic >PSI Jungfraujoch</span> <span class=md-header-nav__topic > Data analysis: integration, scaling and merging (§8–§12) </span> </div> </div> <div class="md-flex__cell md-flex__cell--shrink"> <label class="md-icon md-icon--search md-header-nav__button" for=__search ></label> <div class=md-search data-md-component=search role=dialog > <label class=md-search__overlay for=__search ></label> <div class=md-search__inner role=search > <form class=md-search__form action=search.html method=get name=search > <input type=text class=md-search__input name=q placeholder=""Search"" autocapitalize=off autocomplete=off spellcheck=false data-md-component=query data-md-state=active > <label class="md-icon md-search__icon" for=__search ></label> <button type=reset class="md-icon md-search__icon" data-md-component=reset tabindex=-1 >  </button> </form> <div class=md-search__output > <div class=md-search__scrollwrap data-md-scrollfix> <div class=md-search-result data-md-component=result > <div class=md-search-result__meta > Type to start searching </div> <ol class=md-search-result__list ></ol> </div> </div> </div> </div> </div> </div> <div class="md-flex__cell md-flex__cell--shrink"> <div class=md-header-nav__source > <a href="https://gitea.psi.ch/mx/jungfraujoch" title="Go to repository" class=md-source data-md-source=github > <div class=md-source__icon > <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width=28 height=28 > <use xlink:href="#__gitlab" width=24 height=24 ></use> </svg> </div> <div class=md-source__repository > Jungfraujoch </div> </a> </div> </div> <script src="_static/javascripts/version_dropdown.js"></script> <script> var json_loc = ""versions.json"", target_loc = "../", text = "Versions"; $( document ).ready( add_version_dropdown(json_loc, target_loc, text)); </script> </div> </nav> </header> <div class=md-container > <nav class=md-tabs data-md-component=tabs > <div class="md-tabs__inner md-grid"> <ul class=md-tabs__list > <li class=md-tabs__item ><a href=index.html class=md-tabs__link >Jungfraujoch 1.0.0-rc.174 documentation</a> </ul> </div> </nav> <main class=md-main > <div class="md-main__inner md-grid" data-md-component=container > <div class="md-sidebar md-sidebar--primary" data-md-component=navigation > <div class=md-sidebar__scrollwrap > <div class=md-sidebar__inner > <nav class="md-nav md-nav--primary" data-md-level=0 > <label class="md-nav__title md-nav__title--site" for=__drawer > <a href=index.html title="Jungfraujoch 1.0.0-rc.174 documentation" class="md-nav__button md-logo"> <i class=md-icon ></i> </a> <a href=index.html title="Jungfraujoch 1.0.0-rc.174 documentation">PSI Jungfraujoch</a> </label> <div class=md-nav__source > <a href="https://gitea.psi.ch/mx/jungfraujoch" title="Go to repository" class=md-source data-md-source=github > <div class=md-source__icon > <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width=28 height=28 > <use xlink:href="#__gitlab" width=24 height=24 ></use> </svg> </div> <div class=md-source__repository > Jungfraujoch </div> </a> </div> <ul class=md-nav__list > <li class=md-nav__item > <span class="md-nav__link caption"><span class=caption-text >rugnux — data processing</span></span> <li class=md-nav__item > <a href=RUGNUX.html class=md-nav__link >Rugnux</a> <li class=md-nav__item > <a href=RUGNUX_OVERVIEW.html class=md-nav__link >What Rugnux does</a> <li class=md-nav__item > <a href=RUGNUX_INSTALL.html class=md-nav__link >Installing Rugnux</a> <li class=md-nav__item > <a href=RUGNUX_FORMATS.html class=md-nav__link >What Rugnux reads</a> <li class=md-nav__item > <a href=RUGNUX_TUTORIAL.html class=md-nav__link >Running Rugnux</a> <li class=md-nav__item > <a href=RUGNUX_INTEGRATION.html class=md-nav__link >Rugnux with other programs</a> <li class=md-nav__item > <a href=RUGNUX_REPORT.html class=md-nav__link >The results report</a> <li class=md-nav__item > <a href=RUGNUX_ADVANCED.html class=md-nav__link >Advanced Rugnux</a> <li class=md-nav__item > <a href=RUGNUX_CALIBRATION.html class=md-nav__link >Detector calibration from powder rings (<code class="docutils literal notranslate"><span class=pre >rugnux</span> <span class=pre >--mode</span> <span class=pre >calibration</span></code>)</a> <li class=md-nav__item > <a href=CPU_DATA_ANALYSIS.html class=md-nav__link >CPU-side crystallographic data analysis (Jungfraujoch)</a> <li class=md-nav__item > <a href=CPU_DATA_ANALYSIS_IMAGE.html class=md-nav__link >Data analysis: from images to spots (§0–§3)</a> <li class=md-nav__item > <a href=CPU_DATA_ANALYSIS_INDEXING.html class=md-nav__link >Data analysis: indexing and geometry (§4–§7)</a> <li class=md-nav__item > <input class="md-toggle md-nav__toggle" data-md-toggle=toc type=checkbox id=__toc > <label class="md-nav__link md-nav__link--active" for=__toc > Data analysis: integration, scaling and merging (§8–§12) </label> <a href="#" class="md-nav__link md-nav__link--active">Data analysis: integration, scaling and merging (§8–§12)</a> <nav class="md-nav md-nav--secondary"> <ul class=md-nav__list data-md-scrollfix=""> </ul> </nav> <ul class=md-nav__list > <li class=md-nav__item > <a href="#reflection-prediction" class=md-nav__link >8. Reflection prediction</a> <li class=md-nav__item > <a href="#d-bragg-integration-profile-fitting-over-a-three-ring-roi" class=md-nav__link >9. 2D Bragg integration (profile fitting over a three-ring ROI)</a> <li class=md-nav__item > <a href="#scaling-and-merging" class=md-nav__link >10. Scaling and merging</a> <li class=md-nav__item > <a href="#mosaicity-and-profile-radius-monitoring" class=md-nav__link >11. Mosaicity and “profile radius” monitoring</a> <li class=md-nav__item > <a href="#auxiliary-statistics-i-i-and-wilson-plot" class=md-nav__link >12. Auxiliary statistics: ⟨I/σ(I)⟩ and Wilson plot</a> </ul> <li class=md-nav__item > <a href=CPU_DATA_ANALYSIS_DECISIONS.html class=md-nav__link >Data analysis: space group and validation (§13–§14)</a> <li class=md-nav__item > <span class="md-nav__link caption"><span class=caption-text >Jungfraujoch — acquisition</span></span> <li class=md-nav__item > <a href=JFJOCH_BROKER.html class=md-nav__link >jfjoch_broker</a> <li class=md-nav__item > <a href=JFJOCH_WRITER.html class=md-nav__link >jfjoch_writer</a> <li class=md-nav__item > <a href=JFJOCH_VIEWER.html class=md-nav__link >jfjoch_viewer</a> <li class=md-nav__item > <a href=SOFTWARE_INTEGRATION.html class=md-nav__link >Integration with MX data processing software</a> <li class=md-nav__item > <a href=TOOLS.html class=md-nav__link >Tools</a> <li class=md-nav__item > <a href=DEPLOYMENT.html class=md-nav__link >Deployment</a> <li class=md-nav__item > <a href=DETECTORS.html class=md-nav__link >Supported detectors</a> <li class=md-nav__item > <a href=HARDWARE.html class=md-nav__link >Hardware requirements</a> <li class=md-nav__item > <a href=SOFTWARE.html class=md-nav__link >Software requirements</a> <li class=md-nav__item > <span class="md-nav__link caption"><span class=caption-text >FPGA</span></span> <li class=md-nav__item > <a href=FPGA.html class=md-nav__link >FPGA smartNIC</a> <li class=md-nav__item > <a href=FPGA_LICENSE.html class=md-nav__link >FPGA license</a> <li class=md-nav__item > <a href=FPGA_DESIGN.html class=md-nav__link >FPGA data flow</a> <li class=md-nav__item > <a href=FPGA_NETWORK.html class=md-nav__link >FPGA network</a> <li class=md-nav__item > <a href=FPGA_PCIE_DRIVER.html class=md-nav__link >FPGA PCIe driver</a> <li class=md-nav__item > <a href=FPGA_SETTINGS.html class=md-nav__link >FPGA advanced reference</a> <li class=md-nav__item > <a href=FPGA_DATA_ANALYSIS.html class=md-nav__link >FPGA data analysis</a> <li class=md-nav__item > <span class="md-nav__link caption"><span class=caption-text >Reference</span></span> <li class=md-nav__item > <a href=DETECTOR_GEOMETRY.html class=md-nav__link >Detector geometry</a> <li class=md-nav__item > <a href=OPENAPI.html class=md-nav__link >OpenAPI</a> <li class=md-nav__item > <a href=OPENAPI_SPECS.html class=md-nav__link >OpenAPI specification</a> <li class=md-nav__item > <a href=PYTHON_CLIENT.html class=md-nav__link >OpenAPI Python client</a> <li class=md-nav__item > <a href=CBOR.html class=md-nav__link >CBOR messages</a> <li class=md-nav__item > <a href=HDF5.html class=md-nav__link >HDF5 / NeXus data format</a> <li class=md-nav__item > <a href=IMAGE_STREAM.html class=md-nav__link >Data streams</a> <li class=md-nav__item > <a href=PIXEL_MASK.html class=md-nav__link >Pixel mask</a> <li class=md-nav__item > <a href=WEB_FRONTEND.html class=md-nav__link >Web frontend</a> <li class=md-nav__item > <a href=TESTS.html class=md-nav__link >Tests</a> <li class=md-nav__item > <span class="md-nav__link caption"><span class=caption-text >Project</span></span> <li class=md-nav__item > <a href=ACKNOWLEDGEMENT.html class=md-nav__link >Acknowledgements</a> <li class=md-nav__item > <a href=EXTERNAL_TEST_DATA.html class=md-nav__link >External test data</a> <li class=md-nav__item > <a href=LICENSE.html class=md-nav__link >License</a> <li class=md-nav__item > <a href=THIRD_PARTY_NOTICES.html class=md-nav__link >Third-party software notices</a> <li class=md-nav__item > <a href=VERSIONING.html class=md-nav__link >Semantic versioning</a> <li class=md-nav__item > <a href=SECURITY.html class=md-nav__link >Security</a> <li class=md-nav__item > <a href=RELEASE_CONTENTS.html class=md-nav__link >Release contents</a> <li class=md-nav__item > <a href=REPOSITORIES.html class=md-nav__link >Linux package repositories</a> <li class=md-nav__item > <a href=NAMING.html class=md-nav__link >Naming</a> <li class=md-nav__item > <a href=CHANGELOG.html class=md-nav__link >Changelog</a> </ul> </nav> </div> </div> </div> <div class="md-sidebar md-sidebar--secondary" data-md-component=toc > <div class=md-sidebar__scrollwrap > <div class=md-sidebar__inner > <nav class="md-nav md-nav--secondary"> <ul class=md-nav__list data-md-scrollfix=""> </ul> </nav> </div> </div> </div> <div class=md-content > <article class="md-content__inner md-typeset" role=main > <section class="tex2jax_ignore mathjax_ignore" id=data-analysis-integration-scaling-and-merging-812 > <h1 id=cpu-data-analysis-integration--page-root >Data analysis: integration, scaling and merging (§8–§12)<a class=headerlink href="#cpu-data-analysis-integration--page-root" title="Link to this heading">¶</a></h1> <p>Part of the <a class="reference internal" href=CPU_DATA_ANALYSIS.html ><span class="std std-doc">CPU/GPU data-analysis reference</span></a>; the section numbers are continuous across its four parts.</p> <nav class="contents local" id=on-this-page > <p class=topic-title >On this page</p> <ul class=simple > <li><p><a class="reference internal" href="#reflection-prediction" id=id1 >8. Reflection prediction</a></p> <ul> <li><p><a class="reference internal" href="#enumerating-reciprocal-lattice-points" id=id2 >8.1 Enumerating reciprocal lattice points</a></p> <li><p><a class="reference internal" href="#still-prediction-excitation-error-cutoff" id=id3 >8.2 Still prediction (excitation-error cutoff)</a></p> <li><p><a class="reference internal" href="#rotation-prediction-laue-equation-partiality-model" id=id4 >8.3 Rotation prediction (Laue equation + partiality model)</a></p> <li><p><a class="reference internal" href="#systematic-absences-centering" id=id5 >8.4 Systematic absences (centering)</a></p> </ul> <li><p><a class="reference internal" href="#d-bragg-integration-profile-fitting-over-a-three-ring-roi" id=id6 >9. 2D Bragg integration (profile fitting over a three-ring ROI)</a></p> <ul> <li><p><a class="reference internal" href="#regions-of-interest" id=id7 >9.1 Regions of interest</a></p> <li><p><a class="reference internal" href="#box-summation-seed-and-fallback" id=id8 >9.2 Box summation (seed and fallback)</a></p> <li><p><a class="reference internal" href="#profile-fitted-extraction-default" id=id9 >9.3 Profile-fitted extraction (default)</a></p> <li><p><a class="reference internal" href="#the-prescaling-correction" id=id10 >9.4 The prescaling correction</a></p> <li><p><a class="reference internal" href="#choosing-the-signal-radius-from-the-crystals-own-spots-rotation" id=id11 >9.5 Choosing the signal radius from the crystal’s own spots (rotation)</a></p> <li><p><a class="reference internal" href="#measuring-the-bandwidth" id=id12 >9.6 Measuring the bandwidth</a></p> <li><p><a class="reference internal" href="#the-flight-path" id=id13 >9.7 The flight path</a></p> </ul> <li><p><a class="reference internal" href="#scaling-and-merging" id=id14 >10. Scaling and merging</a></p> <ul> <li><p><a class="reference internal" href="#observation-model" id=id15 >10.1 Observation model</a></p> <li><p><a class="reference internal" href="#partiality-models" id=id16 >10.2 Partiality models</a></p> <li><p><a class="reference internal" href="#smoothing-of-per-frame-scales" id=id17 >10.3 Smoothing of per-frame scales</a></p> <li><p><a class="reference internal" href="#merging-estimator" id=id18 >10.4 Merging estimator</a></p> <li><p><a class="reference internal" href="#merging-statistics" id=id19 >10.5 Merging statistics</a></p> <li><p><a class="reference internal" href="#rotation-datasets-combining-partials-into-fulls-3d-integration" id=id20 >10.6 Rotation datasets: combining partials into fulls (3D integration)</a></p> <li><p><a class="reference internal" href="#r-free-test-set-flags" id=id21 >10.7 R-free test-set flags</a></p> <li><p><a class="reference internal" href="#frenchwilson-amplitudes" id=id22 >10.8 French–Wilson amplitudes</a></p> <li><p><a class="reference internal" href="#reference-data-fixing-the-space-group-and-resolving-the-indexing-ambiguity" id=id23 >10.9 Reference data: fixing the space group and resolving the indexing ambiguity</a></p> <li><p><a class="reference internal" href="#ice-rings-at-the-scale-and-merge-stages" id=id24 >10.10 Ice rings at the scale and merge stages</a></p> </ul> <li><p><a class="reference internal" href="#mosaicity-and-profile-radius-monitoring" id=id25 >11. Mosaicity and “profile radius” monitoring</a></p> <ul> <li><p><a class="reference internal" href="#profile-radius-intrinsic-excitation-error-width" id=id26 >11.1 Profile radius (intrinsic excitation-error width)</a></p> <li><p><a class="reference internal" href="#mosaicity-from-rotation-data" id=id27 >11.2 Mosaicity from rotation data</a></p> </ul> <li><p><a class="reference internal" href="#auxiliary-statistics-i-i-and-wilson-plot" id=id28 >12. Auxiliary statistics: ⟨I/σ(I)⟩ and Wilson plot</a></p> <ul> <li><p><a class="reference internal" href="#per-shell-i-i" id=id29 >12.1 Per-shell ⟨I/σ(I)⟩</a></p> <li><p><a class="reference internal" href="#wilson-plot-b-factor-proxy" id=id30 >12.2 Wilson plot (B-factor proxy)</a></p> </ul> </ul> </nav> <section id=reflection-prediction > <h2 id=reflection-prediction ><a class=toc-backref href="#id1" role=doc-backlink >8. Reflection prediction</a><a class=headerlink href="#reflection-prediction" title="Link to this heading">¶</a></h2> <p>Jungfraujoch predicts reflection positions for integration by enumerating Miller indices within a resolution cutoff and accepting those that satisfy a diffraction condition model.</p> <section id=enumerating-reciprocal-lattice-points > <h3 id=enumerating-reciprocal-lattice-points ><a class=toc-backref href="#id2" role=doc-backlink >8.1 Enumerating reciprocal lattice points</a><a class=headerlink href="#enumerating-reciprocal-lattice-points" title="Link to this heading">¶</a></h3> <p>For a maximum resolution <span class="math notranslate nohighlight">\(d_\mathrm{min}\)</span>, accept <span class="math notranslate nohighlight">\((h,k,l)\)</span> such that: <span class="math notranslate nohighlight">\( \lVert \mathbf{p}(h,k,l)\rVert^2 = \lVert h\mathbf{a}^* + k\mathbf{b}^* + l\mathbf{c}^*\rVert^2 \le \left(\frac{1}{d_\mathrm{min}}\right)^2. \)</span></p> </section> <section id=still-prediction-excitation-error-cutoff > <h3 id=still-prediction-excitation-error-cutoff ><a class=toc-backref href="#id3" role=doc-backlink >8.2 Still prediction (excitation-error cutoff)</a><a class=headerlink href="#still-prediction-excitation-error-cutoff" title="Link to this heading">¶</a></h3> <p>For still images, the diffracting condition is approximated by an excitation-error cutoff: <span class="math notranslate nohighlight">\( \left|\Delta_\mathrm{Ewald}(\mathbf{p})\right| \le \Delta_\mathrm{cut}. \)</span> Accepted reflections are projected to the detector by intersecting the diffracted direction <span class="math notranslate nohighlight">\(\mathbf{S}=\mathbf{S}_0+\mathbf{p}\)</span> with the detector plane, using the current geometry.</p> <p>When the beam has a finite energy bandwidth, this window is <strong>broadened radially per reflection</strong>: the cutoff is combined in quadrature with a bandwidth smear, <span class="math notranslate nohighlight">\(\sqrt{\Delta_\mathrm{cut}^2 + (3\,\sigma_\mathrm{bw})^2}\)</span>, where <span class="math notranslate nohighlight">\(\sigma_\mathrm{bw}\propto|p_z|\)</span> (the reciprocal-space depth along the beam, growing as <span class="math notranslate nohighlight">\(\sim 1/d^2\)</span>). This keeps high-resolution reflections — smeared by the bandwidth into radial streaks — from being clipped. The same <span class="math notranslate nohighlight">\(\sigma_\mathrm{bw}\)</span> is deconvolved from the measured profile radius (§11.1), so it is not double-counted.</p> </section> <section id=rotation-prediction-laue-equation-partiality-model > <h3 id=rotation-prediction-laue-equation-partiality-model ><a class=toc-backref href="#id4" role=doc-backlink >8.3 Rotation prediction (Laue equation + partiality model)</a><a class=headerlink href="#rotation-prediction-laue-equation-partiality-model" title="Link to this heading">¶</a></h3> <p>For rotation/oscillation datasets, Jungfraujoch solves for rotation angles <span class="math notranslate nohighlight">\(\phi\)</span> where the rotated reciprocal lattice point satisfies the Ewald-sphere condition. In an XDS-like notation, define:</p> <ul class=simple > <li><p>rotation axis unit vector <span class="math notranslate nohighlight">\(\mathbf{m}_2\)</span>,</p> <li><p><span class="math notranslate nohighlight">\(\mathbf{S}_0\)</span> incident vector,</p> <li><p><span class="math notranslate nohighlight">\(\mathbf{S}(\phi)=\mathbf{S}_0+\mathbf{p}(\phi)\)</span>.</p> </ul> <p>A key quantity is: <span class="math notranslate nohighlight">\( \zeta = \left|\mathbf{m}_2\cdot \mathbf{e}_1\right|,\quad \mathbf{e}_1 = \frac{\mathbf{S}\times \mathbf{S}_0}{\lVert \mathbf{S}\times \mathbf{S}_0\rVert}, \)</span> which also appears in XDS as the Lorentz component linked to the rotation axis.</p> <p>A Gaussian mosaicity model yields a partiality fraction over an oscillation width <span class="math notranslate nohighlight">\(\Delta\phi\)</span>:</p> <p><span class="math notranslate nohighlight">\( P(\phi;\sigma_M,\zeta,\Delta\phi) = \frac{1}{2}\left[\mathrm{erf}\!\left(\frac{\phi+\Delta\phi/2}{\sqrt{2}\,\sigma_M/\zeta}\right) - \mathrm{erf}\!\left(\frac{\phi-\Delta\phi/2}{\sqrt{2}\,\sigma_M/\zeta}\right)\right], \)</span></p> <p>with mosaicity <span class="math notranslate nohighlight">\(\sigma_M\)</span> in radians.</p> <p>Reflections are predicted if they meet minimum <span class="math notranslate nohighlight">\(\zeta\)</span> and mosaicity-window criteria, and their predicted detector coordinates fall on the active detector area.</p> </section> <section id=systematic-absences-centering > <h3 id=systematic-absences-centering ><a class=toc-backref href="#id5" role=doc-backlink >8.4 Systematic absences (centering)</a><a class=headerlink href="#systematic-absences-centering" title="Link to this heading">¶</a></h3> <p>Systematic absences are applied at the centering level (prior to full space-group symmetry) <strong>when the space group is supplied by the user</strong>. With no user-fixed space group, prediction runs in <span class="math notranslate nohighlight">\(P\)</span> regardless of the centering the lattice search inferred: the centering-absent reflections are integrated so that the space-group search (§13) can confirm or disprove the centering from the measured intensities, and so that a missed superstructure shows up. For each centering symbol:</p> <ul class=simple > <li><p><span class="math notranslate nohighlight">\(I\)</span>: absent if <span class="math notranslate nohighlight">\(h+k+l\)</span> odd,</p> <li><p><span class="math notranslate nohighlight">\(A\)</span>: absent if <span class="math notranslate nohighlight">\(k+l\)</span> odd,</p> <li><p><span class="math notranslate nohighlight">\(B\)</span>: absent if <span class="math notranslate nohighlight">\(h+l\)</span> odd,</p> <li><p><span class="math notranslate nohighlight">\(C\)</span>: absent if <span class="math notranslate nohighlight">\(h+k\)</span> odd,</p> <li><p><span class="math notranslate nohighlight">\(F\)</span>: absent if any of <span class="math notranslate nohighlight">\(h+k, h+l, k+l\)</span> is odd,</p> <li><p><span class="math notranslate nohighlight">\(R\)</span>: absent if <span class="math notranslate nohighlight">\((-h+k+l)\bmod 3 \ne 0\)</span>,</p> <li><p><span class="math notranslate nohighlight">\(P\)</span>: no centering absences.</p> </ul> </section> </section> <hr class=docutils /> <section id=d-bragg-integration-profile-fitting-over-a-three-ring-roi > <h2 id=d-bragg-integration-profile-fitting-over-a-three-ring-roi ><a class=toc-backref href="#id6" role=doc-backlink >9. 2D Bragg integration (profile fitting over a three-ring ROI)</a><a class=headerlink href="#d-bragg-integration-profile-fitting-over-a-three-ring-roi" title="Link to this heading">¶</a></h2> <p>Jungfraujoch integrates each predicted reflection in the detector plane over a CrystFEL-inspired “three-ring” region of interest (§9.1). The <strong>default</strong> extraction is <strong>profile fitting</strong> (Kabsch; §9.3), which weights each pixel by a fitted spot profile and so recovers weak reflections far better than plain summation; plain box summation (§9.2) is retained as the seed for the profile and as a fallback. Both methods share the same ROI and background model, and emit the same per-reflection <span class="math notranslate nohighlight">\((I,\sigma,\text{partiality},d)\)</span>, so scaling, the rotation combine (§10.6) and merging consume either unchanged.</p> <section id=regions-of-interest > <h3 id=regions-of-interest ><a class=toc-backref href="#id7" role=doc-backlink >9.1 Regions of interest</a><a class=headerlink href="#regions-of-interest" title="Link to this heading">¶</a></h3> <p>For each predicted reflection at <span class="math notranslate nohighlight">\((x_p,y_p)\)</span>, define three radii:</p> <ul class=simple > <li><p><span class="math notranslate nohighlight">\(r_1\)</span>: inner signal radius,</p> <li><p><span class="math notranslate nohighlight">\(r_2\)</span>: inner background radius,</p> <li><p><span class="math notranslate nohighlight">\(r_3\)</span>: outer background radius.</p> </ul> <p>The defaults are <span class="math notranslate nohighlight">\(4,6,13\)</span> px for rotation data and <span class="math notranslate nohighlight">\(6,8,14\)</span> px for stills, which have a sparser pattern and can afford the wider ring. <code class="docutils literal notranslate"><span class=pre >--integration-radius</span></code> sets them by hand; on rotation data <span class="math notranslate nohighlight">\(r_1\)</span> is otherwise measured from the crystal’s own spots (§9.5).</p> <p>Pixels are classified by their squared distance <span class="math notranslate nohighlight">\(r^2=(x-x_p)^2+(y-y_p)^2\)</span>:</p> <ul class=simple > <li><p><strong>signal region:</strong> <span class="math notranslate nohighlight">\(r^2 < r_1^2\)</span>,</p> <li><p><strong>background annulus:</strong> <span class="math notranslate nohighlight">\(r_2^2 \le r^2 < r_3^2\)</span>.</p> </ul> <p>Invalid pixels (masked/bad/saturated) are excluded from both sums. In addition, pixels lying inside the signal disk (<span class="math notranslate nohighlight">\(r<r_2\)</span>) of any <em>other</em> predicted reflection that puts at least 5 % of its flux on this frame (partiality <span class="math notranslate nohighlight">\(\ge 0.05\)</span>) are removed from this reflection’s background annulus, so a neighbouring spot cannot leak into the background estimate. Prediction reaches <span class="math notranslate nohighlight">\(\pm4\sigma\)</span> of the rocking curve, so on a finely sliced frame most predictions are the tails of reflections recorded on the frames either side; left in, they would fill every annulus of a dense pattern while the frame shows no spot there. A reflection whose annulus keeps five or fewer clean pixels is dropped. (Both the annulus and that exclusion become ellipses when the option below is used; with it off, which is the default, they are the circles just described.)</p> <p><strong>Radially elongated background ring (opt-in, <code class="docutils literal notranslate"><span class=pre >--integration-stencil</span> <span class=pre ><k></span></code>, default 0).</strong> The three radii above are one triple for the whole run, identical for every reflection at every resolution. A reflection is not round, though: a finite bandwidth streaks it radially by <span class="math notranslate nohighlight">\(\sigma_\mathrm{bw}=\text{bandwidth}\cdot R_\mathrm{px}\)</span>, with <span class="math notranslate nohighlight">\(R_\mathrm{px}\)</span> the distance from the beam centre — the same physical smear as §8.2’s and §11.1’s <span class="math notranslate nohighlight">\(\sigma_\mathrm{bw}\)</span>, expressed here in detector pixels where those sections use reciprocal units; the two forms are never mixed in one formula. Throughout, <span class="math notranslate nohighlight">\(\text{bandwidth}\)</span> is the <strong>rms</strong> relative energy spread: the user-facing <code class="docutils literal notranslate"><span class=pre >--bandwidth</span></code> takes a FWHM (a DMM’s usual specification) and it is divided by 2.355 on input. On a radially smeared spot the fixed <span class="math notranslate nohighlight">\(6\ldots13\)</span> px ring therefore sits only <span class="math notranslate nohighlight">\(\approx1.3\)</span>–<span class="math notranslate nohighlight">\(2.2\)</span> radial <span class="math notranslate nohighlight">\(\sigma\)</span> from the centre — on the reflection’s own tails, which it then measures as background.</p> <p>With <span class="math notranslate nohighlight">\(k>0\)</span> the <strong>background ring becomes an ellipse</strong>, elongated along the beam→reflection direction by <span class="math notranslate nohighlight">\(k\sigma_\mathrm{bw}\)</span>. The <strong>radial</strong> semi-axes become <span class="math notranslate nohighlight">\(r_2+k\sigma_\mathrm{bw}\)</span> and <span class="math notranslate nohighlight">\(r_3+k\sigma_\mathrm{bw}\)</span>; the <strong>tangential</strong> half-widths stay <span class="math notranslate nohighlight">\(r_2\)</span> and <span class="math notranslate nohighlight">\(r_3\)</span>; and the growth is capped at <span class="math notranslate nohighlight">\(2r_3\)</span>, which bounds what a mis-declared bandwidth can do to the bounding box. Pixels are then classified as</p> <ul class=simple > <li><p><strong>signal region:</strong> <span class="math notranslate nohighlight">\(r^2 < r_1^2\)</span> — a circle, unchanged,</p> <li><p><strong>background ring:</strong> <span class="math notranslate nohighlight">\(r^2-q_\mathrm{in}\rho^2 \ge r_2^2\)</span> <strong>and</strong> <span class="math notranslate nohighlight">\(r^2-q_\mathrm{out}\rho^2 < r_3^2\)</span>,</p> </ul> <p>where <span class="math notranslate nohighlight">\(\rho\)</span> is the pixel’s radial offset (its projection on the beam→reflection direction), <span class="math notranslate nohighlight">\(g=\min(k\sigma_\mathrm{bw},\,2r_3)\)</span> is the capped growth, and <span class="math notranslate nohighlight">\(q=1-\big(r/(r+g)\big)^2\)</span> for the boundary concerned. Written this way <span class="math notranslate nohighlight">\(k=0\)</span> gives <span class="math notranslate nohighlight">\(q=0\)</span> and both tests collapse onto <span class="math notranslate nohighlight">\(r^2\)</span> <strong>exactly in floating point</strong>, so the default classifies every pixel exactly as the circular stencil did. The neighbour exclusion above follows: each neighbour’s <strong>inner ellipse</strong>, taken in that neighbour’s own radial frame, is what is masked out of this reflection’s ring.</p> <p>The width is the bandwidth streak alone, and deliberately <strong>not</strong> the profile’s full radial variance of §9.3, which also carries the sensor parallax and weak-spot capture terms. Those two are the only terms there are on a monochromatic beam, and widening the ring by them was measured on the rotation battery: it neither helped the crystals with clean high-resolution shells nor left the weak ones alone. The bandwidth streak, by contrast, is a measured elongation of the recorded spot — principal axis along the radius to within a couple of degrees, and azimuth-independent. Keeping only it also makes the option exactly inert on a monochromatic beam, where <span class="math notranslate nohighlight">\(\sigma_\mathrm{bw}\)</span> is zero.</p> <p>Growing the ring also grows the neighbour exclusion, so on a crowded pattern fewer background pixels survive; a reflection left with too few is rejected outright. On the data this was measured on the loss is under 0.1% of reflections, but it is not structurally zero.</p> <p>Only the ring moves. The signal disk <span class="math notranslate nohighlight">\(r_1\)</span> stays circular, deliberately: it sets <span class="math notranslate nohighlight">\(n_S\)</span>, it sets <span class="math notranslate nohighlight">\(\mathrm{var}(\hat b)\)</span>, it is the domain the profile <em>width</em> is learned over (§9.3), and with <code class="docutils literal notranslate"><span class=pre >--integrator</span> <span class=pre >boxsum</span></code> it drives the all-or-nothing “every signal pixel valid” acceptance gate (§9.2), so growing it would reject any box sum carrying a single bad pixel anywhere along a long streak. In the default <code class="docutils literal notranslate"><span class=pre >gaussian</span></code> mode <span class="math notranslate nohighlight">\(r_1\)</span> does not set the intensity at all — the fit grid, <span class="math notranslate nohighlight">\(\lceil r_2\rceil\)</span>, does.</p> <p>What a circular <span class="math notranslate nohighlight">\(r_1\)</span> loses is flux, and that loss is <strong>not</strong> a function of resolution alone: measured per reflection, it carries a directional component worth several Ų with a definite principal axis, on top of the isotropic part. Nor is there anything in the merge to absorb it. There is <strong>no per-shell scale</strong>, and there cannot usefully be one: every scale in §10 is fitted against a reference built from a reflection’s own symmetry equivalents, and equivalents share <span class="math notranslate nohighlight">\(s^2\)</span> exactly, so any function of <span class="math notranslate nohighlight">\(s^2\)</span> lies in the exact null space of the whole scaling model — a per-shell parameter would have zero residual to fit against. (XDS and DIALS have the same null space, for the same reason.) The isotropic part of the loss is instead degenerate with the overall Wilson <span class="math notranslate nohighlight">\(B\)</span> and is silently reported as part of it, so <strong>the reported <code class="docutils literal notranslate"><span class=pre >WILSON_B</span></code> / <code class="docutils literal notranslate"><span class=pre >_reflns.B_iso_Wilson_estimate</span></code> carries an <span class="math notranslate nohighlight">\(r_1\)</span>-dependent contribution</strong>: measured across a constant-ring-area radius sweep it falls monotonically as the disk grows, by 0.5 Ų on sharp strong data and by up to ~10 Ų on weak wide-spot data. What this costs the <em>data</em> is much less than what it costs the flux, because most of the loss is matched by a proportional <span class="math notranslate nohighlight">\(\sigma\)</span>: it moves no CC<span class="math notranslate nohighlight">\(_{1/2}\)</span> and no <span class="math notranslate nohighlight">\(R_\text{meas}\)</span>, and — to within a few hundredths of an ångström — no resolution cut.</p> <p><strong>Measured spot footprint (automatic).</strong> The radii above are chosen from spots at 5 Å, which at high X-ray energy sit close to the beam. Away from it a spot can grow several times wider — radially from the sensor’s parallax and the obliquity of the incidence, tangentially from the crystal’s azimuthal spread, which rotates the diffracted beam about the incident one and smears the spot along its ring. On small-molecule data at 20–25 keV the standard deviation grows from ~1 px near the beam to ~5 px at the detector edge: the <span class="math notranslate nohighlight">\(r_1 = 4\)</span> disk holds a quarter of the flux there, the <span class="math notranslate nohighlight">\(6\ldots13\)</span> px ring a third of it, and the profile widths learned inside <span class="math notranslate nohighlight">\(r_1\)</span> (§9.3) saturate near <span class="math notranslate nohighlight">\(r_1^2/4\)</span>. So the pre-scan measures every spot it finds with a window that follows the spot — three of its own standard deviations, iterated and re-centred — separately along and across the radius, and tabulates the median widths <span class="math notranslate nohighlight">\(\sigma_\rho,\sigma_\tau\)</span> against the distance from the beam. Wherever <span class="math notranslate nohighlight">\(3\max(\sigma_\rho,\sigma_\tau)>r_1\)</span> the integrator then (i) starts the background ring at <span class="math notranslate nohighlight">\(4\sigma\)</span> along each axis, (ii) sums the reflection over the <span class="math notranslate nohighlight">\(r_1\)</span> disk <strong>and</strong> the <span class="math notranslate nohighlight">\(4\sigma\)</span> footprint ellipse, so the summation — the profile fit’s seed and its fallback — holds the spot rather than its core, and (iii) builds the per-reflection Gaussian at the measured widths on a grid grown to hold them. Where every spot fits the disk nothing is installed and the integration is unchanged bit for bit, which is the case for compact protein spots; like the measured radius, the footprint applies to the canonical pass and not to the geometry pre-pass, and a canonical pass whose wider rings the neighbours starve falls back to the settings without it. The reach is <span class="math notranslate nohighlight">\(4\sigma\)</span> rather than the <span class="math notranslate nohighlight">\(3\sigma\)</span> that decides whether a spot outgrew the disk because wide spots are not Gaussian: mosaic streaks and diffuse halos carry flux past <span class="math notranslate nohighlight">\(3\sigma\)</span>, which a ring starting there reads as background. Judged by refining the published structures with SHELXL, it removes the intensity loss that grew with resolution on the small-molecule sets (rugnux/model intensity in the outermost shell 0.81–0.91 → 0.98–1.02).</p> <p><strong>Split reflections (automatic).</strong> A crystal made of slightly misaligned domains — the ferroelastic domains a crystal forms below a phase transition, or a cracked or split crystal — records each reflection as two or more compact spots on either side of the position the averaged lattice predicts, moving apart with resolution. The widths above are measured about each spot and so see compact spots; the <span class="math notranslate nohighlight">\(r_1\)</span> disk then holds the gap between them and the background ring lands on them, and the loss grows to almost everything at the detector edge. Once the geometry pre-pass has a lattice, every spot it indexes is compared with the predicted position of its own reflection on the same frame, and the mean square of that offset, along and across the radius and in the same distance bins, is added to the pre-scan widths. The table then describes the <em>reflection</em> rather than the spot, and the canonical pass integrates with it exactly as above. Where spots sit on their predictions it moves the widths by the prediction error alone (0.2–1 px on protein data, where no reflection then outgrows <span class="math notranslate nohighlight">\(r_1\)</span>); on an inorganic crystal measured below its ferroelectric transition, where every reflection off one zone is a doublet, the offsets reach 8–16 px at the edge, and SHELXL refinement of the published structure goes from <span class="math notranslate nohighlight">\(R_1 = 0.27\)</span> with a spurious extinction parameter to <span class="math notranslate nohighlight">\(R_1 = 0.05\)</span>.</p> </section> <section id=box-summation-seed-and-fallback > <h3 id=box-summation-seed-and-fallback ><a class=toc-backref href="#id8" role=doc-backlink >9.2 Box summation (seed and fallback)</a><a class=headerlink href="#box-summation-seed-and-fallback" title="Link to this heading">¶</a></h3> <p>Let:</p> <ul class=simple > <li><p><span class="math notranslate nohighlight">\(S = \sum I(x,y)\)</span> over signal pixels,</p> <li><p><span class="math notranslate nohighlight">\(n_S\)</span> = number of valid signal pixels,</p> <li><p><span class="math notranslate nohighlight">\(B = \sum I(x,y)\)</span> over background pixels,</p> <li><p><span class="math notranslate nohighlight">\(n_B\)</span> = number of valid background pixels.</p> </ul> <p>Background per pixel and integrated intensity: <span class="math notranslate nohighlight">\( \hat{b} = \frac{B}{n_B},\qquad \hat{I} = S - n_S \hat{b}, \)</span> with a Poisson-like uncertainty <span class="math notranslate nohighlight">\(\sigma(\hat{I})=\max\!\big(1,\ r_\sigma\hat{I},\ \sqrt{S + n_S^2\,\mathrm{var}(\hat{b})}\big)\)</span>, i.e. <span class="math notranslate nohighlight">\(\sqrt{S}\)</span> floored both at 1 count (pixel values are photon counts) and at a small fraction <span class="math notranslate nohighlight">\(r_\sigma\)</span> of the intensity. The second term under the root is the <strong>uncertainty of the background estimate itself</strong>: <span class="math notranslate nohighlight">\(\hat b\)</span> is measured from a finite number of ring pixels, <span class="math notranslate nohighlight">\(\mathrm{var}(\hat b)=\hat b/n_B\)</span>, and it is subtracted <span class="math notranslate nohighlight">\(n_S\)</span> times over, so it enters squared. Omitting it understates the <strong>variance</strong> by <span class="math notranslate nohighlight">\(1+n_S/n_B\)</span> — 1.11 with the shipped circular stencil (<span class="math notranslate nohighlight">\(n_S = 45\)</span>, <span class="math notranslate nohighlight">\(n_B = 408\)</span>) — and so understates <span class="math notranslate nohighlight">\(\sigma\)</span> by up to <span class="math notranslate nohighlight">\(\sqrt{1+n_S/n_B} \approx 1.05\)</span>, a bound attained on background-limited (weak) reflections and falling towards 1 on strong ones, where <span class="math notranslate nohighlight">\(S\)</span> dominates; with an elongated ring <span class="math notranslate nohighlight">\(n_B\)</span> grows with resolution, so the factor is no longer one number for a run. The same term is carried into the profile fit (§9.3), where it adds <span class="math notranslate nohighlight">\(\big(\sum P/v \,\big/ \sum P^2/v\big)^2\,\mathrm{var}(\hat b)\)</span> — the square of <span class="math notranslate nohighlight">\(\partial I/\partial\hat b\)</span> for that fit; <span class="math notranslate nohighlight">\(n_B\)</span> is the count of pixels behind the <em>final</em> background value, so a clip or trim that discards ring pixels raises it. A box sum is accepted as “observed” only if all signal pixels were valid and <span class="math notranslate nohighlight">\(n_B\)</span> exceeds a minimum — it measures what is in the disk with no model of what should be there, so it cannot renormalise a disk it has lost pixels out of. The profile modes can, and do (§9.3). This box sum is the classical estimator; it is used directly with <code class="docutils literal notranslate"><span class=pre >--integrator</span> <span class=pre >boxsum</span></code>, and otherwise seeds the profile fit below, where <span class="math notranslate nohighlight">\(S\)</span> and <span class="math notranslate nohighlight">\(n_S\)</span> then count only the pixels that were actually read.</p> <p><strong>High-side clipped background (default on).</strong> Because <span class="math notranslate nohighlight">\(\hat{I}=S-n_S\hat{b}\)</span> is a small difference of large numbers for weak reflections, a per-pixel background bias <span class="math notranslate nohighlight">\(\delta\hat{b}\)</span> becomes a <em>fractional</em> intensity bias <span class="math notranslate nohighlight">\(\approx n_S\,\delta\hat{b}/\hat{I}\)</span> that grows as <span class="math notranslate nohighlight">\(\hat{I}\)</span> shrinks — worst at the resolution edge. A plain ring mean reads high there, because neighbour-spot wings that survive the signal-disk mask, tails and zingers are one-sided (positive) contaminants. The ring mean is therefore made robust: pixels above <span class="math notranslate nohighlight">\(\hat{b}+n\sqrt{\hat{b}}\)</span> are rejected and the mean recomputed, with <span class="math notranslate nohighlight">\(n=4\)</span> (<code class="docutils literal notranslate"><span class=pre >--background-clip</span></code>; <span class="math notranslate nohighlight">\(n=0\)</span> disables), whatever the bandwidth. A clean Poisson ring is essentially unchanged by the cut (measured false-rejection rate 0.04–0.39 % at <span class="math notranslate nohighlight">\(4\sigma\)</span>), while a 40-pixel neighbour core at <span class="math notranslate nohighlight">\(+100\)</span> counts shifts the estimate by <span class="math notranslate nohighlight">\(+0.009\)</span> ct/px.</p> <p>The clip cuts only the high tail, which matters: the <strong>symmetric</strong> trimmed mean it replaced (drop the lowest and highest fraction <span class="math notranslate nohighlight">\(f\)</span> of ring pixels, <span class="math notranslate nohighlight">\(f=0.10\)</span>; still reachable with <code class="docutils literal notranslate"><span class=pre >--background-trim</span></code>, which switches the clip off) is <em>not</em> a consistent estimator of the mean of a right-skewed Poisson sample. It sits <span class="math notranslate nohighlight">\(\approx0.1\)</span> ct/px <strong>below</strong> the true mean at every level, and with <span class="math notranslate nohighlight">\(n_S = 45\)</span> signal pixels in the <span class="math notranslate nohighlight">\(r_1\)</span> disk that under-estimate adds <span class="math notranslate nohighlight">\(\approx4.5\)</span> counts to <strong>every</strong> partial — negligible at low resolution, but a large fraction of a partial in the outermost shell. The trim also collapses once contamination exceeds <span class="math notranslate nohighlight">\(\approx10\,\%\)</span> of the ring, where the clip does not. Note that removing a positive background bias <em>lowers</em> <span class="math notranslate nohighlight">\(\langle I/\sigma\rangle\)</span> and <em>raises</em> edge <span class="math notranslate nohighlight">\(R_\text{meas}\)</span>, because both are inflated by information-free counts — so neither may be read as evidence against the change.</p> <p>Both estimators are computed in the shared background pass, but only the trim reaches plain box summation: the high-side clip is skipped for <code class="docutils literal notranslate"><span class=pre >--integrator</span> <span class=pre >boxsum</span></code>, which therefore uses the plain ring mean unless <code class="docutils literal notranslate"><span class=pre >--background-trim</span></code> is given.</p> <p><strong>Radial background correction (opt-in).</strong> A ring mean estimates the background <em>under</em> the signal disk correctly only if the background is flat there. The signal disk and the ring are concentric, so for a background that is <strong>linear</strong> in position <span class="math notranslate nohighlight">\(\langle B\rangle_\mathrm{ring}=\langle B\rangle_\mathrm{disk}\)</span> identically — a plane or gradient fit buys exactly nothing. The leading error is the <strong>curvature</strong> of the radial background, which is negligible on a smooth background but reaches tens of counts on a single reflection sitting on a sharp powder ring. That error is a kernel over radial offset, <span class="math notranslate nohighlight">\( \delta \hat b \;=\; \textstyle\sum_k \kappa_k\, \bar B(r_0+k), \)</span> with <span class="math notranslate nohighlight">\(\kappa\)</span> the annulus-minus-disk histogram of the stencil over radial offset, averaged over azimuth, and <span class="math notranslate nohighlight">\(\bar B(r)\)</span> the image’s own radial background curve. With the fixed circular stencil (<span class="math notranslate nohighlight">\(k=0\)</span>, §9.1) that single kernel serves every reflection. An elongated ring does not: its radial-offset histogram depends on how far that particular reflection’s ring was grown, so <span class="math notranslate nohighlight">\(\kappa\)</span> becomes a small table of kernels, indexed by the growth rounded to whole pixels. The azimuthal average survives the change unaltered, because the stencil is rebuilt in the reflection’s own frame at each azimuth and so stays radially aligned: what is averaged over is the sub-pixel phase of the detector grid against the radius, which is what genuinely differs between reflections. Applying it costs one short dot product per reflection and no extra pixel reads; correcting the background <em>scalar</em> means the box sum, the profile fit and the variance all pick it up. The curve is accumulated from the same annulus pixels the background pass already reads (a pixel’s radius is the reflection’s radius plus the pixel’s projection on the beam→reflection direction, so no per-pixel square root is needed) and specifically from the <strong>clipped</strong> pixels, or it would carry neighbour tails and zingers — which is why the correction is inert under <code class="docutils literal notranslate"><span class=pre >--integrator</span> <span class=pre >boxsum</span></code>, that path having no clip pass.</p> <p>The model is a function of <strong>radius alone</strong>, so it is applied only where that is true of the background. <code class="docutils literal notranslate"><span class=pre >--background-radial</span></code> takes <code class="docutils literal notranslate"><span class=pre >on</span></code>, <code class="docutils literal notranslate"><span class=pre >off</span></code> or <code class="docutils literal notranslate"><span class=pre >auto</span></code>. In Rugnux it is <strong><code class="docutils literal notranslate"><span class=pre >auto</span></code> by default</strong> (the broker keeps it off); under <code class="docutils literal notranslate"><span class=pre >auto</span></code> each image’s peak-excluded ice score (§3.3) is taken after spot detection and before integration, and the correction is applied to that image when the score reaches the same <code class="docutils literal notranslate"><span class=pre >--ice-min-score</span></code> gate. Smooth powder ice <em>is</em> a radial feature and is corrected; ice made of discrete crystallite spots — which the profile channel is blind to and the spot channel catches — leaves no smooth ring to model, and correcting it makes matters worse. Measured against a fixed atomic model, comparing ice bands with resolution-matched decoy bands carrying no ice: on a crystal with pure smooth ice the correction removes <strong>43 % of the bands’ excess amplitude</strong>, and the improvement is <strong>7× larger inside the bands than outside</strong>, which is its stated mechanism; on a crystal whose ice is textured the same correction <em>increased</em> the excess amplitude by half; on a clean crystal it is inert to four decimal places. Auto engages only where a peak-excluded score exists (adaptive spot finding, §3.2) — a plain azimuthal profile carries the Bragg peaks and cannot support an absolute threshold, so without one auto leaves the correction off.</p> </section> <section id=profile-fitted-extraction-default > <h3 id=profile-fitted-extraction-default ><a class=toc-backref href="#id9" role=doc-backlink >9.3 Profile-fitted extraction (default)</a><a class=headerlink href="#profile-fitted-extraction-default" title="Link to this heading">¶</a></h3> <p>A fixed signal disk captures a <em>width-dependent</em> fraction of each spot, which puts a multiplicative floor on the per-observation precision of strong reflections and weights weak reflections poorly. Profile fitting removes this by extracting each intensity against a fitted spot shape, without needing reference intensities. Per frame:</p> <ol class=arabic > <li><p><strong>Seed.</strong> Box-sum every reflection (§9.2) to get a rough intensity and observed centroid, and select strong spots (significance <span class="math notranslate nohighlight">\(\ge 5\)</span>).</p> <li><p><strong>Build the profile.</strong> For <code class="docutils literal notranslate"><span class=pre >gaussian</span></code> (the default) the width is taken <strong>per resolution shell</strong> from the measured second moments of the strong spots (shell-dependent because spot size grows with resolution). The moments are <strong>anisotropic</strong>: each strong spot’s pixels are rotated into its <em>own</em> radial/tangential frame before being accumulated, giving <span class="math notranslate nohighlight">\(\sigma^2_r\)</span> and <span class="math notranslate nohighlight">\(\sigma^2_t\)</span> separately. Stacking the spots in the detector frame instead — they sit at every azimuth — averages the two directions away, leaving only <span class="math notranslate nohighlight">\(\sigma_r^2+\sigma_t^2\)</span>, so radial smearing is read back as a wider <em>tangential</em> spot. For <code class="docutils literal notranslate"><span class=pre >empirical</span></code> the profile is instead the averaged, background-subtracted pixel grid of the shell’s strong spots, accumulated in the detector frame on their <strong>rounded predicted</strong> positions. For <code class="docutils literal notranslate"><span class=pre >gaussian</span></code> only, the profile is then <strong>rebuilt for each reflection</strong>, centred on its <strong>sub-pixel predicted position</strong> (the noise-free geometric centre, not the observed centroid) and, where needed, <strong>elongated only along the radial direction</strong> (away from the beam centre) — because two effects stretch a spot radially but not tangentially:</p> <ul class=simple > <li><p>a finite energy <strong>bandwidth</strong> smears each spot by <span class="math notranslate nohighlight">\(\sigma_\mathrm{bw}=\text{bandwidth}\cdot R_\mathrm{px}\)</span> (<span class="math notranslate nohighlight">\(R_\mathrm{px}\)</span> = distance from the beam centre, large at high resolution), and</p> <li><p>sensor <strong>parallax</strong> — the depth over which a photon converts in a thick Si/CdTe sensor — adds a term <span class="math notranslate nohighlight">\(\propto\tan^2(2\theta)\)</span> (material- and energy-dependent), plus a small fixed weak-spot capture term.</p> </ul> <p>The two enter as a floor on the measured radial excess: <span class="math notranslate nohighlight">\(\sigma^2_\mathrm{radial}=\sigma^2_t+\max\!\left(\sigma^2_r-\sigma^2_t,\ \sigma_\mathrm{bw}^2+c_\mathrm{par}\tan^2(2\theta)\right)\)</span>, tangential unchanged at <span class="math notranslate nohighlight">\(\sigma^2_t\)</span>. The measured excess is what the signal disk can resolve; the analytic term takes over for a streak too long to be measured there. The Gaussian is built on a grid grown to hold the streak — capturing it without the tangential background an isotropic widening would add. The <code class="docutils literal notranslate"><span class=pre >empirical</span></code> profile keeps the fixed per-shell grid and gets none of this.</p> <li><p><strong>Fit (Kabsch).</strong> With profile <span class="math notranslate nohighlight">\(P\)</span>, background <span class="math notranslate nohighlight">\(B\)</span> and the shell variance model, the intensity and its uncertainty are <span class="math notranslate nohighlight">\( I = \frac{\sum P\,(c-B)/v}{\sum P^2/v},\qquad \sigma = \sqrt{\frac{1}{\sum P^2/v}},\qquad v = \max\!\left(B + I\,P,\ \tfrac{1}{2}B\right), \)</span> where <span class="math notranslate nohighlight">\(c\)</span> is the pixel value and the de-biased variance <span class="math notranslate nohighlight">\(v\)</span> (background plus model signal, rather than the down-fluctuating observed count) is iterated (a few passes). The plug-in <span class="math notranslate nohighlight">\(I\)</span> enters <strong>as it is</strong>: half-wave rectifying it, <span class="math notranslate nohighlight">\(v=B+\max(I,0)P\)</span>, lets <span class="math notranslate nohighlight">\(v\)</span> — and with it the reported <span class="math notranslate nohighlight">\(1/\sum P^2/v\)</span> — respond only to <em>upward</em> fluctuations of a noisy estimate, which adds <span class="math notranslate nohighlight">\(\approx0.4\,\sigma\sum P^3/(\sum P^2)^2\)</span> to every <span class="math notranslate nohighlight">\(\sigma\)</span> whatever the count rate. That offset is invisible on strong reflections and a large fractional inflation on weak ones; the <span class="math notranslate nohighlight">\(\tfrac12 B\)</span> clamp keeps <span class="math notranslate nohighlight">\(v\)</span> positive without reintroducing it. As a guard, if the profile intensity runs away from the box-sum seed (by more than ~10 box-sum <span class="math notranslate nohighlight">\(\sigma\)</span>) it falls back to the seed, and the background term is floored at <span class="math notranslate nohighlight">\(0.01\)</span> ct/px — enough to keep <span class="math notranslate nohighlight">\(P^2/v\)</span> finite when the ring mean reads exactly zero, which a ring of <span class="math notranslate nohighlight">\(n_B\)</span> pixels cannot distinguish from any background below <span class="math notranslate nohighlight">\(\approx1/n_B\)</span>. The rotation/excitation partiality is carried exactly as in the box-sum path.</p> </ol> <p><strong>Pixels the fit cannot use (MINPK).</strong> A profile fit is the amplitude of a <em>normalised</em> profile, so a pixel left out of the sum renormalises the estimator by construction: it costs information — <span class="math notranslate nohighlight">\(\sum P^2/v\)</span> shrinks and <span class="math notranslate nohighlight">\(\sigma\)</span> grows — but biases nothing. That is what keeps a reflection whose signal disk is cut by a mask, an untrusted region, a detector gap or an overload: those pixels are simply not read, and the fit is taken over the rest, exactly as the shared pixels of a crowded reflection are (<code class="docutils literal notranslate"><span class=pre >--overlap</span> <span class=pre >exclude</span></code>). The reflection is kept only while enough of the expected profile survives — at least <code class="docutils literal notranslate"><span class=pre >--overlap-minpk</span></code> of the profile mass that falls on the detector at all, default 0.75, which is XDS’s <code class="docutils literal notranslate"><span class=pre >MINPK</span></code> and dials’ <code class="docutils literal notranslate"><span class=pre >valid_foreground_threshold</span></code>. The complete reflections alone teach the profile, its resolution shells and their widths. <code class="docutils literal notranslate"><span class=pre >--integrator</span> <span class=pre >boxsum</span></code> has no profile to renormalise with and keeps the all-or-nothing rule of §9.2.</p> <p>“Biases nothing” holds only while the profile <em>model</em> is exact. Lose the peak and the amplitude is set by the wings alone, so the result stops being a measurement of the reflection and becomes a measurement of how well the fitted shape describes it. The worst case is a pixel invalidated <em>by the flux it saw</em> — a detector’s per-frame overload marker: that pixel goes missing <strong>because</strong> the reflection was bright, so the loss concentrates on the strong low-resolution reflections that are the largest terms of <span class="math notranslate nohighlight">\(R_\mathrm{meas}\)</span>, where the fit reads <span class="math notranslate nohighlight">\(-50\%\)</span> against the symmetry mates. MINPK cannot catch it, because it cuts on profile <em>mass</em> and the peak of a broad spot is a few percent of the mass. So a second condition applies alongside it, on any unreadable pixel whatever made it unreadable: <strong>no unreadable pixel may carry more than 0.9 of the profile’s own peak value</strong>. As a fraction of the peak rather than a radius in pixels, that scales with the spot — for a Gaussian it is a cut at <span class="math notranslate nohighlight">\(\sqrt{-2\ln f}\,\sigma = 0.46\sigma\)</span>, the peak pixel alone where <span class="math notranslate nohighlight">\(\sigma\)</span> is 0.8 px and the crest of the ridge where the profile is a bandwidth streak — and it costs well under 0.1 % of the recovered observations.</p> <p>The integrator is selected by <code class="docutils literal notranslate"><span class=pre >--integrator</span> <span class=pre >boxsum|gaussian|empirical</span></code> (default <code class="docutils literal notranslate"><span class=pre >gaussian</span></code>).</p> </section> <section id=the-prescaling-correction > <h3 id=the-prescaling-correction ><a class=toc-backref href="#id10" role=doc-backlink >9.4 The prescaling correction</a><a class=headerlink href="#the-prescaling-correction" title="Link to this heading">¶</a></h3> <p>The deterministic per-reflection corrections are carried as three multiplicative factors. <code class="docutils literal notranslate"><span class=pre >prescaling_corr</span></code> holds the reciprocal Lorentz factor (rotation only — a still’s Lorentz factor is one) times the reciprocal polarization factor from the geometry-based term (§2.2), and nothing else: it is Lorentz x polarization, which is what <code class="docutils literal notranslate"><span class=pre >LP</span></code> means in every format the field reads. Beside it sit the two terms that describe what happened to the photon between leaving the sample and being counted, and that are kept apart from <code class="docutils literal notranslate"><span class=pre >LP</span></code> because detector response and beam/crystal geometry are different things: <code class="docutils literal notranslate"><span class=pre >qe_corr</span></code>, the sensor’s angle-dependent efficiency, and <code class="docutils literal notranslate"><span class=pre >flight_corr</span></code>, the medium in the flight path (§9.7). The total deterministic correction on a reflection is the product <code class="docutils literal notranslate"><span class=pre >prescaling_corr</span> <span class=pre >*</span> <span class=pre >qe_corr</span> <span class=pre >*</span> <span class=pre >flight_corr</span></code>, and every site that corrects an intensity — the integrator, the scaling fits, the merge ingest, the anisotropy analysis and the unmerged export — multiplies all three. None of them is a scale: the fitted per-image scale and the partiality are separate. The three reach the unmerged MTZ as its <code class="docutils literal notranslate"><span class=pre >LP</span></code>, <code class="docutils literal notranslate"><span class=pre >QE</span></code> and <code class="docutils literal notranslate"><span class=pre >FLIGHT</span></code> columns unchanged (<code class="docutils literal notranslate"><span class=pre >QE</span></code> and <code class="docutils literal notranslate"><span class=pre >FLIGHT</span></code> as divisors normalised to 1 at normal incidence), so raw counts are <code class="docutils literal notranslate"><span class=pre >I</span> <span class=pre >/</span> <span class=pre >LP</span> <span class=pre >*</span> <span class=pre >QE</span> <span class=pre >*</span> <span class=pre >FLIGHT</span></code>.</p> <p>One term is deliberately <strong>not</strong> in it. The <strong>per-pixel solid angle</strong>, which the azimuthal profile does divide out (§2.2), is correctly absent here: a Bragg integration sums all the photons in a reflection, and how many pixels the detector happens to tile that footprint with does not change the count. Detector obliquity does stretch the footprint, and that enters as the parallax term of the spot-width variance rather than as an intensity scale.</p> </section> <section id=choosing-the-signal-radius-from-the-crystals-own-spots-rotation > <h3 id=choosing-the-signal-radius-from-the-crystals-own-spots-rotation ><a class=toc-backref href="#id11" role=doc-backlink >9.5 Choosing the signal radius from the crystal’s own spots (rotation)</a><a class=headerlink href="#choosing-the-signal-radius-from-the-crystals-own-spots-rotation" title="Link to this heading">¶</a></h3> <p>The three radii are one triple for the whole run, but on rotation data they are no longer a fixed constant: <span class="math notranslate nohighlight">\(r_1\)</span> is measured from how wide <em>this</em> crystal’s spots actually are (<code class="docutils literal notranslate"><span class=pre >--adaptive-integration-radius</span></code>, on by default for rotation, off for stills, ignored when <code class="docutils literal notranslate"><span class=pre >--integration-radius</span></code> is given).</p> <p><strong>Why <span class="math notranslate nohighlight">\(r_1\)</span> matters even though it does not set the intensity.</strong> In the default <code class="docutils literal notranslate"><span class=pre >gaussian</span></code> mode the intensity is a profile-fit amplitude over the grid <span class="math notranslate nohighlight">\(\lceil r_2\rceil\)</span> (§9.3), so <span class="math notranslate nohighlight">\(r_1\)</span> is not the integration domain. It <em>is</em> the aperture the profile <strong>width</strong> is learned over, and a second moment taken over a disk of radius <span class="math notranslate nohighlight">\(a\)</span> saturates at <span class="math notranslate nohighlight">\(a^2/4\)</span>. At <span class="math notranslate nohighlight">\(r_1 = 4\)</span> the learned <span class="math notranslate nohighlight">\(\sigma\)</span> can therefore never exceed 2 px, and a crystal whose spots are broader than that is fitted with a profile the model cannot represent.</p> <p><strong>The measurement is independent of the integrator.</strong> It is made in the pre-scan, on the frames the beam-stop projection already reads, so it costs no extra frame reads and there is no feedback loop. On the spots the spot finder has already found, a spot is used only if it is clear of the detector edge and of the direct beam, has no neighbouring spot within 28 px, is one of the 40 strongest in its resolution band, sits on a fully readable disk, and reaches a signal-to-noise of 15 with its centroid within 2 px of the found position. For each surviving spot the background-subtracted <strong>encircled-flux curve</strong> is accumulated in 1-px annuli out to a fixed 14 px aperture and normalised at 8 px — an aperture that owes nothing to <span class="math notranslate nohighlight">\(r_1\)</span>, <span class="math notranslate nohighlight">\(r_2\)</span> or <span class="math notranslate nohighlight">\(r_3\)</span>.</p> <p><strong>Pooling.</strong> Spots are stratified into five resolution bands (2–3, 3–4.5, 4.5–7, 7–12, 12–30 Å), because a weak crystal’s strongest spots sit at high angle and a strong one’s at low angle. Each band with enough members contributes the radius at which its <strong>median</strong> curve reaches 0.80 of its normalised flux — <span class="math notranslate nohighlight">\(r_{80}\)</span> — at the band’s median <span class="math notranslate nohighlight">\(d\)</span>. Those points are fitted by weighted least squares against <span class="math notranslate nohighlight">\(1/d\)</span> (the mosaic contribution to the detector footprint grows as <span class="math notranslate nohighlight">\(1/d\)</span>) and evaluated at a common 5 Å, then clamped to the range the bands actually measured so the fit never extrapolates.</p> <p><strong>The radius.</strong></p> <div class="math notranslate nohighlight"> \[r_1 = \mathrm{clamp}\!\left(\mathrm{round}(2\,r_{80}),\ 4,\ 6\right),\qquad r_2 = r_1 + 2,\qquad r_3 = \sqrt{r_2^2 + 133}\]</div> <p>The factor 2 is not fitted: for a Gaussian <span class="math notranslate nohighlight">\(r_{80} = 1.794\,\sigma\)</span>, so <span class="math notranslate nohighlight">\(r_1 = 2r_{80} = 3.59\,\sigma\)</span>, where the truncated second moment recovers 0.990 of <span class="math notranslate nohighlight">\(\sigma^2\)</span>. The expression for <span class="math notranslate nohighlight">\(r_3\)</span> holds the <strong>background-ring area constant</strong> at its value for the shipped <span class="math notranslate nohighlight">\(4,6,13\)</span> (<span class="math notranslate nohighlight">\(13^2 - 6^2 = 133\)</span>) — a ring that shrank with the disk is what makes a bare <code class="docutils literal notranslate"><span class=pre >--integration-radius</span></code> worse than the default it replaces. The floor of 4 is that shipped default; the ceiling of 6 is pattern density, since <span class="math notranslate nohighlight">\(r_2\)</span> also drives the neighbour-ownership radius and the ring’s inner edge. At <span class="math notranslate nohighlight">\(r_1 = 4\)</span> the triple is bit-for-bit the shipped default, so a crystal with ordinary spots is left exactly where it was.</p> <p><strong>The sample grows until the answer settles.</strong> The frames are measured in tiers of stride 8, 4, 2, 1, each tier’s sample strictly containing the previous one, and the pooling is redone after each. Measuring stops when the new <span class="math notranslate nohighlight">\(r_{80}\)</span> is within 0.40 px of what the smaller sample said <strong>and</strong> is at least 0.25 px clear of both radii at which the rounding in <span class="math notranslate nohighlight">\(r_1\)</span> changes answer. Both conditions are load-bearing: clearance alone lets a small sample settle across a switch, and the step test alone lets it settle <em>on</em> one. Every frame of the sample is still read — the beam-stop mask and the beam centre are unchanged; what the tiers save is the decompression, preprocessing and spot finding the width measurement adds on top of the read.</p> <p><strong>It applies to the final pass only.</strong> A rotation run integrates twice (§7.5), and the widened radius is handed to the canonical second pass, not to the geometry pre-pass. The reason is that post-refinement takes its observed positions from the integrator, and an observed position is a first moment over the signal disk with the background still in it: a flat background adds nothing to the numerator but adds <span class="math notranslate nohighlight">\(n\,b\)</span> to the denominator, so every measured offset is pulled toward its prediction by <span class="math notranslate nohighlight">\(I/(I + n b)\)</span>, and <span class="math notranslate nohighlight">\(n\)</span> nearly doubles between <span class="math notranslate nohighlight">\(r_1 = 4\)</span> and <span class="math notranslate nohighlight">\(r_1 = 6\)</span>. A wider disk therefore <em>under</em>-corrects the geometry — enough, on a crystal whose metric is half a degree off orthorhombic, to flip the second pass’s de-novo Bravais choice.</p> <p><strong>The density guard.</strong> Widening <span class="math notranslate nohighlight">\(r_1\)</span> pushes <span class="math notranslate nohighlight">\(r_2\)</span>, the ring’s inner edge, into the neighbours; a reflection whose ring is left with five or fewer clean pixels has no background and is dropped whole. The integrator counts the rings the <strong>neighbouring reflections</strong> would starve — every predicted neighbour, the rocking-curve tails the background itself does not exclude included, so the count measures the pattern’s density — apart from the ones the detector itself starves (module gaps, the beam stop, the resolution mask), a floor that reaches a couple of percent on some geometries and does not move with <span class="math notranslate nohighlight">\(r_1\)</span>. Where the neighbour-driven count exceeds 1.13 % of the predicted reflections, the pattern is too dense for the widened radius and the final pass is integrated again at the fixed <span class="math notranslate nohighlight">\(4,6,13\)</span>, reported as pass 3 of 3 with the reason in <code class="docutils literal notranslate"><span class=pre >PASS_DECISION</span></code>.</p> <p>Every integration pass, adaptive or not, now logs the radii it used together with the fraction of predicted reflections that lost their background ring, the fraction of rings the neighbouring predictions crowd, and the profile-fit fallback rate.</p> </section> <hr class=docutils /> <section id=measuring-the-bandwidth > <h3 id=measuring-the-bandwidth ><a class=toc-backref href="#id12" role=doc-backlink >9.6 Measuring the bandwidth</a><a class=headerlink href="#measuring-the-bandwidth" title="Link to this heading">¶</a></h3> <p>A finite energy spread <span class="math notranslate nohighlight">\(\sigma\)</span> (<span class="math notranslate nohighlight">\(\Delta\lambda/\lambda\)</span>, rms) smears a reflection along its own radius by <span class="math notranslate nohighlight">\(2\tan\theta\,\sigma\)</span> radians of <span class="math notranslate nohighlight">\(2\theta\)</span> and not at all across it. Most files do not state it — a multilayer monochromator is a beamline option, not a header field — so Rugnux reads it off the spots, on the same isolated strong spots the width of §9.5 is measured on (<code class="docutils literal notranslate"><span class=pre >spot_width::EstimateBandwidth</span></code>). The width settles on fewer spots than this slope needs, so the pre-scan keeps measuring spot shapes for the bandwidth alone until it holds 1000 of them or its sample runs out. For each spot, with <span class="math notranslate nohighlight">\(u\)</span> along the radius and <span class="math notranslate nohighlight">\(v\)</span> across it, the second moments about its centroid are</p> <div class="math notranslate nohighlight"> \[m_u = \tfrac1{12} + p_u + j_r^2\big(s^2 + 4\tan^2\theta\,\sigma^2\big),\qquad m_v = \tfrac1{12} + p_v + j_t^2 s^2,\]</div> <p>with <span class="math notranslate nohighlight">\(j_r\)</span>, <span class="math notranslate nohighlight">\(j_t\)</span> the exact pixels per radian of <span class="math notranslate nohighlight">\(2\theta\)</span> and of the angle across the scattering plane at that spot, <span class="math notranslate nohighlight">\(s\)</span> everything isotropic in angle (divergence, crystal size, mosaic spread), and <span class="math notranslate nohighlight">\(p_u\)</span>, <span class="math notranslate nohighlight">\(p_v\)</span> the sensor parallax, fixed from the sensor’s physics: a photon converting at depth <span class="math notranslate nohighlight">\(z\)</span> (exponential with length <span class="math notranslate nohighlight">\(L\cos\psi\)</span> at angle <span class="math notranslate nohighlight">\(\psi\)</span> to the normal, truncated at the thickness) lands <span class="math notranslate nohighlight">\(z\tan\psi\)</span> along the ray’s in-plane direction. Then</p> <div class="math notranslate nohighlight"> \[y = (m_u - \tfrac1{12} - p_u) - (j_r/j_t)^2\,(m_v - \tfrac1{12} - p_v) = a + \sigma^2\,(2 j_r\tan\theta)^2\]</div> <p>is a straight line whose slope is the bandwidth. It is fitted over eight equal-count bins of the abscissa, each a 20 %-trimmed mean weighted by its own scatter; the slope’s error is the spread of 200 bootstrap re-draws of the spots, inflated by the reduced <span class="math notranslate nohighlight">\(\chi^2\)</span> of the binned fit where the line fits worse than the scatter says, and the log calls the estimate significant at <span class="math notranslate nohighlight">\(z>3\)</span>. It is a <strong>lower bound</strong> — mosaic spread seen along the radius subtracts — and a spread of cell edges is exactly degenerate with it, so what it measures is the effective radial broadening — which is what its consumers need.</p> <p>A significant estimate is the run’s bandwidth, unless the file states one or <code class="docutils literal notranslate"><span class=pre >--bandwidth</span></code> is given (<code class="docutils literal notranslate"><span class=pre >--bandwidth</span> <span class=pre >0</span></code> forces a monochromatic beam); anything short of <span class="math notranslate nohighlight">\(z>3\)</span> leaves the beam monochromatic. From there it acts continuously, with no broadband mode: prediction and partiality (§8), the profile’s radial width (§9.3) and the background ring’s elongation (<code class="docutils literal notranslate"><span class=pre >--integration-stencil</span></code>, §9.1) all scale with it and are exactly what they were at zero bandwidth.</p> </section> <section id=the-flight-path > <h3 id=the-flight-path ><a class=toc-backref href="#id13" role=doc-backlink >9.7 The flight path</a><a class=headerlink href="#the-flight-path" title="Link to this heading">¶</a></h3> <p>A reflection leaving the sample at incidence angle <span class="math notranslate nohighlight">\(\alpha\)</span> to the detector normal reaches its pixel after <span class="math notranslate nohighlight">\(D/\cos\alpha\)</span> of flight rather than <span class="math notranslate nohighlight">\(D\)</span>, so it crosses more of whatever fills the flight path than one arriving head-on and arrives attenuated. This is the <strong>same <span class="math notranslate nohighlight">\(\cos\alpha\)</span> geometry as the sensor crossing of §9.4, with the opposite sign</strong>: the sensor makes an oblique reflection read high, the medium makes it read low.</p> <div class="math notranslate nohighlight"> \[T(\alpha) = \exp\!\left(-\frac{D}{L\cos\alpha}\right), \qquad \text{correction to } I = \frac{T(0)}{T(\alpha)} = \exp\!\left[\frac{D}{L}\left(\frac{1}{\cos\alpha} - 1\right)\right]\]</div> <p>with <span class="math notranslate nohighlight">\(L = 1/\mu\)</span> the attenuation length of the medium at the photon energy, from the same NIST tabulation the sensor uses. Nothing here is fitted: <span class="math notranslate nohighlight">\(\mu\)</span> is tabulated, and <span class="math notranslate nohighlight">\(D\)</span> and <span class="math notranslate nohighlight">\(\lambda\)</span> are stated by the file. Normalising at <span class="math notranslate nohighlight">\(\alpha = 0\)</span> divides out <span class="math notranslate nohighlight">\(\exp(-D/L)\)</span>, a constant for the dataset that the fitted per-image scale absorbs; what is left is the only part of the flight path that is not degenerate with that scale.</p> <p>For <strong>air</strong>, <span class="math notranslate nohighlight">\(L\)</span> falls steeply toward low energy — <strong>8.2 m at 18 keV, 3.0 m at 12.4 keV, 0.089 m at 3.8 keV</strong> — and the size of the correction follows it:</p> <table> <thead> <tr class=row-odd ><th class=head ><p>photon energy</p> <th class=head ><p><span class="math notranslate nohighlight">\(L\)</span> (air)</p> <th class=head ><p><span class="math notranslate nohighlight">\(D/L\)</span> at 160 mm</p> <th class=head ><p>correction at <span class="math notranslate nohighlight">\(\alpha = 30°\)</span></p> <th class=head ><p>at <span class="math notranslate nohighlight">\(\alpha = 55°\)</span></p> <tr class=row-even ><td><p>18 keV</p> <td><p>8.17 m</p> <td><p>0.0196</p> <td><p>+0.30 %</p> <td><p>+1.5 %</p> <tr class=row-odd ><td><p>12.4 keV</p> <td><p>2.99 m</p> <td><p>0.0535</p> <td><p>+0.83 %</p> <td><p>+4.1 %</p> <tr class=row-even ><td><p>8 keV</p> <td><p>0.84 m</p> <td><p>0.191</p> <td><p>+3.0 %</p> <td><p>+15 %</p> <tr class=row-odd ><td><p>3.8 keV</p> <td><p>0.089 m</p> <td><p>1.80</p> <td><p>×1.32</p> <td><p>×3.8</p> </table> <p><strong>Helium</strong> attenuates about 1/600 of air at 3.8 keV — two electrons an atom against nitrogen and oxygen, and a seventh of the density — which is exactly why long-wavelength stations use it. It is not vacuum, and is modelled rather than treated as one, though at these distances it is worth well under a per cent. <strong>Vacuum</strong> leaves every intensity untouched.</p> <section id=what-it-does-to-merged-data > <h4 id=what-it-does-to-merged-data >What it does to merged data<a class=headerlink href="#what-it-does-to-merged-data" title="Link to this heading">¶</a></h4> <p>On an <strong>untilted</strong> detector <span class="math notranslate nohighlight">\(\alpha\)</span> is the scattering angle, so the correction is a pure function of resolution. It therefore cancels within a resolution shell and cannot move <span class="math notranslate nohighlight">\(R_\mathrm{meas}\)</span> or CC<span class="math notranslate nohighlight">\(_{1/2}\)</span> there; <strong>its whole effect on merged data is a shift in the Wilson <span class="math notranslate nohighlight">\(B\)</span></strong>. Friedel mates share <span class="math notranslate nohighlight">\(2\theta\)</span> and so receive an identical factor, which also means it cannot act on an anomalous difference at all. Both statements are quantitative predictions with no free parameter, and both are confirmed: the Wilson-<span class="math notranslate nohighlight">\(B\)</span> shift is reproduced to within 8 % on three datasets spanning an order of magnitude in <span class="math notranslate nohighlight">\(D/L\)</span>, and where a pooled statistic does move, every resolution shell is unchanged and the pooled shift is reproduced by re-weighting alone.</p> <p>That is what <code class="docutils literal notranslate"><span class=pre >FLIGHT_PATH_WILSON_B</span></code> in the results report quotes: the correction’s worth, in the one number it can move.</p> </section> <section id=why-the-medium-is-declared-and-not-detected > <h4 id=why-the-medium-is-declared-and-not-detected >Why the medium is declared and not detected<a class=headerlink href="#why-the-medium-is-declared-and-not-detected" title="Link to this heading">¶</a></h4> <p><strong>No field of the NXmx application definition, and no field of any master file <code class="docutils literal notranslate"><span class=pre >rugnux</span></code> reads, describes the medium in the flight path</strong> — there is no air, helium, vacuum, flight-tube or pressure entry anywhere to detect it from.</p> <p>Inferring it from the physics was considered and <strong>refuted</strong>. The natural idea is that air becomes unusable at low energy, so an implausibly low implied air transmission would mean helium; but in this corpus a <strong>confirmed helium</strong> station sits at 51 % implied transmission and a <strong>confirmed air</strong> station at 63 %. No criterion separates those two without being a threshold fitted between two points, so there is none.</p> <p><code class="docutils literal notranslate"><span class=pre >rugnux</span></code> therefore <strong>assumes air</strong> — which is what a beamline has unless it was built not to have one — and <code class="docutils literal notranslate"><span class=pre >--flight-path</span> <span class=pre >helium|vacuum</span></code> declares otherwise. Because that is an assumption made on the user’s behalf, the report states it (<code class="docutils literal notranslate"><span class=pre >FLIGHT_PATH</span></code>) together with what it was worth (<code class="docutils literal notranslate"><span class=pre >FLIGHT_PATH_WILSON_B</span></code>), and warns where it is worth enough to matter.</p> </section> </section> </section> <hr class=docutils /> <section id=scaling-and-merging > <h2 id=scaling-and-merging ><a class=toc-backref href="#id14" role=doc-backlink >10. Scaling and merging</a><a class=headerlink href="#scaling-and-merging" title="Link to this heading">¶</a></h2> <p>After per-image integration, Jungfraujoch scales observations and merges them into unique reflections. The design is intentionally compatible with XDS/XSCALE concepts, and handles both still and rotation data.</p> <section id=observation-model > <h3 id=observation-model ><a class=toc-backref href="#id15" role=doc-backlink >10.1 Observation model</a><a class=headerlink href="#observation-model" title="Link to this heading">¶</a></h3> <p>For an observation <span class="math notranslate nohighlight">\(j\)</span> of a unique reflection <span class="math notranslate nohighlight">\(h\)</span> on image (or image group) <span class="math notranslate nohighlight">\(i\)</span>, the predicted measured intensity is modeled as: <span class="math notranslate nohighlight">\( I_{ij} \approx G_i \, L_{ij}\, P_{ij}\, I_h, \)</span> where:</p> <ul class=simple > <li><p><span class="math notranslate nohighlight">\(G_i\)</span> is the image scale factor,</p> <li><p><span class="math notranslate nohighlight">\(L_{ij}\)</span> is the whole deterministic per-reflection correction of §9.4 - Lorentz x polarization, the sensor’s efficiency and the flight path together, not the <code class="docutils literal notranslate"><span class=pre >LP</span></code> term alone. Predictions carry its <strong>reciprocal</strong> split across three fields, so <span class="math notranslate nohighlight">\(L = 1/(\texttt{prescaling\_corr} \cdot \texttt{qe\_corr} \cdot \texttt{flight\_corr})\)</span> and the correction below is applied as a multiplication by their product; the scaling fits, the merge ingest and the anisotropy analysis all use the same three,</p> <li><p><span class="math notranslate nohighlight">\(P_{ij}\)</span> is a partiality term (model-dependent),</p> <li><p><span class="math notranslate nohighlight">\(I_h\)</span> is the merged (true) intensity parameter for that unique reflection.</p> </ul> <p>A least-squares objective is minimized: <span class="math notranslate nohighlight">\( \sum_{ij} \left(\frac{I_{ij}^{\mathrm{pred}} - I_{ij}^{\mathrm{obs}}}{\sigma_{ij}}\right)^2 \)</span> solved by robust (Cauchy) weighted least squares, with optional post-fit smoothing of the per-frame scales for rotation series (§10.3).</p> </section> <section id=partiality-models > <h3 id=partiality-models ><a class=toc-backref href="#id16" role=doc-backlink >10.2 Partiality models</a><a class=headerlink href="#partiality-models" title="Link to this heading">¶</a></h3> <p>The partiality applied is fixed by the data type and scaling stage, not chosen from a user menu:</p> <ol class="arabic simple"> <li><p><strong>Rotation partiality</strong> (XDS-like; see §8.3), used for the per-frame scaling of rotation partials: <span class="math notranslate nohighlight">\( P_{ij} = \frac{1}{2}\left[ \mathrm{erf}\!\left(\frac{\Delta\phi_{ij}+\Delta\phi/2}{\sqrt{2}\,\sigma_{M,i}/\zeta_{ij}}\right) - \mathrm{erf}\!\left(\frac{\Delta\phi_{ij}-\Delta\phi/2}{\sqrt{2}\,\sigma_{M,i}/\zeta_{ij}}\right) \right]. \)</span> Here <span class="math notranslate nohighlight">\(\Delta\phi_{ij}\)</span> is observation <span class="math notranslate nohighlight">\(j\)</span>’s rocking offset from its exact Bragg angle on image <span class="math notranslate nohighlight">\(i\)</span>, and the unsubscripted <span class="math notranslate nohighlight">\(\Delta\phi\)</span> is the oscillation width per frame — two different quantities that share a letter. The mosaicity <span class="math notranslate nohighlight">\(\sigma_{M,i}\)</span> is <strong>measured once per image at indexing</strong> (MLE, §11.2) and held fixed during scaling — only smoothed in frame order (§10.3), never re-refined (it is degenerate with the scale <span class="math notranslate nohighlight">\(G\)</span>; §11.2).</p> <li><p><strong>Unity</strong> (<span class="math notranslate nohighlight">\(P_{ij}=1\)</span>): used for the scale-on-fulls refit (§10.6), where each observation is already a complete reflection.</p> <li><p><strong>Fixed</strong>: use the per-reflection partiality carried from prediction. Still/serial images are predicted with <span class="math notranslate nohighlight">\(P=1\)</span>, so a single-pass stills scale is effectively unity/fixed — which is exactly what <code class="docutils literal notranslate"><span class=pre >--simple-stills</span></code> keeps. By default the stills path instead <strong>post-refines a physical partiality</strong>: a small per-crystal orientation tilt <span class="math notranslate nohighlight">\((\delta\psi_x,\delta\psi_y)\)</span> about the two axes perpendicular to the beam is refined against the running merge, and every reflection’s partiality is then recomputed analytically from the refined lattice through its excitation error <span class="math notranslate nohighlight">\(\Delta_\mathrm{Ewald}=\big|\,|\mathbf{q}+\mathbf{S}_0|-1/\lambda\,\big|\)</span> and a Gaussian width <span class="math notranslate nohighlight">\(\sigma^2=\gamma_0^2+(\gamma_e d^*)^2+(\mathrm{bw}\,|q_z|)^2\)</span> — the reciprocal-lattice point’s own radius (resolution-independent), the mosaic/divergence spread, and the bandwidth smear along the beam, in quadrature. The fit typically drives <span class="math notranslate nohighlight">\(\gamma_e\to0\)</span>, leaving the resolution-independent <span class="math notranslate nohighlight">\(\gamma_0\)</span> as the effective width. A tilt moves reflections on opposite sides of the Ewald sphere in opposite directions, so it reshapes the <em>spatial</em> pattern of partialities — a degree of freedom the per-image scale <span class="math notranslate nohighlight">\(G\)</span> does not have, and the reason the tilt is refined rather than a scalar partiality width, which would be degenerate with <span class="math notranslate nohighlight">\(G\)</span>. Nothing is re-integrated (the integrated intensities are fixed); the tilt is hard-bounded at about 1° and held by a soft prior, so it stays inert on sparse or weak crystals. The cycle is merge → per-crystal tilt refinement (with <span class="math notranslate nohighlight">\(G\)</span> profiled out by the same robust Cauchy IRLS used for the per-frame scales, §10.3) → recompute <span class="math notranslate nohighlight">\(P\)</span> → re-merge, repeated a few times.</p> </ol> <p>Reflections below a minimum partiality can be rejected from merging to avoid unstable corrections.</p> </section> <section id=smoothing-of-per-frame-scales > <h3 id=smoothing-of-per-frame-scales ><a class=toc-backref href="#id17" role=doc-backlink >10.3 Smoothing of per-frame scales</a><a class=headerlink href="#smoothing-of-per-frame-scales" title="Link to this heading">¶</a></h3> <p>The per-frame scales <span class="math notranslate nohighlight">\(G_i\)</span> are fit by robust (Cauchy) inverse-variance-weighted ratios; there is no explicit <span class="math notranslate nohighlight">\(G\approx1\)</span> prior. For rotation datasets, optional smoothing enforces the expectation that scale and mosaicity vary slowly across a sweep: <strong>after</strong> the per-frame fit, <span class="math notranslate nohighlight">\(\log G_i\)</span> (and the mosaicity) are replaced by a centred <strong>moving average</strong> over a window spanning a configurable rotation range (XDS DELPHI-like; <code class="docutils literal notranslate"><span class=pre >--smooth-g</span></code>, default 5° for rot3d, off otherwise). It is a post-fit smoothing pass, not a curvature penalty inside the least-squares objective. (The per-frame scale refitted on the combined fulls, §10.6, is smoothed differently: by a penalised smoother whose smoothness is chosen by cross-validation.)</p> <p>The <strong>crystal orientation</strong> is smoothed the same way, and for the same reason. Geometry is re-refined independently on every frame against that frame’s spots alone — as few as a dozen on a sparse crystal — so the per-frame orientation carries a real slow drift (crystal slippage, up to ~1.3° across a sweep) on top of fit noise that scales with spots per frame. Before scaling, the per-frame lattices are de-rotated to a common reference, averaged in frame order, rotated back, and every partial’s <span class="math notranslate nohighlight">\(\Delta\phi\)</span> — hence its partiality — is recomputed from the smoothed lattice. The window is chosen per dataset by leave-one-out cross-validation (does a frame’s neighbours predict its orientation?) rather than fixed, because drift and noise both vary by two orders of magnitude between crystals; it is capped, because the per-frame fit also absorbs a real per-frame systematic that smoothing too wide destroys. Only frames that actually indexed take part: a frame that did not carries an all-zero lattice, which is <em>finite</em> and so passes a validity check written as a finite test, and would otherwise be both averaged into its neighbours’ orientation and scored in the cross-validation that picks the window. Refining <em>less</em> is not an alternative: with per-image refinement off the space group is lost on several crystals.</p> <p>A per-frame scale enters every intensity as <span class="math notranslate nohighlight">\(1/G\)</span>, so a frame whose fit is not determined by its data can amplify it without bound — and <span class="math notranslate nohighlight">\(\sigma\)</span> is amplified by the same factor, which makes it invisible to any <span class="math notranslate nohighlight">\(\sigma\)</span>-based outlier test. A fitted <span class="math notranslate nohighlight">\(G\)</span> far below the run’s median is therefore treated as <em>undetermined</em> rather than as a successful fit, both here and in the separate refit on the combined fulls (§10.6). The bound is a ratio to the run’s own median because <span class="math notranslate nohighlight">\(G\)</span> is not gauge-fixed: it and the merged means have an exact global multiplicative degeneracy, so no absolute value is meaningful.</p> </section> <section id=merging-estimator > <h3 id=merging-estimator ><a class=toc-backref href="#id18" role=doc-backlink >10.4 Merging estimator</a><a class=headerlink href="#merging-estimator" title="Link to this heading">¶</a></h3> <p>After refinement, corrected observations are formed: <span class="math notranslate nohighlight">\( I^{\mathrm{corr}}_{ij} = \frac{I^{\mathrm{obs}}_{ij}}{G_i L_{ij} P_{ij}},\qquad \sigma^{\mathrm{corr}}_{ij} = \frac{\sigma^{\mathrm{obs}}_{ij}}{G_i L_{ij} P_{ij}}. \)</span></p> <p>Unique intensities are merged by inverse-variance weighted mean: <span class="math notranslate nohighlight">\( I_h = \frac{\sum_j w_j I^{\mathrm{corr}}_{ij}}{\sum_j w_j},\qquad w_j = \frac{1}{(\sigma^{\mathrm{corr}}_{ij})^2}. \)</span></p> <p>The weights use an <strong>expected</strong> variance: the Poisson signal part of each <span class="math notranslate nohighlight">\(\sigma^{\mathrm{corr}}_{ij}\)</span> is rebuilt at the reflection’s merged <span class="math notranslate nohighlight">\(\langle I\rangle\)</span> rather than at that observation’s own intensity. Weighting by an observation’s own <span class="math notranslate nohighlight">\(\sigma^2\)</span> biases the inverse-variance mean low below about one photon, because an up-fluctuated observation gets a larger sigma and is then down-weighted too hard. The rotation combine already does this; for stills it is on by default, and <code class="docutils literal notranslate"><span class=pre >--no-expected-variance-merge</span></code> restores the observed-sigma weighting.</p> <p>An internal-consistency term can inflate uncertainties when multiple observations are present, in the spirit of XSCALE.</p> </section> <section id=merging-statistics > <h3 id=merging-statistics ><a class=toc-backref href="#id19" role=doc-backlink >10.5 Merging statistics</a><a class=headerlink href="#merging-statistics" title="Link to this heading">¶</a></h3> <p>The shells are <strong>nine bins of equal width in <span class="math notranslate nohighlight">\(1/d^2\)</span></strong>, laid between the <strong>declared</strong> low-resolution limit (<code class="docutils literal notranslate"><span class=pre >--scaling-low-resolution</span></code>, or the whole sphere where it is switched off) and the highest-resolution reflection the merge actually kept — XDS’s rule and XDS’s count, so at the same resolution limits the two programs’ tables have the same shell boundaries and can be read row for row. <code class="docutils literal notranslate"><span class=pre >--resolution-shells</span></code> changes the count; the binning rule does not change with it.</p> <p>Per-shell and overall merging statistics are computed on corrected intensities, including:</p> <ul class=simple > <li><p>number of observations and of unique reflections, and multiplicity,</p> <li><p>mean <span class="math notranslate nohighlight">\(I/\sigma(I)\)</span>,</p> <li><p><span class="math notranslate nohighlight">\(R_\mathrm{meas}\)</span> (the redundancy-independent Diederichs–Karplus form) from within‑HKL deviations,</p> <li><p><span class="math notranslate nohighlight">\(\mathrm{CC}_{1/2}\)</span>, correlating two half-sets of <strong>equal size</strong>: an observation’s half is the parity of its rank among its own reflection’s observations, ordered by a key built from the raw Miller index and the peak frame. Every reflection measured more than once therefore contributes (a hash of the image alone leaves <span class="math notranslate nohighlight">\(2^{1-n}\)</span> of the multiplicity-<span class="math notranslate nohighlight">\(n\)</span> reflections entirely in one half, with no second mean to correlate), and because a rank is a property of the set rather than of the order it is walked in, a CUDA build and a <code class="docutils literal notranslate"><span class=pre >JFJOCH_USE_CUDA=OFF</span></code> build report the same <span class="math notranslate nohighlight">\(\mathrm{CC}_{1/2}\)</span> and the same <span class="math notranslate nohighlight">\(\mathrm{CC}_\mathrm{anom}\)</span>. It is the split cctbx’s <code class="docutils literal notranslate"><span class=pre >compute_cc_one_half</span></code> — and <code class="docutils literal notranslate"><span class=pre >phenix.merging_statistics</span></code> through it — uses. The stills path balances its halves sequentially instead, in image order. When a reference dataset is supplied, <span class="math notranslate nohighlight">\(\mathrm{CC}_\mathrm{ref}\)</span> is reported beside it,</p> <li><p>completeness against the reflections the cell and symmetry can give over that same declared range, so low-resolution terms lost to the beam stop, to a detector mask or to the low-resolution limit itself count as missing instead of leaving the denominator along with the data,</p> <li><p>the anomalous signal-to-noise <span class="math notranslate nohighlight">\(\mathrm{SigAno}\)</span> and the half-set anomalous correlation <span class="math notranslate nohighlight">\(\mathrm{CC}_\mathrm{anom}\)</span> (below).</p> </ul> <p>The error model is refined as <span class="math notranslate nohighlight">\(\sigma_\mathrm{corr}^2 = a\,\sigma^2 + (b\,\langle I\rangle)^2\)</span>, with <span class="math notranslate nohighlight">\(a\)</span> set by the scatter of weak (counting-limited) reflections and <span class="math notranslate nohighlight">\(b\)</span> the intensity-proportional systematic scatter of the strong ones. On the <strong>rotation</strong> path, <strong>ISa</strong> is the asymptotic (<span class="math notranslate nohighlight">\(I\to\infty\)</span>) signal-to-noise — by definition the reproducibility limit of the strongest reflections (Diederichs, <em>Acta Cryst.</em> <strong>D66</strong> (2010) 733) — and is read directly from the strong symmetry equivalents as the counting-subtracted fractional scatter of well-measured reflection groups (a robust median over strong groups; the <span class="math notranslate nohighlight">\(I/\sigma\)</span> threshold is relaxed on weak or radiation-damaged data that has few strong reflections), rather than as <span class="math notranslate nohighlight">\(1/b\)</span> of the whole-range fit, whose <span class="math notranslate nohighlight">\(b\)</span> is raised slightly by an intermediate-intensity excess and so understates the limit. The asymptotic value is <strong>report-only</strong> — nothing downstream reads it, and the merged <span class="math notranslate nohighlight">\(\sigma\)</span> is not floored at <span class="math notranslate nohighlight">\(b|I|\)</span> (that floor was removed). The per-observation <span class="math notranslate nohighlight">\(\sigma_\mathrm{corr}\)</span> (the merge weights) uses the whole-range <span class="math notranslate nohighlight">\(a,b\)</span>. The <strong>stills</strong> path has no asymptotic estimate and reports <span class="math notranslate nohighlight">\(\mathrm{ISa}=1/b\)</span> directly.</p> <p><span class="math notranslate nohighlight">\(a\)</span> and <span class="math notranslate nohighlight">\(b\)</span> are <strong>reported in XDS’s convention</strong>, which is <span class="math notranslate nohighlight">\(\sigma^2 = a(\sigma_0^2 + b I^2)\)</span> with <span class="math notranslate nohighlight">\(\mathrm{ISa}=1/\sqrt{ab}\)</span>, so the printed pair can be read straight against a <code class="docutils literal notranslate"><span class=pre >CORRECT.LP</span></code>. The internal fit keeps the form above; only the report converts, as <span class="math notranslate nohighlight">\(b_\mathrm{XDS} = b^2/a\)</span>. Note that <span class="math notranslate nohighlight">\(a\)</span> is the same in both conventions and that the two ISa expressions are the same number, <span class="math notranslate nohighlight">\(1/\sqrt{a\cdot b^2/a} = 1/b\)</span> — so the rotation log prints <strong>two</strong> ISa, the whole-range <span class="math notranslate nohighlight">\(1/b\)</span> (XDS’s meaning) and the strong-reflection asymptote beside it, which can only ever be the more optimistic of the two. The mmCIF follows the same split: <code class="docutils literal notranslate"><span class=pre >_reflns.jfjoch_diffrn_ISa</span></code> is the whole-range value, directly comparable with a <code class="docutils literal notranslate"><span class=pre >CORRECT.LP</span></code>, and the asymptote is written separately as <code class="docutils literal notranslate"><span class=pre >_reflns.jfjoch_diffrn_ISa_asymptotic</span></code>, with <code class="docutils literal notranslate"><span class=pre >_reflns.jfjoch_error_model_a</span></code> and <code class="docutils literal notranslate"><span class=pre >_b</span></code> alongside so the number can be re-derived. Note that a file written before this change carries the <em>asymptote</em> under the plain <code class="docutils literal notranslate"><span class=pre >ISa</span></code> name. A third, unrelated <span class="math notranslate nohighlight">\(b\)</span> appears in the space-group search (§13.1); it is fitted with the <span class="math notranslate nohighlight">\(\sigma^2\)</span> coefficient held at 1 and its gate constants are calibrated in that convention.</p> <p><strong>Anomalous signal-to-noise (SigAno).</strong> The strength of the anomalous signal is reported per shell and overall as <span class="math notranslate nohighlight">\(\mathrm{SigAno}=\langle|\Delta I|\rangle / \langle\sigma(\Delta I)\rangle\)</span>, where <span class="math notranslate nohighlight">\(\Delta I = I(+)-I(-)\)</span> over acentric reflections measured in both Bijvoet hands and <span class="math notranslate nohighlight">\(\sigma(\Delta I)=\sqrt{\sigma_+^2+\sigma_-^2}\)</span>. It is computed from the <strong>full-multiplicity</strong> inverse-variance <span class="math notranslate nohighlight">\(I(+)/I(-)\)</span> split (the same one written to the output), i.e. from all observations rather than a half-set. For pure noise <span class="math notranslate nohighlight">\(\mathrm{SigAno}\)</span> approaches the half-normal value <span class="math notranslate nohighlight">\(\sqrt{2/\pi}\approx0.8\)</span>, and it rises above <span class="math notranslate nohighlight">\(1\)</span> once a real anomalous difference is present. A half-set anomalous correlation (<span class="math notranslate nohighlight">\(\mathrm{CC}_\mathrm{anom}\)</span>) is reported beside it: <span class="math notranslate nohighlight">\(\Delta I\)</span> is formed once per half-set and the two are correlated over the acentric pairs where <strong>both</strong> hands split into two non-empty halves, per shell and overall as one correlation rather than a mean of shells. Unlike SigAno it is not a ratio against the error model, so an optimistic <span class="math notranslate nohighlight">\(\sigma\)</span> cannot inflate it. It has no floor either: subtracting the two Bijvoet hands cancels the large common intensity that keeps <span class="math notranslate nohighlight">\(\mathrm{CC}_{1/2}\)</span> non-negative, so on data with little anomalous signal and about two observations per mate it goes strongly negative. That is a property of the statistic and is reported as measured. It agrees with AIMLESS’s <code class="docutils literal notranslate"><span class=pre >CCanom</span></code> and <code class="docutils literal notranslate"><span class=pre >phenix.merging_statistics</span></code>’ <code class="docutils literal notranslate"><span class=pre >cc_anom</span></code>; XDS’s <code class="docutils literal notranslate"><span class=pre >Anomal</span> <span class=pre >Corr</span></code> is a <strong>different quantity and is not comparable with it</strong> — on the same observations it reads two to three times higher in the low shells. <span class="math notranslate nohighlight">\(\mathrm{CC}_\mathrm{anom}\)</span> is a rotation-path statistic (the stills merge forms no half-set anomalous difference), and it is reported in the <code class="docutils literal notranslate"><span class=pre >CCanom</span></code> column of the printed merge-statistics table and as <code class="docutils literal notranslate"><span class=pre >CC_ANOM=</span></code> in the results report — not in the mmCIF. SigAno is emitted only when an anomalous split was made, using the standard PDBx items <code class="docutils literal notranslate"><span class=pre >_reflns.pdbx_absDiff_over_sigma_anomalous</span></code> (overall) and <code class="docutils literal notranslate"><span class=pre >_reflns_shell.pdbx_absDiff_over_sigma_anomalous</span></code> (per shell), and appears as the <code class="docutils literal notranslate"><span class=pre >SigAno</span></code> column of the same table. Where either could not be measured at all — a Friedel-merged run that split no Bijvoet pair, a shell too thin to split one in both hands — the table prints <code class="docutils literal notranslate"><span class=pre >-</span></code> and the results report writes no key, which is not the same statement as a value measured to be zero.</p> </section> <section id=rotation-datasets-combining-partials-into-fulls-3d-integration > <h3 id=rotation-datasets-combining-partials-into-fulls-3d-integration ><a class=toc-backref href="#id20" role=doc-backlink >10.6 Rotation datasets: combining partials into fulls (3D integration)</a><a class=headerlink href="#rotation-datasets-combining-partials-into-fulls-3d-integration" title="Link to this heading">¶</a></h3> <p>In a rotation scan a reflection is recorded as a series of <em>partials</em> spread across the frames its rocking curve crosses. Merging those partials directly would force the merge error model to absorb the rocking-curve slicing as if it were measurement noise, capping the achievable <span class="math notranslate nohighlight">\(I/\sigma\)</span>. For rotation data Jungfraujoch instead <strong>combines</strong> each reflection’s partials into a single <em>full</em> intensity first, then scales and merges the fulls — a 3D integration over the rocking curve.</p> <p>The combine groups each reflection’s partials into rocking events (contiguous runs of frames) and reduces each event to one full:</p> <ul class=simple > <li><p><strong>De-biased weighted sum.</strong> Partials are combined by inverse-variance weighting, where each partial’s variance is its background-noise component plus the <em>model</em> signal shared across the event (Kabsch profile-fit form). Using the shared model signal rather than the individual down-fluctuating intensity stops weak partials from being over-weighted, which would otherwise inflate the merged error model. The weights depend on the full, so the estimate is iterated.</p> <li><p><strong>Captured fraction.</strong> The partiality summed over the event, <span class="math notranslate nohighlight">\(f=\min(1,\sum_j p_j)\)</span>, measures how completely the rocking curve was sampled. A full whose curve was captured below a threshold (<code class="docutils literal notranslate"><span class=pre >--min-captured-fraction</span></code>, default 0.7 for rotation) is dropped — an event seen over only a small fraction of its curve is unreliable however many frames it spans. (The per-partial minimum-partiality cut of §10.2 still applies upstream, in the per-frame scaling.)</p> <li><p><strong>Per-image rejection (opt-in).</strong> A frame whose observations correlate poorly with the merged reference is not measuring the crystal being merged — it may be off-crystal, or on a <em>different</em> crystal where two lattices occupy separate regions of the sample. <code class="docutils literal notranslate"><span class=pre >--min-image-cc</span></code> drops such frames. It has no default: the per-frame correlation measures data quality as much as frame validity, and its typical level varies widely between datasets, so no single absolute bound is generally valid.</p> <li><p><strong>Capture-aware uncertainty.</strong> A full captured incompletely (<span class="math notranslate nohighlight">\(f<1\)</span>) is extrapolated and biased high. The unobserved fraction is charged as an extra systematic uncertainty, <span class="math notranslate nohighlight">\(\sigma^2 \leftarrow \sigma^2 + \big(c\,(1-f)\,I\big)^2\)</span>, so the merge down-weights these extrapolated fulls and the error model treats their scatter as expected. The merge rebuilds every full’s variance at the reflection’s mean intensity (§10.4), and the capture term is rebuilt there too, as <span class="math notranslate nohighlight">\(\big(c\,(1-f)\,\langle I\rangle\big)^2\)</span>. It is enabled by default for the rotation path.</p> <li><p><strong>Overloaded events.</strong> An event in which any partial had a saturated pixel in its signal disk — or a pixel unreadable on that frame alone, beyond the run’s pixel mask, which is how a detector that writes its error value for a pixel it could not count reports an overload — is dropped whole, as XDS drops an overloaded reflection. The brightest part of such a rocking curve is exactly what is missing, so neither the sum of the remaining partials nor their extrapolation by the partiality model measures the reflection: on a strongly diffracting small-molecule crystal these were the strongest low-order reflections, and they read 2–3× low. The integration keeps an overloaded partial, unfitted and flagged, only so that the event can be recognised; nothing else reads it. The count is <code class="docutils literal notranslate"><span class=pre >OBSERVATIONS_REJECTED_OVERLOAD=</span></code> in the report.</p> </ul> <p>The fulls are then re-scaled in the XDS sense — a per-image scale refit directly on the complete reflections under the unity partiality model — and merged (§10.4).</p> <p>Before the combine a per-frame scale can also be fitted on the partials themselves. That needs a frame to hold many rocking events caught at different points of their curves: within one rocking curve a change of scale and an error of the partiality model are the same thing, and on a finely sliced sparse sweep the fit takes one for the other. The rocking events per frame are the prior (the partials are scaled from 50 events per frame), but the counts of small-molecule sweeps (3–43) and proteins (5–900) overlap. So the first merge of a run that is not a space-group search can be made <strong>both ways</strong>, with the partials scaled and with the scale taken from the fulls alone, and the prior stands unless its merge has no resolved error model (<span class="math notranslate nohighlight">\(b\)</span> not resolved from zero: the strong equivalents do not agree to within a measurable systematic error) while the other merge has one. Where the prior is to scale the partials and that merge resolves its error model, the other cannot change the choice and is not made. The two ISa values are deliberately not compared beyond that: the partiality-model error a partial scale takes up is shared by symmetry mates measured at the same rocking geometry, so their agreement cannot see it — a small-molecule sweep with six events per frame read ISa 10.5 with its partials scaled against 8.7 from the fulls alone, and refined to <span class="math notranslate nohighlight">\(R_1\)</span> 0.105 against 0.062. The log states the choice and the ISa of every arm that was made. Because every merged observation is now a counting-statistics-limited full rather than a partiality-divided slice, the error model reaches a far higher asymptotic <span class="math notranslate nohighlight">\(I/\sigma\)</span>.</p> <p>How smooth that scale is over the rotation is left to the data rather than to a fixed window. Each round fits every frame on its own fulls against the current reference, giving a scale <span class="math notranslate nohighlight">\(G_f\)</span> and its information <span class="math notranslate nohighlight">\(D_f=\sum w^2c^2\)</span>; the scale is then the curve <span class="math notranslate nohighlight">\(x=\log G\)</span> minimising <span class="math notranslate nohighlight">\(\sum_f J_f\,(x_f-y_f)^2+\lambda\sum_f(\Delta^2 x)_f^2\)</span>, with <span class="math notranslate nohighlight">\(y_f\)</span> the frame’s own fit in log scale and <span class="math notranslate nohighlight">\(J_f\)</span> its information carried there — a penalised (Whittaker–Eilers) smoother, solved as a five-band linear system. <span class="math notranslate nohighlight">\(\lambda\)</span> is chosen by cross-validation: blocks of frames one rocking curve wide are left out in turn and predicted from the curve through the rest (neighbours closer than a rocking curve share their measurement, since a full sums those frames). A frame of hundreds of fulls is then followed frame by frame, a frame of two or three is carried by its neighbours, a stretch with none is bridged by a straight line, and a scale that falls a hundredfold over a few degrees — an absorbing crystal turning edge-on — is followed where a window would average across it. Once the curve settles, one free fit is shrunk toward it frame by frame by how much of each frame’s deviation its neighbour shares (the lag-1 covariance), which hands back a real per-frame systematic and discards fit noise.</p> <p>After scale-fulls, five <strong>correction surfaces</strong> are fitted on the combined fulls (rotation path, <strong>on by default</strong>; disable all with <code class="docutils literal notranslate"><span class=pre >--no-scaling-corrections</span></code>), each an alternating multiplicative refinement of the per-full scale against the merged reference:</p> <ul class=simple > <li><p><strong>Decay.</strong> Radiation damage weakens later frames more at higher resolution — a resolution×time (Debye–Waller) systematic the resolution-flat per-image scale cannot capture. A single global relative-<span class="math notranslate nohighlight">\(B\)</span> rate is fitted, <span class="math notranslate nohighlight">\(\ln(I_\mathrm{ref}/I_\mathrm{obs}) = 2\,(\mathrm{d}B/\mathrm{d}n)\,(n-\bar n)\,s^2\)</span> (frame <span class="math notranslate nohighlight">\(n\)</span>, <span class="math notranslate nohighlight">\(s^2 = 1/4d^2\)</span>), and folded into the scale. It engages only when the total relative-<span class="math notranslate nohighlight">\(B\)</span> over the run exceeds a physical floor (2 Ų); below that the decay is negligible and “correcting” it only spreads symmetry equivalents (same <span class="math notranslate nohighlight">\(s^2\)</span>, different frames). An optional <strong>per-batch relative-<span class="math notranslate nohighlight">\(B\)</span></strong> (<code class="docutils literal notranslate"><span class=pre >--relative-b[=deg]</span></code>, off unless requested; 10°-of-rotation batches by default) extends the single global rate to a smooth <span class="math notranslate nohighlight">\(B(n)\)</span> curve — the same <span class="math notranslate nohighlight">\(s^2\)</span>-weighted decay fit solved independently over short frame batches, curvature-penalized so it cannot over-fit and cross-validated like the surfaces below — for crystals whose decay is non-linear in dose. Its cross-validation splits on <strong>ASU-group parity</strong>, not the frame parity the surfaces below use: a per-batch parameter owns whole frames and so cannot be scored on a held-out frame, whereas splitting the symmetry equivalents tests whether a batch’s <span class="math notranslate nohighlight">\(B\)</span> generalises to reflections it was not fitted on.</p> <li><p><strong>Absorption.</strong> A smooth multiplicative factor over the diffracted-beam direction expressed in the goniometer (crystal) frame: each full’s predicted detector position gives the lab diffracted direction, de-rotated by the spindle so a fixed crystal-frame direction is sampled at many rotation angles and its grid cell is well-determined. Negligible at hard X-rays / thin crystals; it matters at low photon energy.</p> <li><p><strong>Modulation</strong> (detector-plane flat-field). A smooth multiplicative factor over where each reflection lands on the detector (predicted <span class="math notranslate nohighlight">\(x,y\)</span>): symmetry-equivalents land at different positions as the crystal rotates, over-determining the surface. It absorbs detector-response and geometric systematics that inflate <span class="math notranslate nohighlight">\(R_\mathrm{meas}\)</span>.</p> <li><p><strong>Absorption as spherical harmonics.</strong> The same crystal-frame absorption as a smooth function instead of a grid: the logarithm of the factor is a sum of real spherical harmonics of the de-rotated diffracted-beam direction, degrees 1 to 6 (48 terms; the incident-beam path depends on the rotation angle alone and is part of the per-image scale). It is fitted through 32 × 64 equal-solid-angle direction cells, one ridge-regularised least-squares step on the coefficients per round, with a prior of width 0.1/l on each degree-l coefficient. It is a candidate like the others and passes the same held-out test; the grid is then tested on what it left.</p> <li><p><strong>Time-dependent absorption.</strong> The same surface as <em>Absorption</em>, but over (rotation angle × detector position) instead of the crystal-frame direction alone. The two agree while the illuminated volume stays put — the incident path then depends only on the spindle angle, which the per-image scale already takes, and the exit path is fixed in the crystal frame. Once the diffracting volume drifts through the beam the exit path becomes a function of the spindle angle as well, and nothing time-independent describes it. Fitted on 12 rotation bins × a 10×10 equal-occupancy detector grid.</p> </ul> <p>The surfaces overlap, so the order decides what is adopted: modulation first (every frame measures the static detector pattern, so its fine grid is the best determined), then time-dependent absorption, then the spherical-harmonic absorption, then the goniometer-frame grid, whose cells collect directions from the whole sweep and the whole resolution range and which, fitted first, takes up part of what the other two describe.</p> <p>Each cell’s factor is fitted under a <strong>prior pull to 1</strong> (a Gaussian prior of width 0.1 on its logarithm): a cell moves off 1 in proportion to the information its observations carry, so a cell with little signal stays near 1 instead of being fitted to its noise. Negative observations enter the fit as measured. On the detector-plane surfaces (modulation, time-dependent absorption) the component that is a function of resolution alone is projected out within resolution shells, since the symmetry equivalents of a reflection share one resolution and such a factor cannot be determined from them.</p> <p>Each surface is <strong>cross-validated</strong> on the half-set agreement it is meant to improve: fitted on even-numbered frames and used to merge the odd ones, fitted on the odd frames and used to merge the even ones, and kept only if the correlation between the two half-set means, taken within resolution shells, rises above the same two halves merged with no surface. Each half is corrected by a surface it did not help to fit, so a surface fitted to noise lowers the correlation, and a correlation within a shell is blind to the resolution-dependent scale the surface cannot determine. The change is averaged over the shells on Fisher’s <span class="math notranslate nohighlight">\(z = \operatorname{atanh}(CC)\)</span>, not on <span class="math notranslate nohighlight">\(CC\)</span>: the strong shells, where a multiplicative error matters, sit at <span class="math notranslate nohighlight">\(CC_{1/2} \approx 0.999\)</span>, where even a large reduction of the error moves <span class="math notranslate nohighlight">\(CC\)</span> in the fourth decimal, and averaged on <span class="math notranslate nohighlight">\(CC\)</span> itself the shells of pure noise beyond the reach of the data decide the sign.</p> <p><strong>Radiation-damage report (rotation, report-only).</strong> Independently of whether any decay correction is applied, Rugnux measures and reports the relative Debye–Waller <span class="math notranslate nohighlight">\(B\)</span> across the sweep: the per-image scale’s correlation to the merge and the per-image mosaicity versus frame (dose), together with a per-batch relative-<span class="math notranslate nohighlight">\(B\)</span> curve whose first→last change is a single headline number (measured before any decay correction, against the least-damaged early wedge). It is written to the log and to the merged mmCIF as a data-quality-vs-dose diagnostic and <strong>never</strong> alters the merged intensities — distinct from the decay correction above, which does fold into the scale.</p> <p>Each batch’s <span class="math notranslate nohighlight">\(B\)</span> is fitted on <strong>resolution-shell means</strong>, not on single observations: <span class="math notranslate nohighlight">\(\ln(I_\mathrm{ref}/I_\mathrm{obs})\)</span> of one weak observation is unbounded and biased downwards — the observation appears in the response and in its own weight, and the logarithm needs <span class="math notranslate nohighlight">\(I_\mathrm{obs} > 0\)</span>, which keeps only the upward half of the noise — and on decayed data that bias grows with dose until it reverses the sign of the answer. The shells are laid inside the range the run actually diffracted to, and the fit carries an intercept as well as a slope, so a batch that is merely <em>dimmer</em> than the run (an attenuated beam, a mis-fitted frame scale) is not reported as damage. A batch whose shells are too weak to fit, or whose solved value reaches the bound the smoothing solve clamps to, is reported as <strong>absent</strong> rather than as a number. The first→last headline is reported only where a straight line describes the curve: radiation damage is progressive, so a curve that dips and recovers is a disturbance rather than dose, and is left to the sweep-quality report (<a class="reference internal" href=RUGNUX_REPORT.html ><span class="std std-doc">the results report</span></a>) to name.</p> <p><strong>Frame disposition (rotation).</strong> After the correction surfaces are fitted, and on the corrected fulls, Rugnux measures <strong>ΔCC1/2</strong> — the overall CC1/2 of the merged data with a group of images minus the CC1/2 without it, over the reflections that group touches — for each 10° batch of the sweep and for each stretch the sweep-quality diagnostic flagged. It is computed in the σ-τ form (no random half-dataset split, so the answer is the same every run) with each reflection’s error variance taken from the <strong>observed</strong> scatter of its own observations rather than from the error model’s σ’s: a bad stretch claims the same σ’s as a good one, so an error-model estimate would read a batch that adds noise as a batch that adds precision, inverting the sign of the measurement. The leave-one-out is a subtraction of the group’s own <span class="math notranslate nohighlight">\((n, \sum I, \sum I^2)\)</span> from the per-reflection totals, so measuring a group costs one pass over its observations rather than a re-merge, and reflections the group holds the only observations of are excluded from both sides — the published statistic’s own restriction. Its standard error is reported beside it as that of a single CC1/2 on the same reflection count, <span class="math notranslate nohighlight">\((1-CC^2)/\sqrt{n_\mathrm{refl}-3}\)</span>, and a ΔCC1/2 smaller than that says nothing; a group whose ΔCC1/2 is positive or near zero is not evidence of harm. A batch is removed only where its ΔCC1/2 is <strong>both</strong> several standard errors below zero (Fisher-transformed) <strong>and</strong> well below the run’s own per-batch distribution (median − 3 robust σ, measured once before anything is removed), <strong>and</strong> where the per-image CC to the merge — an independent channel, measured on the partials one frame at a time — also says the stretch agrees with the run worse than a typical frame does, because selecting frames by their disagreement with the merge and then reporting that the merge improved is circular; its edges are then slid frame by frame with the whole stretch re-measured at each position — so the range is located finely while the count of reflections the verdict rests on stays that of the stretch — the worst range is removed, the reference is re-formed and the scan repeats, never past a quarter of the sweep and never over a stretch narrower than one rocking event. The resulting per-frame <code class="docutils literal notranslate"><span class=pre >merged</span></code> / <code class="docutils literal notranslate"><span class=pre >downgraded</span></code> / <code class="docutils literal notranslate"><span class=pre >rejected</span></code> ledger is described in <a class="reference internal" href=RUGNUX_REPORT.html ><span class="std std-doc">the results report</span></a>.</p> </section> <section id=r-free-test-set-flags > <h3 id=r-free-test-set-flags ><a class=toc-backref href="#id21" role=doc-backlink >10.7 R-free test-set flags</a><a class=headerlink href="#r-free-test-set-flags" title="Link to this heading">¶</a></h3> <p>A fraction of the unique reflections (<code class="docutils literal notranslate"><span class=pre >rfree_fraction</span></code>, default 0.05) is flagged as a <strong>free (test) set</strong>, written to the output (MTZ <code class="docutils literal notranslate"><span class=pre >FreeR_flag</span></code>, mmCIF <code class="docutils literal notranslate"><span class=pre >_refln.status</span></code> = <code class="docutils literal notranslate"><span class=pre >f</span></code>, a text-HKL column) for model validation (§14) and for downstream refinement. The flag is a pure function of the reflection’s orbit under the <strong>lattice holohedry</strong> — the point group of the cell’s metric, found as twin laws are (Le Page two-folds within 3° obliquity, taking the lattice of the cell’s own basis vectors), which contains the merging group — together with its Friedel mate. Where the cell does not carry the merging group (a space group forced on a metric without it), the Friedel-merged (Laue) ASU index of the merging group is used instead. That gives four properties:</p> <ul class=simple > <li><p>all symmetry- and Friedel-equivalent reflections share one flag — in particular a Bijvoet pair <span class="math notranslate nohighlight">\(I(+)/I(-)\)</span>, kept as two separate merged rows in anomalous mode, is <strong>never split</strong> across the work and free sets (which would bias R-free);</p> <li><p><strong>twin-law mates share one flag too</strong>, since a twin law is a lattice symmetry the crystal lacks. Keyed on the merging group instead, nearly every free reflection’s twin mate lands in the working set, and in twin refinement the free reflection’s calculated intensity then carries the working-set fit. The same set is what phenix.refine generates by default (<code class="docutils literal notranslate"><span class=pre >use_lattice_symmetry</span></code>). It also makes the free set independent of the space group a file is merged in: the merged file, the P1 cross-check and a re-merge in any subgroup carry one free set (nested, where the small-data floor below lifts the fraction of one of them more than another’s);</p> <li><p>the free/work decision is a deterministic hash of that key, so the same reflection always lands in the same set — reproducible run-to-run and independent of the order in which observations were merged;</p> <li><p>the hash depends only on the reflection index, <strong>not</strong> on this dataset’s resolution range or which reflections it happens to contain, so a uniform draw takes ~<code class="docutils literal notranslate"><span class=pre >rfree_fraction</span></code> of the distinct reflections free and — crucially — <strong>every dataset of one crystal form gets the same free set</strong>. That cross-dataset consistency is what a multi-dataset campaign (ensemble refinement, PanDDA) requires; a per-shell stratification tied to each dataset’s own <span class="math notranslate nohighlight">\(d_\mathrm{min}\)</span> would break it.</p> </ul> <p>On small data, where <code class="docutils literal notranslate"><span class=pre >rfree_fraction</span></code> (default 0.05) would give too few test reflections for a statistically stable R-free (Brünger’s ~500–2000 rule), the fraction is <strong>floored</strong> so at least ~500 distinct reflections are free — capped at 10 % so a large test set never steals working data. For ordinary data this floor is inactive and the fraction stays flat at <code class="docutils literal notranslate"><span class=pre >rfree_fraction</span></code>, preserving the cross-dataset-identical property above; it only lifts the fraction on genuinely small datasets, where per-dataset R-free stability outweighs cross-dataset identity (and a shared reference set is the way to keep exact identity there).</p> <p>When a reference (<code class="docutils literal notranslate"><span class=pre >--reference</span></code>: an MTZ, or a PDB structure-factor mmCIF, which is converted to one with <code class="docutils literal notranslate"><span class=pre >_refln.status</span></code> <code class="docutils literal notranslate"><span class=pre >f</span></code>/<code class="docutils literal notranslate"><span class=pre >o</span></code> becoming flag 0/1) carries a <code class="docutils literal notranslate"><span class=pre >FreeR_flag</span></code> column, its test set is <strong>imported</strong> instead: every merged reflection whose Laue-ASU index matches the reference takes the reference’s flag (reflections absent from the reference keep the hash flag). The match is made in the reference’s frame — on rotation data the merge is first put onto the reference’s axes, choosing among every description of the lattice on them (and every alternative indexing) by the intensity correlation with the reference — and only where the merge then matches it: an intensity CC of at least 0.5 over at least half of the merged reflections in the reference’s resolution range. Below that the reference is not the same crystal form on the same axes, its flags would land on unrelated reflections, and they are not imported (<code class="docutils literal notranslate"><span class=pre >REFERENCE_MISMATCH</span></code>). This lets a whole fragment-screening campaign inherit one shared free set from the apo/reference dataset. The CCP4/refmac convention (test set = flag 0, including the historical 0–19 form) is assumed, with the complement taken automatically if flag 0 would be the majority (a phenix-style file where 1 marks free).</p> </section> <section id=frenchwilson-amplitudes > <h3 id=frenchwilson-amplitudes ><a class=toc-backref href="#id22" role=doc-backlink >10.8 French–Wilson amplitudes</a><a class=headerlink href="#frenchwilson-amplitudes" title="Link to this heading">¶</a></h3> <p>The last step of the merge estimates a Bayesian structure-factor amplitude <span class="math notranslate nohighlight">\(|F|\)</span> for each unique reflection from its intensity <span class="math notranslate nohighlight">\(I\)</span> and error <span class="math notranslate nohighlight">\(\sigma\)</span>, so the output carries amplitudes alongside intensities (a naïve <span class="math notranslate nohighlight">\(\sqrt{\max(I,0)}\)</span> turns every weak or negative measurement into a biased — or zero — amplitude). With the Wilson prior for the true intensity <span class="math notranslate nohighlight">\(J\ge 0\)</span> at that resolution,</p> <p><span class="math notranslate nohighlight">\( P_\mathrm{acentric}(J) \propto e^{-J/\Sigma},\qquad P_\mathrm{centric}(J) \propto J^{-1/2}\,e^{-J/2\Sigma}, \)</span></p> <p>and a Gaussian likelihood <span class="math notranslate nohighlight">\(\mathcal{N}(I;J,\sigma^2)\)</span>, the posterior mean amplitude and its uncertainty are</p> <p><span class="math notranslate nohighlight">\( \langle |F|\rangle = \frac{\int_0^\infty \sqrt{J}\,\mathcal{N}(I;J,\sigma^2)\,P(J)\,\mathrm{d}J}{\int_0^\infty \mathcal{N}(I;J,\sigma^2)\,P(J)\,\mathrm{d}J},\qquad \sigma_F = \sqrt{\langle J\rangle - \langle|F|\rangle^2}. \)</span></p> <p>The prior mean is <span class="math notranslate nohighlight">\(\Sigma = \varepsilon\,K_\mathrm{shell}\,a(\mathbf{h})\)</span>, where <span class="math notranslate nohighlight">\(\varepsilon\)</span> is the reflection’s epsilon (symmetry-enhancement) multiplicity, <span class="math notranslate nohighlight">\(a(\mathbf{h}) = \exp(-\tfrac12\mathbf{s}^\mathsf{T}B\,\mathbf{s})\)</span> carries the deviatoric anisotropy tensor <span class="math notranslate nohighlight">\(B\)</span> of §13.5 along the reflection’s own direction, and <span class="math notranslate nohighlight">\(K_\mathrm{shell} = \sum I/\varepsilon \,/ \sum a\)</span> over its resolution shell, so the priors of a shell still average to its measured Wilson mean. The amplitudes are first made with <span class="math notranslate nohighlight">\(a = 1\)</span> at the end of the merge and made again once the tensor has been fitted; only <span class="math notranslate nohighlight">\(F\)</span>/<span class="math notranslate nohighlight">\(\sigma_F\)</span> change, never an intensity. With an isotropic prior the weak direction of an anisotropic crystal gets a prior set mostly by the strong direction, which turns its noise into amplitude; for isotropic data the tensor is near zero and the prior reduces to the shell mean, so it is used whenever a tensor was fitted. A shell whose <span class="math notranslate nohighlight">\(K\)</span> is not positive takes that of the nearest lower-resolution shell. As in <code class="docutils literal notranslate"><span class=pre >ctruncate</span></code>, an intensity more than 3.7σ below zero gets no amplitude (the intensity is kept) and does not enter the shell mean. Strong reflections (<span class="math notranslate nohighlight">\(I>20\sigma\)</span>) short-circuit to <span class="math notranslate nohighlight">\(|F|=\sqrt{I}\)</span>, where the French–Wilson bias is below 0.3%; a reflection with an unusable <span class="math notranslate nohighlight">\(I/\sigma\)</span> falls back to <span class="math notranslate nohighlight">\(\sqrt{\max(I,0)}\)</span>. The integral is evaluated numerically with a log-shift for stability.</p> <p>Amplitudes are written as MTZ <code class="docutils literal notranslate"><span class=pre >F</span></code>/<code class="docutils literal notranslate"><span class=pre >SIGF</span></code> (and <code class="docutils literal notranslate"><span class=pre >F(+)</span></code>/<code class="docutils literal notranslate"><span class=pre >F(-)</span></code>) and mmCIF <code class="docutils literal notranslate"><span class=pre >_refln.F_meas_au</span></code>/<code class="docutils literal notranslate"><span class=pre >F_meas_sigma_au</span></code>, alongside the intensity columns; the SHELX <code class="docutils literal notranslate"><span class=pre >.hkl</span></code> holds intensities only. The <strong>same</strong> <span class="math notranslate nohighlight">\(|F|\)</span> feed the model-validation step (§14), so the reflection file and the maps use one consistent set of amplitudes.</p> </section> <section id=reference-data-fixing-the-space-group-and-resolving-the-indexing-ambiguity > <h3 id=reference-data-fixing-the-space-group-and-resolving-the-indexing-ambiguity ><a class=toc-backref href="#id23" role=doc-backlink >10.9 Reference data: fixing the space group and resolving the indexing ambiguity</a><a class=headerlink href="#reference-data-fixing-the-space-group-and-resolving-the-indexing-ambiguity" title="Link to this heading">¶</a></h3> <p>A reference dataset (<code class="docutils literal notranslate"><span class=pre >--reference</span></code>, MTZ or SF-mmCIF) supplies known intensities for the same crystal form, and is used in two ways.</p> <p><strong>Fix the space group and cell.</strong> Unless overridden on the command line (<code class="docutils literal notranslate"><span class=pre >-S</span></code> for the space group, <code class="docutils literal notranslate"><span class=pre >-C</span></code> for the cell), the reference’s space group is adopted and its cell is used as the soft reference cell — indexing may still drift the cell within tolerance, so a small mismatch between reference and data is absorbed rather than rejected. This applies to both stills and rotation data.</p> <p><strong>Resolve the indexing (merohedral) ambiguity.</strong> When the lattice symmetry is higher than the crystal’s Laue symmetry (e.g. <span class="math notranslate nohighlight">\(P3\)</span>, <span class="math notranslate nohighlight">\(P4\)</span>, <span class="math notranslate nohighlight">\(P6\)</span>, <span class="math notranslate nohighlight">\(C2\)</span>), more than one indexing of the same lattice is geometrically valid, and the two solutions produce <em>different</em> merged intensities that a self-consistent scale cannot tell apart — only an external reference can. The candidate reindexings are the identity together with the twin-law cosets of the metric symmetry (from the unit-cell metric and the Laue group); each is scored by the intensity correlation <span class="math notranslate nohighlight">\(\mathrm{CC}_\mathrm{ref}\)</span> of the reindexed merge against the reference, and the data are re-merged in the best-correlating indexing. The reindex is <strong>metric-preserving</strong> — only the <span class="math notranslate nohighlight">\(hkl\)</span> labels change, the cell is unchanged — and it is a no-op for a holohedral crystal, which has no twin laws (the lattice and Laue symmetry coincide). For rotation data this is done once, after the space group is determined, and the whole merge is then repeated in the chosen indexing. For stills it has to be done <strong>per image</strong>, at integration time: each crystal is indexed independently, so a run resolves the ambiguity image by image (the image’s partiality/Lorentz-corrected intensities are correlated with the reference under each candidate operator, which is scale-invariant, and the best-correlating one is adopted once and for good) — otherwise the merge would average reflections that are not symmetry mates. In neither workflow is the reference a <strong>scale</strong> target: both scale against their own data (§10.2), so <span class="math notranslate nohighlight">\(\mathrm{ISa}\)</span> and the merging statistics come from the data alone and no cross-dataset systematic is imported. Because the stills choice is made at integration time, a later re-merge of stored reflections cannot repair a dataset integrated without a reference. Where there is no reference dataset but there is a <strong>model</strong> (<code class="docutils literal notranslate"><span class=pre >--model</span></code>), the reference intensities are computed from it instead - <span class="math notranslate nohighlight">\(|F_\mathrm{model}|^2\)</span> from the atomic structure factors with a flat bulk-solvent contribution at the standard constants (<span class="math notranslate nohighlight">\(k_\mathrm{sol}=0.35\)</span>, <span class="math notranslate nohighlight">\(B_\mathrm{sol}=46\)</span> Å<span class="math notranslate nohighlight">\(^2\)</span>), which are not fitted because there are no observations yet. Nothing is scaled against them; they serve only to rank the candidate indexings, and the correlation that does the ranking is scale-invariant. This needs the cell and space group up front (<code class="docutils literal notranslate"><span class=pre >-C</span></code> / <code class="docutils literal notranslate"><span class=pre >-S</span></code>, as serial indexing wants anyway); on rotation data the same job is done after the merge, in §14.5, where a merge exists to fit the model to properly.</p> </section> <section id=ice-rings-at-the-scale-and-merge-stages > <h3 id=ice-rings-at-the-scale-and-merge-stages ><a class=toc-backref href="#id24" role=doc-backlink >10.10 Ice rings at the scale and merge stages</a><a class=headerlink href="#ice-rings-at-the-scale-and-merge-stages" title="Link to this heading">¶</a></h3> <p>Where the gate of §3.3 has found ice, reflections falling within <span class="math notranslate nohighlight">\(\pm w\)</span> in <span class="math notranslate nohighlight">\(q = 2\pi/d\)</span> (§1.2) of a hexagonal-ice band (<span class="math notranslate nohighlight">\(w=0.03\)</span> Å<span class="math notranslate nohighlight">\(^{-1}\)</span> offline, about the measured ring half-width) are marked. Marked reflections are <strong>excluded where a model is fitted</strong> — the per-frame scale <span class="math notranslate nohighlight">\(G\)</span>, the per-image correlation, and the <span class="math notranslate nohighlight">\(P1\)</span> merge the space-group search runs on — because ice contamination is a <em>positive bias</em>, not extra scatter, and a least-squares scale absorbs it into <span class="math notranslate nohighlight">\(G\)</span> and into the error-model <span class="math notranslate nohighlight">\(b\)</span>, where it damages every other reflection on the same frame. They are also left out of the <strong>resolution-cut fit</strong> (§13.4), which is the one consumer that reads the <em>merged</em> reflections rather than the observations: an ice-flagged observation marks its merged reflection, on the rotation merge’s own accumulator (host and device alike) as well as on the stills one. They are otherwise <strong>kept in the final merge</strong>, which is also what the established scaling programs do by default, so the affected shells keep their completeness.</p> <p>Nothing on an ice band is deleted from the merged output. Deleting the bands was implemented, measured against an external arbiter rather than against the merge’s own statistics, and removed: on the one rotation-battery crystal where a band was both dead by its own merged <span class="math notranslate nohighlight">\(\mathrm{CC}_{1/2}\)</span> and scorable by anomalous peak height, dropping it changed the mean anomalous density at the known sites by <span class="math notranslate nohighlight">\(-0.001\pm0.018\,\sigma\)</span> — about 2 % of the site height — while removing 1149 unique reflections whose mean <span class="math notranslate nohighlight">\(I/\sigma\)</span> was 3.62 against the dataset’s own 3.05, i.e. better-than-average data, and costing 6 to 8 points of completeness in the affected shell.</p> </section> </section> <hr class=docutils /> <section id=mosaicity-and-profile-radius-monitoring > <h2 id=mosaicity-and-profile-radius-monitoring ><a class=toc-backref href="#id25" role=doc-backlink >11. Mosaicity and “profile radius” monitoring</a><a class=headerlink href="#mosaicity-and-profile-radius-monitoring" title="Link to this heading">¶</a></h2> <section id=profile-radius-intrinsic-excitation-error-width > <h3 id=profile-radius-intrinsic-excitation-error-width ><a class=toc-backref href="#id26" role=doc-backlink >11.1 Profile radius (intrinsic excitation-error width)</a><a class=headerlink href="#profile-radius-intrinsic-excitation-error-width" title="Link to this heading">¶</a></h3> <p>The “profile radius” is the intrinsic angular width of a reflection — crystal mosaicity plus beam divergence — estimated from the spread of <span class="math notranslate nohighlight">\(\Delta_\mathrm{Ewald}\)</span> over indexed spots, <span class="math notranslate nohighlight">\( R \approx \sqrt{\tfrac{1}{N}\sum_i \Delta_{\mathrm{Ewald},i}^2}. \)</span> When the beam has a finite energy bandwidth, that bandwidth smears each reflection radially by <span class="math notranslate nohighlight">\(\sigma_\mathrm{bw}\approx \mathrm{bandwidth}\cdot\lambda/2d^2\)</span> (largest at high resolution), which also broadens the measured <span class="math notranslate nohighlight">\(\Delta_\mathrm{Ewald}\)</span> spread. Since prediction re-applies the bandwidth term per reflection (§8.2), this contribution is deconvolved from the estimate — <span class="math notranslate nohighlight">\(R^2 = \langle\Delta_\mathrm{Ewald}^2\rangle - \langle\sigma_\mathrm{bw}^2\rangle\)</span> — so that <span class="math notranslate nohighlight">\(R\)</span> is the intrinsic width and bandwidth is not double-counted. Still predictions use an excitation-error cutoff proportional to <span class="math notranslate nohighlight">\(R\)</span>.</p> </section> <section id=mosaicity-from-rotation-data > <h3 id=mosaicity-from-rotation-data ><a class=toc-backref href="#id27" role=doc-backlink >11.2 Mosaicity from rotation data</a><a class=headerlink href="#mosaicity-from-rotation-data" title="Link to this heading">¶</a></h3> <p>For rotation data the mosaicity <span class="math notranslate nohighlight">\(\sigma_M\)</span> is estimated by maximum likelihood from the rocking offsets <span class="math notranslate nohighlight">\(\tau\)</span> of indexed spots, using the XDS reflection-fraction model <span class="math notranslate nohighlight">\(R(\tau;\sigma_M/\zeta)\)</span> (Kabsch 2010): each spot’s exact Bragg angle is located near its frame, <span class="math notranslate nohighlight">\(\zeta\)</span> (the rotation-axis Lorentz component) is computed, and <span class="math notranslate nohighlight">\(\sigma_M\)</span> is chosen to maximize <span class="math notranslate nohighlight">\(\sum_i \log R(\tau_i;\sigma_M/\zeta_i)\)</span>.</p> <p>The <span class="math notranslate nohighlight">\(\phi\)</span> search window for the Bragg angle is set <strong>wider than the oscillation</strong>, so that reflections recorded at large rocking offset are included. These tail reflections carry most of the information about the mosaic width; a window limited to the oscillation range would truncate the <span class="math notranslate nohighlight">\(\tau\)</span> distribution and bias <span class="math notranslate nohighlight">\(\sigma_M\)</span> low.</p> <p>The fit uses only the <strong>strongest 250 spots</strong> of an image, whatever the indexing spot budget (<code class="docutils literal notranslate"><span class=pre >--max-spots</span></code>) is. A spot is detected when <span class="math notranslate nohighlight">\(I_\mathrm{full}R(\tau)\)</span> clears the finder threshold, so selecting spots by intensity censors on <span class="math notranslate nohighlight">\(R(\tau)\)</span>: a deeper list holds proportionally more large-<span class="math notranslate nohighlight">\(\tau\)</span> partially recorded spots and the fit widens with it. Left uncapped, <span class="math notranslate nohighlight">\(\sigma_M\)</span> therefore tracks the spot budget rather than the crystal — and since an over-wide mosaicity mis-states every partiality, the merge degrades sharply with it.</p> <p>The estimated mosaicity feeds the rotation prediction (how many frames each reflection spans, §8.3) and the rotation partiality (§10.2). It is <strong>held fixed during scaling</strong>: in the per-image scale fit the mosaicity is degenerate with the scale <span class="math notranslate nohighlight">\(G\)</span> (both rescale the predicted intensity), so refining it there is unstable. A correct mosaicity matters because it controls both how much of each rocking curve is captured and the partiality used to form fulls (§10.6); too small a value truncates the captured curve and over-peaks the partiality, degrading the combined fulls.</p> </section> </section> <hr class=docutils /> <section id=auxiliary-statistics-i-i-and-wilson-plot > <h2 id=auxiliary-statistics-i-i-and-wilson-plot ><a class=toc-backref href="#id28" role=doc-backlink >12. Auxiliary statistics: ⟨I/σ(I)⟩ and Wilson plot</a><a class=headerlink href="#auxiliary-statistics-i-i-and-wilson-plot" title="Link to this heading">¶</a></h2> <section id=per-shell-i-i > <h3 id=per-shell-i-i ><a class=toc-backref href="#id29" role=doc-backlink >12.1 Per-shell ⟨I/σ(I)⟩</a><a class=headerlink href="#per-shell-i-i" title="Link to this heading">¶</a></h3> <p>For monitoring integration quality, Jungfraujoch reports mean <span class="math notranslate nohighlight">\(\langle I/\sigma(I)\rangle\)</span> in a fixed number of resolution shells. Shelling is performed in <span class="math notranslate nohighlight">\(1/d^2\)</span> space (typical of crystallographic practice).</p> </section> <section id=wilson-plot-b-factor-proxy > <h3 id=wilson-plot-b-factor-proxy ><a class=toc-backref href="#id30" role=doc-backlink >12.2 Wilson plot (B-factor proxy)</a><a class=headerlink href="#wilson-plot-b-factor-proxy" title="Link to this heading">¶</a></h3> <p>A Wilson-type analysis is computed by binning intensities by resolution and fitting: <span class="math notranslate nohighlight">\( \langle I\rangle \propto \exp\!\left(-\frac{B}{2}\frac{1}{d^2}\right), \)</span> i.e. <span class="math notranslate nohighlight">\( \log \langle I\rangle = \mathrm{const} - \frac{B}{2}\left(\frac{1}{d^2}\right). \)</span> A linear regression of <span class="math notranslate nohighlight">\(\log\langle I\rangle\)</span> vs <span class="math notranslate nohighlight">\(1/d^2\)</span> provides an estimate of <span class="math notranslate nohighlight">\(B\)</span>, subject to basic quality checks (e.g. <span class="math notranslate nohighlight">\(R^2\)</span> threshold).</p> <p>A <strong>dataset-wide</strong> Wilson <span class="math notranslate nohighlight">\(B\)</span> is also estimated over the merged reflections — restricted to the meaningful resolution range (skipping the low-resolution non-linear region below ~4 Å and shells past the signal limit <span class="math notranslate nohighlight">\(\langle I/\sigma\rangle < 1\)</span>, so it is insensitive to how far the merged data extend) — and written to the merged mmCIF as <code class="docutils literal notranslate"><span class=pre >_reflns.B_iso_Wilson_estimate</span></code> (and reported as <code class="docutils literal notranslate"><span class=pre >WILSON_B=</span></code>), the analogue of XDS’s Wilson-line <span class="math notranslate nohighlight">\(B\)</span>. It is diagnostic only and is not fed back into scaling. <strong>It is the XDS convention, not the CCP4/Phenix one, and the two are not comparable.</strong> The slope here is fitted to <span class="math notranslate nohighlight">\(\log\langle I\rangle\)</span> directly; TRUNCATE, <code class="docutils literal notranslate"><span class=pre >ctruncate</span></code> and <code class="docutils literal notranslate"><span class=pre >phenix.xtriage</span></code> fit <span class="math notranslate nohighlight">\(\log(\langle I\rangle/\Sigma)\)</span>, dividing out <span class="math notranslate nohighlight">\(\Sigma=\sum_j f_j^2(s)\)</span>, the falloff of the atomic form factors for an assumed composition. Leaving <span class="math notranslate nohighlight">\(\Sigma\)</span> in the slope inflates <span class="math notranslate nohighlight">\(B\)</span> by roughly 2 to 8 Ų (measured across in-house merges; the arithmetic gives +6.9 Ų over 4.0-1.5 Å for a generic protein), and the fitted range accounts for more still: against <code class="docutils literal notranslate"><span class=pre >ctruncate</span></code> and <code class="docutils literal notranslate"><span class=pre >phenix.xtriage</span></code> on the same merged files this number runs 10 to 36 Ų high, in the same direction every time - though those two disagree with each other by 7 to 16 Ų, so there is a band rather than a right answer. Read it as a relative quantity, comparable between Rugnux runs and against XDS, and do not compare it with a value quoted from a CCP4 or Phenix log. The same caveat applies to <code class="docutils literal notranslate"><span class=pre >_reflns.B_iso_Wilson_estimate</span></code> in the merged mmCIF, whose deposited values are conventionally the <span class="math notranslate nohighlight">\(\Sigma\)</span>-normalised kind. It is also where the flux the fixed integration disk clips lands: that loss is degenerate with an overall <span class="math notranslate nohighlight">\(B\)</span>, so the reported number carries an <span class="math notranslate nohighlight">\(r_1\)</span>-dependent contribution and is not a property of the crystal alone (§9.1). The <strong>per-image</strong> estimate (used for the live radiation-damage plot) is accepted only when the fit is well-correlated and physically plausible (<span class="math notranslate nohighlight">\(0 < B < 200\)</span> Ų); on a bad frame (an indexing glitch, too few reflections) the Wilson line runs wildly steep, so an implausible <span class="math notranslate nohighlight">\(B\)</span> is reported as NaN rather than a spurious hundreds-of-Ų value.</p> </section> </section> </section> </article> </div> </div> </main> </div> <footer class=md-footer > <div class=md-footer-nav > <nav class="md-footer-nav__inner md-grid"> <a href=CPU_DATA_ANALYSIS_INDEXING.html title="Data analysis: indexing and geometry (§4–§7)" class="md-flex md-footer-nav__link md-footer-nav__link--prev" rel=prev > <div class="md-flex__cell md-flex__cell--shrink"> <i class="md-icon md-icon--arrow-back md-footer-nav__button"></i> </div> <div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title"> <span class=md-flex__ellipsis > <span class=md-footer-nav__direction > "Previous" </span> Data analysis: indexing and geometry (§4–§7) </span> </div> </a> <a href=CPU_DATA_ANALYSIS_DECISIONS.html title="Data analysis: space group and validation (§13–§14)" class="md-flex md-footer-nav__link md-footer-nav__link--next" rel=next > <div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title"><span class=md-flex__ellipsis > <span class=md-footer-nav__direction > "Next" </span> Data analysis: space group and validation (§13–§14) </span> </div> <div class="md-flex__cell md-flex__cell--shrink"><i class="md-icon md-icon--arrow-forward md-footer-nav__button"></i> </div> </a> </nav> </div> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-footer-copyright > <div class=md-footer-copyright__highlight > © Copyright 2024, Paul Scherrer Institute. </div> Created using <a href="http://www.sphinx-doc.org/">Sphinx</a> 8.1.3. and <a href="https://github.com/bashtage/sphinx-material/">Material for Sphinx</a> </div> </div> </div> </footer> <script src="_static/javascripts/application.js"></script> <script>app.initialize({version: "1.0.4", url: {base: ".."}})</script> |