"""Magnon / spin-wave report (textual summary written to disk)."""
from __future__ import annotations
import logging
log = logging.getLogger(__name__)
[docs]
def print_report(shells, j_values, bond_data, evals,
ordering_lt, tc_lt, tc_sw,
S, qmesh, structure, output_file,
j_values_note=None):
"""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 ``--jring`` ring-renormalisation
record, so effective couplings are never shown without their origin.
"""
lines = []
def out(msg=""):
print(msg)
lines.append(msg)
na, nb, nc = qmesh
n_sub = bond_data["n_sub"]
W = 72
out("=" * W)
out(" MAGNON SPIN-WAVE THEORY (multi-sublattice, LSWT)")
out(" Heisenberg model + Random Phase Approximation (Tyablikov)")
out("=" * W); out()
out(f" Formula : {structure.composition.reduced_formula}")
out(f" Space grp : {structure.get_space_group_info()}")
out(f" Spin S : {S} (2S+1 = {int(2*S+1)}, S2 = {S**2:.4f})")
out(f" Sublattices: {n_sub} (magnetic sites in primitive cell)")
out(f" q-mesh : {na} x {nb} x {nc} = {na*nb*nc} points"); out()
out(" Convention : H = +sum_ij J_ij S_i.S_j (AFM: J>0, FM: J<0)")
out(" Input : JS2 = +(E_uu+E_dd-E_ud-E_du)/4 [meV.S2]"); out()
out(" LSWT dynamical matrix:")
out(" D_ab(q) = d_ab [sum_g J_ag(0) s_a s_g] - J_ab(q) s_a s_b")
out(" Magnon energies = 2S * eigenvalues of D(q)"); out()
title = " Exchange parameters (meV.S2)"
if j_values_note:
title += " [ring-renormalised EFFECTIVE couplings]"
out("-" * W); out(title); out("-" * W)
out(" {:<8} {:>10} {:>12} {:>6} {:>12}".format(
"Shell", "d_mean (A)", "JS2 (meV)", "mult.", "z*JS2 (meV)"))
out("-" * W)
j0 = 0.0
for sh in shells:
lbl = sh["label"]
if lbl not in j_values:
continue
J_n = j_values[lbl]
mult = sh["n_bonds"]
j0 += mult * J_n
out(" {:<8} {:>10.4f} {:>12.4f} {:>6} {:>12.4f}".format(
lbl, sh["distance"], J_n, mult, mult*J_n))
out("-" * W)
if j_values_note:
for ln in str(j_values_note).splitlines():
out(ln)
out("-" * W)
out(f" JS2(q=0) = {j0:.4f} meV.S2 J(q=0)/S2 = {j0/S**2:.4f} meV")
out(f" Per sublattice: J(q=0)/(N_sub*S2) = {j0/S**2/n_sub:.4f} meV"); out()
out("-" * W); out(" J(q) eigenvalue range over full BZ"); out("-" * W)
out(f" lambda_max = {evals.max():+.4f} meV.S2")
out(f" lambda_min = {evals.min():+.4f} meV.S2"); out()
out("=" * W); out(" METHOD 1 — LUTTINGER-TISZA (LT)"); out("=" * W)
q0_f = ordering_lt["q0_frac"]
q0_c = ordering_lt["q0_cart"]
ratios = ordering_lt["ratios"]
out(" q0^LT = argmin lambda_min(J(q))")
out(f" q0 fractional : ({q0_f[0]:+.6f}, {q0_f[1]:+.6f}, {q0_f[2]:+.6f})")
out(" q0 rational : (" +
", ".join(f"{p}/{m}" if m > 1 else str(p) for p, m in ratios) + ")")
out(f" q0 Cartesian : ({q0_c[0]:+.4f}, {q0_c[1]:+.4f}, {q0_c[2]:+.4f}) A-1")
out(f" lambda_min(q0): {ordering_lt['lam0']:+.4f} meV.S2"); out()
out(f" Ordering : {ordering_lt['character']}"); out()
out(" Critical temperatures (Tyablikov RPA on J(q) eigenvalues):")
if tc_lt["note"]:
out(f" NOTE: {tc_lt['note']}")
else:
out(f" T_c^LT-MF = {tc_lt['Tc_MF']:8.1f} K "
f"[k_B T_c = -(S+1)/(3S) x lambda_min]")
out(f" T_c^LT-RPA = {tc_lt['Tc_RPA']:8.1f} K "
f"[Tyablikov RPA on J(q) spectrum]")
if tc_lt["Tc_MF"] > 0:
out(f" RPA/MF ratio = {tc_lt['Tc_RPA']/tc_lt['Tc_MF']:.3f}")
out()
method2 = tc_sw.get("method", "RPA")
out("=" * W)
if method2 == "LSWT-RPA":
out(" METHOD 2 — LSWT-RPA (q0 = Γ, primitive cell)")
out(" D^ord_αβ(q) = δ_αβ Σ_γ J_αγ(0)s_αs_γ − J_αβ(q) s_αs_β")
out(" T_c from Tyablikov RPA on acoustic branch ω_0(q) = 2S λ_min(D^ord)")
else:
out(" METHOD 2 — LT ACOUSTIC-BRANCH RPA (q0 ≠ Γ, LSWT needs supercell)")
out(" Uses λ_min(q) = acoustic eigenvalue of J(q) from LT scan")
out(" T_c from Tyablikov RPA on λ_min(q) branch (acoustic only)")
out("=" * W); out()
if tc_sw["note"]:
out(f" NOTE: {tc_sw['note']}")
elif method2 == "LSWT-RPA":
out(f" ω₀_min = {tc_sw['om_min']:10.6f} meV (near-Goldstone minimum)")
out(f" ω₀_mean = {tc_sw['om_mean']:10.4f} meV")
out(f" T_c^LSWT-RPA = {tc_sw['Tc_RPA']:8.1f} K")
else:
out(f" λ_min range = [{tc_sw['lam_min']:+.4f}, {ordering_lt['lam0']:+.4f}] meV.S2")
out(f" T_c^LT-ac-RPA = {tc_sw['Tc_RPA']:8.1f} K")
out()
out("=" * W); out(" SUMMARY"); out("=" * W)
out(f" Ordering wavevector q0: {ordering_lt['character']}")
if not tc_lt["note"] and not tc_sw["note"]:
method2_lbl = tc_sw.get("method", "RPA")
out(f" T_c^LT-MF = {tc_lt['Tc_MF']:8.1f} K (Luttinger-Tisza MF)")
out(f" T_c^LT-RPA = {tc_lt['Tc_RPA']:8.1f} K (Luttinger-Tisza RPA, all branches)")
out(f" T_c^{method2_lbl:<10}= {tc_sw['Tc_RPA']:8.1f} K (acoustic branch only)")
out("=" * W)
with open(output_file, "w") as f:
f.write("\n".join(lines) + "\n")
log.info("Report written to %s", output_file)