Repository navigation
Releases
XinRefinement v1.6.2 — Stop button now actually stops
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, sha256c51f786881c1ed8d965b27b31c9d406e2b51a648f6246a45a84f3f09e2abced1) 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 viaconfig_for'sreplace()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.py5 cases). - golden CLI Rwp 50/50
3.4074156640940005unchanged (cancel_cb=Nonekeeps 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 nostatus='failed'fake record. - Also fixed a "happened to hold" existing assertion:
test_serialize_uses_relative_pathsoriginally asserted "same drive letter → relative path", but_rel()is the prefix semantics ofrelative_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 (
.exelayer) are byte-reproducible: after fixingSOURCE_DATE_EPOCH(to git commit time) andPYTHONHASHSEED=0, two builds of the same commit yield completely identicalXinRefinement.exe/XinRefinementCLI.exehashes (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.exeinside can be byte-matched. - One process constraint:
SOURCE_DATE_EPOCHtakes 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 tagv1.6.2), withSOURCE_DATE_EPOCH=1794219055fixed to that commit's commit time; inside the artifactXinRefinement.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
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, sha25664afe606ebc5f6bd11811a243dad16dec54f76bace9c607dbbe04324f29658de) 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+1and 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.py15 cases). - golden CLI Rwp 50/50
3.4074156640940005unchanged, 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° reads2θ = 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 containLICENSE(2411 B) andLICENSE-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
cellstep; theprofilestep'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
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, sha2565fb97ea234732a3f825cc4e60806b5d9a5ae470b80acdc7edb7e1a6c0044c439) 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). CifStructuregainsname_mineral:parse_ciftakes 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 twoTiO2.acan'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
*; whenanisoexists, 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 byresolve_axes(system, requested) = crystal-system-allowed ∩ user-checked, illegal axes silently dropped; whenNone(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 stays9.5196; recipe message changed from/ 2 paramsto/ 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.py17 cases,test_v16_gui.py20 cases, plus 4 cases that no longer skip after installinggemmilocally). - golden CLI Rwp 50/50
3.4074156640940005unchanged;axes_override=Nonepath has a dedicated test guarding "bit-identical". - Built artifact actually runs:
XinRefinementCLI.exe refine … --recipe --time-budget-min 3produces a report normally (5.8391%); frozen package confirmed to contain the three new modulesnaming/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, seeCHANGELOG.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+2is demoted to diagnostic reference. - Unit-cell component checkboxes only affect the
cellstep; theprofilestep'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
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)
- M10/M10b stepwise projection:
cellandprofilesteps 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. Theprofilestep 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" inmicro/Bsteps to apply unconditionally. - 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.
- Structure-factor vectorization:
compute_peakstriple-vectorized, 26–37× speedup with bit-identical numerics (guardrailtests/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,
combobasis averagemax|Δ|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-minyourself. - 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...
XinRefinement v1.1.0 — Import XinMatch phase analysis (.xinm), self-contained project files
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.xrpfield-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
.xinrembeds 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.xrpwhen you want the old "store paths only" behavior. .xrpis 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 inxinrefinement/__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
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 --esdValidation 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
- Run
XinRefinement-1.0.0-setup.exe, optionally select install language; - 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;
- 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.