API Examples
This page demonstrates the SCE public API, feature by feature: SCEBasis → SCEDataset → fit(SCEFit, ...) → SCEModel → Magesty.save / Magesty.load. Runnable scripts for several of these examples live in the examples/ directory of the repository. For an end-to-end walkthrough on a real system, see the Tutorials instead.
save and load are intentionally not exported to avoid clashing with the generic names from JLD2, FileIO, CSV.jl, etc.; call them as Magesty.save(...) / Magesty.load(...).
Configuration file format
Magesty reads structure and interaction settings from a TOML file. Fit parameters (estimator, regularization, torque weight) are passed in Julia at fit time and do not live in the TOML.
[general]
name = "bccfe"
kd = ["Fe"] # list of element names
nat = 16 # total number of atoms in the supercell
periodicity = [true, true, true] # apply periodic boundary to all (x, y, z) directions
[symmetry]
tolerance = 1e-5 # symmetry detection tolerance
isotropy = true # restrict to Lf = 0 (isotropic exchange) terms
[interaction]
nbody = 2 # maximum interaction body order
[interaction.body1]
lmax.Fe = 0 # on-site (1-body) maximum angular momentum per element
[interaction.body2]
lsum = 2 # cutoff sum of l values for 2-body basis functions
cutoff."Fe-Fe" = -1 # pairwise cutoff radius in Å (-1 = include all pairs)
[structure]
kd_list = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] # element index per atom
lattice = [
[5.66, 0.0, 0.0],
[0.0, 5.66, 0.0],
[0.0, 0.0, 5.66],
]
position = [
[0.00, 0.00, 0.00],
[0.25, 0.25, 0.25],
# ... (16 atoms total)
]For a full key reference see Input Keys.
Example 1: Basic flow
Load a TOML template, build the dataset, fit, and inspect in-sample metrics. torque_weight ∈ [0, 1] is the convex combination of the per-sample energy MSE and torque MSE the augmented least-squares problem minimizes; the default 1.0 is a torque-only fit. Set torque_weight = 0.5 to weigh both equally, or 0.0 to fit on energies alone.
using Magesty
basis = SCEBasis("input.toml")
dataset = SCEDataset(basis, "EMBSET")
f = fit(SCEFit, dataset, Ridge(lambda = 1e-4); torque_weight = 1.0)
println("RMSE energy: ", rmse_energy(f) * 1000, " meV")
println("R^2 energy: ", r2_energy(f))
println("RMSE torque: ", rmse_torque(f) * 1000, " meV")
println("j0: ", intercept(f), " number of SCE coefficients: ", length(coef(f)))
Magesty.save(SCEModel(f), "model.xml")Example 2: Building from a CIF file (AtomsIO)
AtomsIO is not a Magesty dependency — install it into the active environment when you want to load structure files. The interaction keyword mirrors the TOML [interaction] table.
using Magesty
using AtomsIO # ] add AtomsIO
system = load_system("FePt.cif")
basis = SCEBasis(
system;
interaction = (
body1 = (lmax = Dict(:Fe => 2, :Pt => 2),),
body2 = (lsum = 2,
cutoff = Dict((:Fe, :Fe) => -1.0,
(:Fe, :Pt) => -1.0,
(:Pt, :Pt) => -1.0)),
),
tolerance_sym = 1e-3,
isotropy = false,
)
dataset = SCEDataset(basis, "EMBSET")
f = fit(SCEFit, dataset, Ridge(lambda = 1e-4))Example 3: Estimator comparison (reuse one dataset)
SCEDataset stores the unweighted energy and torque design matrices, so you can sweep estimators and torque weights without rebuilding it.
using Magesty
dataset = SCEDataset(SCEBasis("input.toml"), "EMBSET")
for est in (OLS(), Ridge(lambda = 1e-4), Ridge(lambda = 1e-2))
f = fit(SCEFit, dataset, est)
println(est, ": RMSE energy = ", rmse_energy(f))
endExample 4: Train / test split
SCEDataset supports vector indexing; the slice is a fresh dataset that shares the same SCEBasis.
using Magesty
dataset = SCEDataset(SCEBasis("input.toml"), "EMBSET")
n_train = round(Int, 0.8 * length(dataset))
train = dataset[1:n_train]
test = dataset[(n_train + 1):end]
f = fit(SCEFit, train, Ridge(lambda = 1e-4))
println("in-sample RMSE energy: ", rmse_energy(f))
println("out-sample RMSE energy: ", rmse_energy(f, test))Example 5: Cache the SALC basis
Building an SCEBasis runs the heavy SALC construction. Persist it to XML and reload it later to skip that step. The XML schema is shared with the Monte Carlo package SpinClusterMC.jl.
using Magesty
# Session 1: cache the basis.
Magesty.save(SCEBasis("input.toml"), "basis.xml")
# Session 2: reload, fit, save the model.
basis = Magesty.load(SCEBasis, "basis.xml")
dataset = SCEDataset(basis, "EMBSET")
f = fit(SCEFit, dataset, Ridge(lambda = 1e-4))
Magesty.save(SCEModel(f), "model.xml")Example 6: Predict and evaluate on custom configurations
using Magesty
using LinearAlgebra
basis = SCEBasis("input.toml")
dataset = SCEDataset(basis, "EMBSET")
f = fit(SCEFit, dataset, Ridge(lambda = 1e-4))
model = SCEModel(f)
num_atoms = basis.structure.supercell.num_atoms
# Every column of a spin-direction matrix must be a unit vector
# (tolerance 1e-6); predict_energy / predict_torque throw an
# ArgumentError on non-unit or non-finite columns.
# Ferromagnetic spin configuration along +z.
spins_fm = zeros(3, num_atoms)
spins_fm[3, :] .= 1.0
println("FM energy: ", predict_energy(model, spins_fm), " eV")
# Spiral configuration in the xy-plane.
spins_spiral = zeros(3, num_atoms)
for i in 1:num_atoms
angle = 2π * (i - 1) / num_atoms
spins_spiral[1, i] = cos(angle)
spins_spiral[2, i] = sin(angle)
end
T = predict_torque(model, spins_spiral)
println("Max torque magnitude: ", maximum(norm.(eachcol(T))))Example 7: Evaluate a saved model on new data
SCEDataset accepts a fitted SCEModel or SCEFit in place of an SCEBasis: it reuses the basis embedded in it (model.basis / fit.dataset.basis) without rebuilding the SALCs. This is convenient when reloading a saved model to score it on a fresh EMBSET file.
using Magesty
# Reload a model saved earlier and score it on new reference data.
model = Magesty.load(SCEModel, "model.xml")
new_data = SCEDataset(model, "EMBSET_new") # basis taken from the model
ŷ_E = predict_energy(model, new_data)
ŷ_T = predict_torque(model, new_data)Example 8: Inspect symmetry, structure, and SALCs
using Magesty
basis = SCEBasis("input.toml")
sym = basis.symmetry
println("Space group: ", sym.international_symbol,
" (#", sym.spacegroup_number, ")")
println("Number of symmetry operations: ", sym.nsym)
cell = basis.structure.supercell
println("Number of atoms: ", cell.num_atoms)
println("Lattice (columns):")
display(Matrix(cell.lattice_vectors))
println("Number of SALCs: ", length(basis.salcbasis.salc_list))Example 9: GCV diagnostics (penalty and data sufficiency)
Generalized cross-validation scores the combined energy+torque objective from a single fit. Use gcv_lambda to pick a ridge penalty and gcv_learning_curve to check whether there is enough training data (a flattening curve). Both work only for the linear estimators (OLS, Ridge, AdaptiveRidge). The raw GCV score is in the weighted-objective unit and only meaningful in relative comparison; gcv_r2 (and the gcv_r2 columns on the sweep results) gives the companion predictive R² on a fixed scale (1 perfect, 0 matches the null model). See Cross-validation diagnostics for the formula.
using Magesty
dataset = SCEDataset(SCEBasis("input.toml"), "EMBSET")
# Penalty selection: one SVD serves the whole lambda path.
path = gcv_lambda(dataset, 10.0 .^ (-6:0.5:0))
println("lambda_best = ", path.lambda_best)
f = fit(SCEFit, dataset, Ridge(lambda = path.lambda_best))
println("GCV at lambda_best: ", gcv(f), " (R2 = ", gcv_r2(f), ")")
write_gcv_lambda(path, "gcv_lambda.txt")
# python tools/FitCheck_gcv_lambda.py gcv_lambda.txt --r2
# Data sufficiency: average GCV over random subsets at each size.
curve = gcv_learning_curve(dataset, Ridge(lambda = path.lambda_best); repeats = 8)
write_gcv_learning_curve(curve, "gcv_learning_curve.txt")
# python tools/FitCheck_gcv_learning_curve.py gcv_learning_curve.txt --r2