Skip to content

Releases

XinRefinement v1.6.2 — Stop button now actually stops

Choose a tag to compare

@PolyXRD PolyXRD released this 09 Nov 10:11

XinRefinement v1.6.2 — Stop button now actually stops

Baseline v1.6.1 | Theme: "Stop" taking effect only after the current step finishes is an implementation defect. After triggering one-click run-all, wanting to abort, you press stop but have to wait a long time—not because it computes slowly, but because the cancel flag is only checked between steps, and all the multiple-round fits inside a single step run to completion before returning to the loop top.

Numerics unchanged: golden 50/50 still 3.4074156640940005, CLI/API bit-identical.

Download

File Size SHA256
XinRefinement-1.6.2-setup.exe 75.9 MB 135cb1e87cf47522c25ba903f28cee244d83d8043b760ba13f6f6ee214c93b2f

Release installer (includes registration mechanism, zh/en/ja trilingual install UI). The internal evaluation portable package (XinRefinement-internal-1.6.2-portable.zip, 114.2 MB, sha256 c51f786881c1ed8d965b27b31c9d406e2b51a648f6246a45a84f3f09e2abced1) is for internal acceptance only and is not published with this Release.

The change at a glance

v1.6.1 v1.6.2
Press "Stop" Wait for current step to fully finish (cell step measured 19.8 s) Interrupt at next residual evaluation (~16 ms)
What cancel is recorded as May generate a "step failed" record + false alarm box Pass cancel through, log writes "Refinement stopped by user", no failed record left
Progress bar Stuck at "Executing 'Microstructure'…" Changed to "Stopped"
Already-computed results Discarded Partial results refilled as normal (pattern, Rwp, each results tab)

Why it was slow (root cause)

RefineWorker.run()'s loop is:

for step in todo:
    if self._cancel:        # ← only here checks the stop flag
        break
    result, rec = self._runner.run_step(step, ...)

So the moment stop takes effect = the moment the current step finishes, and a single step internally often does more than one fit:

Step What it internally actually does
Unit cell parameters solve → refine full cell → solve again with new cell (3 fits)
Microstructure PO probe (per phase × per candidate axis each a short fit) → formal solve → re-solve
Peak-shape parameters up to 4 LM (Rwp>10% always triggers restart chain)

More critically: the engine already had fine-grained hooks RefineConfig.cancel_cb + _check_cancel() (placed at the residual-function entry, covering both the in-house LM and scipy backends), but the GUI never wired them up—only one test in the whole repo used them, effectively dead code.

What changed

1. Three residual closures wired up to the cancel hook

  • Main solve: already had it.
  • Unit-cell engine refine_cell_full: independent residual closure, previously had no check at all—the cell step is one of the slowest in the chain, the place that most needed to be fast was instead the slowest.
  • Coordinate engine refine_sites: numerical Jacobian each iteration does O(n_free) "multiphase whole-pattern + linear layer" evaluations, so the xyz step can also stop instantly.

2. Recipe layer no longer records "cancel" as "failure"

Each engine call in the recipe was wrapped in except Exception → StepRecord("failed")—correct for genuine numeric failures (continue to next step), but wrong for user cancel. Now the 7 fallbacks first pass through RefineCancelled; run_all catches it inside the step and returns the completed portion (run-all semantics is "stop at the last completed step", not throwing the exception to the caller).

3. UI finishing

  • The cancel callback is hung into inputs.config, and via config_for's replace() automatically carries to each step's config.
  • After cancel: progress bar "Stopped", log appends "=== Refinement stopped by user (interrupted at residual evaluation) ===", already-computed partial results refilled as normal (pattern + Rwp + each results tab), and no failed-alarm box pops up.

Validation

  • Full regression 532 cases: 532 passed / 0 failed / 0 skipped, EXIT=0 (v1.6.1 was 527, added tests/test_v162_cancel.py 5 cases).
  • golden CLI Rwp 50/50 3.4074156640940005 unchanged (cancel_cb=None keeps the engine bit-identical, guarded by a dedicated test).
  • New tests guard behavior: cancel stops at the Nth residual evaluation (calls == 3), stop latency < 0.5 s, Rwp bit-identical with no callback, unit-cell engine independently effective, run-all cancel leaves no status='failed' fake record.
  • Also fixed a "happened to hold" existing assertion: test_serialize_uses_relative_paths originally asserted "same drive letter → relative path", but _rel() is the prefix semantics of relative_to, and it only passed because pytest's tmp happened to be on C: and the project on D:. Changed to test the real contract.

Build and Reproducibility

This release did a reproducibility check on the build chain, with conclusions in two layers:

  • PyInstaller artifacts (.exe layer) are byte-reproducible: after fixing SOURCE_DATE_EPOCH (to git commit time) and PYTHONHASHSEED=0, two builds of the same commit yield completely identical XinRefinement.exe / XinRefinementCLI.exe hashes (measured twice, including different --distpath). So the source version can be traced back from the artifact.
  • Installer (Inno Setup layer) is not byte-reproducible: same input, same SOURCE_DATE_EPOCH, two packagings yield same size, different bytes (PE header timestamp comes from the compiler stub, unaffected by epoch). So the SHA256 in the table above is valid only for this one asset of this Release, not representing "anyone rebuilding gets the same hash string"; a same-source rebuild's installer hash will differ, but the two .exe inside can be byte-matched.
  • One process constraint: SOURCE_DATE_EPOCH takes the HEAD commit time at build, so commit first, build after. If built before commit, the PE timestamp in the artifact points to the previous commit—content unaffected, but the source version can't be traced back from the artifact, and the rebuild hash won't match.
  • How this asset was actually built: built after 06b115c (the release commit pointed to by tag v1.6.2), with SOURCE_DATE_EPOCH=1794219055 fixed to that commit's commit time; inside the artifact XinRefinement.exe = 2b2436de…, XinRefinementCLI.exe = 16c99cb3…, both byte-reproducible from the same source.

Known Limitations

  • Stop is cooperative: the engine checks the flag at the residual evaluation, so the in-progress single evaluation will finish (sample 1 ~16 ms; longer for more phases and points). No thread hard-kill—that would leave numpy/BLAS internal state and temporary caches unreclaimable.
  • Sample 7 still not meeting the bar (+3.39 pt, cristobalite near-amorphous broad-packet starting-point problem), this release did not touch the engine.

Installation and Registration

Same as v1.6.1: installer embeds zh/en/ja trilingual UI, registration code interoperable with the XinMatch family (%APPDATA%\PolyXRD\Registration\XinRefinement.json). Internal evaluation build needs no registration or install, marked in red in the status bar.

License

This software uses the MIT license (LICENSE / LICENSE-CN, viewable in full from the help menu). Built-in crystal structure data comes from COD (CC0) and ICSD-derived public literature, containing no PDF cards or ICDD data.

XinRefinement v1.6.1 — Pattern interaction toolbar and license information

Choose a tag to compare

@PolyXRD PolyXRD released this 09 Nov 02:34

XinRefinement v1.6.1 — Pattern interaction toolbar and license information

Baseline v1.6.0 | Theme: give the pattern an operating console. v1.6.0 solved "how to present results", but the pattern itself was still read-only—only matplotlib's built-in row of English icons, squeezed below the canvas; zooming into an angle range meant scroll-wheel trial and error, measuring a peak position meant screenshot comparison. This release has six minor UI tweaks and adds full copyright and license information to the help page.

Numerics unchanged: golden 50/50 still 3.4074156640940005, CLI/API behavior bit-identical.

Download

File Size SHA256
XinRefinement-1.6.1-setup.exe 75.8 MB 34cb1288a16d282e1aa87d8a9446e40996225c92a5dcffd9a5cfa92dcef49b9d

Release installer (includes registration mechanism, zh/en/ja trilingual install UI). The internal evaluation portable package (XinRefinement-internal-1.6.1-portable.zip, 114.2 MB, sha256 64afe606ebc5f6bd11811a243dad16dec54f76bace9c607dbbe04324f29658de) is for internal acceptance only and is not published with this Release.

The change at a glance

v1.6.0 v1.6.1
Quick-icon position Squeezed below the pattern, chip 36 Moved above the pattern, chip 30 (one size smaller)
Pattern operations Only matplotlib's built-in pan/zoom Added a 9-key dedicated toolbar: reset / horizontal box / vertical box / rectangle box / pan left / pan right / zoom in / zoom out / crosshair
Measure peak position Compare with a ruler after screenshot Crosshair red vertical line follows the cursor reading (angle + intensity), right-click to drop a manual peak position, right-click again to delete
Status bar "internal evaluation build" Right-aligned, small, grey Centered, one size larger, red bold
Bottom-left title "Refinement Log (run-code)" "Refinement Log"
Help menu Only "About…" (one-line summary) Added "Copyright & License…", opening a full license text dialog
Step gears All same color Eight colors to distinguish (one color per step + tooltip)

Main Changes

1. Pattern toolbar (9 keys)

Layout: middle column top-to-bottom = matplotlib navigation bar → pattern toolbar → pattern canvas.

# Button Behavior
① Original pattern Restore initial viewport (including d-mode reversed axis direction)
② Horizontal box Drag an angle interval, changes horizontal axis only
③ Vertical box Drag an intensity interval, changes vertical axis only
④ Rectangle box Drag a rectangle, changes both axes
⑤ Pan left Viewport moves left by 1/4 width
⑥ Pan right Viewport moves right by 1/4 width
⑦ Zoom in Anchored at viewport center, zoom in stepwise
⑧ Zoom out Same as above reversed
⑨ Crosshair Red vertical line follows mouse, top-right shows 2θ = …° · I = …; right-click drops a manual peak position

Key points:

  • ②③④ are three clearly-divided tools, not "box-select presses both axes": when only pressing the horizontal axis, the vertical axis auto-fits the current viewport, otherwise zooming a weak peak still gets flattened by the whole-pattern's highest peak, looking like the button did nothing.
  • ⑤⑥⑦⑧ are discrete operations: pan by a fixed fraction of viewport width, zoom anchored at viewport center, not taking mouse position (clicking the button, where the mouse is has no semantics).
  • ⑨ manual peak positions shown as red dashed line + vertical angle label; right-click again within 0.25° of a drop point deletes it. The vertical line disappears after exiting the crosshair tool, manual peak positions are kept. When switching patterns (loading new data), both are cleared together—keeping the previous sample's annotations is more misleading than none.
  • Pattern operations and matplotlib's built-in pan/zoom are mutually exclusive (two drag systems fight for the same mouse event); click the same button again to exit, returning the original axis-click semantics.

2. Navigation bar shrunk one size and moved above the pattern

NavigationToolbar2QT moved from below the canvas to above, chip 36 → 30, iconSize 24 → 20, color scheme unchanged. So the two rows of "pattern operations" sit together, no need to search across the canvas.

3. Status bar "internal evaluation build" centered, larger, red bold

  • AlignRight → AlignCenter, font size +1 and bold, color #d32f2f.
  • Still on the status bar's permanent section: crosshair reading and axis hints both output via statusBar().showMessage(); ordinary widgets get temporarily pushed out, only the permanent section survives.

4. Bottom-left only writes "Refinement Log"

Title drops "(run-code)", synced across zh/en/ja. Window behavior unchanged.

5. Copyright & license in the help page

  • Help menu adds "Copyright & License…" (above "About…", with a separator), opening LicenseDialog: license summary + read-only full text (monospace, scrollable).
  • The "About" dialog also gets the same button entry.
  • Full text is read from disk at runtime (licenses/LICENSE / licenses/LICENSE-CN), not duplicated in code—the two texts would inevitably drift. The source tree points to the two repository-root files; the installer bundles them via the packaging script; when not found, it prompts "license file missing" rather than failing to open the dialog.

6. Step gears in eight colors

Zero / background / scale / unit cell / peak shape / coordinates / microstructure / temperature factor each a color, font size 16 bold, tooltip gives the step name. Color is only recognition aid, not encoding state (whether run, whether converged still read elsewhere), avoiding the misreading "red means failed".

Validation

  • Full regression 527 cases: 527 passed / 0 failed / 0 skipped, EXIT=0 (v1.6.0 was 512, added tests/test_v161_gui.py 15 cases).
  • golden CLI Rwp 50/50 3.4074156640940005 unchanged, confirming this release did not touch the engine.
  • Canvas behavior measured offscreen: xlim (5.0, 89.98) → zoom out to (26.245, 68.735) → pan right 1/4 → (36.8675, 79.3575) → reset back to (5.0, 89.98); horizontal box-select drag 20→40 gives (20.0, 40.0) and the rubber band disappears on release; crosshair at 25.3° reads 2θ = 25.300° · I = 39728, right-click produces [25.3], right-click again returns [].
  • Built-artifact check: XinRefinementCLI.exe --version = XinRefinement 1.6.1; frozen package's _internal/licenses/ confirmed to contain LICENSE (2411 B) and LICENSE-CN (1887 B), so the license dialog in the installed build reads the full text, not just the summary.

Known Limitations

  • Sample 7 still not meeting the bar (+3.39 pt, cristobalite near-amorphous broad-packet starting-point problem), this release did not touch the engine.
  • Pattern operations are a view-layer feature: zoom/pan/crosshair/manual peak positions do not enter the results table, do not enter the report, do not participate in fitting, and are not saved to the project file.
  • Unit-cell component checkboxes only affect the cell step; the profile step's joint refinement still releases fully by crystal system (same as v1.6.0).

Installation and Registration

Same as v1.6.0: installer embeds zh/en/ja trilingual UI, registration code interoperable with the XinMatch family (%APPDATA%\PolyXRD\Registration\XinRefinement.json). Internal evaluation build needs no registration or install, marked in red in the status bar.

License

This software uses the MIT license (LICENSE / LICENSE-CN, viewable in full from the help menu). Built-in crystal structure data comes from COD (CC0) and ICSD-derived public literature, containing no PDF cards or ICDD data.

XinRefinement v1.6.0 — UI overhaul and unit-cell checkboxes now actually work

Choose a tag to compare

@PolyXRD PolyXRD released this 08 Nov 21:36

XinRefinement v1.6.0 — UI overhaul and unit-cell checkboxes now actually work

Baseline v1.5.0 | Theme: engine untouched, make the results clear. v1.5.0 chased fit quality to MAUD's +0.21 pt, but the UI still organized information the v1.0 way: phases shown by CIF filename (1Entry_96-500-0036), bottom-left a string of static placeholder rows, click ▶ with no idea what this step refines, quantification and unit-cell parameters only findable row by row from the summary table. This release reworks those four areas, and fixes a functional defect left over from v1.5—the row of checkboxes in the unit-cell popup never got passed to the engine.

Numerics unchanged: golden 50/50 still 3.4074156640940005, CLI/API behavior bit-identical.

Download

File Size SHA256
XinRefinement-1.6.0-setup.exe 75.8 MB c35fceaa37dafa9ebf14f48ca55d7bc62e0c87a86f6fcb44e5230f25c1aa17f2

Release installer (includes registration mechanism, zh/en/ja trilingual install UI). The internal evaluation portable package (XinRefinement-internal-1.6.0-portable.zip, 114.2 MB, sha256 5fb97ea234732a3f825cc4e60806b5d9a5ae470b80acdc7edb7e1a6c0044c439) is for internal acceptance only and is not published with this Release.

The change at a glance

v1.5.0 v1.6.0
How phases display CIF filename (1Entry_96-500-0036) chemical formula · phase name (TiO2 · Anatase), three modes selectable, formula default
Bottom-left run-log area Static placeholder rows MAUD-style scrolling log: start line / param: old → new / done · Rwp a → b
Click nav ▶ Starts running directly, no word on what it refines First pops a step description window (this step's params + current values + explanation), then writes the same content to the log on confirm
Marker-bar label CIF filename Display name, right-click to switch formula/phase name/filename
Results area Summary / convergence / correlation three tabs Added phase quantification (5 columns, total row) and unit cell & microstructure (11 columns)
Unit-cell popup checkboxes Decorative (checked but no effect, non-a/c all greyed) Actually work: RefineConfig.cell_axes → engine; greyed by crystal system with reason
Edit→Phase Parameters Single column vertical, buttons pushed out when multiphase Two-column grid + scroll area, min width 880

Main Changes

1. Phase display, three modes (formula default)

New core/profile/naming.py: formula (default) / label / file.

  • Formula normalization does only two safe things: compactify (O2 Si → SiO2) and regroup within group by "metal → non-metal → anion" with order-preserving then regrouping (O4 S Zn → ZnSO4, Co0.1 Li1.03 Ni0.77 Co0.1 Mn0.1 O2 → Co0.2Li1.03Ni0.77Mn0.1O2). Does not change any element count, nor does electronegativity sorting (that would disrupt familiar order of multi-metal systems like NCA).
  • CifStructure gains name_mineral: parse_cif takes the first non-empty of _chemical_name_mineral → _chemical_name_common → _chemical_name_systematic.
  • Three-level fallback (formula → label → file) guarantees any CIF has a displayable string.
  • Auto-distinguish same-name polymorphs: anatase/rutile/brookite all have formula TiO2; in the log two TiO2.a can't tell which is moving → append phase name when display names collide.

2. Bottom-left run-log window (MAUD-style scrolling log)

Read-only QPlainTextEdit, NoWrap, max 4000 lines (long chains don't grow memory unboundedly), auto-scroll to bottom, with a "Clear log" button. Three line types:

=== Start "Unit Cell Parameters" ===
  Initial parameters:
  zero: 0.0299
  TiO2 · Anatase.a: 3.785
  ...
  TiO2 · Anatase.a: 3.785 → 3.78215
=== "Unit Cell Parameters" done · Rwp 7.1816 → 6.9034 ===
  • Parameter lines filtered by relative 1e-9: floating-point jitter doesn't spam the screen, real changes always print.
  • When the first snapshot has no "previous frame" to compare, print the initial parameter list—otherwise the log shows only one Rwp line and you can't tell what this round refined.
  • Unit cells flattened per phase into keys like TiO2 · Anatase.a, so which phase and which axis is moving is visible at a glance.

3. Navigation step description window (corresponds line-by-line with the run-log window)

Click any ▶ first pops a window, then writes the same content to the log before starting. Each of the eight steps has a checklist: zero gives current value, background gives mode/node count/peak-shape baseline, scale gives per-phase S, unit cell gives the components actually refined per phase, peak shape gives U/V/W/η/asym, xyz gives over-budget strategy, micro gives X/Y/anisotropy/texture, B gives ΔB_iso and the 6 Ų upper bound. Button label is "▶ Start Refinement" not "OK".

4. Results area gains two tabs

  • Phase Quantification: display name / wt% / e.s.d. / Z / unit-cell volume, with a total row at the end (QPA theoretically normalizes to 100, the total row is the fastest consistency check).
  • Unit Cell & Microstructure: display name / refined components / a b c α β γ / V / crystallite size D / microstrain ε, laid out across 11 columns.
  • Anisotropic phases: coefficients are Cholesky 3×3, the UI takes the mean of singular values (volume average) converted to the same dimension as isotropic Å / ε, and marks the D column with *; when aniso exists, the scalar X/Y are no longer read (after micro step enables Popa LB, scalar is no longer refined; reading X/Y would always show initial values).

5. Fix: the unit-cell popup checkboxes were decorative (v1.5 leftover defect)

  • Symptom: non-a/c checkboxes all setEnabled(False) with note "all 6 parameters opened by P1.2"—that statement long expired; worse, _settings["cell"] was never passed to the engine, so refinement always released fully by crystal system, making the checkboxes purely decorative.
  • Fix: RefineConfig.cell_axes (phase name → allowed components) → recipe._run_cell → refine_cell_full(axes_override=...) → apply_cell_scales(axes=...). The convention is uniformly given by resolve_axes(system, requested) = crystal-system-allowed ∩ user-checked, illegal axes silently dropped; when None (never set), bit-identical to v1.5.
  • Popup reworked: one group box per phase, 3×2 checkbox grid, two columns side by side (four phases no longer need to scroll 24 rows); non-free axes greyed by crystal system with reason tooltip (cubic frees only a; tetragonal/hexagonal/trigonal free a, c; monoclinic adds β; triclinic frees all).
  • End-to-end: check only a → 3.785 → 3.78215, c stays 9.5196; recipe message changed from / 2 params to / 1 param (previously counted by full crystal-system release).

6. Fix: Edit→Phase Parameter Editor layout

Two-column grid (unit cell / preferred orientation / texture & anisotropy / microstrain / symmetry two groups side by side) + QScrollArea + min width 880: bottom buttons no longer pushed out of the window when multiphase. Phase-select combo uses display names.

Validation

  • Full regression 512 cases: 512 passed / 0 failed / 0 skipped, EXIT=0 (v1.5.0 was 471; added test_v16_naming_and_cell_axes.py 17 cases, test_v16_gui.py 20 cases, plus 4 cases that no longer skip after installing gemmi locally).
  • golden CLI Rwp 50/50 3.4074156640940005 unchanged; axes_override=None path has a dedicated test guarding "bit-identical".
  • Built artifact actually runs: XinRefinementCLI.exe refine … --recipe --time-budget-min 3 produces a report normally (5.8391%); frozen package confirmed to contain the three new modules naming / cell / dialogs.
  • Also fixed two test issues masked by skip (started really running after installing gemmi): test_joint_s1's golden constant stopped before M11 (now measured same as golden), test_xyz_phase_select's bit-identical assertion differed by 8e-11 in full run (BLAS multi-threaded reduction not bit-reproducible, the strategy itself not in the numeric path) → aligned and relaxed respectively, see CHANGELOG.md.

Known Limitations

  • Sample 7 still not meeting the bar (+3.39 pt, cristobalite near-amorphous broad-packet starting-point problem), v1.6 did not touch the engine, leaving this for a later dedicated effort.
  • Acceptance criterion changed basis: project proposal §5 item 6 changed to "same-dataset mean difference ≤ 1 pt and no single-sample difference > 4 pt"; the old per-sample ≤ MAUD+2 is demoted to diagnostic reference.
  • Unit-cell component checkboxes only affect the cell step; the profile step's joint refinement still releases fully by crystal system (it's a different degree-of-freedom set; mixing them would distort the parameter-correlation diagnosis).

Installation and Registration

Same as v1.5.0: installer embeds zh/en/ja trilingual UI, registration code interoperable with the XinMatch family (%APPDATA%\PolyXRD\Registration\XinRefinement.json). Internal evaluation build needs no registration or install, marked in red in the status bar.

License

This software uses the MIT license. Built-in crystal structure data comes from COD (CC0) and ICSD-derived public literature, containing no PDF cards or ICDD data.

XinRefinement v1.5.0 — MAUD-comparable fit quality

Choose a tag to compare

@PolyXRD PolyXRD released this 07 Nov 15:46

XinRefinement v1.5.0 — MAUD-comparable fit quality

Baseline v1.4.0 | Theme: turn "comparable to MAUD" from an evaluation conclusion into a product capability, and trace the remaining gaps on 7 real samples down to root causes one by one. This release adds no new UI modules; what changed is the default behavior and guardrails of the fitting engine—same data, same set of CIFs, the resulting Rwp is lower, and lower with physical justification.

Download

File Size SHA256
XinRefinement-1.5.0-setup.exe 75.8 MB 429adb70eaa8df4754c9607ecdc9826f641582ace798f3ae9c7575e1e07ffdc0

Release installer (includes registration mechanism, zh/en/ja trilingual install UI). The internal evaluation portable package is for internal acceptance only and is not published with this Release.

The change at a glance

v1.4.0 v1.5.0
7-sample mean Rwp 14.38% 11.42% (MAUD 11.20%, gap 3.18 → 0.21 pt)
Microstructure anisotropy Manual coefficient per phase GUI on by default + two guardrails
Texture EWIMV discrete ODF Added spherical harmonics (Legendre) ODF, GUI on by default
xyz underdetermined Whole-step marked failed (4 sample coords none refined) Phase-wise downgrade, only when over budget
Long-sample time No cap (>1 h for ill-conditioned samples) Recipe wall-clock budget, GUI default 45 min
Pure corundum standard 28.58% 13.64% (beats GSAS-II's 15.8%)
Polymorph mismatch Only warned in log Results panel shows clickable button "Try α-cristobalite and re-refine"

Main Changes

Engine: three "false minimum" fixes (the biggest gain class this release)

  1. M10/M10b stepwise projection: cell and profile steps freeze each other's degrees of freedom, so the zero-point offset has nowhere to go and gets absorbed by the unit cell and asymmetry separately. The profile step is changed to joint refinement (cell+UVW+η+asym+zero), bringing single-phase pure corundum 28.58% → 14.63%. Also changed the "revert if no improvement" in micro/B steps to apply unconditionally.
  2. M11 window-boundary peak leakage: reflection enumeration strictly filters by window, so a strong peak entirely outside the window is discarded wholesale, yet its tail inside the window should contribute intensity (measured: a 140–150° segment ate 18.8% of the whole-pattern weighted residual). After expanding enumeration by 3°, pure corundum 14.63% → 13.64%. This is a general systematic-bias fix.
  3. Structure-factor vectorization: compute_peaks triple-vectorized, 26–37× speedup with bit-identical numerics (guardrail tests/test_peaks_vectorized.py).

Microstructure: M4 Popa LB per-phase anisotropy (GUI on by default)

Per-phase size/strain anisotropic broadening. In the 7-sample shootout, sample 1 −3.49 pt, sample 7 −3.81 pt—the largest single gain of v1.5. Default-on requires two guardrails:

  • Identifiability: phases with fewer than 8 independent crystallographic directions are skipped automatically (otherwise 6 parameters cannot be identified);
  • Revert entirely if no improvement: if Rwp does not drop after stacking, revert to the previous step's parameters.

CLI / library API still default off (--aniso-auto to enable explicitly), ensuring existing scripts get bit-identical results.

Texture: M3 spherical-harmonic ODF (GUI on by default, order 10)

Under fiber symmetry the exact subspace of spherical harmonics T(χ)=1+Σ c_l·P_l(cosχ), even orders 2–10. Versus EWIMV, same accuracy with 4–15 fewer degrees of freedom, and the basis is orthogonal, spherical mean identically 1 (structurally eliminates the scale null space), no second-difference penalty needed. CLI --texture-model harmonic --texture-degree N.

The measured conclusion is written into the project proposal: order matters more than model family, and part of EWIMV's Rwp advantage comes from overfitting (sample 3's T(χ) fitted into a quasi-binary mask). "Copy MAUD's per-phase Harmonic toggle" was disproven by supplementary-phase experiments and not productized.

Quantification: M9 xyz step phase-wise downgrade

v1.4 summed the free coordinates of all phases and compared to xyz_max_params=40, so samples 4/5/6/7's xyz steps all failed—not a single coordinate refined, yet two of them were the largest-gap samples. Changed to phase-wise downgrade (descending greedy retention, other phases fixed-structure), only when over budget:

Sample Before downgrade After downgrade
5 17.204 16.026
6 12.603 12.207
7 17.193 16.930

Diagnosis: sample 7's gap is cristobalite polymorph, not peak shape or background

Sample 7's cristobalite was given the high-temperature β (Fd-3m), but the sample is actually low-temperature α (P4₁2₁2): incremental basis 18.06 → 15.59, end-to-end 17.193 → 15.357, and that phase's ΔB_iso hit the 6 Ų upper bound and vanished (overfitting signal eliminated).

On the product side we only suggest, never auto-change: when all three trigger conditions are met (ΔB_iso at upper bound + same chemical-formula normalization + space group equals the baseline polymorph), the results panel shows a button "Try α-cristobalite and re-refine". The structural model is experimental fact (synthesis temperature, phase-transition history); the software has no authority to decide for the user.

External validation: IUCr CPD quantification round-robin

Those 7 ShiJiKong samples were both tuning targets and acceptance targets, insufficient to prove generalization. This release connects to the IUCr CPD standard set (each point has weighed truth + XRF cross-check):

  • Sample 1 all eight points reconciled, combo basis average max|Δ| 2.84 pt, same order as the round-robin participants' standard deviation (2.4–2.7 pt); 1a/1b clearly better than community level.
  • Sample 2 (preferred orientation) 31.21 → 15.05, all thanks to Popa LB.
  • Sample 3 (contains 29.5% amorphous) 12.42%, note that reporting such samples must first state the normalization basis.
  • Sample 4 (micro-absorption) is our capability boundary, labeled honestly: the Brindley factor and per-phase scale are strictly degenerate (correction changes Rwp by only 1e-12), so only wt% normalization allocation can be changed, not the fit.

Validation Data (7-sample acceptance)

Basis identical to project proposal §1.7 (tent 19 nodes, per-phase ΔB_iso, xyz, EWIMV texture, Popa LB, 5–120°/sample 5 is 10–100°). The same string of numbers was repeatedly reproduced during v1.5—hard evidence of "zero regression on the default path".

Sample MAUD 2.99993 v1.5.0 Gap Verdict (≤ MAUD+2) Time
1 10.90 11.880 +0.98 ✅ PASS 34.2 min
2 6.34 6.968 +0.63 ✅ PASS 2.2 min
3 10.29 8.123 −2.17 ✅ PASS (beat) 5.2 min
4 9.96 10.263 +0.30 ✅ PASS 77.5 min
5 14.87 15.865 +1.00 ✅ PASS 35.7 min
6 14.76 12.122 −2.64 ✅ PASS (beat) 41.4 min
7 11.30 14.689 +3.39 ❌ FAIL 49.7 min
Mean 11.20 11.42 +0.21 6/7 PASS 245.9 min

How to read:

  • 6 of the 7 samples reproduced to 3 decimal places (sample 5 excepted, see below), showing v1.5's default path did not drift during development—the hardest evidence of "zero regression", not a self-assessment.
  • Sample 7 is the only item not meeting the bar, +3.39 pt. Its root cause is found (cristobalite given as high-temperature β, sample actually low-temperature α), but the product does not auto-change the user's CIF; after manual replacement this project measured 14.031 (−0.66 pt), still above MAUD's 11.30—MAUD treats this phase as a near-amorphous broad packet (size 27 Å / strain 0.0164), while our Popa LB starting point is 58× off from it, and the numerical Jacobian can't get across. This is a known hard nut, labeled honestly, not concealed.
  • Sample 4 is the 77.5 min slow sample (5 phases 40 free coordinates × numerical Jacobian), this run's 10.2634 matches §1.7's 10.263; see "Known Limitations" for the time upper bound.
  • Sample 5's 15.865 is 0.077 lower than §1.7's 15.942: M9 phase-wise downgrade let its xyz step actually run (4 phases / 36 free coefficients), and later micro/B steps absorbed most of it, net gain −0.077. This is a recorded change, not drift.
  • If sample 7 is excluded, the mean of the remaining 6 samples is 10.87%, already better than MAUD's 11.20%—but this number should not be used for promotion, sample 7 is the problem we haven't solved yet.

Known Limitations

  • Sample 4's xyz downgrade path is slow (40 free coordinates × numerical Jacobian). GUI has a 45 min wall-clock budget backstop; CLI defaults to unlimited; for batch scripts pass --time-budget-min yourself.
  • Quantification (QPA) decoupled from Rwp: 1b measured Rwp 29.99% but wt% error only 0.22 pt. For QPA acceptance look at wt%, not Rwp.
  • Background node densification is not free: full 7-sample verdict shows 2.5°/node pulls the mean to 10.90 (literally beats MAUD), but QPA collapses (sample 4 quartz −7.6 vol%). The UI is opened up to 60 nodes, but when QPA is the goal, do not exceed 1 node/4°.
  • Two things that "looked like they should help, measured no help" were turned off as negative results: per-atom B_iso (+equalTo grouping) on samples 1/7 ten phases 90 scan points max gain 0.0025 pt; LM damping aligned to MAUD's λ=0.01 three settings had a 0.042 pt spread and was non-monotonic. Knobs retained, defaults unchanged.

Installation and Registration

Same as v1.0.0: installer embeds zh/en/ja trilingual UI, registration code interoperable with the XinMatch family (%APPDATA%\PolyXRD\Registration\XinRefinement.json). Internal evaluation build needs no registration or install, marked in red in the sta...

Read more

XinRefinement v1.1.0 — Import XinMatch phase analysis (.xinm), self-contained project files

Choose a tag to compare

@PolyXRD PolyXRD released this 05 Nov 03:16

XinRefinement v1.1.0 — Import XinMatch phase analysis (.xinm), self-contained project files

Baseline v1.0.0 | Released alongside XinMatch 3.4.0. The refinement engine, parameter model, CLI parameters, and registration mechanism are unchanged; this release adds one import channel, one project container format, and fixes a v1.0.0 .xrp field-dropping defect.

Download

File Purpose
XinRefinement-1.1.0-setup.exe Release installer (includes registration mechanism, zh/en/ja trilingual install UI)

The internal evaluation portable package is for internal acceptance only and is not published with this Release.

Key Capabilities

Import XinMatch phase analysis (.xinm)

File → Import XinMatch Phase Analysis…. Loads three things at once, no more manual copying:

Loaded Source Notes
Pattern .xinm pattern/pattern.csv Two-column CSV, %.10g round-trip with zero error; wavelength is also passed to the canvas (d-axis conversion available immediately)
Phase CIF .xinm cifs/*.cif Cached per phase then routed through the existing _load_cif_paths, same code path as manual import
Unit cell / space group manifest Checked against CIF parsed values: if consistent, pre-fill "Phase Parameter Editor"; if significantly inconsistent (>0.1% / >0.05°), the CIF value takes precedence and a list is shown

Phases without structure (PDF2-type, or COD archives where no CIF was found) are listed as "reference only": shown in gray, not involved in refinement, with a human-readable reason, and a CIF can be manually specified to fill them in.

The import kernel core/io/xinm_import.py does not depend on Qt and is shared by GUI / CLI / library API; it includes manifest validation, version negotiation (prompts to upgrade if version exceeds supported), zip-bomb protection (512 MB decompression cap), line-by-line CSV parsing with readable errors.

Project file .xinr: self-contained zip container

Item v1.0.0 v1.1.0
Form Plain JSON, stores only external paths zip: manifest.json + pattern/ + cifs/ + result/
Move to another machine Useless if files missing Embeds pattern CSV + CIF + full refinement results by default; can still open and reproduce after moving the original files
Results Stores only R-factor summary result/refine.json (same schema as CLI --out) + navigation history; opening refills the results panel and convergence curve

Extension routing: .xinr → container; .xrp → v1.0.0 JSON, permanently read-only compatible.
Also fixes the v1.0.0 .xrp bug that dropped phase_edits (phase parameter edits disappeared after reopening the project).

Library API

capabilities() now returns xinm_import: "1", so consumers can probe the capability bit and decide whether to expose the import entry.

Validation Data

Item Result
S1 full closed loop (real COD archives 7206075 + 9001681) .xinm import → refinement Rwp 3.409617019598457, bit-identical to P0 golden; GUI one-click run-all path and "manual import same pattern/CIF" have bit-identical Rwp; save .xinr → delete temp pattern and .xinm → reopen and reproduce (rel 1e-9)
Quantification Two phases 50.03 / 49.97 wt%
S2 mixed sources COD + user CIF packed into cifs/; PDF2 phase structural=false listed as "reference only"; no ICDD-derived file inside the package
S3 exception boundaries Missing CIF member / corrupt zip / missing pattern / version=2 each give readable errors, no crash
S4 family registration XinRefinement.json and XinMatch.json manage their own; registration code algorithm interoperability unchanged
Full test suite 286 all green (259 in v1.0.0; added test_xinm_import.py 13 cases, test_project_zip.py 7 cases, etc.)

Installation and Registration

Same as v1.0.0: trilingual installer (Simplified Chinese / English / Japanese), {autopf}\XinRefinement; on first launch the release build prompts "unregistered" (can enter registration code); internal evaluation build needs no registration. The registration code is interoperable with the XinMatch family—the same code works for both software.

Build and Binary Acceptance (six rounds)

Round Result Measured
1 · Reproducible build ✅ Two consecutive builds from the same source, XinRefinement.exe / XinRefinementCLI.exe byte-identical: a2c9fcc80b0f291e… / 7d1ed1ec7efc8642… (SOURCE_DATE_EPOCH + PYTHONHASHSEED=0)
2 · CLI cold start ✅ Both variants print XinRefinement 1.1.0, no missing DLLs
3 · CLI end-to-end ✅ Both variants refine v50_wide.xy anatase.cif rutile.cif --peak-height → rwp = 3.409617, 2 phases, success = true (synthetic pattern is peak-height basis, so --peak-height is needed; without it, integral basis gives rwp = 5.935965, bit-identical to source)
4 · GUI smoke 🟡 offscreen pass Offscreen launch + self-check JSON: release {release, registration_enabled: true, unregistered}, internal {internal, false, internal evaluation build}; double-click visual inspection still needs human
5 · Install/uninstall ✅ silent Silent install 606 files → installed --version normal → silent uninstall leaves 0 residue; Chinese install UI needs human visual check. (Installer requires admin by default; non-interactive environment cannot escalate privilege to re-verify; since build is reproducible and setup.exe SHA-256 is byte-identical to last round, last round's conclusion still holds)
6 · Dual-variant difference ✅ Tagged release / internal; setting XINREFINEMENT_VARIANT=internal on the release build still reports release (anti-bypass)

Release artifacts:

File Bytes SHA-256
XinRefinement-1.1.0-setup.exe 67,586,555 046179e6f3ef45d651b241b41dafb273990b423667bc066e5eba2b3982a44926
XinRefinement-internal-1.1.0-portable.zip 113,946,723 a9012229b268bae36741bbd7cd9d5782ecf55a814ea907d4b39dded701db09d1

Known Notes

  • The release build .xinr embeds CIF and pattern, so the project file is larger than v1.0.0's .xrp (pattern stored as CSV text, ~2 MB per 100k points). Save as .xrp when you want the old "store paths only" behavior.
  • .xrp is read-only compatible, not written back: opening an old file and saving again will write JSON or container based on the extension.
  • The frozen package has no pyproject.toml; the version comes from the literal constant in xinrefinement/__init__.py (__version__ = "1.1.0"), not from the runtime environment's install metadata.
  • Not code-signed: on first run Windows SmartScreen may warn "Unknown publisher".

License

Same as v1.0.0: see repository root LICENSE / LICENSE-CN (same source as XinMatch).

XinRefinement v1.0.0

Choose a tag to compare

@PolyXRD PolyXRD released this 04 Nov 16:59

First official release build.

XinRefinement is a general-purpose desktop software for powder diffraction whole-pattern fitting (Rietveld structure refinement / Le Bail / profile refinement), offering three entry points—a Qt6 GUI, a command line, and a Python library API—covering the complete workflow of "import pattern + phase CIF → whole-pattern fitting → quantification and uncertainty → report". It is scoped separately from its sibling project XinMatch: XinMatch handles peak-shape processing and phase analysis, while XinRefinement does only whole-pattern fitting.

Download

File For Notes
XinRefinement-1.0.0-setup.exe Windows x64 Release installer (Simplified Chinese / English / Japanese), includes offline PolyXRD-Reg registration

The internal evaluation build (portable, no-install zip) is for internal testing only and is not distributed in this Release.

Key Capabilities

Refinement Engine

  • Variable projection (analytic elimination of scale factors and background) + dual backends LM / scipy-trf, with bound constraints and multi-start restarts
  • spglib all 230 space groups, including H/R settings and Wyckoff-site symmetric null-space free coordinates
  • Peak shape: Caglioti FWHM, TCH size/strain separation, η (pseudo-Voigt), low-angle asymmetry, Kα1/Kα2 doublet
  • Structure: atomic coordinates, isotropic B, occupancy; March–Dollase preferred orientation
  • Quantification and uncertainty: Hill–Howard QPA, Schur-complement parameter e.s.d., Durbin–Watson
  • Le Bail intensity extraction + multiphase quantification; CIF write-back (unit cell / coordinates / B / occupancy)

GUI

  • Three-panel main window + 8-step guided refinement (single-step ▶ / gear settings / one-click run-all / stop)
  • MAUD-style phase marker bar: one bar per phase, colored by phase, bar height ∝ intra-phase relative intensity; narrowed residual panel
  • Axis switching: horizontal axis 2θ ↔ d value (Bragg conversion, auto reverse), vertical axis counts ↔ log ↔ √; left-click axis to cycle, right-click for menu
  • Edit → Phase Parameter Editor: unit-cell six parameters, March–Dollase preferred orientation, space group / point group / Laue group
  • Results area three tabs: summary (R factors / content / DW / e.s.d.), convergence curve, correlation matrix
  • Trilingual interface (zh / en / ja, switch without restart), project file .xrp, HTML+PNG+JSON report export

Command Line

XinRefinementCLI refine pattern.xy phase_a.cif phase_b.cif -o out.json
XinRefinementCLI refine pattern.xy phase_a.cif phase_b.cif --recipe --report out_dir --quant --esd

Validation Data

Item Result
Unit / integration tests 259 all green (engine, GUI offscreen smoke, CLI, i18n, registration and variants)
Synthetic benchmark bench_b (family golden) 8/8 PASS, Rwp 3.41–4.04%, bit-identical to pre-port
Full benchmark bench_full (5 crystal systems × 1–3 phases) 18/18 PASS
Real sample NCM811 Converged, Rwp 11.25%
Cross-validation vs GSAS-II Max unit-cell relative difference 0.031% (real sample) / 0.015% (two-phase)

Installation and Registration

  1. Run XinRefinement-1.0.0-setup.exe, optionally select install language;
  2. On first launch, a prompt "This software is unregistered. All rights reserved. Commercial use prohibited" appears—click "OK" to continue the trial, or "Enter registration code" to complete registration;
  3. After installation, the Start menu contains XinRefinement (GUI) and XinRefinementCLI (command line).

Registration codes are issued per "organization / department name", verified offline, no internet required, and reusable across multiple machines of the same organization; for application and technical support: sshztx@outlook.com.

Known Notes

  • Not code-signed; on first run Windows SmartScreen may warn "Unknown publisher"—choose "Run anyway".
  • The registration code uses offline HMAC verification (key shipped with the software, obfuscation-level strength).
  • Installer ~67 MB (includes Qt / matplotlib runtime); GUI cold start ~2–4 s.

License

Dual license: academic license (non-commercial research and teaching) or a separately negotiated commercial license.
Copyright (c) 2026 PolyXRD Team. See repository LICENSE / LICENSE-CN.