Commit 0dc70d63 authored by Vacaliuc, Bogdan's avatar Vacaliuc, Bogdan
Browse files

slides: add slide 5 — a peak drag traced from click to plot



Four-lane swim-lane sequence diagram showing what happens when a
user drags a peak line on the detector plot: 9 numbered steps
flowing UI → Configuration/DataManager → Reduction back-end →
Mantid → back.

Lane colours match the rest of the deck (warm-orange for Qt UI,
amber for Configuration, blue for back-end, green for Mantid).

Mid-slide cost banner names the expensive parts: 2–4 deepcopies
per drag, one full MRR.PyExec per cross-section, all synchronous
on the Qt thread, 1–10 s of frozen UI.

Bottom banner enumerates the five debts scientists perceive as
"Qt leaking into the reduction": synchronous reduction, deepcopy
chain, class-attribute globals, signal-timing fragility, widget
lifecycle. Documents that there are zero Qt imports in the back-end
but the lifecycle fabric drives everything.

Supports Day-1 "why does the UI freeze?" and Day-2 "what does the
reduction actually do?" discussions.

Co-Authored-By: default avatarClaude Opus 4.7 (1M context) <noreply@anthropic.com>
parent 00a81b9e
Loading
Loading
Loading
Loading
+183 KiB
Loading image diff...
+195 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 1080" width="1920" height="1080" font-family="Helvetica, Arial, sans-serif">
  <defs>
    <linearGradient id="headerGrad" x1="0" x2="0" y1="0" y2="1">
      <stop offset="0%" stop-color="#002E5D"/>
      <stop offset="100%" stop-color="#004B8D"/>
    </linearGradient>
    <linearGradient id="uiLane" x1="0" x2="0" y1="0" y2="1">
      <stop offset="0%" stop-color="#FFF3E6"/>
      <stop offset="100%" stop-color="#FEE4CB"/>
    </linearGradient>
    <linearGradient id="cfgLane" x1="0" x2="0" y1="0" y2="1">
      <stop offset="0%" stop-color="#FFF8E7"/>
      <stop offset="100%" stop-color="#FAEBC0"/>
    </linearGradient>
    <linearGradient id="reduxLane" x1="0" x2="0" y1="0" y2="1">
      <stop offset="0%" stop-color="#EEF7FB"/>
      <stop offset="100%" stop-color="#D6ECF6"/>
    </linearGradient>
    <linearGradient id="mantidLane" x1="0" x2="0" y1="0" y2="1">
      <stop offset="0%" stop-color="#F0F7EE"/>
      <stop offset="100%" stop-color="#DDECD8"/>
    </linearGradient>
    <marker id="flowArrow" viewBox="0 0 12 9" refX="11" refY="4.5" markerWidth="9" markerHeight="7" orient="auto">
      <path d="M 0 0 L 12 4.5 L 0 9 z" fill="#002E5D"/>
    </marker>
    <filter id="softShadow" x="-10%" y="-10%" width="120%" height="120%">
      <feGaussianBlur in="SourceAlpha" stdDeviation="2"/>
      <feOffset dx="1" dy="2" result="offset"/>
      <feComponentTransfer><feFuncA type="linear" slope="0.22"/></feComponentTransfer>
      <feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
    </filter>
  </defs>

  <rect width="1920" height="1080" fill="#FAFAFA"/>

  <!-- Header -->
  <rect x="0" y="0" width="1920" height="100" fill="url(#headerGrad)"/>
  <text x="960" y="55" font-size="34" font-weight="700" fill="white" text-anchor="middle">A Peak Drag, Traced from Click to Plot</text>
  <text x="960" y="85" font-size="17" fill="#B8D4E8" text-anchor="middle">What happens in the 1–10 seconds between moving a peak line and seeing the new reflectivity</text>

  <!-- ─── Swim lanes ─── -->
  <rect x="40" y="130" width="280" height="735" rx="10" fill="url(#uiLane)" stroke="#E87722" stroke-width="2"/>
  <text x="180" y="162" font-size="18" font-weight="700" fill="#8C3A00" text-anchor="middle">Qt GUI layer</text>

  <rect x="330" y="130" width="480" height="735" rx="10" fill="url(#cfgLane)" stroke="#D2A000" stroke-width="2"/>
  <text x="570" y="162" font-size="18" font-weight="700" fill="#3A2C00" text-anchor="middle">Configuration / DataManager</text>

  <rect x="820" y="130" width="520" height="735" rx="10" fill="url(#reduxLane)" stroke="#4A90C2" stroke-width="2"/>
  <text x="1080" y="162" font-size="18" font-weight="700" fill="#002E5D" text-anchor="middle">Reduction back-end (runs on Qt thread!)</text>

  <rect x="1350" y="130" width="530" height="735" rx="10" fill="url(#mantidLane)" stroke="#6DA64F" stroke-width="2"/>
  <text x="1615" y="162" font-size="18" font-weight="700" fill="#1F5B1F" text-anchor="middle">Mantid (the reduction engine)</text>

  <!-- Step 1: user drags -->
  <g>
    <circle cx="90" cy="225" r="22" fill="#E87722" stroke="#8C3A00" stroke-width="2"/>
    <text x="90" y="232" font-size="20" font-weight="700" fill="white" text-anchor="middle">1</text>
    <text x="125" y="218" font-size="15" font-weight="700" fill="#002E5D">User drags peak line</text>
    <text x="125" y="238" font-size="13" fill="#3E2C00">on the detector plot.</text>
    <text x="125" y="255" font-size="12" fill="#606060" font-style="italic">mplwidget mouse event</text>
  </g>
  <path d="M 180 278 L 180 305" stroke="#002E5D" stroke-width="2.5" marker-end="url(#flowArrow)"/>

  <!-- Step 2: Qt signal -->
  <g>
    <circle cx="90" cy="340" r="22" fill="#E87722" stroke="#8C3A00" stroke-width="2"/>
    <text x="90" y="347" font-size="20" font-weight="700" fill="white" text-anchor="middle">2</text>
    <text x="125" y="333" font-size="15" font-weight="700" fill="#002E5D">Qt signal fires</text>
    <text x="125" y="352" font-size="12" fill="#606060" font-family="monospace">motion_notify_event</text>
    <text x="125" y="370" font-size="12" fill="#3E2C00" font-style="italic">MainHandler.changeRegionValues</text>
  </g>
  <path d="M 295 395 L 330 395" stroke="#002E5D" stroke-width="2.5" marker-end="url(#flowArrow)"/>

  <!-- Step 3: update_configuration_from_ui -->
  <g>
    <circle cx="360" cy="395" r="22" fill="#D2A000" stroke="#6F5500" stroke-width="2"/>
    <text x="360" y="402" font-size="20" font-weight="700" fill="white" text-anchor="middle">3</text>
    <text x="395" y="388" font-size="15" font-weight="700" fill="#3A2C00">update_configuration_from_ui()</text>
    <text x="395" y="406" font-size="12" fill="#3A2C00">Read every widget → write Configuration instance</text>
    <text x="395" y="422" font-size="12" fill="#3A2C00" font-style="italic">~40 widget.value() / isChecked() / currentIndex() calls</text>
  </g>
  <path d="M 570 445 L 570 475" stroke="#002E5D" stroke-width="2.5" marker-end="url(#flowArrow)"/>

  <!-- Step 4: DataManager.update_configuration -->
  <g>
    <circle cx="360" cy="510" r="22" fill="#D2A000" stroke="#6F5500" stroke-width="2"/>
    <text x="360" y="517" font-size="20" font-weight="700" fill="white" text-anchor="middle">4</text>
    <text x="395" y="497" font-size="15" font-weight="700" fill="#3A2C00">DataManager.update_configuration</text>
    <text x="395" y="515" font-size="12" fill="#3A2C00">Routes Configuration to every loaded NexusData.</text>
    <text x="395" y="532" font-size="11" fill="#8C3A00" font-style="italic">class-attributes of Configuration are NOT copied</text>
  </g>
  <path d="M 570 560 L 570 590" stroke="#002E5D" stroke-width="2.5" marker-end="url(#flowArrow)"/>

  <!-- Step 5: deepcopy -->
  <g>
    <circle cx="360" cy="625" r="22" fill="#D2A000" stroke="#6F5500" stroke-width="2"/>
    <text x="360" y="632" font-size="20" font-weight="700" fill="white" text-anchor="middle">5</text>
    <text x="395" y="611" font-size="15" font-weight="700" fill="#3A2C00">deepcopy × N cross-sections</text>
    <text x="395" y="629" font-size="12" fill="#3A2C00" font-family="monospace">self.configuration = copy.deepcopy(configuration)</text>
    <text x="395" y="647" font-size="11" fill="#C8102E" font-style="italic">2–4 deepcopies per peak drag · ~1–5 ms each</text>
  </g>
  <path d="M 795 720 L 820 720" stroke="#002E5D" stroke-width="2.5" marker-end="url(#flowArrow)"/>

  <!-- Step 6: calculate_reflectivity -->
  <g>
    <circle cx="850" cy="720" r="22" fill="#4A90C2" stroke="#002E5D" stroke-width="2"/>
    <text x="850" y="727" font-size="20" font-weight="700" fill="white" text-anchor="middle">6</text>
    <text x="885" y="706" font-size="15" font-weight="700" fill="#002E5D">NexusData.calculate_reflectivity(…)</text>
    <text x="885" y="724" font-size="12" fill="#1F3A5F">Groups workspaces; builds ~35 MRR kwargs;</text>
    <text x="885" y="741" font-size="12" fill="#1F3A5F">applies <tspan font-family="monospace">_as_ints</tspan> (one of two local variants).</text>
  </g>
  <path d="M 1315 760 L 1350 760" stroke="#002E5D" stroke-width="2.5" marker-end="url(#flowArrow)"/>

  <!-- Step 7: MRR.PyExec -->
  <g>
    <circle cx="1380" cy="760" r="22" fill="#6DA64F" stroke="#1F5B1F" stroke-width="2"/>
    <text x="1380" y="767" font-size="20" font-weight="700" fill="white" text-anchor="middle">7</text>
    <text x="1415" y="744" font-size="15" font-weight="700" fill="#002E5D">MagnetismReflectometryReduction.PyExec</text>
    <text x="1415" y="762" font-size="12" fill="#1F3A1F">859 LOC Python, per cross-section executes:</text>
    <text x="1415" y="780" font-size="11.5" fill="#1F5B1F" font-family="monospace">Rebin · RefRoi (×2) · Minus · NormaliseByCurrent ·</text>
    <text x="1415" y="796" font-size="11.5" fill="#1F5B1F" font-family="monospace">Divide · ConvertToPointData · Rebin · cleanup</text>
  </g>
  <path d="M 1610 810 L 1610 835 L 885 835 L 885 830" stroke="#002E5D" stroke-width="2.5" fill="none" marker-end="url(#flowArrow)"/>

  <!-- Step 8: CrossSection caches -->
  <g>
    <circle cx="850" cy="820" r="22" fill="#4A90C2" stroke="#002E5D" stroke-width="2"/>
    <text x="850" y="827" font-size="20" font-weight="700" fill="white" text-anchor="middle">8</text>
    <text x="885" y="805" font-size="15" font-weight="700" fill="#002E5D">CrossSectionData caches Q, R, dR</text>
    <text x="885" y="823" font-size="12" fill="#1F3A5F">Applies QuickNXS compat scale factor</text>
    <text x="885" y="841" font-size="11.5" fill="#8C3A00" font-style="italic">— with +1.0 on this path, without on the CSD path (~1% drift)</text>
  </g>
  <path d="M 820 850 L 320 850 L 320 440" stroke="#002E5D" stroke-width="2.5" fill="none" marker-end="url(#flowArrow)"/>

  <!-- Step 9: plot redraws -->
  <g>
    <circle cx="90" cy="455" r="22" fill="#E87722" stroke="#8C3A00" stroke-width="2"/>
    <text x="90" y="462" font-size="20" font-weight="700" fill="white" text-anchor="middle">9</text>
    <text x="125" y="441" font-size="15" font-weight="700" fill="#002E5D">Plot widget redraws</text>
    <text x="125" y="460" font-size="12" fill="#3E2C00" font-style="italic">matplotlib canvas update</text>
    <text x="125" y="477" font-size="11" fill="#606060">(UI thread finally unblocked)</text>
  </g>

  <!-- Time / cost banner inline -->
  <g transform="translate(40, 880)">
    <rect x="0" y="0" width="1840" height="44" rx="8" fill="#002E5D" stroke="#001A35" stroke-width="1.5"/>
    <text x="20" y="28" font-size="14.5" fill="#F5C13A" font-weight="700">Cost of one peak-drag:</text>
    <text x="220" y="28" font-size="13" fill="#E0ECF5">2–4 × <tspan font-family="monospace" fill="white">deepcopy(Configuration)</tspan>  ·  one full MRR.PyExec per cross-section (N=2 or 4)  ·  all synchronous on the Qt main thread  ·  on a slow FUSE mount: <tspan fill="#F5C13A" font-weight="700">1–10 seconds of frozen UI per drag</tspan></text>
  </g>

  <!-- ─── Bottom "where the debt accumulates" 5-column banner ─── -->
  <g transform="translate(40, 935)">
    <text x="920" y="20" font-size="18" font-weight="700" fill="#8C3A00" text-anchor="middle">Where the debt accumulates — five patterns scientists perceive as "Qt leaking into the reduction"</text>

    <g transform="translate(0, 32)">
      <rect x="0" y="0" width="360" height="95" rx="6" fill="#FEF4EE" stroke="#E87722" stroke-width="1.2"/>
      <text x="15" y="22" font-size="13.5" font-weight="700" fill="#002E5D">●  Synchronous reduction</text>
      <text x="15" y="42" font-size="11.5" fill="#3E2C00">Steps 6–7 run on the Qt event-loop thread.</text>
      <text x="15" y="58" font-size="11.5" fill="#3E2C00">No QThread, no async — the UI freezes during</text>
      <text x="15" y="74" font-size="11.5" fill="#3E2C00">every reduction.</text>

      <rect x="370" y="0" width="360" height="95" rx="6" fill="#FEF4EE" stroke="#E87722" stroke-width="1.2"/>
      <text x="385" y="22" font-size="13.5" font-weight="700" fill="#002E5D">●  Deepcopy chain</text>
      <text x="385" y="42" font-size="11.5" fill="#3E2C00">Configuration copied on every UI event.</text>
      <text x="385" y="58" font-size="11.5" fill="#3E2C00">Class attributes bypass the copy — still</text>
      <text x="385" y="74" font-size="11.5" fill="#3E2C00">shared globals across runs.</text>

      <rect x="740" y="0" width="360" height="95" rx="6" fill="#FEF4EE" stroke="#E87722" stroke-width="1.2"/>
      <text x="755" y="22" font-size="13.5" font-weight="700" fill="#002E5D">●  Class-attribute globals</text>
      <text x="755" y="42" font-size="11.5" fill="#3E2C00">22 "global options" live on the class itself.</text>
      <text x="755" y="58" font-size="11.5" fill="#3E2C00">Changing one in the UI retroactively</text>
      <text x="755" y="74" font-size="11.5" fill="#3E2C00">affects every loaded run.</text>

      <rect x="1110" y="0" width="360" height="95" rx="6" fill="#FEF4EE" stroke="#E87722" stroke-width="1.2"/>
      <text x="1125" y="22" font-size="13.5" font-weight="700" fill="#002E5D">●  Qt signal timing</text>
      <text x="1125" y="42" font-size="11.5" fill="#3E2C00" font-family="monospace">valueChanged</text>
      <text x="1220" y="42" font-size="11.5" fill="#3E2C00"> vs </text>
      <text x="1250" y="42" font-size="11.5" fill="#3E2C00" font-family="monospace">editingFinished</text>
      <text x="1125" y="58" font-size="11.5" fill="#3E2C00">fire at different moments. Changing which</text>
      <text x="1125" y="74" font-size="11.5" fill="#3E2C00">signal is connected can change results.</text>

      <rect x="1480" y="0" width="360" height="95" rx="6" fill="#FEF4EE" stroke="#E87722" stroke-width="1.2"/>
      <text x="1495" y="22" font-size="13.5" font-weight="700" fill="#002E5D">●  Widget lifecycle</text>
      <text x="1495" y="42" font-size="11.5" fill="#3E2C00">Signals can fire before a widget is ready.</text>
      <text x="1495" y="58" font-size="11.5" fill="#3E2C00">Row index −1 → negative-index corruption.</text>
      <text x="1495" y="74" font-size="11.5" fill="#3E2C00">One SEGV fix committed 2026-03-21.</text>
    </g>
  </g>

  <!-- Footer -->
  <rect x="0" y="1055" width="1920" height="25" fill="#001A35"/>
  <text x="40" y="1073" font-size="12" fill="#6A90B0">Sources: 12-parameter-propagation-and-qt-leakage.md §1–§5 · quicknxsv2 src/quicknxs/interfaces/event_handlers/main_handler.py · data_handling/data_set.py</text>
  <text x="1880" y="1073" font-size="12" fill="#6A90B0" text-anchor="end">Reflectometry Hack-a-thon 2026 · slide 5 of 8</text>
</svg>