|
| 1 | +# Visualization |
| 2 | + |
| 3 | +MeanFieldTheories.jl provides built-in visualization through a **Makie package extension**. |
| 4 | +Load any Makie backend before calling the plot functions — no other change to your code is needed: |
| 5 | + |
| 6 | +```julia |
| 7 | +using CairoMakie # save to file (PNG, PDF, SVG, …) |
| 8 | +using GLMakie # interactive window |
| 9 | +using WGLMakie # Jupyter / Pluto notebook |
| 10 | +``` |
| 11 | + |
| 12 | +--- |
| 13 | + |
| 14 | +## Lattice Visualization |
| 15 | + |
| 16 | +```@docs |
| 17 | +plot_lattice |
| 18 | +``` |
| 19 | + |
| 20 | +`plot_lattice(lattice; kwargs...)` draws a tiled lattice with any number of bond sets |
| 21 | +and optional sublattice coloring. |
| 22 | + |
| 23 | +### Example: Honeycomb lattice |
| 24 | + |
| 25 | +```julia |
| 26 | +using MeanFieldTheories |
| 27 | +using CairoMakie |
| 28 | + |
| 29 | +# Honeycomb primitive vectors and 2-site unit cell |
| 30 | +const a1 = [0.0, sqrt(3.0)] |
| 31 | +const a2 = [1.5, sqrt(3.0)/2] |
| 32 | + |
| 33 | +unitcell = Lattice( |
| 34 | + [Dof(:cell, 1), Dof(:sub, 2, [:A, :B])], |
| 35 | + [QN(cell=1, sub=1), QN(cell=1, sub=2)], |
| 36 | + [[0.0, 0.0], [1.0, 0.0]]; |
| 37 | + vectors = [a1, a2]) |
| 38 | + |
| 39 | +lattice = Lattice(unitcell, (4, 4)) |
| 40 | + |
| 41 | +nn_bonds = bonds(lattice, (:p, :p), 1) |
| 42 | +nnn_bonds = bonds(lattice, (:p, :p), 2) |
| 43 | + |
| 44 | +fig = plot_lattice(lattice; |
| 45 | + bond_lists = [nn_bonds, nnn_bonds], |
| 46 | + bond_colors = [:tomato, :steelblue], |
| 47 | + bond_labels = ["NN", "NNN"], |
| 48 | + bond_widths = [1.5, 1.0], |
| 49 | + bond_styles = [:solid, :dash], |
| 50 | + site_groupby = :sub, |
| 51 | + site_colors = [:seagreen, :mediumpurple], |
| 52 | + title = "Honeycomb lattice (4×4)") |
| 53 | + |
| 54 | +save("lattice_bonds.png", fig) |
| 55 | +``` |
| 56 | + |
| 57 | + |
| 58 | + |
| 59 | +--- |
| 60 | + |
| 61 | +## Magnetization Visualization |
| 62 | + |
| 63 | +```@docs |
| 64 | +plot_magnetization |
| 65 | +``` |
| 66 | + |
| 67 | +`plot_magnetization(mags, positions; kwargs...)` encodes the full 3D spin vector in a |
| 68 | +single top-view panel: |
| 69 | + |
| 70 | +| Element | Meaning | |
| 71 | +|---------|---------| |
| 72 | +| **Arrow** | direction = $(m_x, m_y)$; length $\propto \sqrt{m_x^2+m_y^2}$ | |
| 73 | +| **⊙ dot** | $m_z > 0$ (spin out of page), size $\propto m_z$ | |
| 74 | +| **⊗ cross** | $m_z < 0$ (spin into page), size $\propto |m_z|$ | |
| 75 | + |
| 76 | +`mags` is the output of [`local_magnetization`](@ref), and `positions` is a vector of |
| 77 | +site coordinates (e.g. `unitcell.coordinates`). Bond pairs for the visualization |
| 78 | +can be extracted from the package's bond infrastructure with a small helper: |
| 79 | + |
| 80 | +```julia |
| 81 | +function bond_pairs(bond_list, lattice) |
| 82 | + state_idx = Dict(s => i for (i, s) in enumerate(lattice.position_states)) |
| 83 | + seen = Set{Tuple{Int,Int}}() |
| 84 | + for b in bond_list |
| 85 | + length(b.states) == 2 || continue |
| 86 | + i = get(state_idx, b.states[1], nothing) |
| 87 | + j = get(state_idx, b.states[2], nothing) |
| 88 | + (isnothing(i) || isnothing(j)) && continue |
| 89 | + push!(seen, (min(i, j), max(i, j))) |
| 90 | + end |
| 91 | + return sort!(collect(seen)) |
| 92 | +end |
| 93 | +``` |
| 94 | + |
| 95 | +### Case 1 — Square lattice Néel AFM (pure z) |
| 96 | + |
| 97 | +All spins point along ±z; only ⊙/⊗ markers appear, no arrows. |
| 98 | + |
| 99 | +```julia |
| 100 | +sq_uc = Lattice( |
| 101 | + [Dof(:site, 4)], |
| 102 | + [QN(site=i) for i in 1:4], |
| 103 | + [[0.0,0.0],[1.0,0.0],[0.0,1.0],[1.0,1.0]]; |
| 104 | + vectors = [[2.0,0.0],[0.0,2.0]]) |
| 105 | + |
| 106 | +sq_bonds = bond_pairs(bonds(sq_uc, (:p,:p), 1), sq_uc) |
| 107 | + |
| 108 | +mz0 = 0.45 |
| 109 | +afm_mags = [ |
| 110 | + (label=(site=1,), n=1.0, mx=0.0, my=0.0, mz=+mz0, m=mz0, theta_deg=0.0, phi_deg=0.0), |
| 111 | + (label=(site=2,), n=1.0, mx=0.0, my=0.0, mz=-mz0, m=mz0, theta_deg=180.0, phi_deg=0.0), |
| 112 | + (label=(site=3,), n=1.0, mx=0.0, my=0.0, mz=-mz0, m=mz0, theta_deg=180.0, phi_deg=0.0), |
| 113 | + (label=(site=4,), n=1.0, mx=0.0, my=0.0, mz=+mz0, m=mz0, theta_deg=0.0, phi_deg=0.0), |
| 114 | +] |
| 115 | + |
| 116 | +fig = plot_magnetization(afm_mags, sq_uc.coordinates; |
| 117 | + title = "Square-lattice Néel AFM (pure z)", |
| 118 | + bonds = sq_bonds) |
| 119 | +save("case1_square_afm_z.png", fig) |
| 120 | +``` |
| 121 | + |
| 122 | + |
| 123 | + |
| 124 | +### Case 2 — Triangular lattice 120° Néel order (pure in-plane) |
| 125 | + |
| 126 | +Spins rotate by 120° per sublattice in the xy-plane; only arrows appear, no ⊙/⊗. |
| 127 | +The magnetic unit cell is the minimal √3×√3 reconstruction (3 sites, one per sublattice). |
| 128 | + |
| 129 | +```julia |
| 130 | +tri_uc = Lattice( |
| 131 | + [Dof(:site, 3)], |
| 132 | + [QN(site=i) for i in 1:3], |
| 133 | + [[0.0, 0.0], [1.0, 0.0], [0.5, sqrt(3)/2]]; |
| 134 | + vectors = [[3/2, sqrt(3)/2], [0.0, sqrt(3)]]) |
| 135 | + |
| 136 | +tri_bonds = bond_pairs(bonds(tri_uc, (:p,:p), 1), tri_uc) |
| 137 | + |
| 138 | +m0 = 0.45 |
| 139 | +tri_mags = [ |
| 140 | + (label=(site=1,), n=1.0, mx=0.0, my=+m0, mz=0.0, ...), # 90° |
| 141 | + (label=(site=2,), n=1.0, mx=-m0*sqrt(3)/2, my=-m0/2, mz=0.0, ...), # −150° |
| 142 | + (label=(site=3,), n=1.0, mx=+m0*sqrt(3)/2, my=-m0/2, mz=0.0, ...), # −30° |
| 143 | +] |
| 144 | + |
| 145 | +fig = plot_magnetization(tri_mags, tri_uc.coordinates; |
| 146 | + title = "Triangular-lattice 120° Néel (pure xy)", |
| 147 | + bonds = tri_bonds, |
| 148 | + axis_padding = 0.4) |
| 149 | +save("case2_triangular_120neel_xy.png", fig) |
| 150 | +``` |
| 151 | + |
| 152 | + |
| 153 | + |
| 154 | +### Case 3 — Canted 120° Néel under uniform magnetic field (all three components) |
| 155 | + |
| 156 | +A uniform field along +z cants all spins by θ = 55° while preserving the 120° in-plane |
| 157 | +arrangement. Each site shows both ⊙ (uniform positive $m_z$) and an in-plane arrow. |
| 158 | + |
| 159 | +```julia |
| 160 | +θ_cant = 55 * π / 180 |
| 161 | +mz_c = m0 * cos(θ_cant) |
| 162 | +mxy_c = m0 * sin(θ_cant) |
| 163 | + |
| 164 | +cant_mags = [ |
| 165 | + (label=(site=1,), n=1.0, mx=0.0, my=+mxy_c, mz=mz_c, ...), |
| 166 | + (label=(site=2,), n=1.0, mx=-mxy_c*sqrt(3)/2, my=-mxy_c/2, mz=mz_c, ...), |
| 167 | + (label=(site=3,), n=1.0, mx=+mxy_c*sqrt(3)/2, my=-mxy_c/2, mz=mz_c, ...), |
| 168 | +] |
| 169 | + |
| 170 | +fig = plot_magnetization(cant_mags, tri_uc.coordinates; |
| 171 | + title = "Triangular-lattice canted 120° Néel (field ∥ z)", |
| 172 | + bonds = tri_bonds, |
| 173 | + axis_padding = 0.4) |
| 174 | +save("case3_canted_120neel_xyz.png", fig) |
| 175 | +``` |
| 176 | + |
| 177 | + |
| 178 | + |
| 179 | +!!! tip "Running the full script" |
| 180 | + The complete runnable script for all three cases is at |
| 181 | + `examples/Plot_magnetization/run.jl`. It uses [`local_magnetization`](@ref) output |
| 182 | + directly from a solved HF problem rather than manually constructed NamedTuples. |
0 commit comments