API reference
This page is auto-generated by sphinx.ext.autodoc from the docstrings of
the mag4 package.
mag4 — Heisenberg J extraction (four-state method) and multi-sublattice LSWT. |
Constants and data structures
Physical constants and shared data structures.
- mag4.constants.DIST_EQUIV_TOL: float = 0.01
Default tolerance (Å) for merging equivalent M–M distances into the same J shell.
- class mag4.constants.CrystalData(cell, all_species, target_species, nb_species, total_atoms, elements, lattice_matrix, lattice_vectors)
Bases:
tuple- all_species
Alias for field number 1
- cell
Alias for field number 0
- elements
Alias for field number 5
- lattice_matrix
Alias for field number 6
- lattice_vectors
Alias for field number 7
- nb_species
Alias for field number 3
- target_species
Alias for field number 2
- total_atoms
Alias for field number 4
- class mag4.constants.Dimer(idx_a, idx_b, label_a, label_b, distance, coupling_name)
Bases:
tuple- coupling_name
Alias for field number 5
- distance
Alias for field number 4
- idx_a
Alias for field number 0
- idx_b
Alias for field number 1
- label_a
Alias for field number 2
- label_b
Alias for field number 3
- class mag4.constants.FourStateConfig(idx, coupling_name, state_label, spin_vector, dimer)
Bases:
tuple- coupling_name
Alias for field number 1
- dimer
Alias for field number 4
- idx
Alias for field number 0
- spin_vector
Alias for field number 3
- state_label
Alias for field number 2
- mag4.constants.next_config_idx()[source]
Monotonically increasing integer used to tag
FourStateConfig.- Return type:
Geometry
Lattice geometry helpers (matrix construction, fractional ↔ Cartesian, PBC distances).
- mag4.lattice.lattice_matrix(a, b, c, alpha_deg, beta_deg, gamma_deg)[source]
Build a 3×3 lattice matrix (columns = a, b, c) from cell parameters.
- Parameters:
- Returns:
Lattice matrix with the three lattice vectors as columns.
- Return type:
ndarray, shape (3, 3)
- mag4.lattice.get_cartesian_coords(atoms, lat_mat)[source]
Cartesian coordinates of a list of atoms in the legacy
[label, x, y, z, …]format.
Reading / writing
CIF reading: CrystalData construction, M–M pair extraction, distance shells.
- mag4.io_cif.crystal_from_cif(cif_path, element)[source]
Build a
CrystalDatadirectly from a CIF file (no POSCAR needed).- Parameters:
- Raises:
ValueError – If
elementis not present in the structure.- Return type:
- mag4.io_cif.get_mm_distances(cif_path, element, cutoff)[source]
Return sorted list of unique M–M pairs within
cutoffÅ.Each entry is a dict with keys
site_i, site_j, label_i, label_j, image, distance.
- mag4.io_cif.group_inequivalent(results, tol=0.01)[source]
Cluster M–M pairs into distance shells (J1, J2, …).
Returns a list of dicts
{label, distance, dist_min, dist_max, multiplicity, pairs}.
- mag4.io_cif.get_distance_shells(cif_path, element, cutoff, tol=0.01)[source]
Lightweight version of
group_inequivalent()for the magnon pipeline.Returns
[{"label": "J1", "distance": d_mean, "n_bonds": k}, …].
VASP POSCAR I/O — read into CrystalData and write supercell geometry.
Layer: VASP-specific (POSCAR format; also the geometry source for the WIEN2k path).
- mag4.io_poscar.read_poscar(poscar, species_index)[source]
Parse a VASP POSCAR / CONTCAR file.
- Parameters:
- Return type:
- mag4.io_poscar.write_poscar(crystal, name)[source]
Write a single POSCAR for the supercell geometry. Returns the file path.
- Return type:
- Parameters:
crystal (CrystalData)
name (str)
Supercell
Build (na × nb × nc) supercells from a CrystalData.
- mag4.supercell.expand_supercell(crystal, na, nb, nc)[source]
Expand a
CrystalDataby(na × nb × nc).Returns the original instance unchanged when na = nb = nc = 1.
- Return type:
- Parameters:
crystal (CrystalData)
na (int)
nb (int)
nc (int)
- mag4.supercell.shortest_periodic_image(lat_mat, max_n=3)[source]
Shortest non-zero supercell lattice vector length.
Iterates over
(n_a, n_b, n_c) ∈ [-max_n, max_n]^3 \ {(0,0,0)}and returns the minimum|n_a a + n_b b + n_c c|. For cubic / orthorhombic / hexagonal cells this reduces tomin(|a|,|b|,|c|); for general triclinic cells the enumeration catches the short diagonal combinations.- Parameters:
lat_mat (
ndarray) –3×3matrix whose columns are the Cartesian lattice vectorsa, b, c(mag4’sCrystalData.lattice_matrixconvention).max_n (
int) – Range of integer coefficients to try.3is enough for all standard Bravais types — only severely skewed triclinic lattices could need more, and mag4 doesn’t build those.
- Return type:
- mag4.supercell.find_dimer_direction(conv_lat_mat, target_species, distance, *, dist_tol=0.01, max_image=1)[source]
Return a representative unit vector along a J-shell dimer in the conventional cell.
For each pair
(i, j)of magnetic atoms intarget_speciesand each periodic-image translationTwithin[-max_image, max_image]^3of the conv lattice, returns the first(r_j + T − r_i)/|.|whose length matchesdistancewithindist_tol.Noneif no such pair exists in the conv cell (rare: would mean the J shell only appears across multiple conv-cell copies in the supercell).
- mag4.supercell.find_all_dimer_directions(conv_lat_mat, target_species, distance, *, dist_tol=0.01, max_image=1)[source]
All distinct unit bond directions of a J shell in the conventional cell.
Like
find_dimer_direction()but returns every symmetry-equivalent direction (deduplicated by sign, since±ugive the same periodic-image distance). A single coupling can run along several directions at once — e.g. a honeycomb J1 has three — and in an anisotropic supercell they isolate differently, so the isolation check must test all of them, not just one representative. Returns[]if the shell has no in-cell pair.
- mag4.supercell.check_four_state_isolation(conv_lat_mat, multipliers, couplings, target_species=None, *, dimer_vectors=None, rel_tol=0.001, max_multiplicity=None, dimer_multiplicity=None)[source]
Per-J algebraic isolation check for the four-state method.
A Jk dimer
(i, j)is isolable in the chosen supercell when its four-state combinationE_uu + E_dd − E_ud − E_dureturns only Jk. Flipping(i, j)touches one bond for every periodic image ofjthaticouples to (all other atoms are a fixed bath that cancels), so the combination equals4·(Σ over those bonds of their J). Therefore:clean — none of those images sits at a different coupling distance
Jm (m≠k). If one does, the formula mixes Jk with Jm and the dimer is contaminated (crosslists the offending Jm). Cleanliness is the whole criterion — this is the “check the cancellation of the other dimers in the 4-state equation” rule.multiplicity
M— the number of images atd_kitself (≥ 1, the bond included). When a compact cell wraps the bond onto its own image through the same Jk (e.g. an axial bond along a doubled axis links±a,M = 2), the combination yieldsM·Jk·S²and the extractor divides by the self-coefficient4·Minstead of the textbook4.
This is purely a property of which images fall at which coupling distance, so it is frame-independent and handles oblique cells. It replaces the older geometric surrogates — bounding box
Σ|u_i|L_i, per-axisn_i·L_i ≥ 3·d, and the “3 copies ofdalong the bond” chain rule — all of which demanded a fully isolated (M = 1) dimer and so over-sized the cell. The algebraic rule accepts the smallest cell in which the cross terms cancel, multiplicity and all.Worked examples (
--cutoffgiving the J set): tetragonal La₂CuO₄ (J1∥a,b; J2∥[110]) → 2×2×1 (J1M=2→ /8, J2M=4→ /16); CsVI₃ (J1, J2 both ∥c) → 1×1×2 (J2M=2), J1 alone → 1×1×1 (M=2); SrFeO₂ (J1,J2,J3) → 2×2×3 (J2,J3M=2).The bond directions come from
target_species(all symmetry-equivalent directions) or a singledimer_vectorsentry; with neither, a shell has no testable direction and is passed optimistically (M=1).max_multiplicity(opt-in) caps the accepted dimer multiplicity: a shell is accepted only when it is clean and its multiplicityM(the number of Jk images linking the dimer atoms — the4·Mdivisor) does not exceed the cap.max_multiplicity=1demands a fully isolated dimer (textbook divisor 4) — the geometric-style behaviour exposed on the CLI as--full-isolation— and grows the cell (La₂CuO₄ 2×2×1 → 3×3×1, CsVI₃ 1×1×2 → 1×1×3, SrFeO₂ 2×2×3 → 3×3×3) in exchange for larger, less noise-sensitive cells; intermediate caps (e.g.2) trade cell size against the largest divisor. A clean shell whoseMexceeds the cap is reportedok=Falsewith an emptycross(the failure is high multiplicity, not contamination).dimer_multiplicity(post-selection only) maps each coupling to the multiplicity of the actually selected dimer — the exact4·Mthe report divides by, counted from that dimer’s supercell images. When given it overrides the direction-based worst case for clean shells, so the displayed multiplicity and the cap decision match the formula exactly. The supercell search omits it (no dimer is chosen yet) and stays pessimistic.Returns a dict with keys:
axes— tuple(L_a, L_b, L_c)of supercell axis lengths.d_max— longest coupling distance (the in-range threshold).per_shell— list of{name, d, dimer_dir, multiplicity, cross, ok}dicts, one per coupling.dimer_diris the chosen clean direction (the one of smallest multiplicity),crossthe sorted list of contaminating Jm (empty when clean),ok = (cross is empty). A shell with several symmetry-equivalent directions is OK if any one is clean (mag4 selects that representative dimer).ok—Trueiff every shell is clean.failing—(name, d)tuples for contaminated shells.recommended— smallest(na, nb, nc)(≥multipliers, by atom count) for which every shell is clean.
- mag4.supercell.find_optimal_supercell(crystal, couplings, tol, max_mult=4, *, ligand_elements=None, bond_cutoff=2.5, angle_tol=1.0, ll_cutoff=0.0, signed_dihedral=False, isolation=True, max_multiplicity=None)[source]
Return the smallest
(na, nb, nc)supercell that resolves and isolates every coupling for the four-state method.Candidates are enumerated over
[1..max_mult]^3and ranked by(volume, max(na,nb,nc), (na,nb,nc))so that (1,1,1) is tried first and the most compact / isotropic cells win ties.Two acceptance criteria are applied to each candidate, in order of size:
Resolve — every coupling has a distinct intra-cell dimer pair (
can_assign_unique_dimers()).Isolate (when
isolation=True, the default) — every J shell is also four-state-clean (check_four_state_isolation()): no periodic image of the partner couples to it through a different Jm, so the four-state combination returns only Jk (mixed in with its own multiplicityM, which the extractor divides out as4·M). This is the algebraic cancellation rule — much weaker than demanding a fully isolated dimer — so the chosen cell is the smallest in which the cross terms vanish.
The smallest candidate passing both is returned. If none isolates within
max_multthe function falls back to the smallest resolving cell and logs a warning; the CLI’s post-selection isolation check then errors (the four-state method is only valid for an isolated dimer), telling the user to raise--max-supercellor lower--cutoff. Passisolation=Falseto restore the legacy resolve-only selection, ormax_multiplicity=Nto cap the accepted dimer multiplicity —1demands a fully isolated dimer (divisor 4, the old geometric “3 copies of d” rule), larger values trade cell size against the largest4·Mdivisor.When
ligand_elementsis supplied the geometric fingerprint of each candidate pair must also match the target — sub-shells likeJ1a/J1bare then resolved as distinct couplings, which may require a larger supercell than distance-only mode.- Raises:
RuntimeError – When no candidate up to
max_multin each direction yields a complete, conflict-free dimer assignment. The message lists the couplings that remained unresolvable.- Return type:
- Parameters:
- mag4.supercell.find_supercell_for_enumeration(crystal, couplings, max_mult=4, *, tol=0.001)[source]
Smallest supercell with no coupling folding onto a periodic self-image.
This is the energy-mapping sizing criterion (distinct from the four-state
find_optimal_supercell()). The all-orders enumeration needs every coupling distancedto stay strictly below the shortest lattice translation; otherwises_i·s_jcollapses tos_i² = +1in every order, the coefficient becomes a constant absorbed intoE0, and the coupling is indeterminate in the fit.Returns the smallest
(na, nb, nc)(size-ordered) for whichshortest_periodic_image(super) > d_max + tol. If none withinmax_multqualifies, returns the largest candidate (which has the longest lattice translations, i.e. the fewest folds) and warns.- Return type:
- Parameters:
crystal (CrystalData)
couplings (list)
max_mult (int)
tol (float)
Four-state method
Site-permutation symmetry operations on the magnetic sublattice.
- mag4.symmetry.find_symmetry_ops(cart_coords, lat_vecs, tol=0.05, *, crystal=None)[source]
Find site-permutation symmetry operations of the magnetic sublattice.
Candidate operations (rotation + translation pairs) come from spglib, so any axis orientation and any space group is handled — the result no longer depends on which lattice vector is the stacking axis. Each candidate is then verified site-by-site (tolerance
tolÅ) and returned as a permutation tuple;perm[i]is the index of the site to which siteiis mapped.- Parameters:
cart_coords (Cartesian coordinates of the magnetic sites.)
lat_vecs (3×3 lattice matrix, rows = a, b, c.)
tol (site-matching tolerance in Å (also spglib’s
symprec).)crystal (optional
mag4.constants.CrystalDataof the full) – structure. When given, symmetry is computed on the full decorated crystal (magnetic + non-magnetic atoms, distinguished by element), so every returned operation is a true crystal symmetry — configurations it merges are guaranteed degenerate. When omitted, only the bare magnetic sublattice is analysed (its symmetry can be higher than the crystal’s; combine withmag4.geometry.filter_symops_by_fingerprint()if ligand environments matter).
- Return type:
Dimer search for the four-state method (direct intra-cell Cartesian distances).
- mag4.dimers.check_coupling_spacing(couplings, user_tol)[source]
Inspect spacing between consecutive J distances and return a safe dimer-search tolerance.
Warns when:
--tolis larger than half the minimum spacing (two J shells could share a dimer).Two consecutive J values are closer than
2*DIST_EQUIV_TOL(probably the same shell).
When two adjacent couplings are intentionally at the same distance (e.g. sub-shells
J1a/J1bproduced by geometry-aware grouping — distinguished by their bridging-angle fingerprint, not by distance), the zero gap between them is not a noise issue: it is dropped from the warning and from thesafe_tolcomputation so the dimer search can still use a non-zero distance tolerance. The angle filter infind_dimers()keeps the sub-shells apart.
- mag4.dimers.find_dimers(atoms, lat_mat, couplings, tol, verbose=True, *, structure=None, ligand_elements=None, bond_cutoff=2.5, angle_tol=1.0, ll_cutoff=0.0, signed_dihedral=False)[source]
Find all intra-cell atom pairs for each coupling distance ±
tol.Set
verbose=Falseto silence info logs and the missing-coupling warning block; useful when scanning many trial supercells.- Return type:
- Parameters:
Design principle
The four-state method requires that the two atoms of a dimer be DISTINCT sites in the working cell so that they can be assigned independent spins. Using periodic images to “find” a coupling would conflate atom j with its image j + (h, k, l), which is already a different atom in the supercell.
The correct workflow is therefore:
Build a supercell large enough that every coupling distance d_Jn appears as a genuine intra-cell pair (direct Cartesian distance).
Use plain
scipy.spatial.distance.cdist()here — no wrapping, no image search.
If a coupling is not found as a direct pair the cell is too small along one axis. The function diagnoses which axis and tells the user exactly which
--supercellflag to try.Angle-aware mode
When
structure(apymatgen.core.Structureof the same supercell, with the magnetic atoms placed at indices0..N_mag-1— seemag4.geometry.crystal_to_pymatgen()) andligand_elementsare supplied, eachcouplingsentry must be a triple[label, distance, fingerprint]. A candidate pair is then accepted only when both its distance and its M–L–M bridging-angle fingerprint match the target. This lets the four-state pipeline distinguish J shells that are degenerate in distance but route super-exchange through different bridging geometries.
- mag4.dimers.can_assign_unique_dimers(dimers, couplings)[source]
Return True iff the greedy assignment used by the four-state pipeline can give every coupling a distinct (idx_a, idx_b) pair.
Mirrors the assignment logic in
mag4.cli.fourstateso the optimal supercell search rejects cells that find the right distances but cannot assign them all to non-overlapping site pairs.
Generate, group and merge the four spin configurations of the four-state method.
- mag4.spinconfig.build_reference_bath(n_atoms, user_ref=None)[source]
Build the reference magnetic bath.
Default behaviour is fully ferromagnetic (all +1). Pass
user_refas a list of ±1 of lengthn_atomsto override.- Raises:
ValueError – If the length is wrong or values other than ±1 are present.
- Return type:
- Parameters:
- mag4.spinconfig.parse_magmom_tokens(s)[source]
Expand a VASP MAGMOM-style string into a flat list of floats.
Accepts plain values and
n*vrepetitions, whitespace- or comma-separated:"32*0 1 -1 2*1.5"→[0.0]*32 + [1.0, -1.0, 1.5, 1.5].
- mag4.spinconfig.build_reference_from_magmom(magmom, crystal)[source]
±1 reference bath from a MAGMOM-style string of the WORKING SUPERCELL.
The string is written exactly as one would set VASP’s
MAGMOMfor the supercell — POSCAR atom order (crystal.all_speciesspecies blocks),n*vrepetitions allowed — e.g. for a 2×2×1 La₂CuO₄ √2 cell:32*0 1 1 1 -1 -1 -1 -1 1 -1 -1 -1 1 1 1 1 -1 64*0
Only the SIGNS of the magnetic entries are used (any magnitude, e.g. ±1 or ±0.6); non-magnetic entries must be 0 and are validated against the species layout so an atom-order mismatch fails loudly. As a shorthand the string may instead carry just the
N_magmagnetic values (in the magnetic-sublattice order). Returns the ±1 bath incrystal.target_speciesorder.
- mag4.spinconfig.parse_ref_couplings(spec, coupling_names)[source]
Parse
"J1:AFM,J2:FM"→{"J1": -1, "J2": +1}.Raises
ValueErroron unknown coupling names or unrecognised signs.
- mag4.spinconfig.build_reference_from_couplings(atoms, lat_mat, couplings, spec, tol)[source]
Build a reference bath that respects the requested coupling signs.
- Parameters:
atoms (magnetic atoms in legacy
[label, x, y, z, …]format.)lat_mat (3×3 lattice matrix (columns are lattice vectors).)
couplings (
[[name, distance], …].)spec (e.g.
"J1:AFM,J2:FM".)tol (distance tolerance for the bond search.)
``(ref_bath (Returns) –
used (report)``. report carries the method)
any
conflict (frustration)
``(satisfied (and per-coupling) –
counts. (total)`` bond)
- Return type:
- mag4.spinconfig.build_reference_from_kvector(atoms, kfrac, supercell, node_tol=1e-06)[source]
Build a collinear reference bath
s_i = sign(cos(2π k·R_i)).- Parameters:
atoms (magnetic atoms in legacy
[label, x, y, z, …]format, with) – fractional coordinates in the working (super)cell.kfrac (propagation vector (3
Fraction) in parent reciprocal-lattice units.)supercell (
(na, nb, nc)multipliers used to build the working cell.)``(ref_bath (Returns) –
report)``.
node_tol (float)
- Return type:
- mag4.spinconfig.generate_four_states(ref_bath, dimer, coupling_name)[source]
Generate the four spin configurations
uu, dd, ud, dufor one dimer.Only the two atoms of the dimer differ between configurations; the rest of the magnetic bath is frozen to
ref_bath.- Return type:
- Parameters:
- mag4.spinconfig.find_unique_configs(all_configs, sym_ops, *, time_reversal=True)[source]
Group configurations equivalent under the magnetic (grey) group.
The group is the unitary crystal operations
sym_opsplus, whentime_reversalis True (the default), each of them composed with time reversal T (global spin flip) — exact in a collinear calculation without spin-orbit coupling. Returns a list of groups; the energy of any member of a group can be used interchangeably in the J extraction formulas.time_reversal=Falserestricts to the unitary subgroup — used by--check-degeneracyto keep the T-redundant configurations as separate DFT runs whose energy agreement then tests the antiunitary symmetry (and the completeness of the spin model).- Return type:
- Parameters:
- mag4.spinconfig.time_reversal_pairs(unitary_groups, sym_ops)[source]
Pairs of unitary-only classes that time reversal proves degenerate.
unitary_groupsmust come fromfind_unique_configs()withtime_reversal=False. Two classes pair up when an ANTIUNITARY operation P×T (unitary op composed with global spin flip) maps one representative onto the other — their DFT energies must then coincide even though no unitary operation relates them (they typically differ in total Sz). Returns 1-based(uid_a, uid_b)index pairs; since the unitary subgroup has index 2 in the grey group, each class belongs to at most one pair.
VASP input generation
VASP input generation: per-config POSCAR / INCAR / KPOINTS for the four-state pipeline.
Layer: VASP-specific (one of the two DFT back-ends; see also wien2k.py).
- mag4.vasp.build_magmom_lines(unique_groups, crystal, spin_magnitude)[source]
Return one MAGMOM string per unique configuration.
Atom order matches the POSCAR (
crystal.all_speciesorder). Magnetic atoms get±spin_magnitude; all others get 0.- Return type:
- Parameters:
unique_groups (List[List[FourStateConfig]])
crystal (CrystalData)
spin_magnitude (float)
- mag4.vasp.FUNCTIONAL_CHOICES = ('PBE', 'PBE+U', 'PBE0', 'R2SCAN', 'METAGGA')
Recognised values for the
functionalargument ofincar_content().PBE— plain GGA, no on-site Hubbard term, no exact exchange.PBE+U— Dudarev/Liechtenstein DFT+U on the magnetic element only.PBE0— unscreened hybrid,AEXX=0.25by default.R2SCAN— convenience alias forcingMETAGGA=R2SCAN.METAGGA— meta-GGA family;--metaggaselects the flavour (R2SCAN / SCAN / RSCAN / …).
- mag4.vasp.XC_CHOICES = ('PBE', 'PBE+U', 'PBE0', 'R2SCAN', 'METAGGA')
Backward-compatible alias (the historic flag was
--xc).
- mag4.vasp.incar_content(magmom, cfg_label, *, functional=None, xc=None, elements=None, magnetic_element=None, ldaul=None, ldauu=None, ldauj=0.0, aexx=0.25, encut=500.0, metagga='R2SCAN', lmaxtau='auto', ldau_l=None, ldau_u=None, ldau_j=None)[source]
Return the INCAR file content as a string for a given config label.
- Parameters:
magmom (
str) – Pre-built MAGMOM string for this configuration.cfg_label (
str) – Human-readable tag printed inSYSTEM.functional (
Optional[str]) – Exchange-correlation choice. One ofFUNCTIONAL_CHOICES(PBE,PBE+U,PBE0,R2SCAN,METAGGA). Case-insensitive.xc (
Optional[str]) – Backward-compatible alias forfunctional(the historic flag was--xc).functionaltakes precedence when both are supplied.elements (
Optional[List[str]]) – POSCAR element order — needed whenfunctional == "PBE+U"so the LDAU arrays carry one slot per element, and used by the meta-GGA LMAXTAU heuristic.magnetic_element (
Optional[str]) – Symbol of the magnetic species (Cu, Ni, Gd, …). The LDAU values apply only to this element; every other element gets-1 / 0.0 / 0.0.ldaul (
Optional[int]) – Hubbard parameters for the magnetic element.ldaulis the orbital quantum number (2for d,3for f);ldauuandldaujare in eV. Required whenfunctional == "PBE+U".ldauu (
Optional[float]) – Hubbard parameters for the magnetic element.ldaulis the orbital quantum number (2for d,3for f);ldauuandldaujare in eV. Required whenfunctional == "PBE+U".ldauj (
float) – Hubbard parameters for the magnetic element.ldaulis the orbital quantum number (2for d,3for f);ldauuandldaujare in eV. Required whenfunctional == "PBE+U".aexx (
float) – Fraction of exact exchange in the PBE0 hybrid (default 0.25). Ignored for non-hybrid functionals.encut (
float) – Plane-wave cut-off in eV written toENCUT(default 500).metagga (
str) – Meta-GGA flavour written to theMETAGGAtag whenfunctional == "METAGGA"(default R2SCAN, but typical choices include SCAN, RSCAN, R2SCAN, R2SCANL). Whenfunctional == "R2SCAN"the value is forced to R2SCAN.lmaxtau (
Union[int,str]) – Meta-GGALMAXTAUtag (s→2, p→4, d→6, f→8)."auto"(default): 8 if a lanthanide/actinide is present, else VASP’s default 6 is used (line commented out). Force 6 explicitly for f-in-core POTCARs (RE_3, La_s) where the 4f is not in the valence.ldau_l (
Optional[int]) – Deprecated hyphen-style aliases forldaul/ldauu/ldauj— kept so older callers (and tests) keep working.ldau_u (
Optional[float]) – Deprecated hyphen-style aliases forldaul/ldauu/ldauj— kept so older callers (and tests) keep working.ldau_j (
Optional[float]) – Deprecated hyphen-style aliases forldaul/ldauu/ldauj— kept so older callers (and tests) keep working.
- Return type:
- mag4.vasp.write_incar(magmom, name, total_atoms, **kwargs)[source]
Write an INCAR for Config #1. Returns the file path.
Extra keyword arguments are forwarded to
incar_content()(functional/xc,elements,magnetic_element,ldaul/ldauu/ldauj,aexx,encut,metagga,lmaxtau).
- mag4.vasp.DEFAULT_KSPACING = 0.3
Default reciprocal-space k-point spacing (Å⁻¹), VASP-style. Denser than VASP’s own 0.5 Å⁻¹ default — well converged for the magnetic insulators / semiconductors this is usually applied to. METALS need a finer mesh (smaller KSPACING, ~0.15–0.2 Å⁻¹, with appropriate smearing): pass –kspacing explicitly for those.
- mag4.vasp.resolve_kmesh(lat_mat, *, kspacing=None, kdens=None)[source]
Resolve the Γ-mesh and how it was chosen.
kspacing(VASP-style, Å⁻¹) takes precedence; elsekdens(legacy, Å); elseDEFAULT_KSPACING. Returns(mesh, mode, value)withmodein{"kspacing", "kdens"}.
- mag4.vasp.write_kpoints(lat_mat, path, *, kspacing=None, kdens=None)[source]
Write a Γ-centred automatic KPOINTS file.
The mesh follows VASP’s KSPACING rule by default (denser than VASP’s 0.5 Å⁻¹); pass
kdensto use the legacy real-space density instead.
- mag4.vasp.create_config_dirs(unique_groups, crystal, magmom_lines, name, kdens=None, compound='', write_vasp_extras=True, *, kspacing=None, functional=None, xc=None, magnetic_element=None, ldaul=None, ldauu=None, ldauj=0.0, aexx=0.25, encut=500.0, metagga='R2SCAN', lmaxtau='auto', ldau_l=None, ldau_u=None, ldau_j=None)[source]
Create one directory per unique configuration with POSCAR (+ optional INCAR + KPOINTS).
Layout:
<compound>/config1/POSCAR <compound>/config1/INCAR (only if write_vasp_extras) <compound>/config1/KPOINTS (only if write_vasp_extras) <compound>/config2/...
- Parameters:
write_vasp_extras (bool, default
True) – WhenFalse, only POSCAR is written (useful for the WIEN2k workflow, where POSCAR is later converted tocase.structvia WIEN2k’sxyz2structtool, and the magnetic information lives incase.instinstead).unique_groups (List[List[FourStateConfig]])
crystal (CrystalData)
name (str)
kdens (float | None)
compound (str)
kspacing (float | None)
functional (str | None)
xc (str | None)
magnetic_element (str | None)
ldaul (int | None)
ldauu (float | None)
ldauj (float)
aexx (float)
encut (float)
metagga (str)
ldau_l (int | None)
ldau_u (float | None)
ldau_j (float | None)
- Return type:
WIEN2k input generation
WIEN2k input generation: configN.inst + configN.struct per four-state
spin configuration.
Layer: WIEN2k-specific (one of the two DFT back-ends; see also vasp.py).
In each configN/ directory mag4 writes:
POSCAR– same VASP file used by--code vasp(kept for cross-checking; also accepted as input by WIEN2k’sxyz2struct).configN.inst– produced by piping theu/d/nspin pattern into WIEN2k’sinstgen -askscript. Named after the directory so thecase == basename(pwd)convention works out of the box.configN.struct– written directly by Python (noxyz2structneeded). Every atom is emitted as inequivalent (MULT=1) with a unique label of the form<element><n>("Cs1","V 9","I17"…) so WIEN2k cannot merge atoms via symmetry analysis. Only the identity symmetry op is written. This guarantees the atom order inconfigN.structmatches the order assumed byconfigN.inst.
Mapping convention
Each entry of the spin pattern maps as follows:
+1 (or any positive value) → ``u``
-1 (or any negative value) → ``d``
0 (non-magnetic atom) → ``n``
So a MAGMOM line -7 2*7 -7 3*7 4*0 becomes d u u d u u u n n n n.
- mag4.wien2k.ANG_TO_BOHR: float = 1.8897261339212517
Ångström → Bohr conversion factor used by WIEN2k struct files.
- mag4.wien2k.SYMMORPHIC_SG_NUMBERS: frozenset = frozenset({1, 2, 3, 5, 6, 8, 10, 12, 16, 21, 22, 23, 25, 35, 38, 42, 44, 47, 65, 69, 71, 75, 79, 81, 82, 83, 87, 89, 97, 99, 107, 111, 115, 119, 121, 123, 139, 143, 146, 147, 148, 149, 150, 155, 156, 157, 160, 162, 164, 166, 168, 174, 175, 177, 183, 187, 189, 191, 195, 196, 197, 200, 202, 204, 207, 209, 211, 215, 216, 217, 221, 225, 229})
The 73 symmorphic space groups (no glide planes / no screw axes). Used by the
--no-reducebranch inwrite_symmetric_case_files()to decide whether to refine orbit groupings (_cell_compatible_symmetry + _relabel_…) or to fall back to “every atom MULT=1” (_ungrouped_symmetry).For symmorphic parents, the refined point group at origin is closed under composition modulo the supercell lattice, so the union-find in _cell_compatible_symmetry produces correct supercell-as-P-Bravais orbits. For non-symmorphic parents the parent’s intrinsic glide / screw translations alias with supercell-induced internal translations and the union-find over-merges (e.g. MULT=16 in a Pmna 2×2×1 supercell where Pmna’s max Wyckoff multiplicity is 8).
- mag4.wien2k.magmom_to_udn(values)[source]
Translate a flat list of MAGMOM values to the
u/d/nletters.- Parameters:
values (sequence of float) – One value per atom in POSCAR order. Sign carries the spin direction; zero means non-magnetic.
- Returns:
One letter per atom:
"u"for positive,"d"for negative,"n"for zero.- Return type:
Examples
>>> magmom_to_udn([-7, 7, 7, -7, 7, 7, 7, 0, 0, 0, 0]) ['d', 'u', 'u', 'd', 'u', 'u', 'u', 'n', 'n', 'n', 'n']
- mag4.wien2k.build_udn_lines(unique_groups, crystal, spin_magnitude=1.0)[source]
Per-unique-config list of u/d/n letters in POSCAR order.
Returns a list of length
len(unique_groups); each element is the list of u/d/n letters for one configuration. Order matchesmag4.vasp.build_magmom_lines().- Return type:
- Parameters:
unique_groups (List[List[FourStateConfig]])
crystal (CrystalData)
spin_magnitude (float)
- mag4.wien2k.wien2k_label(element_symbol, idx)[source]
Format an atom label per WIEN2k case.struct convention.
Two-letter element: e.g.
"Cs1"→ cols 1-2 ="Cs".One-letter element:
"V 1"(letter + space) → cols 1-2 ="V ".
The convention matters because instgen reads the element from the first two characters of each
RMT=line incase.struct.
- mag4.wien2k.write_stub_struct(crystal, path)[source]
Write a minimal
case.structgood enough for instgen.Only the per-atom
RMT=line is emitted (one per atom in POSCAR order) because that is the only thing instgen parses. Usewrite_struct_file()to produce a full, ready-to-use struct file.- Return type:
- Parameters:
crystal (CrystalData)
path (str)
- class mag4.wien2k.ConfigSymmetry(spacegroup_number, spacegroup_symbol, rotations, translations, origin_shift, new_to_old, orbits, frac_coords_new, mapping_to_primitive=<factory>)[source]
Bases:
objectSymmetry analysis of a single spin-decorated configuration.
The cell is kept verbatim from the input
CrystalData; only the atom ordering (within the cell) and the symmetry-op list are derived.- Parameters:
- spacegroup_number, spacegroup_symbol
Space group reported by spglib for the spin-decorated structure.
- rotations, translations
Symmetry operations in the input cell basis, ready to be written verbatim into a WIEN2k struct file (
rotationsis integer; the translations are in fractional coordinates).
- origin_shift
Origin shift that spglib would apply to put the structure into the standard setting. Kept for diagnostics; not applied to atom positions here — we keep input positions so the rotations/translations in the input basis act directly.
- new_to_old
new_to_old[i]is the original POSCAR index of the atom that now sits at positioniin the symmetrised order. Length = number of atoms.
- orbits
List of Wyckoff orbits in the new order: each orbit is a list of consecutive new-order indices that share a symmetry equivalent class.
len(orbits)is the number of inequivalent sites (== WIEN2k’sNEQUIV).
- frac_coords_new
Fractional coordinates in the new order (shape
(n_atoms, 3)).
- mapping_to_primitive: List[int]
For each atom in the new order, the index of the corresponding atom in spglib’s primitive cell. Used by the WIEN2k struct writer to fold orbits by lattice-centering equivalence (only one rep per primitive index is emitted under an F/B/C-centred letter, matching WIEN2k’s “list one of four / one of two” convention).
- class mag4.wien2k.MagneticGroupInfo(uni_number, bns_number, og_number, msg_type, parent_number, parent_symbol, n_unitary, n_primed, mpg_symbol='', mpg_kind='')[source]
Bases:
objectMagnetic (Shubnikov) space group of one spin-decorated configuration.
spglib classifies the configuration (collinear moments included, so the PRIMED operations — unitary op × time reversal — are counted) into one of the 1651 magnetic space-group types. spglib provides the standard numbers but not the symbol string; the BNS number is the citable label (symbol lookup: Bilbao MGENPOS / ISO-MAG).
msg_typeis the Shubnikov type: 1 = colorless (no primed ops), 2 = grey (contains pure time reversal — only for zero-moment structures), 3 = black-white (half the point operations primed), 4 = black-white with anti-translations (a translation × T maps the sublattices).- Parameters:
- point_group()[source]
Magnetic point group with its kind, e.g.
mmm1' (grey).Dropping the translations from the magnetic space group gives the magnetic point group exactly: an anti-translation (type IV) becomes pure time reversal 1’, so the point group is GREY
G1'; a type-III group keeps half its point operations primed and is written in the ShubnikovG(H)notation (parent, unitary subgroup); a type-I group is the ordinary (colourless) symbol.- Return type:
- mag4.wien2k.analyze_config_magnetic_group(crystal, group, *, spin_magnitude=1.0, symprec=0.001)[source]
Magnetic space group (Shubnikov/BNS) of a spin-decorated configuration.
Unlike
analyze_config_symmetry()— which colours up/down spins as different species and therefore sees only the UNITARY part of the magnetic group — this passes the collinear moments to spglib’s magnetic dataset, so operations combined with time reversal (primed) are included. ReturnsNonewhen spglib is too old (< 2.0) or fails; callers should then simply omit the magnetic-group line.- Return type:
- Parameters:
crystal (CrystalData)
group (List[FourStateConfig])
spin_magnitude (float)
symprec (float)
- mag4.wien2k.analyze_config_symmetry(crystal, group, *, spin_magnitude=1.0, symprec=0.001)[source]
Run spglib on the spin-decorated configuration and pack the result.
Atoms in
CrystalDataare coloured according to their spin (see_spglib_atom_types()), so the detected space group is the magnetic space group of the configuration projected onto the user’s cell — equivalent to “highest symmetry compatible with this cell”.The returned
ConfigSymmetrycarries:the rotations/translations in the input cell basis (so we can write them straight into a WIEN2k struct file);
the permutation that groups atoms by their Wyckoff orbit, with orbits ordered by the index of their representative — this becomes the new atom order on disk.
On any spglib error or empty dataset we fall back to a P1 (identity-only)
ConfigSymmetryso the rest of the pipeline can proceed unchanged.- Return type:
- Parameters:
crystal (CrystalData)
group (List[FourStateConfig])
spin_magnitude (float)
symprec (float)
- mag4.wien2k.write_struct_file(crystal, path, *, title=None, rmt=2.0, npt=781, r0=None, calc_mode='RELA', lattice=None)[source]
Write a complete WIEN2k
case.structfrom aCrystalData.Every atom is emitted as a separate inequivalent site (
MULT=1) with a unique label of the form<element><n>("Cs1","V 9","I17"…). Only the identity symmetry operation is written, so WIEN2k will not merge or reorder atoms. This guarantees that the atom order inconfigN.structmatches the order assumed byconfigN.inst.See
write_struct_file_symmetric()for the spin-aware variant that uses real-space symmetry (default in the CLI; this function is kept as a fallback behind--no-symmetrize-struct).- Parameters:
crystal (
CrystalData) – The (super-)cell as produced bymag4.supercell.path (
str) – Where to write the struct file (full path including filename).title (
Optional[str]) – Free-text title written on line 1. Defaults to a mag4-tagged note.rmt (
Union[float,Dict[str,float]]) – Muffin-tin radius in bohr. Either a single value applied to every element (default 2.0) or a dict{element_symbol: value}.npt (
int) – Number of radial mesh points (default 781; must be odd).r0 (
Union[float,Dict[str,float],None]) – First radial mesh point in bohr.None(default) → picked per-Z via_default_r0(); a single float applies to all elements; a dict allows per-element overrides.calc_mode (
str) –"RELA"(relativistic, default) or"NREL"(non-relativistic).lattice (
Optional[str]) – WIEN2k single-letter lattice code. Auto-detected from the cell angles whenNone.
- Return type:
Notes
The per-atom header line follows the format declared in the WIEN2k user guide:
(A10, 5X, I5, 5X, F10.8, 5X, F10.5, 5X, F10.5)forname, NPT, R0, RMT, Zrespectively. The 5X gaps carry decorative text (" NPT="," R0="," RMT="," Z:") for readability — WIEN2k skips them when parsing.
- mag4.wien2k.write_struct_file_symmetric(crystal, config_sym, path, *, title=None, rmt=2.0, npt=781, r0=None, calc_mode='RELA', lattice=None, labels=None)[source]
Write a WIEN2k
case.structthat exposes the configuration’s detected space-group symmetry.One inequivalent block is emitted per Wyckoff orbit (
MULT= orbit size), with the orbit’s representative listed on the leadingATOMline and the remaining members on continuation lines between theMULT=line and the per-orbitNPT/R0/RMTline. Each orbit gets a unique<element><n>label so WIEN2k cannot merge orbits that happen to share an element. The trailing symmetry block lists every rotation/translation reported by spglib in the input cell basis.Atom positions are written in the order encoded by
config_sym.frac_coords_new— i.e. spglib’s equivalence classes grouped together, with orbits ordered by their representative index.
- mag4.wien2k.write_case_struct_files(crystal, n_configs, parent_dir, *, rmt=2.0, npt=781, r0=None)[source]
Write
configN.structinto everyconfigN/subdirectory.The cell and atom list are identical across configurations; only the spin pattern (in
configN.inst) differs. We therefore write the same struct content N times, naming each file after its directory.
- mag4.wien2k.write_symmetric_case_files(unique_groups, crystal, parent_dir, *, instgen_path=None, spin_magnitude=1.0, symprec=0.001, rmt=2.0, npt=781, r0=None, reduce_cell=False)[source]
Write
configN.structandconfigN.instper config with a shared, symmetry-derived atom order.For each configuration:
Run
analyze_config_symmetry()once on the spin-decorated cell.Write
configN.structviawrite_struct_file_symmetric()— one block per Wyckoff orbit, full symmetry-op tail, unique per-orbit labels.Drive instgen with one
u/d/nanswer per orbit (the spin of the orbit’s representative) and save the resultingconfigN.inst.
Because both files are produced from the same
ConfigSymmetry, their atom orders are guaranteed to agree without any post hoc reordering.Returns a dict with four keys:
"struct"(list of struct paths),"inst"(list of inst paths),"sym"(list ofConfigSymmetryresults, one per config — handy for tests), and"crystals"(the per-config reducedCrystalDataused to write each struct — used by the report writer to record per-config cell metadata for mag4-extract’s cell-size rescaling).When
reduce_cellis true (--reduce; the default is off, so every config keeps the one working supercell and shares identical numerics) each config is rewritten in its colored-structure primitive cell viamag4.geometry.reduce_to_primitive()— multiplicities then match the parent SG’s standard Wyckoff table, and downstream DFT runs are correspondingly cheaper for high-symmetry configs.
- mag4.wien2k.write_job_scripts(parent_dir, *, functional=None, kspacing=None, element=None, ldaul=None, ldauu=None, ldauj=None)[source]
Write
job.initandjob.runintoparent_dirand chmod +x them.Both scripts iterate over the sibling
config*/directories viaforeach; they belong at the compound level, not inside each configN/. Existing files are overwritten so re-runningmag4-magneticis safe. The template follows P. Blaha’s recommended workflow:setrmt(so the muffin-tin radii mag4 writes into the struct files are placeholders),init_lapw -sp -prec 2n -noreduc -nohdlo -nokshift(-nokshiftis always present, so the k-mesh is unshifted by default), and an interactive check when sgroup proposes an origin-shifted struct (accept only if lattice type and atom count are unchanged — sgroup may e.g. reduce P to CXY, which would silently change the cell the report’s energy formulas refer to).kspacing(Å⁻¹, VASP convention; defaults to mag4’s 0.3) is converted to bohr⁻¹ and inserted asinit_lapw -numk -1 <spacing>so the WIEN2k k-mesh matches the VASP one. Whenfunctional == "PBE+U"andldauuis given, theinit_orb -orbheredoc is generated active with<element> <l> <U> <J>fromelement/ldaul/ldauu/ldauj, andjob.runcallsrunspwith-orb; otherwise the block stays commented out as a template.ldauu/ldaujare taken in eV (VASP convention — the very same values the INCAR’sLDAUU/LDAUJreceive) and divided byRY_TO_EVon the way out, becauseinit_orbreads U and J in Rydberg.ldaulis a quantum number and is passed through unchanged. A comment above the heredoc echoes both the eV input and the Ry output so the conversion is checkable in the generated script.
- mag4.wien2k.find_instgen()[source]
Locate the WIEN2k
instgenscript.Lookup order:
$WIEN2k_INSTGENenvironment variable, if set.instgenon$PATH.instgen_lapwon$PATH(the canonical WIEN2k name).$WIENROOT/instgen_lapw.~/bin/instgen.
Returns the absolute path or
Noneif not found.
- mag4.wien2k.run_instgen(udn_letters, crystal, instgen_path)[source]
Run
instgen -askonce and return the producedcase.insttext.Internally uses a temporary directory containing a stub
case.struct, pipes theu/d/nletters as answers, runs instgen, reads back the resultingcase.instand cleans up.- Raises:
RuntimeError – If instgen exits non-zero or fails to produce
case.inst.- Return type:
- Parameters:
crystal (CrystalData)
instgen_path (str)
- mag4.wien2k.write_case_inst_files(unique_groups, crystal, parent_dir, instgen_path=None, spin_magnitude=1.0)[source]
For every unique configuration, write a
configN.instfile inconfigN/.The output file is named after the directory (
config1.instinsideconfig1/, etc.) so that WIEN2k’s standardcase == basename(pwd)convention works directly on the user’s HPC machine.- Parameters:
unique_groups (
List[List[FourStateConfig]]) – Same conventions asmag4.vasp.build_magmom_lines().crystal (
CrystalData) – Same conventions asmag4.vasp.build_magmom_lines().spin_magnitude (
float) – Same conventions asmag4.vasp.build_magmom_lines().parent_dir (str) – Compound (or run) directory hosting the
configN/subdirectories.instgen_path (str, optional) – Path to the
instgenscript. Auto-detected viafind_instgen()if omitted.
- Returns:
Paths of the written
configN.instfiles, in config order.- Return type:
Energy parsing
DFT total-energy parsers (VASP OUTCAR, WIEN2k case.scf).
The get_energy() dispatcher returns an EnergyResult describing
both the converged total energy (in eV) and a structured convergence
status, which the upcoming mag4-extract CLI uses to decide whether each
configuration is safe to feed into the J-extraction formulas.
Only the fields actually needed for the four-state pipeline are extracted:
the last total energy printed in the file (assumed converged);
a coarse convergence flag (the calculation finished cleanly);
for WIEN2k: the last
:DIS(charge distance) value and the verbatim:DIS/:ENElines, so the user can verify the parser picked up the right data and that the SCF converged tightly enough.
Both parsers are streaming (line-by-line) and tolerate large output files without loading them entirely into memory.
- class mag4.energy.EnergyResult(energy_eV, converged, source, n_iter=0, diagnostics=<factory>, last_dis=None, last_dis_line=None, last_ene_line=None)[source]
Bases:
objectOutcome of parsing one DFT output file.
- Parameters:
- energy_eV
Last total energy reported in the file, expressed in eV.
Nonewhen no energy could be parsed (typically: the calculation never produced one — crashed early, file truncated, etc.).- Type:
float or None
- converged
Coarse convergence flag (see backend-specific notes in
extract_vasp_energy()/extract_wien2k_energy()).- Type:
- mag4.energy.extract_vasp_energy(outcar_path)[source]
Parse a VASP
OUTCARand return its converged total energy.- Convergence rule (simple, code-agnostic):
convergedisTruewhen all of the following hold:the file exists and is readable,
at least one
free energy TOTENline was parsed,no
WARNING: ... NELM = Nline was emitted,the SCF actually converged — either VASP’s electronic marker
aborting loop because EDIFF is reachedor the ionicreached required accuracyappears — or the run finished cleanly (Voluntary context switches:).
The clean-finish marker is sufficient but not required: a job OOM-killed or timed out during the final clean-up drops that tail without invalidating the already-converged energy, so the EDIFF marker rescues it (see
diagnostics).Tighter convergence checks (per-element forces, stress, EDIFF / EDIFFG numerical thresholds) are intentionally NOT enforced here — they vary per project; see
diagnosticsfor the raw flags so the caller can decide what to do.
- Returns:
Always non-
None; check.energy_eVand.convergedto interpret the outcome.- Return type:
- Parameters:
outcar_path (str)
- mag4.energy.extract_wien2k_energy(scf_path)[source]
Parse a WIEN2k SCF file and return its converged total energy in eV.
- Return type:
- Parameters:
scf_path (str)
- Convergence rule (simple):
convergedisTruewhen all of the following hold:the file exists and is readable,
at least one
:ENEline was parsed,at least one
:ITE…:iteration block was detected,the energy was emitted by the last iteration block of the file (i.e. the calculation didn’t crash mid-iteration).
This does not enforce a numeric threshold on
:DIS(charge distance) — the value is surfaced inEnergyResult.last_disand the verbatim:DISline inEnergyResult.last_dis_lineso the user can eyeball that the SCF converged tightly enough.
Conversion:
E [eV] = E [Ry] × 13.605693122994.
- mag4.energy.get_energy(config_dir, code, scf_basename=None)[source]
Convenience dispatcher: parse the right output for the requested code.
- Parameters:
config_dir (str) – Path to a single
configN/directory.code ({'vasp', 'wien2k'}) –
Which output file to look for inside
config_dir:vasp→OUTCARwien2k→ SCF file (seescf_basename).
scf_basename (str, optional) – WIEN2k only. Basename (with or without the
.scfsuffix) of the SCF file to parse insideconfig_dir. Useful when several SCF runs are stored side by side under names likepbe.scf,r2scan.scf. When omitted (default) the standard WIEN2k convention<basename(config_dir)>.scfis used — i.e.config5/config5.scfforconfig5/.
- Return type:
- mag4.energy.read_vasp_magnetization(config_dir)[source]
Total magnetization (μ_B) of a converged VASP run, or
None.Prefers the last
mag=inOSZICAR; falls back to theOUTCARnumber of electron … magnetization …line. Collinear (ISPIN=2) runs report a single scalar moment.
- mag4.energy.read_vasp_site_moments(config_dir, ion_indices)[source]
Per-ion magnetic moments (μ_B) from a VASP
OUTCAR.Parses the last
magnetization (x)block (the converged one; needsLORBIT=10/11in the INCAR — mag4 setsLORBIT=11by default) and returns thetotcolumn (last field of each ion row) for the requested 1-based ion indices. Ion order follows the POSCAR; mag4 writes the magnetic atoms first, so they are ions1 … n_mag.Returns
{ion: float or None}(Nonefor an ion the block didn’t list, or{}-like all-None when no block/OUTCAR was found).
- mag4.energy.read_site_moments(config_dir, code, ion_indices, scf_basename=None)[source]
Per-site magnetic moments for the requested code (dispatcher).
- mag4.energy.read_wien2k_magnetization(config_dir, scf_basename=None)[source]
Total magnetic moment in the cell (μ_B) from the last
:MMTOTline.The SCF file is resolved exactly as in
get_energy()(<basename(config_dir)>.scfunlessscf_basenameoverrides it).
- mag4.energy.read_magnetization(config_dir, code, scf_basename=None)[source]
Total magnetization (μ_B) for the requested code, or
None.
- class mag4.energy.BandGap(gap_eV, metallic, vbm_eV=None, cbm_eV=None, source='', note='')[source]
Bases:
objectBand gap of one configuration (eV).
gap_eVis the conduction–valence separation (0.0 when metallic);metallicisTruewhen occupied and empty states overlap (no gap),Nonewhen the file carried no usable eigenvalue information.- Parameters:
- mag4.energy.extract_vasp_gap(outcar_path, *, occ_tol=0.5, gap_tol=0.05)[source]
Band gap from the eigenvalue/occupation block at the end of a VASP OUTCAR.
Reads the last eigenvalue dump (after the final
E-fermiline, all k-points and spin channels), splits bands into occupied (occ >occ_tol) and empty, and reportsCBM − VBM. Metallic when they overlap.
- mag4.energy.extract_wien2k_gap(scf_path, *, occ_tol=0.5, gap_tol=0.05)[source]
Band gap from a WIEN2k SCF file.
Prefers the
:GAPline (printed when lapw2 finds a gap). Otherwise — e.g. with temperature smearing — uses theBandranges/:BANnnnnnblock: VBM = highestemaxof an occupied band (occ >occ_tol), CBM = lowesteminof an empty band; energies are in Ry and converted to eV.
J-value extraction
J-value extraction from a completed four-state DFT campaign.
Pipeline (mirrors what the user did manually before this module existed):
Parse the
four_state_report.datwritten bymag4.cli.fourstate.main()to recover, for each shellJₙ, which 4 unique configurations contribute to the formulaJS² = (E_uu + E_dd − E_ud − E_du) / 4.Read each configuration’s converged DFT total energy from
configN/viamag4.energy.get_energy().Combine energies with the formulas to produce
JS²(andJif a spin S is given).
This module is the pure-Python core; the CLI in mag4.cli.extract
adds argument parsing, pretty-printing and j_values.dat output.
- mag4.extract.DEFAULT_REPORT_NAME = 'four_state_report.dat'
Name of the report file produced by
mag4-magnetic(default).
- class mag4.extract.Formula(name, distance_A, config_uu=None, config_dd=None, config_ud=None, config_du=None, incomplete=False, divisor=4, raw='')[source]
Bases:
objectOne row of the EXTRACTION FORMULAS section.
- Parameters:
- config_uu, config_dd, config_ud, config_du
Unique-configuration indices (1-based, matching the
configN/directory names) whose energies are combined as(E_uu + E_dd − E_ud − E_du) / 4.- Type:
- incomplete
True when the report flagged this shell as INCOMPLETE (cell too small to give a distinct dimer). In that case the four config indices are unset and J cannot be computed.
- Type:
- mag4.extract.parse_report_formulas(report_path)[source]
Extract every
J … = ( E[#…] + …) / 4line from a report file.- Parameters:
report_path (str) – Path to a
four_state_report.datproduced bymag4-magnetic.- Returns:
One entry per coupling shell discovered in the report, in the order they appear. Shells flagged INCOMPLETE in the report are included with
incomplete=True(so the caller can warn about them) but with no config indices.- Return type:
list of
Formula- Raises:
FileNotFoundError – If
report_pathdoesn’t exist.ValueError – If the file exists but contains no recognisable formula or INCOMPLETE line — most likely the wrong file was passed.
- mag4.extract.parse_report_config_cells(report_path)[source]
Read per-config
n_atomsvalues from a four_state_report.dat.The report emits a
Cell: a=… | … | n_atoms=N | …line under eachConfig #uid:block when WIEN2k cell reduction is on. Returns{config_index → n_atoms}. Older reports without the line yield an empty dict and the caller falls back to assuming every config sits in the same cell (today’s behaviour, no rescaling).
- mag4.extract.read_wien2k_struct_atom_count(struct_path)[source]
Sum the
MULT=Nfields across a WIEN2kcase.structfile.This is the authoritative atom count WIEN2k’s
:ENErefers to. Wheneverinit_lapw’ssgrouprewrites the struct (cell reduction, lattice-letter correction), mag4’s pre-emption fromfour_state_report.datcan disagree – the post-sgroupfile on disk is the truth. ReturnsNoneif the file is missing or noMULT=field could be parsed.Example: a P6_3/mmc primitive cell with 2 Cr + 2 Sb listed (each MULT=2) returns 4.
- mag4.extract.read_struct_atom_counts(compound_dir, n_configs)[source]
For every
configN/directory undercompound_dir, sum theMULT=fields ofconfigN.struct(whatever WIEN2k’sinit_lapwleft on disk after possibly acceptingsgroup’s rewrite) and return{config_index → atom_count}.Missing struct files or unparseable ones are simply skipped – callers should fall back on the report’s recorded counts.
- mag4.extract.parse_report_supercell_atoms(report_path)[source]
Read the total atom count in the supercell from a four_state_report.dat header.
The four-state formula is defined on the supercell mag4 built (e.g. NiO 1×1×2 → 16 atoms). When configs are computed in reduced cells (Fm-3m conv → 2 atoms, etc.), their WIEN2k :ENE values must be scaled up to the supercell size before applying the formula. This helper returns the integer that
mag4-extractshould use as the scaling target.Returns
Nonefor reports written by mag4 versions before theTotal atoms in supercellline was added; the caller then falls back tomath.lcm(per-config n_atoms).
- mag4.extract.parse_report_supercell(report_path)[source]
Read the supercell multipliers
(na, nb, nc)from a four-state report header lineSupercell : na×nb×nc.Noneif absent.
- mag4.extract.parse_report_config_sz(report_path)[source]
{config_index: Sz}from the per-configSpins:lines, whereSz = Σ s_iof the ±1 spin vector (the net collinear moment quantum number of that configuration).
- mag4.extract.parse_report_tr_pairs(report_path)[source]
[(configA, configB), …]from the TIME-REVERSAL DEGENERACY CHECK section written bymag4-magnetic --check-degeneracy(any method).Each pair is related only by an antiunitary operation (unitary crystal op × time reversal), so the two DFT energies must coincide; the caller reports
|ΔE|per pair. Empty when the report has no such section.
- class mag4.extract.JResult(name, distance_A, formula, energies_eV=<factory>, JS2_eV=None, JS2_meV=None, J_meV=None, J_K=None, error=None)[source]
Bases:
objectOne row of the final J table emitted by
compute_j_values().- Parameters:
- energies_eV
Per-state total energies in eV. Keys:
"uu","dd","ud","du".Nonewhen the corresponding config did not converge (or didn’t exist).
- JS2_eV, JS2_meV
Computed
JS² = (E_uu + E_dd − E_ud − E_du) / 4.Noneif the formula could not be evaluated (any energy missing).- Type:
float or None
- J_meV, J_K
Computed
J = JS² / S²in meV and Kelvin (J / k_B). Only populated when a positive spinSwas passed.- Type:
float or None
- mag4.extract.compute_j_values(formulas, energies, spin=None, config_n_atoms=None, n_supercell_atoms=None)[source]
Apply each formula to the matching energies and return the J table.
- Parameters:
formulas (list of
Formula) – Output ofparse_report_formulas().energies (dict[int, EnergyResult]) – Map
config_index → EnergyResultfor every config the formulas reference. Indices come from theconfigN/dir name, 1-based. Configs that didn’t converge MUST still be present withEnergyResult.converged = Falseso this function can attribute the failure correctly.spin (float, optional) – Magnetic spin quantum number
S. When given (>0), eachJS²is divided byS²to also reportJin meV and in Kelvin.config_n_atoms (dict[int, int], optional) – Map
config_index → n_atomsfromparse_report_config_cells(). When supplied, each contributing config’s total energy is rescaled byN_LCM / N_Xbefore applying the four-state formula — necessary whenmag4-magneticwrote each config in its own primitive cell (--reduce-cell, the default). When omitted every config is assumed to sit in the same cell (legacy behaviour, no rescaling).n_supercell_atoms (int | None)
- Returns:
One entry per input formula, same order.
- Return type:
list of
JResult
- mag4.extract.read_all_energies(compound_dir, code, max_index=None, scf_basename=None)[source]
Walk
<compound_dir>/configN/and run the energy parser on each.- Parameters:
compound_dir (str) – Root directory containing the
configN/subdirectories.code ({'vasp', 'wien2k'}) – Forwarded to
mag4.energy.get_energy().max_index (int, optional) – If given, only configs
config1…configNare read. Useful to short-circuit on very large compound dirs.scf_basename (str | None)
- Returns:
Map
config_index → EnergyResult. Empty if noconfigN/directory was found.- Return type:
- mag4.extract.write_j_values_dat(results, path, compound_dir, code, spin=None, supercell=None, energies=None, config_sz=None, mags=None)[source]
Persist the four-state results to a fixed-width text file.
Beyond the J table this records the supercell used and a per-configuration block (total energy, SCF iterations, converged moment vs the configuration’s
Sz) so the file is self-contained — everything shown on screen, plus the moment/Sz consistency check.# mag4-extract — four-state J values # compound: <dir> code: vasp spin: 3.5 # supercell: 1x1x2 # # Per-configuration energies # config iter energy (eV) Sz mag exp|mag| status # … # # name d_mean(A) JS2(eV) JS2(meV) J(meV) J(K) status J1 3.8987 -0.12345678 -123.456789 -10.082 -116.99 OK
Reports and pipeline
Pretty-printers for distance tables, J-shell summaries and four-state reports.
- mag4.reports.print_mm_distances(results, element, cutoff, structure)[source]
Tabular dump of all M–M pairs found within
cutoff.- Return type:
- mag4.reports.print_inequivalent(groups, element, tol, *, ligand_elements=None, bond_cutoff=2.5, angle_tol=1.0, ll_cutoff=0.0, z_formula_units=1)[source]
Tabular dump of the inequivalent J shells.
The
Multip.column gives the number of distinct M–M dimers per primitive cell, taking periodic boundary conditions into account (computed via pymatgen’sStructure.get_neighbors()with canonical(min_idx, max_idx, image)deduplication). A second/f.u.column normalises that byz_formula_units(the number of formula units in the primitive cell, taken from the reduced composition).When
ligand_elementsis supplied the shells were produced bymag4.geometry.group_inequivalent_geom(); an extraM-L-M bridgecolumn is shown. Without it, the legacymag4.io_cif.group_inequivalent()output is printed.
- mag4.reports.print_report(all_configs, unique_groups, atoms, couplings, ref_bath, n_sym_ops, supercell, output_file=None, *, per_config_cells=None, n_supercell_atoms=None, multiplicities=None, config_coefficients=None, ring_coefficients=None, ring_edge=None, spectator_shells=None, spectator_coefficients=None, tr_pairs=None, config_msgs=None)[source]
Print (and optionally write) the full four-state analysis report.
per_config_cells(optional) is a list, one dict per unique config inunique_groupsorder, with keyscell(6-tuple a,b,c,α,β,γ),n_atoms(int),volume_A3(float),sg_number(int),sg_symbol(str). When supplied, a one-line cell summary is emitted after eachConfig #Nblock somag4-extractcan rescale per-config energies for the four-state formula.- Return type:
- Parameters:
all_configs (List[FourStateConfig])
unique_groups (List[List[FourStateConfig]])
atoms (list)
couplings (list)
ref_bath (list)
n_sym_ops (int)
output_file (str | None)
n_supercell_atoms (int | None)
config_coefficients (List | None)
ring_edge (float | None)
spectator_shells (List | None)
spectator_coefficients (List | None)
config_msgs (List | None)
High-level CIF analysis pipeline (loads structure, prints distance tables, returns crystal + couplings).
- mag4.pipeline.run_cif_analysis(cif_path, element, cutoff, tol=0.01, *, ligand_elements=None, bond_cutoff=2.5, angle_tol=1.0, ll_cutoff=0.0, signed_dihedral=False)[source]
Print distance / J tables and return
(crystal, couplings).couplingsis a list of items that can be fed straight intomag4.dimers.find_dimers():Without
ligand_elements:[[label, distance], ...](legacy form, distance is the only discriminator).With
ligand_elementsset:[[label, distance, fingerprint], ...]wherefingerprintis anmag4.geometry.AngleFingerprintcarrying the M–L–M bridging geometry — used byfind_dimersas a secondary discriminator so that pairs at the same distance but different bridging angles end up in different J shells (J1a,J1b, …).
Sub-package mag4.magnon
Inequivalent magnetic sites and bond table for the multi-sublattice exchange matrix.
- mag4.magnon.sublattice.get_inequivalent_sites(cif_path, element)[source]
Group magnetic sites into crystallographically inequivalent classes.
Uses
pymatgen.symmetry.analyzer.SpacegroupAnalyzerto derive Wyckoff orbits and returns one list of global site indices per class.
- mag4.magnon.sublattice.get_sublattice_bonds(cif_path, element, cutoff, shells, j_values, tol=0.01)[source]
Build the bond table for the full
N_mag × N_magprimitive-cell exchange matrix.Every magnetic atom is treated as its own sublattice. The reciprocal-space exchange matrix is
\[J_{αβ}(q) = Σ_{\text{images}} J_n · \exp(i\,q · r_{αβ,\text{image}})\]
q-point grid generation for the BZ scan.
- mag4.magnon.qmesh.build_qmesh(structure, na, nb, nc)[source]
Γ-centred uniform q-mesh over the full primitive BZ.
- mag4.magnon.qmesh.auto_qmesh(structure, target_points=125000)[source]
Anisotropy-adapted q-mesh targeting ~``target_points`` q-points.
Reciprocal-space exchange matrix J(q) and its eigenvalue spectrum.
- mag4.magnon.exchange.build_jq_matrices(bond_data, q_points)[source]
Compute \(J_{αβ}(q) = Σ_{\text{bonds}(α,β)} JS²_n · e^{i\,q · r_{αβ}}\).
Returns a complex array of shape
(N_q, N_sub, N_sub).
- mag4.magnon.exchange.compute_eigenvalues_all_q(Jq)[source]
Diagonalise the Hermitian J(q) at every q.
Returns a real array
(N_q, N_sub)of eigenvalues sorted descending. Used for T_c and q₀ search; the magnon dispersion uses the LSWT dynamical matrix instead (seemag4.magnon.dispersion).
Ground-state ordering analysis: Luttinger-Tisza q₀ and spin direction assignment.
- mag4.magnon.ordering.analyse_ordering(Jq, evals, q_frac, q_cart, structure)[source]
Find
q₀ = argmin λ_min(q)and characterise the magnetic ordering.The output dict contains:
idx0, q0_cart, q0_frac, lam0, eigvec, ratios, character, sc_dims, commensurate.- Return type:
- mag4.magnon.ordering.get_spin_dirs_from_q0(bond_data, q0_frac)[source]
Assign collinear spin directions in the primitive cell from the ordering wavevector.
\[s_α = \mathrm{sign}\bigl(\cos(2π\,q₀ · r_α)\bigr)\]where
r_αis the fractional coordinate of site α. This is the TB2J strategy: the LSWT dynamical matrix is then built directly in the primitive cell, giving exactlyn_submagnon branches with a Goldstone mode atk = q₀. No supercell replication is required.
Critical temperature (mean-field and Tyablikov RPA) from J(q).
- mag4.magnon.tc.compute_tc(evals, S, lam0)[source]
Mean-field and Luttinger–Tisza Tyablikov RPA critical temperature.
Both estimates use the Hermitian J(q) eigenvalue spectrum sampled on the BZ grid.
Returns a dict with keys
Tc_MF, Tc_RPA, note.- Return type:
- mag4.magnon.tc.compute_tc_lswt(bond_data, S, ordering_lt, q_cart, evals_lt)[source]
Method-2 T_c, chosen based on q₀.
- Return type:
- Parameters:
- q₀ ≈ Γ (FM / type-I AFM)
LSWT is exact in the primitive cell. Build \(D^{ord}\) with \(s_α = \mathrm{sign}(v_α)\) and apply the Tyablikov RPA to the acoustic (lowest) magnon branch:
k_B T_c^{LSWT-RPA} = (S+1)/(3S) / ⟨1/ω_0(q)⟩_{ω_0 > 0}- q₀ ≠ Γ (commensurate or incommensurate ordering)
\(D^{ord}\) in the primitive cell has negative eigenvalues because \(s_α = \mathrm{sign}(\cos 2π q₀·r_α)\) is not the exact collinear ground state for q₀ ≠ Γ. Use the LT acoustic-branch RPA — Tyablikov RPA applied to
λ_min(q)directly:k_B T_c^{LT-ac-RPA} = (S+1)/(3S) / ⟨1/(λ_min(q₀) − λ_min(q))⟩_{q ≠ q₀}
High-symmetry k-path generation (auto via pymatgen, or user-supplied label string).
- mag4.magnon.kpath.get_kpath(structure, n_points=100, custom_labels=None)[source]
Generate a high-symmetry k-path using pymatgen
HighSymmKpath.- Parameters:
- Returns:
Keys:
kpoints_cart, kpoints_frac, distances, tick_positions, tick_labels, break_indices, prim_rec_matrix, hs_frac.- Return type:
Magnon dispersion via the eigenvalues of J(q) in the primitive cell.
- mag4.magnon.dispersion.compute_magnon_dispersion(bond_data, kpath, S, ordering)[source]
Compute the magnon dispersion in the primitive cell.
\[ω_k(q) = 2S × \bigl(λ_k(J(q)) − λ_{\min}(q_0)\bigr)\]where
λ_k(J(q))are the eigenvalues of the Hermitian exchange matrix at each k-point along the path, sorted ascending. This gives exactlyn_subbranches in the primitive BZ for any ordering wavevector q₀. The Goldstone mode (ω = 0) is atk = q₀by construction of the shift, so no supercell or spectral unfolding is needed.
Magnon band-structure plotting (matplotlib, optional).
- mag4.magnon.plot.plot_magnon_bands(magnon_bands, kpath, S, n_sub, name)[source]
Plot the magnon band structure and save to
<name>_magnon_bands.png.Returns the output path, or
""if matplotlib is unavailable.
Magnon / spin-wave report (textual summary written to disk).
- mag4.magnon.report.print_report(shells, j_values, bond_data, evals, ordering_lt, tc_lt, tc_sw, S, qmesh, structure, output_file, j_values_note=None)[source]
Pretty-print the LSWT/RPA report and write it to
output_file.j_values_note(optional): provenance lines printed directly under the exchange-parameter table — e.g. the--jringring-renormalisation record, so effective couplings are never shown without their origin.
Sub-package mag4.analysis
Bridging-ligand bond-angle analysis (M – L – M).
- mag4.analysis.bond_angles.get_mm_pairs(structure, element, cutoff)[source]
Return sorted list of unique M–M pairs within
cutoffÅ.Mirrors
mag4.io_cif.get_mm_distances()but returns lighter dicts (nolabel_*fields) suited to the bond-angle workflow.
- mag4.analysis.bond_angles.group_into_shells(results, tol=0.01)[source]
Cluster M–M pairs into distance shells (J1, J2, …).
- Parameters:
tol (float)
- mag4.analysis.bond_angles.find_bridging_angles(structure, site_i, site_j, jimage, ligand_elements, bond_cutoff)[source]
Find every ligand L bridging
site_iandsite_j(atjimage).A ligand L bridges the pair when
dist(site_i, L) < bond_cutoffANDdist(site_j + jimage, L) < bond_cutoff.Returns a list of dicts sorted by angle.
- mag4.analysis.bond_angles.find_dihedral_paths(structure, site_i, site_j, jimage, ligand_elements, bond_cutoff, ll_cutoff)[source]
Find every M1–L1–L2–M2 super-super-exchange path between
site_i(home) andsite_j(atjimage).A path exists when:
L1is bonded toM1(distance ≤bond_cutoff);L2is bonded toM2 + jimage(distance ≤bond_cutoff);L1andL2are bonded to each other (distance ≤ll_cutoff);L1andL2are distinct ligand atoms (otherwise the path is a direct M–L–M bridge already covered byfind_bridging_angles()).
The returned dihedral is the signed torsion angle of the four-atom path in degrees, in
[-180°, +180°]following the IUPAC convention.Returns a list of dicts, deduplicated on
(L1 identity, L2 identity)and sorted by signed dihedral.
- mag4.analysis.bond_angles.compress_angles(ang_vals, tol=0.01)[source]
Group similar angles:
[159.07, 159.07, 159.07]→'159.07° (x3)'.
- mag4.analysis.bond_angles.print_header(structure, element, ligand_elements, cutoff, bond_cutoff)[source]
Print a header summarising the structure and the search parameters.
- mag4.analysis.bond_angles.print_shell_summary(groups, element)[source]
Brief summary of the J shells.
- mag4.analysis.bond_angles.print_angles_for_shell(group, structure, element, ligand_elements, bond_cutoff, lines_out)[source]
Bond-angle table for one J shell. Appends each printed line to
lines_out.
- mag4.analysis.bond_angles.write_report(lines_out, output_file)[source]
Write accumulated report lines to
output_file.
Extract converged magnetic moments from WIEN2k .scf files.
Layer: WIEN2k-specific (backs the mag4-moments CLI).
- mag4.analysis.wien2k.parse_report_target_moment(report_path)[source]
Read the intended per-site
|MAGMOM| = 2·Sfrom a mag4 report.Returns the target moment (μ_B) or
Noneif the report has noMAGMOM (S = …, |MAGMOM| = 2·S = …)header (older/other reports).
- mag4.analysis.wien2k.find_report(base_dir='.')[source]
Locate a
*_report.datinbase_dir(prefersfour_state_report).
- mag4.analysis.wien2k.find_cases(base_dir='.', scf_basename=None, code='wien2k')[source]
Auto-detect config subdirectories with results for
code(sorted).code='wien2k'→ directories with a.scffile (or exactly<scf_basename>.scfwhen given);code='vasp'→ directories with anOUTCAR.
- mag4.analysis.wien2k.extract_moments_from_scf(scf_path, atom_indices)[source]
Parse a WIEN2k
.scffile and return the converged:MMIxxx:values.Line format:
:MMI009: MAGNETIC MOMENT IN SPHERE 9 = 2.85000
The last occurrence of each tag (i.e. the converged value) is kept.
- mag4.analysis.wien2k.extract_total_moment(scf_path)[source]
Return the last
:MMTOT:total magnetic moment in cell.
- mag4.analysis.wien2k.build_table(cases, atom_indices, scf_basename=None, target=None, tol=0.5, code='wien2k')[source]
Textual table of moments for each case + statistics + a target check.
codeselects the parser:'wien2k'reads per-sphere:MMI:values from<dir>/<scf_basename>.scf;'vasp'reads the per-iontotcolumn of the lastmagnetization (x)block in<dir>/OUTCAR(needsLORBIT=10/11). For VASP the indices are 1-based POSCAR ion numbers (mag4 writes the magnetic atoms first, so they are1 … n_mag).Returns
(lines, summary)wheresummarycarriesn_data(configs with a readable SCF),n_missing(SCF not found),failures(list of(case, reason)where a site did not reach thetargetmoment withintol, or collapsed to ~0), andtarget.targetis the expected per-site|μ|(e.g.2·S= the generated|MAGMOM|). WhenNonethe check only flags collapsed moments (|μ| < 0.1 μ_B), which usually mean the config fell into a non-magnetic solution rather than the intended spin state.
Sub-package mag4.cli
CLI: full four-state pipeline (CIF or POSCAR → spin configurations + VASP/WIEN2k inputs).
- mag4.cli.fourstate.parse_couplings(s)[source]
Parse
"J1:3.94,J2:6.10"→[["J1", 3.94], ["J2", 6.10]].
CLI: extract J values from a completed four-state DFT campaign.
Usage example:
cd MyCompound/ # (contains configN/ + four_state_report.dat)
mag4-extract --code vasp --spin 3.5
# → prints a table; also writes MyCompound/j_values.dat
CLI: multi-sublattice LSWT magnon dispersion + Tyablikov RPA T_c.
- class mag4.cli.magnon.ExtractedJs(j_values, spin, dists, skipped, ring, sigmas, rms_meV)[source]
Bases:
NamedTupleContents of a
j_values.datfile, parsed for mag4-magnon.- Parameters:
- mag4.cli.magnon.parse_j_values_file(path)[source]
Read a
j_values.datwritten by mag4-extract.Handles both the four-state format (
name d JS2(eV) JS2(meV) J(meV) J(K) status) and the energy-mapping format (name d JS2(meV) [sig(meV)] …).j_valuesare the JS² values in meV (mag4-magnon’s native input); aJringrow’s JS2 column holds the fitted Jring·S⁴ and is returned separately inring. The per-coupling standard errors (sig(meV)column, newer files) and the fit RMS from the header feed the statistical-significance test used by the dimensionality guard.- Return type:
- Parameters:
path (str)
- mag4.cli.magnon.apply_ring_renormalisation(j_values, shells, edge_label, js4_meV, *, tol=0.02)[source]
Fold a four-spin ring coupling into effective bilinear JS² (LSWT).
Harmonic (LSWT) reduction of the cyclic four-spin operator about the Néel state (cf. Coldea et al., PRL 86, 5377 (2001)): each pair operator inside the quartic term is multiplied by the classical value of its partner bond, so the magnon Hamiltonian is EXACTLY that of a bilinear model with, per bond and in native JS² units (Jring·S⁴ enters directly — no explicit S factors),
edge shell (2 plaquettes/bond): JS² → JS² − 2·JringS⁴ diagonal shell (1 plaquette/bond): JS² → JS² − 1·JringS⁴
edge_labelnames the shell the square plaquette is built on; the diagonal shell is located geometrically (distance = √2 × edge, withintolrelative). If the diagonal shell carries no fitted J it is induced with JS² = −JringS⁴. Returns(new_j_values, lines)wherelinesdocument the transformation for the report.
CLI: M – L – M bridging-angle analysis from a CIF.
CLI: extract converged magnetic moments from a tree of WIEN2k .scf files.
Layer: WIEN2k-specific.
Beyond tabulating the per-site moments, it checks that every configuration
reached its intended magnetic state — a config whose moment collapsed to ~0
(non-magnetic solution) or drifted far from the generated |MAGMOM| = 2·S
has an unreliable total energy and would poison a J / Jring extraction.