Skip to content

Support for spin-polarized calculations#257

Draft
chselz wants to merge 16 commits into
grimme-lab:mainfrom
chselz:spgfn
Draft

Support for spin-polarized calculations#257
chselz wants to merge 16 commits into
grimme-lab:mainfrom
chselz:spgfn

Conversation

@chselz

@chselz chselz commented Apr 17, 2026

Copy link
Copy Markdown

This PR implements spin-polarized xTB calculations. References were generated with tblite 0.5.0.
All new and also all old tests are passing.
I also updated the documentation to let the users know that this feature is now availible.
the spin polaraization can be activated either through the command line or via defining a new interaction via the factory function.
This feature was desired in #163.

I hope that these changes are not to invasive to the rest of the code base, but it necessitated the creation of spin channels to allow for an unrestricted SCF mode. What i did not do is timing tests to check if UHF takes double as long as RHF but there is a test checking that RHF and UHF results without spinpol give the same result for closed shell molecule

Christian Selzer and others added 11 commits April 16, 2026 16:32
Signed-off-by: Christian Selzer <cselzer@uni-bonn.de>
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>
This reverts commit 8f26f7a.
was ai try for unrestricted scf
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>
from .field import *
from .list import InteractionList, InteractionListCache
from .solvation import *
from .spin import *
Comment thread src/dxtb/_src/components/interactions/list.py Fixed
Comment thread src/dxtb/_src/components/interactions/spin/factory.py Fixed
Comment thread src/dxtb/_src/components/interactions/spin/spinpolarisation.py Fixed
Comment thread src/dxtb/_src/components/interactions/spin/spinpolarisation.py Fixed
Comment thread test/test_spinpol/test_spinconst.py Fixed
Comment thread test/test_wavefunction/test_spin.py Fixed
Comment thread test/test_wavefunction/test_spin.py Fixed
Comment thread test/test_wavefunction/test_spin.py Fixed
Comment thread test/test_wavefunction/test_spin.py Fixed
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds spin-polarized xTB support by introducing a spin-polarisation interaction and enabling an unrestricted (UHF) SCF flow, with CLI/config hooks plus tests and documentation updates.

Changes:

  • Add SpinPolarisation interaction (with spin-constant data + factory) and expose it via dxtb.components.
  • Extend SCF to support nspin=2 (UHF) and route interaction potentials/energies by spin channel.
  • Add CLI flags (--spinpol, --uhf-mode), documentation, and tblite-based reference tests for energies/gradients.

Reviewed changes

Copilot reviewed 34 out of 34 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
test/test_wavefunction/test_spin.py Adds tests for spin-representation conversion utilities.
test/test_spinpol/test_spinconst.py Validates loading of spin constants and WLL construction.
test/test_spinpol/test_gradient.py Adds spin-polarized gradient/force checks vs tblite references.
test/test_spinpol/test_energy.py Adds spin-polarized energy and spin-shell energy/potential tests.
test/test_spinpol/samples.py Adds tblite reference data for spin-polarized cases/constants.
test/test_spinpol/init.py Introduces new test package for spin polarization.
test/test_singlepoint/test_energy.py Adds RHF vs UHF closed-shell equivalence tests.
test/test_cli/test_args.py Adds CLI/config test to ensure --spinpol implies UHF mode.
src/dxtb/components/spinpolarisation.py Public re-export module for spin-polarisation component.
src/dxtb/components/init.py Registers spinpolarisation in component package/lazy loader.
src/dxtb/_src/wavefunction/spin.py Adds spin basis conversion helpers (charge/mag ↔ up/down).
src/dxtb/_src/wavefunction/init.py Exposes spin utilities from wavefunction package.
src/dxtb/_src/scf/base.py Implements UHF-aware density/charge/potential/H build and nspin inference.
src/dxtb/_src/constants/defaults.py Adds defaults for spin polarization and UHF mode.
src/dxtb/_src/components/interactions/spin/spinpolarisation.py Implements the SpinPolarisation interaction and its cache/WLL build.
src/dxtb/_src/components/interactions/spin/factory.py Factory for creating SpinPolarisation from atomic numbers.
src/dxtb/_src/components/interactions/spin/constants.py Adds spin-constant table used by the interaction.
src/dxtb/_src/components/interactions/spin/init.py Exposes spin interaction APIs from the new subpackage.
src/dxtb/_src/components/interactions/list.py Makes Potential.mono spin-dimensioned when charges.nspin > 1; registers reset/update.
src/dxtb/_src/components/interactions/dispersion/d4sc.py Minor formatting/comment tweak in error message path.
src/dxtb/_src/components/interactions/coulomb/secondorder.py Minor signature formatting/comment tweak.
src/dxtb/_src/components/interactions/container.py Adds nspin to Charges container.
src/dxtb/_src/components/interactions/base.py Adds spin-channel routing logic to Interaction potential/energy evaluation.
src/dxtb/_src/components/interactions/init.py Re-exports the new spin interaction package.
src/dxtb/_src/components/classicals/dispersion/d4.py Normalizes dtype/device handling for D4 inputs.
src/dxtb/_src/cli/driver.py Adds --spinpol wiring to include spin-polarisation interaction.
src/dxtb/_src/cli/argparser.py Adds --uhf-mode and --spinpol CLI options.
src/dxtb/_src/calculators/types/energy.py Normalizes positions dtype/device in singlepoint().
src/dxtb/_src/calculators/config/scf.py Adds uhf_mode to SCF configuration.
src/dxtb/_src/calculators/config/main.py Plumbs uhf_mode from args into config (incl. spinpol implication).
src/dxtb/main.py Fixes CLI entry import to use ._src.cli.
docs/source/02_indepth/components_interactions.rst Adds spin-polarisation component to interaction docs.
docs/source/01_quickstart/getting_started.rst Documents restricted vs unrestricted and spin-polarisation usage.
.vscode/settings.json Updates editor environment settings.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +186 to +198
wll = torch.zeros((ihelp.nsh, ihelp.nsh), **self.dd)

for atom_idx, number in enumerate(numbers):
for ishell in range(ihelp.shells_per_atom[atom_idx]):
for jshell in range(ihelp.shells_per_atom[atom_idx]):
ishell_idx = ihelp.shell_index[atom_idx] + ishell
jshell_idx = ihelp.shell_index[atom_idx] + jshell
l1 = ihelp.angular[jshell_idx]
l2 = ihelp.angular[ishell_idx]
wll[jshell_idx, ishell_idx] = self.spinconst[
atom_idx, lidx[l1, l2]
]

Comment on lines +254 to +255
"Positions are required for ES2 cache."
) # ? why es2
numbers: Tensor,
device: torch.device | None = None,
dtype: torch.dtype | None = None,
) -> SpinPolarisation | None:
Comment thread src/dxtb/_src/cli/argparser.py Outdated
action="store_true",
help=(
"R|Use unrestricted SCF mode\n"
"note this takes roughly twice the time than for restricted SCF"
self,
cache: InteractionCache,
qat: Tensor,
**_: Any, # ich hätte es qsh genannt
Comment on lines +35 to +55
@pytest.mark.parametrize("dtype", [torch.float, torch.double])
# @pytest.mark.parametrize("name", sample_list)
def test_updown_to_magnet(dtype: torch.dtype) -> None:
dd: DD = {"device": DEVICE, "dtype": dtype}
tol = torch.finfo(dtype).eps ** 0.5

qsh_before = torch.tensor(
[[-0.82824398, -0.33807717, -0.83367885], [0.0, 0.0, 0.0]]
)

qsh_after = spin.updown_to_magnet_2(qsh_before)

ref_qsh_after = torch.tensor(
[
[-0.82824398, -0.33807717, -0.83367885],
[-0.82824398, -0.33807717, -0.83367885],
]
)
assert (
pytest.approx(ref_qsh_after.cpu(), rel=1e-7, abs=tol) == qsh_after.cpu()
)
Comment thread src/dxtb/_src/wavefunction/spin.py Outdated
return x


# === General Forumlas ===
self,
cache: ES2Cache,
qat: Tensor,
**_: Any, # missleading variable name qsh would be better
Comment thread test/test_spinpol/samples.py Outdated
"""Gradient of spGFN2-xTB"""

spconst: Tensor
"""Spin Constants (same forsp GFN1-xTB and spGFN2-xTB)"""
Comment thread src/dxtb/_src/scf/base.py Outdated
Comment on lines +1071 to +1076
# Store alpha eigenvalues/vectors (for compatibility)
self._data.evals = evals_a
self._data.evecs = evecs_a

# Combine eigenvalues for Fermi smearing: shape (..., 2, nao)
emo = torch.stack([evals_a, evals_b], dim=-2)
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>
@overload
def get_interaction(
self, name: Literal["SpinPolarisation"]
) -> SpinPolarisation: ...
@chselz chselz marked this pull request as draft April 23, 2026 12:50
@codecov

codecov Bot commented Apr 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 73.90244% with 107 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.53%. Comparing base (67c7b4b) to head (dc5ea96).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/dxtb/_src/wavefunction/spin.py 42.25% 38 Missing and 3 partials ⚠️
...c/components/interactions/spin/spinpolarisation.py 75.60% 15 Missing and 5 partials ⚠️
src/dxtb/_src/scf/unrolling/default.py 68.96% 11 Missing and 7 partials ⚠️
src/dxtb/_src/scf/base.py 89.10% 1 Missing and 10 partials ⚠️
...c/dxtb/_src/components/classicals/dispersion/d4.py 50.00% 2 Missing and 3 partials ⚠️
src/dxtb/_src/scf/mixer/anderson.py 37.50% 3 Missing and 2 partials ⚠️
src/dxtb/_src/scf/mixer/simple.py 50.00% 2 Missing and 1 partial ⚠️
src/dxtb/_src/cli/driver.py 33.33% 1 Missing and 1 partial ⚠️
src/dxtb/_src/components/interactions/list.py 81.81% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #257      +/-   ##
==========================================
- Coverage   86.08%   85.53%   -0.55%     
==========================================
  Files         205      210       +5     
  Lines       10499    10849     +350     
  Branches     1224     1284      +60     
==========================================
+ Hits         9038     9280     +242     
- Misses       1099     1177      +78     
- Partials      362      392      +30     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

chselz added 3 commits May 18, 2026 12:59
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>
Signed-off-by: Christian Selzer <s6chselz@uni-bonn.de>
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.

3 participants