API Reference
Quantum System
Types
MeanFieldTheories.QuantumNumber — Type
QuantumNumber{Names, Types}A wrapper around NamedTuple for representing quantum numbers with cleaner display.
Type Parameters
Names: Tuple of field names (Symbols)Types: Tuple type for values
Examples
# Create from keyword arguments
qn = QuantumNumber(site=1, spin=2)
qn.site # 1
qn.spin # 2
# Display: QN(site=1, spin=2)MeanFieldTheories.Dof — Type
DofSpecification for a single degree of freedom (DOF).
Fields
name::Symbol: Name of the DOF (e.g., :site, :spin, :orbital)size::Int: Number of states for this DOFlabels::Union{Nothing, Vector}: Optional labels for states (e.g., [:up, :down])
MeanFieldTheories.SystemDofs — Type
SystemDofs{Q}Defines all degrees of freedom in the system, with optional constraints, custom ordering, and symmetry blocks.
Type Parameter
Q: QuantumNumber type with DOF names as keys
Fields
dofs::Vector{Dof}: List of degrees of freedomvalid_states::Vector{Q}: All valid quantum number combinations (sorted)blocks::Vector{UnitRange{Int}}: Index ranges for symmetry blocks (single block [1:N] if no blocking)qn_to_idx::Dict{Q, Int}: Fast O(1) lookup from quantum numbers to linear indices
Constructor
SystemDofs(dofs::Vector{Dof}; constraint=nothing, sortrule=collect(length(dofs):-1:1))Arguments
dofs: Vector of Dof specificationsconstraint: Optional functionqn -> Boolto filter valid states (not saved after construction)sortrule: Vector or nested vector specifying sort priority and blocking (not saved after construction)- Default:
[N, N-1, ..., 1](reverse order: first dof varies fastest, no blocking) - Elements are indices into
dofsarray - Nested syntax for symmetry blocks:
[4, 3, 2, 1]- No blocking[[4], 3, 2, 1]- Block by 4th DOF (e.g., spin)[[4, 3], 2, 1]- Block by 4th DOF, then by 3rd within each block[[4, 3, 2], 1]- Block by 4th, 3rd, 2nd sequentially
- Default:
Design Rationale
- Default reverse order ensures first DOF (typically site) varies fastest for cache-friendly access
- Nested sortrule syntax naturally creates block-diagonal structure for conserved quantum numbers
- Blocks enable efficient matrix operations (multiplication, diagonalization) by exploiting sparsity
Examples
# Example 1: Default ordering (reverse, no explicit blocks)
dofs = SystemDofs([
Dof(:site, 3),
Dof(:spin, 2)
])
# sortrule = [2, 1]: spin slow, site fast
# valid_states = [QN(site=1,spin=1), QN(site=2,spin=1), QN(site=3,spin=1),
# QN(site=1,spin=2), QN(site=2,spin=2), QN(site=3,spin=2)]
# blocks = [1:6] (single block containing all states)
# Example 2: With symmetry blocks (spin conservation)
dofs = SystemDofs([
Dof(:site, 3),
Dof(:spin, 2)
], sortrule = [[2], 1])
# blocks = [1:3, 4:6] (spin-up block, spin-down block)
# Example 3: Nested blocks (spin and valley conservation)
dofs = SystemDofs([
Dof(:site, 4),
Dof(:orbital, 2),
Dof(:spin, 2),
Dof(:valley, 2)
], sortrule = [[4, 3], 2, 1])
# blocks = [1:8, 9:16, 17:24, 25:32] (K↑, K↓, K'↑, K'↓)
# Example 4: With constraint
dofs = SystemDofs([
Dof(:site, 4),
Dof(:spin, 2)
], constraint = qn -> qn.site <= 2, sortrule = [[2], 1])
# blocks = [1:2, 3:4] (only first 2 sites, with spin blocks)MeanFieldTheories.Lattice — Type
Lattice{D, Q, T}Represents a lattice structure mapping position states to real-space coordinates.
Type Parameters
D: Spatial dimension (e.g., 2 for 2D, 3 for 3D)Q: QuantumNumber type for position statesT: Coordinate element type (subtype of Real)
Fields
position_dofs::Vector{Dof}: Degrees of freedom that define positionsposition_states::Vector{Q}: All valid position statescoordinates::Vector{SVector{D,T}}: Coordinate for each position state (length D)vectors::Union{Nothing, Vector{SVector{D,T}}}: Supercell vectors for PBC (optional)
Notes
position_states[i]corresponds tocoordinates[i]vectorsis set automatically when using tiling constructor
Examples
# 2D honeycomb lattice with 16 unit cells and 2 sublattices
position_dofs = [Dof(:moire_cell, 16), Dof(:sublattice, 2, [:A, :B])]
position_states = [QN(moire_cell=1, sublattice=1), QN(moire_cell=1, sublattice=2), ...]
coordinates = [[0.0, 0.0], [1.0, 0.0], ...]
lattice = Lattice(position_dofs, position_states, coordinates)MeanFieldTheories.Bond — Type
Bond{Q, T}Represents a bond connecting one or more sites.
Fields
states::Vector{Q}: Position states (length determines bond order: 1=onsite, 2=two-body, etc.)coordinates::Vector{SVector{D,T}}: Physical (unwrapped) coordinates for each site. For periodic bonds these may lie outside the simulation cell.icoordinates::Vector{SVector{D,T}}: Unit-cell origin for each site.icoordinates[n]is the lattice vector of the unit cell that containscoordinates[n]. For intra-cell bonds all entries are zero vectors. For bonds that cross a periodic boundary, the entry for the image site carries the lattice shift, e.g.[-2.0, 0.0].
Examples
# Onsite bond — icoordinates default to zero vectors
bond = Bond([state1], [coord1])
# Two-body intra-cell bond
bond = Bond([state1, state2], [coord1, coord2])
# Two-body bond crossing a periodic boundary (site 2 lives in cell [-2, 0])
bond = Bond([state1, state2], [coord1, coord2], [[0.0, 0.0], [-2.0, 0.0]])MeanFieldTheories.FermionOp — Type
FermionOp{Q} <: OperatorRepresents a single fermion operator (creation or annihilation).
Fields
qn::Q: Quantum number identifying the statedag::Bool: true = creation operator (c†), false = annihilation operator (c)
MeanFieldTheories.Operator — Type
OperatorAbstract type for quantum operators. Subtypes include FermionOp (and potentially SpinOp, BosonOp, etc. in the future).
MeanFieldTheories.Operators — Type
Operators{T}Represents a product of operators with a coefficient.
Operators are stored in their original order (no automatic reordering). Reordering to canonical form (creation-annihilation alternating order: c†c c†c...) happens when building matrices/tensors via build_onebody_matrix or build_interaction_tensor.
Type Parameters
T: Numeric type for coefficient (e.g., Float64, ComplexF64)
Fields
value::T: Coefficientops::Vector{<:Operator}: Operators in original order
Examples
i = QN(site=1, spin=1)
j = QN(site=2, spin=1)
# One-body: -t * c†_i c_j
op = Operators(-t, [cdag(i), c(j)])
# Two-body: U * c†_i↑ c_i↑ c†_i↓ c_i↓
op = Operators(U, [cdag(i_up), c(i_up), cdag(i_dn), c(i_dn)])
# Pair hopping: stored as-is, reordered when building tensor
op = Operators(J, [cdag(i_up), cdag(i_dn), c(j_dn), c(j_up)])Quantum Number Functions
MeanFieldTheories.qn2linear — Function
qn2linear(dofs::SystemDofs, qn) -> IntConvert quantum numbers to linear index (position in valid_states). Uses O(1) hash table lookup for fast performance. Accepts QuantumNumber or NamedTuple.
Examples
dofs = SystemDofs([Dof(:site, 4), Dof(:spin, 2)])
idx = qn2linear(dofs, QN(site=1, spin=2)) # O(1) lookup
idx = qn2linear(dofs, (site=1, spin=2)) # also worksMeanFieldTheories.linear2qn — Function
linear2qn(dofs::SystemDofs, idx::Int) -> QuantumNumberConvert linear index back to quantum numbers.
Examples
dofs = site_spin_system(4)
qn = linear2qn(dofs, 2) # returns QN(site=1, spin=2)MeanFieldTheories.ndofs — Function
ndofs(dofs::SystemDofs) -> IntReturn number of degrees of freedom.
MeanFieldTheories.total_dim — Function
total_dim(dofs::SystemDofs) -> IntReturn total dimension (number of valid states).
MeanFieldTheories.site_spin_system — Function
site_spin_system(Nsite::Int) -> SystemDofsCreate a simple site+spin system (standard case).
Lattice Functions
MeanFieldTheories.get_coordinate — Function
get_coordinate(lattice::Lattice, state) -> Vector{T}Return the coordinate for a given quantum state. The input can be QuantumNumber or NamedTuple, and can contain extra keys beyond the position DOFs.
Examples
lattice = Lattice(...) # with position_dofs = [:moire_cell, :sublattice]
# Exact position state
coord = get_coordinate(lattice, QN(moire_cell=1, sublattice=2))
# Full quantum state with extra DOFs
coord = get_coordinate(lattice, QN(moire_cell=1, sublattice=2, spin=1, valley=1))MeanFieldTheories.bonds — Function
bonds(lattice, boundary, neighbors)Generate bonds based on neighbor specification.
Arguments
lattice::Lattice: The lattice structure (must have vectors set)boundary::NTuple{D, Symbol}: Boundary conditions, :p (periodic) or :o (open)neighbors: Neighbor specification:Int: n-th nearest neighbor (0=onsite, 1=nearest, 2=next-nearest, ...)Vector{Int}: multiple neighbor orders [n1, n2, ...]Vector{<:Real}: specific distances [r1, r2, ...]
Returns
Vector{Bond} with appropriate number of sites per bond
Examples
# Create lattice with tiling (vectors auto-set)
lattice = Lattice(unitcell, (4, 4))
# Onsite bonds - returns Vector{Bond} with 1 site per bond
onsite = bonds(lattice, (:p, :p), 0)
# onsite[1] displays as: Bond{1}(QN(cell=1, sub=1) @ [0.000, 0.000])
# Nearest neighbor bonds - returns Vector{Bond} with 2 sites per bond
nn = bonds(lattice, (:p, :o), 1)
# nn[1] displays as: Bond{2}(QN(cell=1, sub=1) @ [0.000, 0.000], QN(cell=1, sub=2) @ [0.500, 0.289])
# Multiple neighbor orders
nn_and_nnn = bonds(lattice, (:p, :p), [1, 2])
# Specific distances
specific = bonds(lattice, (:p, :p), [1.0, 1.732])MeanFieldTheories.is_positive_direction — Function
is_positive_direction(delta::Vector) -> BoolCheck if delta vector is in "positive" direction using lexicographic ordering. The first non-zero component must be positive.
Operator Constructors
MeanFieldTheories.c — Function
c(qn) -> FermionOpCreate an annihilation operator for quantum state qn.
MeanFieldTheories.cdag — Function
cdag(qn) -> FermionOpCreate a creation operator (c†) for quantum state qn.
Operator Generators
MeanFieldTheories.generate_onebody — Function
generate_onebody(dofs, bonds, value; order=(cdag, :i, c, :j), hc=true)Generate one-body terms from bonds.
Arguments
dofs::SystemDofs: Full DOF specification (position + internal DOFs)bonds::Vector{Bond}: One- or two-site bonds from bonds() functionvalue: Coefficient, either:Number: constant value for all internal DOF combinationsFunction(delta, qn1, qn2) -> Number: custom function to constrain DOFs.delta = coord(op1_site) - coord(op2_site)in the user-specified order.
Keyword Arguments
order::Tuple: Format(op_type1, label1, op_type2, label2)where labels are Symbols acting as site indices.- Default:
(cdag, :i, c, :j)— standard hopping c†i cj - Same label (e.g.
(cdag, :i, c, :i)) — on-site/chemical-potential term - Any operator order is accepted; operators are reordered to c†c alternating form automatically with the fermionic sign absorbed into the coefficient.
- Default:
hc::Bool: whether to include the Hermitian conjugate terms. Ignored (no hc added) when both labels are identical (on-site terms), since c†i ci is already self-contained and adding h.c. would double-count.
Returns
NamedTuple with three parallel Vector fields:
.ops::Vector{Operators}: operator terms in c†c order, fermionic sign absorbed.delta::Vector{SVector{D,T}}: physical displacementcoord(op1) - coord(op2)after reordering; sign is flipped for H.c. terms.irvec::Vector{SVector{D,T}}: unit-cell displacement after reordering; zero for intra-cell bonds, non-zero for bonds crossing a periodic boundary
NOTICE
- All internal DOFs are mixed unless constrained via the
valuefunction.
Examples
result = generate_onebody(dofs, nn_bonds, -1.0)
result.ops # Vector{Operators}
result.delta # physical displacements
result.irvec # unit-cell displacements (use this for momentum-space HF)
# On-site chemical potential (no h.c. added automatically)
result = generate_onebody(dofs, onsite_bonds, -μ, order=(cdag, :i, c, :i))
# Unconventional input order — reordered internally with correct sign
result = generate_onebody(dofs, nn_bonds, 1.0, order=(c, :i, cdag, :j))MeanFieldTheories.generate_twobody — Function
generate_twobody(dofs, bonds, value; order=(cdag, :i, c, :i, cdag, :j, c, :j))Generate two-body interaction terms from bonds.
Arguments
dofs::SystemDofs: Full DOF specification.bonds::Vector{Bond}: Bonds with 1–4 sites.value: Coefficient, either:Number: constant for all internal DOF combinations.Function(deltas, qn1, qn2, qn3, qn4) -> Number: custom amplitude function.deltas[m] = coord(op_m) - coord(op_4)for m = 1,2,3 (always 3 entries).
Keyword Arguments
order::Tuple: Format(type1, label1, type2, label2, type3, label3, type4, label4)where labels are Symbols acting as free Einstein summation indices. Each unique Symbol is an independent position index summed over all injective assignments to bond sites (different Symbols → different sites).Common patterns:
(cdag, :i, c, :i, cdag, :j, c, :j)— density-density Σ{i≠j} ni n_j (default)(cdag, :i, c, :i, cdag, :i, c, :i)— on-site Σi ni² (use with 1-site bonds)(cdag, :i, c, :j, cdag, :i, c, :j)— exchange-type(cdag, :i, c, :j, cdag, :i, c, :k)— three distinct positions (requires 3-site bond)
Returns
NamedTuple with three parallel Vector fields:
.ops::Vector{Operators}: operator terms in creation-annihilation alternating order (c†c c†c), fermionic sign absorbed into the coefficient..delta::Vector{NTuple{3, SVector{D,T}}}: physical displacements(δ1, δ2, δ3),δm = coord(op_m) - coord(op_4)after reordering..irvec::Vector{NTuple{3, SVector{D,T}}}: unit-cell displacements(τ1, τ2, τ3),τm = icoord(op_m) - icoord(op_4)after reordering.
Examples
# Nearest-neighbor Coulomb: V Σ_{i≠j,σσ'} n_{iσ} n_{jσ'} (full ordered-pair sum)
twobody = generate_twobody(dofs, nn_bonds, V)
# Onsite Hubbard U: Σ_i U n_{i↑} n_{i↓}
twobody = generate_twobody(dofs, onsite_bonds,
(deltas, qn1, qn2, qn3, qn4) ->
(qn1.spin == qn2.spin) && (qn3.spin == qn4.spin) && (qn1.spin !== qn3.spin) ? U/2 : 0.0,
order = (cdag, :i, c, :i, cdag, :i, c, :i))
# NN Coulomb in x-direction only (filter by deltas[1] = coord(i) - coord(j))
twobody = generate_twobody(dofs, nn_bonds,
(deltas, qn1, qn2, qn3, qn4) -> abs(deltas[1][1]) ≈ 1.0 && iszero(deltas[1][2]) ? V : 0.0)
# Pair hopping: Σ_{i≠j} J c†_{i↑} c†_{i↓} c_{j↓} c_{j↑}
twobody = generate_twobody(dofs, nn_bonds,
(deltas, qn1, qn2, qn3, qn4) ->
(qn1.spin, qn2.spin, qn3.spin, qn4.spin) == (1, 2, 2, 1) ? J : 0.0,
order = (cdag, :i, cdag, :i, c, :j, c, :j))MeanFieldTheories.build_onebody_matrix — Function
build_onebody_matrix(dofs::SystemDofs, ops::AbstractVector{<:Operators})Build Hermitian one-body Hamiltonian matrix from Operators. Each Operators must have exactly 2 FermionOp. Operators are reordered to c†c format internally.
MeanFieldTheories.build_interaction_tensor — Function
build_interaction_tensor(dofs::SystemDofs, ops::AbstractVector{<:Operators})Build two-body interaction tensor from Operators. Each Operators must have exactly 4 FermionOp. Operators are reordered to creation-annihilation alternating order (c†c c†c) internally.
The tensor V[i,j,k,l] corresponds to c†i cj c†k cl in creation-annihilation alternating order.
Real-Space Hartree-Fock
MeanFieldTheories.build_T — Function
build_T(dofs, ops)Build sparse one-body Hamiltonian (N×N) from one-body operators. Only stores elements within the same symmetry block (from dofs.blocks). Returns a sparse Hermitian matrix.
MeanFieldTheories.build_U — Function
build_U(dofs, ops; include_fock=true)Build sparse HF interaction matrix (N²×N²) from two-body operators. Applies the antisymmetrization formula directly, skipping the V tensor. Only stores columns where (k,l) share a symmetry block. Returns a sparse matrix of size (N²×N²).
Set include_fock=false for Hartree-only (no exchange) calculation. The 4-term decomposition is:
- Hartree:
U_{ij,kl} += V_{ijkl} + V_{klij} - Fock:
U_{ij,kl} -= V_{kjil} + V_{ilkj}(skipped if include_fock=false)
MeanFieldTheories.solve_hfr — Function
solve_hfr(dofs, ops, block_occupations; kwargs...)Solve Hartree-Fock equations using self-consistent field (SCF) iteration.
When the interaction is strong (large U/t), the HF energy landscape has multiple local minima. Use n_restarts > 1 to run multiple random initializations and automatically select the lowest-energy converged solution.
Arguments
dofs::SystemDofs: System degrees of freedomops: All operators (one-body with 2 FermionOp and two-body with 4 FermionOp)block_occupations: Particle number —Intfor single block,Vector{Int}for multiple blocks
Keyword Arguments
temperature::Float64 = 0.0: Temperature (0 for ground state)max_iter::Int = 1000: Maximum SCF iterations per restarttol::Float64 = 1e-6: Convergence tolerance for Green's function residualmix_alpha::Float64 = 0.5: Linear mixing parameter (0 < α ≤ 1), used when DIIS history is insufficient or disabled.diis_m::Int = 8: DIIS history window length. Stores the lastdiis_miterates and solves a small linear system to extrapolate the optimal density matrix. Set to0to disable DIIS and use pure linear mixing.G_init = nothing: Initial Green's function for the first restartene_cutoff::Float64 = 100.0: Energy cutoff for exp overflow at finite Tn_restarts::Int = 1: Number of random restarts; returns lowest-energy converged resultseed::Union{Nothing, Int} = nothing: Random seed for reproducibilityinclude_fock::Bool = true: Include Fock exchange terms. Set tofalsefor Hartree-only.verbose::Bool = true: Print iteration informationfield_strength::Float64 = 0.0: Amplitude of a random symmetry-breaking field added toh_efffor the firstn_warmupiterations of each restart (linearly decayed to zero). Set to a value comparable to the hopping scale to escape paramagnetic or other high-symmetry saddle points without prior knowledge of the ordered phase.n_warmup::Int = 15: Number of warmup iterations during which the symmetry-breaking field is active. Should be well below the typical SCF convergence count.
Returns
NamedTuple: (G, eigenvalues, eigenvectors, energies, mu_list, converged, iterations, residual, ncond, sz)
ncond: Total particle numberΣ G[i,i]sz: Total spin Sz (only if dofs contains a:spinDof with size=2, otherwisenothing)
Momentum-Space Hartree-Fock
Preprocessing
MeanFieldTheories.build_Tr — Function
build_Tr(dofs, ops, irvec) -> NamedTupleParse one-body operators and build the real-space hopping table T_{ab}(r).
Scans irvec to find all unique displacement vectors, then accumulates hopping amplitudes into one d_int × d_int matrix per displacement. Non-one-body operators (i.e. those with length(op.ops) ≠ 2) in ops are silently skipped, so the full operator list from generate_onebody (which may include metadata entries) can be passed directly.
Arguments
dofs::SystemDofs: DOFs of one magnetic unit cell.ops::Vector{<:Operators}: operator terms; only those with exactly 2 FermionOps (one-body) are used.opsmay contain operators of other sizes — they are ignored.irvec::Vector{<:AbstractVector{<:Real}}: displacement vector per operator term, paired one-to-one withops.
Returns
NamedTuple (mats, rvec) where:
mats::Vector{Matrix}: oned_int × d_inthopping matrix per unique displacement; element type matcheseltypeof the operator values (e.g.Float64orComplexF64).rvec::Vector{Vector{Float64}}: the corresponding displacement vectors, same order asmats.
If ops is empty, emits a warning and returns (mats=Matrix{Float64}[], rvec=Vector{Float64}[]).
MeanFieldTheories.build_Tk — Function
build_Tk(T_r) -> FunctionReturn a closure that evaluates the kinetic Hamiltonian at a given k-point:
T_{ab}(k) = Σ_r T_{ab}(r) · exp(i k · r)Arguments
T_r: Real-space hopping table frombuild_Tr(NamedTuple withmatsanddelta).
Returns
A callable T_func(k) returning Matrix{ComplexF64} of shape (d_int, d_int), where k is a momentum vector. Returns nothing if T_r.mats is empty (no hopping terms); the caller should skip the kinetic contribution in that case.
MeanFieldTheories.build_Vr — Function
build_Vr(dofs, ops, irvec) -> NamedTupleParse two-body operators and build the real-space interaction table V̄^{abcd}(τ1, τ2, τ3).
Scans irvec for unique (τ1,τ2,τ3) triples, then accumulates interaction amplitudes into one d_int×d_int×d_int×d_int array per unique triple. Non-four-body operators in ops are silently skipped.
Arguments
dofs::SystemDofs: DOFs of one magnetic unit cell;d_int = length(dofs.valid_states).ops::Vector{<:Operators}: Two-body operators in creation-annihilation alternating order (4 FermionOp entries); typicallygenerate_twobody(...).ops.irvec: Per-operator unit-cell displacements(τ1, τ2, τ3)paired one-to-one withops; typicallygenerate_twobody(...).irvec.
Returns
NamedTuple (mats, taus) where:
mats::Vector{Array{T,4}}: oned_int×d_int×d_int×d_intarray per unique (τ1,τ2,τ3);mats[n][a,b,c,d]= V̄^{abcd} for the n-th displacement triple.taus::Vector{NTuple{3,Vector{Float64}}}: the corresponding (τ1,τ2,τ3) triples, same order asmats.
Notes
- Raw V̄ values are stored without antisymmetrization; the HF self-energy symmetrization is handled inside
build_heff_k!. - The density-density special case (all τ1=τ3=0) is detected downstream in
build_heff_k!to select the real-space path.
MeanFieldTheories.build_Vk — Function
build_Vk(V_r, kgrid) -> FunctionFourier-transform V̄^{abcd}(τ1, τ2, τ3) to the three-momentum interaction kernel:
Ṽ^{abcd}(k1, k2, k3) = Σ_{τ1,τ2,τ3} V̄^{abcd}(τ1, τ2, τ3)
· exp(-i k1·τ1 + i k2·τ2 - i k3·τ3)Returns a closure (k1_idx, k2_idx, k3_idx) -> Array{ComplexF64, 4} that evaluates the interaction kernel at any three k-points on demand. The fourth momentum k4 = k1 + k3 - k2 is fixed by momentum conservation.
Arguments
V_r: Real-space interaction table frombuild_Vr.kgrid: k-grid struct providingk_pointsandnk.
Returns
A callable Ṽ(k1_idx, k2_idx, k3_idx) returning Array{ComplexF64, 4} of shape (d_int, d_int, d_int, d_int).
Notes
- Only needed for the general (non-density-density) path in
build_heff_k!. - For density-density interactions (all τ1=τ2, τ3=0 in Vr.entries), `buildheffk!
uses real-space directly fromVr` without calling this.
MeanFieldTheories.build_Uk — Function
build_Uk(V_k) -> FunctionReturn a closure that, given two k-points k and q, assembles the antisymmetrized HF interaction matrix of shape (d²,d²):
U[α+(β-1)d, μ+(ν-1)d] = Ṽ^{μναβ}(k,k,q) [Hartree 1]
+ Ṽ^{αβμν}(q,q,k) [Hartree 2]
- Ṽ^{μβαν}(k,q,q) [Fock 1]
- Ṽ^{ανμβ}(q,k,k) [Fock 2]This is the k-space analogue of the real-space build_U, encoding all four Wick contraction channels (Theory §4). The row index α+(β-1)d corresponds to Σ^{αβ}(q) and the column index μ+(ν-1)d corresponds to G^{μν}(k), both following Julia's column-major convention (i.e. vec(M)[α+(β-1)*d] == M[α,β]).
Once U_func = build_Uk(V_k), the HF self-energy is
Σ(q) = (1/Nk) Σ_k reshape(U_func(k, q) * vec(G(k)), d, d)Returns nothing if V_k is nothing.
k-point Utilities
MeanFieldTheories.build_kpoints — Function
build_kpoints(unitcell_vectors, box_size) -> Vector{Vector{Float64}}Generate the k-point grid for a D-dimensional lattice.
k = (m1/n1)*b1 + (m2/n2)*b2 + ... for m_i in 0:n_i-1where bi are reciprocal lattice vectors satisfying ai · bj = 2π δ{ij}.
Arguments
unitcell_vectors::Vector{Vector{Float64}}: Direct lattice vectors [a1, a2, ...].box_size::NTuple{D,Int}: Number of unit cells along each direction.
MeanFieldTheories.initialize_green_k — Function
initialize_green_k(Nk, d; G_init=nothing, n_fill=nothing, rng=default_rng()) -> Array{ComplexF64, 3}Initialize the k-space Green's function stored as G[a, b, k_idx] of shape (d, d, Nk) (column-major: each G[:,:,ki] is a contiguous d×d matrix in memory).
If G_init is provided, validates shape and Hermiticity at each k and returns it.
Otherwise, each G(k) is a random Hermitian matrix with eigenvalues in [0, 1] and mean filling n_fill / (Nk * d) per orbital. This initialization has physical magnitude (unlike a small perturbation around zero), so the first SCF step produces a non-trivial Hartree–Fock field that can break symmetry and escape paramagnetic saddle points. If n_fill is not provided, eigenvalues are drawn uniformly from [0, 1].
Arguments
Nk::Int: Number of k-pointsd::Int: Internal dimension per unit cell
Keyword Arguments
G_init: Pre-initialized G array of shape(d, d, Nk), ornothingn_fill::Union{Nothing,Int}: Total electron number; used to set the mean occupation per k-point so that the initial filling is approximately correct.rng: Random number generator
Returns
Array{ComplexF64, 3} of shape (d, d, Nk).
MeanFieldTheories.green_k_to_tau — Function
green_k_to_tau(G_k, kpoints, taus) -> Vector{Matrix{ComplexF64}}Compute G(τ) at each displacement in taus via direct Fourier sum:
G^{ab}(τ) = (1/Nk) Σ_k G^{ab}(k) · exp(-i k · τ)Arguments
G_k::Array{ComplexF64, 3}: Shape(d, d, Nk).kpoints::AbstractVector{<:AbstractVector{Float64}}: k-point list of length Nk.taus::Vector{Vector{Float64}}: displacement vectors at which to evaluate G(r).
SCF Solver
MeanFieldTheories.solve_hfk — Function
solve_hfk(dofs, onebody, twobody, kpoints, n_electrons; kwargs...)Solve Hartree-Fock in momentum space via SCF iteration.
Each k-point is diagonalized independently (parallelized over Threads.nthreads() threads). The magnetic unit cell is encoded in dofs; kpoints samples its Brillouin zone. For broken-symmetry phases (AFM, CDW, …) pass an enlarged magnetic unit cell.
Arguments
dofs::SystemDofs: Internal DOFs of one magnetic unit cell.onebody: Result ofgenerate_onebody(...); needs.opsand.irvec.twobody: Result ofgenerate_twobody(...); needs.opsand.irvec.kpoints::AbstractVector{<:AbstractVector{Float64}}: k-points (usebuild_kpointsfor uniform grids).n_electrons::Int: Total electron number across all k-points.
Keyword Arguments
temperature::Float64 = 0.0max_iter::Int = 1000tol::Float64 = 1e-8: Convergence threshold ‖ΔG‖_F / (Nk·d²).diis_m::Int = 8: DIIS history window (0 = disabled).G_init = nothing: Initial G_k of shape(d, d, Nk); random ifnothing.ene_cutoff::Float64 = 100.0n_restarts::Int = 1: Random restarts; returns lowest-energy converged result.seed::Union{Nothing,Int} = nothinginclude_fock::Bool = trueverbose::Bool = truefield_strength::Float64 = 0.0: Amplitude of the random symmetry-breaking field added to Hk for the first `nwarmupiterations of each restart. The field is a random Hermitian matrix (same for all k, independent per restart), linearly decayed to zero over the warmup phase. Set to a value comparable to the hopping scale (e.g.1.0) combined withn_restarts > 1` to escape paramagnetic saddle points without prior knowledge of the ordered phase.n_warmup::Int = 15: Number of warmup iterations during which the symmetry-breaking field is active (linearly decayed to zero). Only used whenfield_strength > 0. Should be well below the typical SCF convergence count so the system can converge freely after the field has vanished.
Returns
NamedTuple: G_k, eigenvalues, eigenvectors, energies, mu, kpoints, converged, iterations, residual, ncond.
MeanFieldTheories.energy_bands — Function
energy_bands(dofs, onebody, twobody, kgrid, G_k, qpoints; include_fock=true)
-> (bands::Matrix{Float64}, eigenvectors::Array{ComplexF64,3})Compute strict HF band energies along arbitrary q-points from a converged G_k on a uniform k-grid. Returns the band matrix (d × Nq) and the eigenvector tensor (d × d × Nq), where eigenvectors[:, n, qi] is the n-th eigenvector at q-point qi (columns sorted by ascending eigenvalue).
Analysis
MeanFieldTheories.local_spin — Function
local_spin(dofs, G; spin_dof=:spin, kwargs...) -> Vector{NamedTuple}Compute the local magnetic moment on each site from a converged HF density matrix, with optional label-based filtering via keyword arguments.
A site is defined by all quantum numbers except the spin DOF, so the function works for any system (simple site+spin, multi-orbital, supercell, …).
Compatible with both HF solvers:
- HFr: pass
result.G— a real-space density matrix of shape(N, N) - HFk: pass
result.G_k— a k-space density matrix of shape(d, d, Nk); the BZ averageG_loc = Σ_k G_k / Nkis computed automatically.
Requirements
dofs must contain exactly one spin DOF of size 2 (default name :spin). Spin label convention: value 1 → spin-up, value 2 → spin-down.
Keyword arguments
spin_dof: name of the spin DOF (default:spin)- any other keyword: filter criterion on the site label. The value can be a single integer or a vector of integers. Only sites matching all criteria are returned.
Returns
A Vector of NamedTuples, one per (matching) site, ordered by each site's first linear index in dofs.valid_states. Each entry contains:
| field | description |
|---|---|
label | NamedTuple of non-spin QNs, e.g. (site=2,) or (cell=1, sub=2) |
n | total occupation ⟨n↑⟩ + ⟨n↓⟩ |
mx,my,mz | spin vector (⟨Sˣ⟩, ⟨Sʸ⟩, ⟨Sᶻ⟩) |
m | magnitude √(mx²+my²+mz²) |
theta_deg | polar angle from +z axis (degrees, 0°=up, 180°=down) |
phi_deg | azimuthal angle in xy-plane from +x (degrees) |
Spin-component formulas
Given G_loc[a,b] = ⟨c†_a c_b⟩:
mz = (G[↑,↑] − G[↓,↓]) / 2mx = Re(G[↑,↓])my = Im(G[↑,↓])
Examples
result = solve_hfk(...)
# All sites
mags = local_spin(dofs, result.G_k)
# Only orbital=1 (a orbital) across all sites
mags_a = local_spin(dofs, result.G_k; orbital=1)
# Only B sublattice
mags_B = local_spin(dofs, result.G_k; sub=2)
# B sublattice at cells 3 and 4
mags_B34 = local_spin(dofs, result.G_k; sub=2, cell=[3,4])
print_spin(mags_B)MeanFieldTheories.print_spin — Function
print_spin(mags; digits=4, io=stdout)Pretty-print the output of local_spin.
Columns: site label | n | mx | my | mz | |m| | θ(°) | φ(°)
Example output
site n mx my mz |m| θ(°) φ(°)
────────────────────────────────────────────────────────────────────────────────
1 1.0000 0.0000 0.0000 0.2341 0.2341 0.00 0.00
2 1.0000 0.0000 0.0000 -0.2341 0.2341 180.00 0.00MeanFieldTheories.spin_structure_factor — Function
spin_structure_factor(mags, lattice, qpoints) -> Vector{Float64}Compute the magnetic structure factor
\[S(\mathbf{q}) = \frac{1}{N_s} \left|\sum_s e^{i\mathbf{q}\cdot\mathbf{d}_s}\, \vec{m}_s\right|^2\]
at each q-point, where the sum runs over all sites, \mathbf{d}_s is the real-space coordinate of site s (looked up from lattice), and \vec{m}_s = (m_x, m_y, m_z) is its local magnetic moment (e.g. from local_spin).
Arguments
mags: output oflocal_spinlattice: theLatticeused to build the system; site coordinates are extracted automatically frommag.labelviaget_coordinateqpoints: collection of q-vectors to evaluate at (same format asbuild_kpoints)
Returns
Vector{Float64} of length length(qpoints).
Example
mags = local_spin(dofs, result.G_k)
qpts = build_kpoints([a1, a2], (100, 100))
Sq = spin_structure_factor(mags, lattice, qpts)MeanFieldTheories.magnetic_ordering_wavevector — Function
magnetic_ordering_wavevector(mags, lattice, qpoints) -> NamedTupleFind the magnetic ordering wavevector Q as the q-point that maximises spin_structure_factor.
Arguments
mags: output oflocal_spinlattice: theLatticeused to build the systemqpoints: collection of q-vectors to search over (same format asbuild_kpoints)
Returns
A NamedTuple with fields:
| field | description |
|---|---|
Q | the q-vector with the largest S(q) |
Sq_max | the peak value S(Q) |
Sq | full Vector{Float64} of S(q) at all q-points |
Example
mags = local_spin(dofs, result.G_k)
qpts = build_kpoints([a1, a2], (100, 100))
res = magnetic_ordering_wavevector(mags, lattice, qpts)
println("Q = ", res.Q, " S(Q) = ", res.Sq_max)MeanFieldTheories.local_charge — Function
local_charge(dofs, G; kwargs...) -> Vector{NamedTuple}Compute the local charge (occupation) for each valid quantum state from a converged HF density matrix.
All DOFs (spin, orbital, sublattice, …) are treated equally: no DOF is singled out or summed over. Each valid state in dofs gets its own occupation n = G_loc[i, i]. The full quantum-number label is preserved so that charge_structure_factor can extract spatial coordinates via the Lattice.
Compatible with both HF solvers:
- HFk: pass
result.G_k— BZ average is computed automatically. - HFr: pass
result.G— used directly.
Keyword arguments
Any keyword filters on the quantum-number label (same convention as local_spin).
Returns
Vector of NamedTuples with fields label and n, one per matching state, sorted by linear index.
Example
charges = local_charge(dofs, result.G_k)
charges_A = local_charge(dofs, result.G_k; sub=1)MeanFieldTheories.charge_structure_factor — Function
charge_structure_factor(charges, lattice, qpoints) -> Vector{Float64}Compute the charge structure factor
\[N(\mathbf{q}) = \frac{1}{N_s} \left|\sum_s e^{i\mathbf{q}\cdot\mathbf{d}_s}\, \delta n_s\right|^2\]
where $\delta n_s = n_s - \bar{n}$ is the deviation of the local charge from its spatial average. Subtracting the mean suppresses the trivial $\mathbf{q}=0$ peak from uniform filling, revealing true CDW modulations.
Arguments
charges: output oflocal_chargelattice: theLatticeused to build the system; site coordinates are extracted automatically viaget_coordinateqpoints: collection of q-vectors to evaluate at (same format asbuild_kpoints)
Returns
Vector{Float64} of length length(qpoints).
Example
charges = local_charge(dofs, result.G_k)
qpts = build_kpoints([a1, a2], (100, 100))
Nq = charge_structure_factor(charges, lattice, qpts)MeanFieldTheories.charge_ordering_wavevector — Function
charge_ordering_wavevector(charges, lattice, qpoints) -> NamedTupleFind the charge ordering wavevector Q as the q-point that maximises charge_structure_factor.
Returns
A NamedTuple with fields:
| field | description |
|---|---|
Q | the q-vector with the largest N(q) |
Nq_max | the peak value N(Q) |
Nq | full Vector{Float64} of N(q) at all q-points |
Example
charges = local_charge(dofs, result.G_k)
qpts = build_kpoints([a1, a2], (100, 100))
res = charge_ordering_wavevector(charges, lattice, qpts)
println("Q = ", res.Q, " N(Q) = ", res.Nq_max)Visualization
MeanFieldTheories.plot_lattice — Function
plot_lattice(lattice; kwargs...) -> FigureVisualize a Lattice: sites (optionally colored by a DOF) and one or more sets of bonds drawn as line segments.
Requires Makie to be loaded before calling:
using GLMakie # interactive
using CairoMakie # save to fileArguments
lattice: theLatticeto visualize
Keyword arguments
| kwarg | default | description |
|---|---|---|
bond_lists | nothing | Vector{Vector{Bond}} or single Vector{Bond} |
bond_colors | auto palette | per-list line colors |
bond_labels | [] | per-list legend labels |
bond_widths | 1.5 | per-list or scalar line width |
bond_styles | :solid | per-list or scalar line style (:solid, :dash, …) |
site_groupby | nothing | Symbol: DOF name to split sites into colored groups |
site_colors | auto palette | per-group or scalar site color |
site_labels | auto | per-group labels (default: "<dof>=<value>") |
site_markersize | 10 | site marker size |
title | "" | figure title |
Example
using CairoMakie
unitcell = Lattice([Dof(:cell,1), Dof(:sub,2,[:A,:B])],
[QN(cell=1,sub=1), QN(cell=1,sub=2)],
[[0.0,0.0],[1.0,0.0]]; vectors=[[0.0,√3],[1.5,√3/2]])
lattice = Lattice(unitcell, (4, 4))
nn = bonds(lattice, (:p,:p), 1)
nnn = bonds(lattice, (:p,:p), 2)
fig = plot_lattice(lattice;
bond_lists = [nn, nnn],
bond_colors = [:tomato, :steelblue],
bond_labels = ["NN", "NNN"],
site_groupby = :sub,
site_colors = [:seagreen, :mediumpurple])
save("lattice.png", fig)MeanFieldTheories.plot_spin — Function
plot_spin(mags, positions; kwargs...) -> FigureVisualize local magnetic moments as arrows on a lattice.
Requires Makie to be loaded before calling:
using GLMakie # interactive 3D
using CairoMakie # save to file
using WGLMakie # Jupyter notebookArguments
mags: output oflocal_spinpositions: coordinates of each site, same order asmags. Each element is a 2- or 3-component vector[x, y]or[x, y, z].
Keyword arguments
| kwarg | default | description |
|---|---|---|
arrow_color | :crimson | arrow color |
arrow_frac | 0.33 | max arrow length = min_dist × arrow_frac |
shaft_lw | 2.0 | arrow shaft line width |
head_px | 13 | arrowhead marker size (pixels) |
head_frac | 0.32 | fraction of arrow length taken by the head |
site_markersize | 20 | outer circle marker size |
dot_size_frac | 0.45 | inner ⊙/⊗ max size as fraction of site_markersize |
bonds | nothing | bond list from bonds — passed directly, no helper needed |
bond_color | :gray60 | bond line color |
bond_width | 1.5 | bond line width |
unitcell_vecs | nothing | two lattice vectors to draw unit cell outline |
unitcell_origin | nothing | origin of unit cell (defaults to first site) |
axis_padding | 0.5 | extra space around sites on each axis |
title | "" | figure title |
Layout
Single top-view (xy) panel. All three spin components are encoded simultaneously:
- Arrow: direction =
(mx, my), length ∝sqrt(mx²+my²)(in-plane projection) - ⊙ dot:
mz > 0(spin out of page), size ∝mz - ⊗ cross:
mz < 0(spin into page), size ∝|mz|
Sites with purely in-plane spins show only arrows; purely z-polarized sites show only ⊙/⊗; canted spins show both simultaneously.
Example
using CairoMakie
result = solve_hfk(...)
mags = local_spin(dofs, result.G_k)
nn = bonds(lattice, (:p, :p), 1)
fig = plot_spin(mags, unitcell.coordinates;
bonds = nn,
unitcell_vecs = [[3/2, √3/2], [0.0, √3]],
title = "KMH ground state")
save("magnetization.pdf", fig)See Visualization for detailed usage and examples.
Excitations
MeanFieldTheories.solve_ph_excitations — Function
solve_ph_excitations(dofs, onebody, twobody, hf_result, qpoints,
reciprocal_vecs; solver=:TDA, n_list=nothing,
eta=1e-10, verbose=true) -> NamedTupleCompute the particle-hole excitation spectrum along a q-path.
Shifted momenta (k+q, p-q, …) are folded into the first BZ. If the folded point falls on the HF k-mesh, its eigensystem is reused; otherwise it is recomputed on the fly via energy_bands.
Particle-hole pairs are auto-detected per k-point from eigenvalues vs mu.
Keyword n_list
Optional vector of band indices allowed as particle (unoccupied) states. Default nothing means all bands are candidates. Use e.g. n_list=[1,2] to restrict the particle-hole space to low-energy bands only.
Keyword eta
Regularization added to the diagonal of the Bosonic BdG matrix before Cholesky decomposition (RPA only). Default 1e-10. Increase (e.g. 1e-6) for systems where the matrix is nearly singular near Goldstone modes.
Keyword solver
:TDA(default): Tamm-Dancoff (Hermitian, M×M). Builds only A.:RPA: Full RPA. Builds A, B, and D, then solves the 2M×2M eigenvalue problem: (A B; -B† D)(X; Y) = ε(X; Y). The D block uses D(q) = -A(-q)* when the ±q occupation spaces match, otherwise it is computed directly with the residual occupation factor.
Utilities
MeanFieldTheories.rd — Function
rd(x) -> NumberRound a number to PRECISION decimal places. Works with Real and Complex.
MeanFieldTheories.PRECISION — Constant
Global precision for numerical rounding (number of decimal places). All floating-point values (coordinates, hopping amplitudes, interactions, phases, etc.) are rounded to this precision to avoid floating-point comparison issues.