Skip to content

[wien2k] fix dmftproj port for non-centrosymmetric cells - #19

Open
krystophny wants to merge 6 commits into
TRIQS:unstablefrom
krystophny:fix/dmftproj-noncentrosym
Open

krystophny wants to merge 6 commits into
TRIQS:unstablefrom
krystophny:fix/dmftproj-noncentrosym

Conversation

@krystophny

@krystophny krystophny commented Jun 24, 2026 •

Copy link
Copy Markdown

Fixes the non-centrosymmetric generator paths; rebased directly onto #16. This is separate from the converter-frame correction in #18.

The tellurium context from dft_tools#148 exposes three conventions hidden by centrosymmetric d-shell fixtures: normalized px,py,pz cubic p harmonics, transpose basis changes for antiunitary operations, and composition of the representative local frame with the atom-mapping symmetry operation.

The composed local rotation is now shared by ctqmcout, parproj and outband, including the non-SO local matrix path. Symqmc and sympar use the antiunitary transpose. The earlier nonidentity-struct rejection is removed because every affected projector writer now applies the reference frame.

Validation: the small Te symmetry reference agrees with native dmftproj at 1e-7 and all matrices are unitary. An independent test checks that Te's composed p-shell rotation is unitary and differs from the mapping operation alone. The common, Te, odd-l, non-SO, ctqmcout, parproj and outband tests pass locally. The out-of-tree full Te comparison remains limited by the retained 193 MB almblm input and is documented in the discussion.

Cubic-p normalization and the shared antiunitary correction are inherited from #12. This layer keeps the composed local-frame correction and tellurium regression without duplicating those fixes. Eleven focused generator regressions pass.

Review this layer. The PR continues to target unstable because its head is in a fork.

Full TRIQS integration is checked by GitHub CI; it was not run locally.

Chris&AI

@krystophny

Copy link
Copy Markdown
Author

GPT Sol rebased this onto the reviewed odd-l stack and completed the local-frame fix across ctqmcout, parproj and outband. Preserved the p-basis and antiunitary corrections, removed the struct guard only after every projector writer used the composed frame, and added an independent Te rotation regression. Seven affected local tests pass; CI is running.

Chris&AI

@krystophny

krystophny commented Oct 12, 2026 •

Copy link
Copy Markdown
Author

GPT Sol re-reviewed the non-centrosymmetric changes using the linked Te discussion and restacked them on updated #16. The Te symmetry, shared-parser, partial-projector and non-SO regressions pass. Generator and converter frame fixes remain distinct; this PR is ready for review. Latest-head CI is running/queued.

Chris&AI

@krystophny
krystophny marked this pull request as ready for review October 12, 2026 17:48
@krystophny
krystophny force-pushed the fix/dmftproj-noncentrosym branch from 132dd4e to 0901c14 Compare October 27, 2026 16:35
@krystophny

Copy link
Copy Markdown
Author

Rebased on #16; the cubic-p and antiunitary fixes are now inherited from #12. Preserved the composed local rotation correction and tellurium comparison. Eleven generator regressions pass.

Chris&AI

@krystophny
krystophny force-pushed the fix/dmftproj-noncentrosym branch 2 times, most recently from 4a54328 to ad27f97 Compare October 28, 2026 19:18
krystophny and others added 3 commits October 28, 2026 20:26
The port was validated only on centrosymmetric, trivial-rotloc, d-shell
fixtures (CaOs2, SrVO3). Non-centrosymmetric spin-orbit cells (the
dft_tools#148 regime) hit three convention bugs in the p-shell and
local-frame paths:

- _CUBIC[1]: the cubic l=1 (p) transform was un-normalized and row-ordered
  pz,py,px instead of the Fortran px,py,pz, so P P^dag != I. _CUBIC[2] (d)
  was correct, so the d-shell tests never caught it.
- The non-mixing magnetic (time-inversion) operations used conj(P.T) where
  the antiunitary basis change needs P.T (symqmc and sympar shell matrices,
  and the ctqmcout Rloc rotrep). The mixing path already used P.T; the
  non-mixing path was inconsistent. It is a no-op for real bases
  (complex-identity, cubic-d), so only the p-shell with its imaginary p_y
  row was affected.
- The non-mixing ctqmcout path dropped the struct reference local rotation
  rotloc_ref (beta=pi/2 for Te) that the mixing path composes via
  rotloc_rotl_so. Both the Rloc rotrep and the projector rotation now
  compose it; the projector uses dmat(R[isym]) @ dmat(rotloc_ref), the
  orbital dmat of the full rotloc (rot_projectmat.f), no spin part, no time
  reversal.

Verified against the dmftproj Fortran on the identical lapw2 -almd output
(tellurium, P3_1 21, SP+SO): case.symqmc and case.sympar match to 6e-8 and
case.ctqmcout (Rloc rotrep + all projectors) to 2e-8, the float32
cubic-template floor; the only larger field is elecn (36 vs 35.999998, a
float32 almblm value). Centrosymmetric CaOs2 is byte-identical.

Adds a non-centrosymmetric tellurium case.symqmc regression test (small
symmetry inputs, no almblm) and a cubic l=1 unitarity check.
Native dmftproj symmetrizes a paramagnetic density matrix also under time
reversal (symmetrize_mat.f calls add_timeinv when not spin-polarized),
since the magnetic group of a paramagnet is a grey group. The Python port
skipped that step. For centrosymmetric cells the space-group average alone
already makes the density real, so the CaOs2 references did not notice;
for a non-centrosymmetric cell such as Te the p-shell density kept an
imaginary part of order 0.6 where native has ~1e-17.

Apply D <- (D + U conj(D) U^H)/2 with the shell time-reversal operator
after the symmetry average.
set_rotloc.f fills the spinor rotloc only for l >= 1. For an s shell,
outputqmc.f instead builds the 2x2 spinor directly from the Euler angles
of the composed proper rotation krotm(isym) krotm(rotloc), with the
time-reversal column swap for a magnetic op. The port reused the l >= 1
composition (phase-only spinor of the symmetry op times the
representative spinor), which differs from native in two ways:
- for a non-magnetic mapping op, by an overall SU(2) sign;
- for a magnetic mapping op (b = pi), by more than a sign, so the
  rotated s-shell density matrix in parproj was wrong.

Port euler() from set_rotloc.f and build the l = 0 spinor as native
does. Checked against native dmftproj on Te with a correlated s shell,
including a reordered dmftsym where a magnetic op maps the atoms: ctqmcout
and parproj agree to 6e-14.
@the-hampel

Copy link
Copy Markdown
Member

Rebased onto unstable after the #16 squash merge. Your commit is unchanged; its tree is identical to your head. On top we added two fixes:

  • eb7aeb4 adds the time-reversal average to the paramagnetic non-SO parproj. Native symmetrize_mat.f calls add_timeinv whenever the run is not spin-polarized; the port skipped that call. It came in with [wien2k] port and verify the non-spin-orbit (ifSO=0) path #15 and was not noticed because for CaOs2 the space-group average alone already makes the density real. On Te the p-shell density keeps an imaginary part of about 0.6, where native has ~1e-17.
  • 3f9c03d builds the SO s-shell Rloc the way native does. outputqmc.f takes it directly from the Euler angles of the composed rotation, since set_rotloc.f only fills l >= 1; euler() is ported from there. The port had reused the l >= 1 composition instead. With a non-magnetic mapping op that only flips the overall sign of the written 2x2 block. When the op that maps the atoms is magnetic, though, the rotated s density in parproj is wrong (up/dn 25.67/25.67 vs native 24.77/26.58).

Checked locally (clang++ 22.1 / gfortran 15.2): ctest 33/33. Without your 193 MB Te almblm we generated a synthetic one: the real Te.struct/Te.dmftsym with random Alm/Blm (3 k-points, 40 bands in the window). Python and native dmftproj then agree in every output file (ctqmcout, symqmc, sympar, parproj, oubwin) to ≤1.3e-13, for p with SO (two windows), p without SO (spin-polarized and paramagnetic), and s with SO. The magnetic case uses a copy of Te.dmftsym with the operations reordered so that a 2-fold axis maps the atoms. The composed local frame is therefore also verified in the projectors, not only in symqmc.

A question on test data: the composed frame in ctqmcout/parproj/outband currently has no CI reference, and the Te symqmc reference is single precision (atol 1e-7). Could you share a much reduced real Te almblm (1 k-point, ~30 bands, so tens of kB gzipped)? Then we would add native double-precision references for Te. If that is impractical, we can instead add the synthetic almblm (random coefficients) together with native references. Do you have a preference?

Also, the commit message still lists the cubic-p normalisation and the antiunitary transpose, which already came in with #12. We will adjust it when squash-merging.


Attribution: this comment was produced by Claude (Opus 5.5) running in Claude Code, driven by @the-hampel.

@the-hampel
the-hampel force-pushed the fix/dmftproj-noncentrosym branch from ad27f97 to 3f9c03d Compare October 29, 2026 15:40
Wien2k 19.1 lapw2 -almd output for trigonal Te (spin-polarized, spin-orbit)
at one general k-point with 34 bands, produced on the converged density of
the calculation that Te.struct and Te.dmftsym come from. Te.README.md records
the provenance and the native dmftproj commands for references.
@krystophny

Copy link
Copy Markdown
Author

Thanks for the time-reversal and s-shell fixes; I prefer the real file, so b2e4ffb adds test/python/wien2k/Te.almblmup.gz and Te.almblmdn.gz with a Te.README.md on provenance, and leaves the native references and CI registration to you as offered. They are Wien2k 19.1 lapw2 -almd output with spin polarization and spin-orbit coupling on the converged density of the calculation behind Te.struct and Te.dmftsym, at one general k-point with 34 bands (31 to 64, which includes the six 5s bands below the shipped window of bands 37 to 62). The spin-polarized SO path needs both spin files, so the pair is about 89 kB each rather than tens of kB in total. On these files native dmftproj and the Python port agree to at most 9.2e-15 absolute in ctqmcout, symqmc, sympar, parproj and oubwin for p with SO (windows ±0.5 and ±0.4 Ry), s with SO, and p without SO, both spin-polarized and paramagnetic (with the up file read as Te.almblm, as the CaOs2_noso tests do), and ctest passes 33/33 locally. Fixing the commit message at squash is fine with me.

Chris&AI

The composed local frame of a non-centrosymmetric cell, the SO s-shell
Rloc and the paramagnetic time-reversal average in parproj had no CI
coverage: the Te test checked only symqmc, at single precision, and the
CaOs2 fixtures never reach these paths. With the reduced real Te almblm
pair, compare ctqmcout, symqmc, sympar and parproj against native
dmftproj at 1e-11 for p with SO, s and p with SO, s and p with SO under
a dmftsym whose mapping operation is magnetic, and the non-SO path both
spin-polarized and paramagnetic.

The included d shell is left out to keep parproj references small
(about 175 kB gzipped in total). The pre-fix generators fail the s-shell
cases by 1.4 and the paramagnetic parproj by 1.8e-4.

Te.README.md records the native dmftproj recipe per reference; it
regenerates all references byte for byte (gzip -n).
@the-hampel

Copy link
Copy Markdown
Member

Thanks for the real Te files. On top of b2e4ffb, 3c816d0 adds the native references and the CI test Py_wien2k_te_python. It compares ctqmcout, symqmc, sympar and parproj against native dmftproj at 1e-11 for five cases:

  • Te: p with SO;
  • Te_sp: s and p correlated with SO, window -0.8 0.5 so that the 5s bands are inside;
  • Te_sp_mag: like Te_sp, but with Te_mag.dmftsym. That is Te.dmftsym with the operations reordered so that a two-fold axis, a magnetic operation under SO, maps the atoms;
  • Te_noso: s included and p correlated, without SO, spin-polarized;
  • Te_noso_paramag: like Te_noso, paramagnetic.

We left out the included d shell to keep the parproj references small, about 175 kB gzipped in total. Te.README.md now lists the native dmftproj recipe for each reference. The .gz files are binary in the PR diff, so the recipe lets anyone regenerate and check them: it reproduces all 13 byte for byte (gzip -n, no timestamp).

Checked locally:

  • ctest 34/34.
  • Python vs native on your files: ≤2e-14 for every case and output file.
  • Run with the generators from before our two fixes, the new test fails: the s-shell cases by 1.4, the paramagnetic parproj by 1.8e-4.

With Te.symqmc.gz now a double-precision native reference, wien2k_symqmc_te.ref.npy (single precision) in wien2k_symqmc_noncentrosym.py is redundant. Its header also still describes the cubic-p and time-reversal bugs that #12 fixed. Fine to leave as is, or drop the .npy check in a follow-up, as you prefer.


Attribution: this comment was produced by Claude (Opus 5.5) running in Claude Code, driven by @the-hampel.

@krystophny

Copy link
Copy Markdown
Author

Thanks for adding the native references. I removed the redundant .npy comparison and fixture, kept the unitarity checks, and updated the test description. Both the Te native-reference test and the non-centrosymmetric unitarity test pass through CTest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants