Skip to content

Architecture

How the internals fit together. This mirrors the developer notes in CLAUDE.md / AGENTS.md.


Startup order (critical)

main.py enforces a strict import order:

  1. multiprocessing.freeze_support() — must be first (Windows PyInstaller requirement).
  2. apply_patches() from app/patches.py — must run before any dara import.
  3. Ray initialization with tuned memory / CPU caps.
  4. MainWindow launch.

Violating this order causes crashes or incorrect behaviour. The MCP server mirrors the same order (with Ray initialized lazily on the first job).


Monkeypatching (app/patches.py)

Four dara internals are patched before any dara module is imported:

  • dara.xrd.get_xrdml_data — fixes XRDML parsing for newer file schemas.
  • dara.bgmn.download_bgmn.download_bgmn — redirects the BGMN binary download to a writable temp dir.
  • BGMNWorker.__init__ / EflechWorker.__init__ — points both refinement engines to that writable dir.

This is required because dara's default paths are not writable in PyInstaller bundles.


Analysis pipeline (app/analysis_worker.py)

AnalysisWorker(QThread) runs the entire analysis off the main thread:

  1. Patches requests.get (mirror fallback), CODDatabase.download_structures (concurrent downloads, failure resilience) and dara.search.tree.batch_refinement (Ray task throttling, progress tracking, deepcopy GC fix) — all applied inline for one run and restored in finally.
  2. CIF acquisition — checks the persistent per-chemical-system cache and skips COD download if CIFs already exist.
  3. Pattern isolation — copies the input to a fresh temp dir as pattern.xy.
  4. search_phases() — the core search / refinement call; returns ranked Solution objects.
  5. Packaging — the top solutions become dicts with fit metrics, phase weight fractions, profile / peaks tables, raw .lst, and structural data.
  6. Emits results_ready with the list of result dicts.

Ray is initialized once at startup and never shut down mid-session — a ray.shutdown() would kill the cluster for all subsequent runs.


UI layout

MainWindow (QMainWindow)
└── QSplitter (horizontal)
    ├── SidebarWidget   — inputs, file pickers, advanced params, run/cancel
    └── ResultsArea     — welcome page → one tab per ranked solution

Each result tab contains a Plotly chart, phase-weight bars, metric cards (Rwp / Rp / GoF / Durbin–Watson), a lattice-parameter table, a peaks table, and a raw .lst viewer with export.


Design system (app/theme.py)

All colours, fonts, and spacing constants live here — UI code never hardcodes values inline. Theming is token-based: THEMES holds three switchable palettes — A (Helios, light native — default), B (Graphite, dark instrument), and C (Mineral, light + scope) — read via tokens(), with set_theme() / cycle_theme() to switch.


Key design decisions

  • Ray throttlingbatch_refinement uses a sliding window (MAX_IN_FLIGHT = min(100, cpu_count * 4)) to prevent worker explosion with large phase counts. Results are deepcopy'd immediately to free Ray object store memory.
  • CIF cache — a per-chemical-system persistent cache avoids re-downloading from COD on repeated analyses of the same system.
  • Deferred temp-dir cleanup — a short delay before removing the temp dir lets orphaned Ray tasks referencing pattern.xy finish naturally without killing the cluster.