mag4.ring16

16-state method for the four-spin ring (cyclic) exchange Jring.

Layer: common — the ring analogue of the four-state method.

Where the four-state method flips the two atoms of a bond (2² = 4 states) and combines them to isolate a pairwise J, the 16-state method flips the four atoms of one square plaquette (i, j, k, l) over a fixed bath (2⁴ = 16 states) and projects with the four-spin parity χ = s_i s_j s_k s_l (mathematically, the Walsh character of the spin-flip group Z₂⁴):

Jring·S⁴ = (1 / 16M) · Σ_configs  χ(config) · E(config)

By parity (character) orthogonality the projection annihilates every lower-order term — the constant, single-spin (bath couplings), pairwise bonds, and partial (≤3-spin) plaquettes — leaving only the genuine four-spin coefficient. M is the plaquette multiplicity (how many squares close on the same four atoms under PBC; M = 1 for a fully isolated plaquette).

χ is invariant under both a global spin flip (even number of factors) and any permutation of the four corners, so symmetry-equivalent configurations carry the same χ and the same energy. The 16 states therefore collapse (via mag4.spinconfig.find_unique_configs()) to a handful of inequivalent ones, and the projection is rewritten over the representatives with integer weights w_u = (group size)·χ_u:

Jring·S⁴ = (Σ_u w_u · E[#u]) / (16M)

Intended for 2D square-lattice ring exchange (cuprates, etc.); the plaquette is the nearest-neighbour square auto-detected by mag4.energy_mapping.select_ring_plaquettes().

Functions

analyze_pairwise_extractability(records, labels)

Which couplings are identifiable from the ring configs, and why not.

compute_jring(formula, energies)

Apply the ring formula to the per-config energies.

find_ring_supercell(crystal, couplings, *[, ...])

Smallest supercell in which the target square plaquette is fully isolated — multiplicity M = 1: four independent atoms forming exactly one square, so the four-state-style divisor is exactly 16 (no 16·M).

format_ring_formula(weights, divisor, edge)

Jring (...) = ( +w1*E[#1] +w2*E[#2] ) / D (machine-parseable).

generate_ring_16(ref_bath, quad)

The 16 spin configurations flipping the four plaquette atoms over the bath.

parse_ring_design(report_path)

Parse the PAIRWISE-J ANALYSIS design rows of a 16-state report.

parse_ring_report(report_path)

Parse the Jring = ( ) / D line of a 16-state report.

ring_chi(config, quad)

Four-spin parity χ = Π_{m∈plaquette} s_m (±1) for one configuration.

ring_design_records(crystal, couplings, ...)

Design-matrix rows of the unique ring configs (no extra DFT needed).

ring_weights(unique_groups, quad)

Integer projection weight w_u = (group size)·χ_u per unique config.

select_ring_quad(crystal, couplings, *[, ...])

Pick the NN square plaquette for the ring term and return it as four distinct cell-atom indices.

write_ring_report(path, unique_groups, ...)

Print (and optionally write) the 16-state ring report.

Classes

RingFormula(terms, divisor[, edge])

Parsed ring extraction formula: Jring·S⁴ = Σ w_i·E[#i] / divisor.

RingPlaquette(idx, labels, edge, multiplicity)

One square plaquette selected for the ring term.

class mag4.ring16.RingPlaquette(idx, labels, edge, multiplicity)[source]

Bases: object

One square plaquette selected for the ring term.

idx are the four cell-atom indices of the corners (in crystal.target_species order); labels their labels; edge the square edge length (Å); multiplicity the number of distinct squares that close on exactly these four atoms (the M in the 16M divisor).

Parameters:
idx: Tuple[int, int, int, int]
labels: Tuple[str, str, str, str]
edge: float
multiplicity: int
mag4.ring16.select_ring_quad(crystal, couplings, *, prefer_label=None, prefer_dist=None, tol_pos=0.15)[source]

Pick the NN square plaquette for the ring term and return it as four distinct cell-atom indices.

Raises ValueError if no coupling shell forms a square, or if every square folds onto fewer than four distinct atoms (cell too small — enlarge it).

Return type:

RingPlaquette

Parameters:
mag4.ring16.find_ring_supercell(crystal, couplings, *, max_mult=4, prefer_label=None, prefer_dist=None)[source]

Smallest supercell in which the target square plaquette is fully isolated — multiplicity M = 1: four independent atoms forming exactly one square, so the four-state-style divisor is exactly 16 (no 16·M).

This is the ring analogue of the four-state isolation criterion. At M > 1 the four atoms wrap onto their own J1 images through the periodic boundary, so the plaquette is not independent of its copies and the DFT energies pick up self-interaction bias — the projection math tolerates it, but the physical extraction does not.

Searches (na, nb, nc) from smallest atom count upward (a layered square plaquette isolates with nc = 1). Returns ((na, nb, nc), expanded crystal, plaquette). Raises RuntimeError if no cell within max_mult per axis reaches M = 1.

Return type:

Tuple[Tuple[int, int, int], CrystalData, RingPlaquette]

Parameters:
mag4.ring16.ring_design_records(crystal, couplings, unique_groups, quad, *, prefer_label=None, prefer_dist=None, tol=0.0005)[source]

Design-matrix rows of the unique ring configs (no extra DFT needed).

For each unique configuration the model energy is exactly

E[#u] = E0 + Σ_k c_k·(J_k·S²) + c_ring·(Jring·S⁴)

with c_k = Σ_{⟨ij⟩∈k} s_i s_j over the unique bonds of shell k and c_ring the four-spin plaquette coefficient. Returns energy-mapping style records {"number", "degeneracy", "sz", "coefficients"} where the ring column is labelled "Jring" — the raw material for deciding if and how the pairwise J_k can be extracted from the very configurations the 16-state method already computes for Jring.

Return type:

List[dict]

Parameters:
mag4.ring16.analyze_pairwise_extractability(records, labels)[source]

Which couplings are identifiable from the ring configs, and why not.

A coupling is not extractable when its design-matrix column is constant across the configurations (the plaquette flips never change its bond sum) or lies in the span of the constant + the other columns (degenerate — only a combination is constrained). Returns {label: (extractable, reason)}.

Return type:

Dict[str, Tuple[bool, str]]

Parameters:
mag4.ring16.ring_chi(config, quad)[source]

Four-spin parity χ = Π_{m∈plaquette} s_m (±1) for one configuration.

Return type:

int

Parameters:
mag4.ring16.generate_ring_16(ref_bath, quad)[source]

The 16 spin configurations flipping the four plaquette atoms over the bath.

Only the four plaquette corners differ between configs; the rest stay at ref_bath. state_label is the u/d pattern of the corners.

Return type:

List[FourStateConfig]

Parameters:
mag4.ring16.ring_weights(unique_groups, quad)[source]

Integer projection weight w_u = (group size)·χ_u per unique config.

All members of a symmetry group share χ (permutation/flip invariant), so the per-group weight is well defined; this is verified defensively.

Return type:

List[int]

Parameters:
mag4.ring16.format_ring_formula(weights, divisor, edge)[source]

Jring (...) = ( +w1*E[#1] +w2*E[#2] ) / D (machine-parseable).

Return type:

str

Parameters:
mag4.ring16.write_ring_report(path, unique_groups, atoms, quad, supercell, n_sym_ops, *, n_supercell_atoms=None, ref_bath=None, design_records=None, coupling_dists=None, config_sgs=None, config_msgs=None, symmetric_structs=False, tr_pairs=None)[source]

Print (and optionally write) the 16-state ring report.

Optional extras (all analysis-only — the written structures stay P1):

  • design_records (from ring_design_records()) — adds the PAIRWISE-J ANALYSIS section: the design-matrix rows of the unique configs and, per coupling, whether/why it is extractable from the very energies computed for Jring.

  • coupling_dists{label: d(Å)} shown next to each verdict.

  • config_sgs — per-config (spacegroup_number, symbol) of the spin-decorated structure (spglib), shown as an extra column.

  • tr_pairs (from --check-degeneracy) — config pairs related only by the antiunitary symmetry (unitary op × time reversal); their DFT energies must coincide and mag4-extract reports |ΔE| per pair.

Return type:

None

Parameters:
class mag4.ring16.RingFormula(terms, divisor, edge=None)[source]

Bases: object

Parsed ring extraction formula: Jring·S⁴ = Σ w_i·E[#i] / divisor.

Parameters:
terms: List[Tuple[int, int]]
divisor: int
edge: float | None = None
property config_ids: List[int]
mag4.ring16.parse_ring_report(report_path)[source]

Parse the Jring = ( ) / D line of a 16-state report.

Return type:

RingFormula

Parameters:

report_path (str)

mag4.ring16.parse_ring_design(report_path)[source]

Parse the PAIRWISE-J ANALYSIS design rows of a 16-state report.

Returns {"labels": [...], "records": [{"number", "degeneracy", "sz", "coefficients"}, ...]} (energy-mapping record format, Jring column included) or None when the report has no analysis section (older reports).

Return type:

Optional[dict]

Parameters:

report_path (str)

mag4.ring16.compute_jring(formula, energies)[source]

Apply the ring formula to the per-config energies.

energies maps config id → object with an energy_eV attribute (an mag4.energy.EnergyResult). Returns (Jring·S⁴ in eV, problems); the value is None when an energy is missing/unparsed.

Return type:

Tuple[Optional[float], List[str]]

Parameters: