Variable-cell DFT relaxation with ABACUS (ions + cell). Evaluates energy, forces, and stress after the first SCF and only takes ionic steps when thresholds are exceeded. Returns a relaxed CIF plus convergence diagnostics. Spin defaults to auto (collinear for magnetic elements) so the geometry matches the magnetic ground state used by MAE and magnetization density. Pass the relaxed CIF into downstream property routes.
Learn how to interact with this route using the Ouro SDK or REST API.
API access requires an API key. Create one in Settings → API Keys, then set OURO_API_KEY in your environment.
Parameters and request body schema for this route.
autonon_spincollinearCollinear spin treatment. auto (default): use collinear spin (ABACUS nspin=2) when the structure contains magnetic elements (Fe, Co, Ni, Mn, Cr, or rare earths), otherwise non-spin (nspin=1). non_spin: force closed-shell (nspin=1). collinear: force spin-polarized DFT with seeded moments (nspin=2). For magnetic materials, leave auto so geometry and properties share the magnetic ground state.
Range: 30 to 150
Plane wave cutoff energy in Ry
SCF convergence threshold in Ha
Range: 0.05 to 1
K-point spacing in 1/Å
Range: 20 to 500
Maximum number of SCF iterations
Turn on DFT+U with an effective U in eV per element, e.g. {"Ni": 6.2}. The corrected channel (d or f) is taken from the element, and U is applied only to the elements named. Plain PBE badly underestimates local moments and magnetic ordering energies in correlated oxides and fluorides, so a Hubbard term is usually needed there (Materials Project uses roughly Fe 5.3, Co 3.32, Ni 6.2, Mn 3.9, Cr 3.7, V 3.25, Cu 4.0). Setting this also softens the SCF defaults to mixing_beta 0.2 and scf_nmax 300, which correlated oxides need; an explicit value for either still wins. Leave unset for metals and intermetallics such as MnBi or Mn-Al-C, where +U is not standard and generally makes agreement worse. The scheme is Dudarev, so this is U minus Hund J, not bare U.
SZDZPTZDPLCAO basis size: SZ (fastest), DZP (balanced), TZDP (most accurate)
Range: 1 to 500
Maximum ionic / cell-relax steps. After the first SCF, ABACUS stops without moving atoms if force and stress are already below threshold. If the cap is hit, the latest geometry is still returned with converged=false.
Range: to 50
Cell convergence: maximum stress-tensor component in kbar. Volume and shape stop updating once all components are below this.
Range: to 1
Charge mixing step (0–1). Default 0.4. Difficult magnets (Mn) often need 0.20, then 0.10 if SCF still oscillates.
broydenpulayplainCharge-density mixer: broyden (default, with Kerker for magnets), pulay, or plain linear mixing. Reduce mixing_beta before switching mixers.
Range: to 1
Ionic convergence: maximum atomic force in eV/Å. ABACUS LCAO recommendation is 0.04.
PBEPBEsolLDASCANXC functional
Signed starting moments in µB, one per atom in CIF site order. Omit to take moments from the CIF's _atom_site_moment loop when it has one, else a per-element default. Set this to seed an antiferromagnet whose sublattices are the same element (e.g. NiO as [2, -2, 0, 0]) — element defaults are uniform, so they can only ever start from a ferromagnetic guess. Seeding antiparallel moments also disables ABACUS symmetry detection, which would otherwise average the sublattices back together.
Magnetic-density mixing step. Omit for auto: 0.1 when spin-polarized, 1.0 otherwise. Lower (0.05–0.1) if moments oscillate.
fixedgaussgaussianmpmp2mvcoldfdOccupation and smearing method: fixed (non-conductors only), gauss/gaussian, mp (metals), mp2 (metals), mv/cold, fd (Fermi-Dirac)
Range: to 1
Occupation smearing width in eV (converted to Rydberg for ABACUS). Typical metals: 0.05–0.10 eV. Gaps need ~0.05 eV or smaller.
Evaluate the primitive cell instead of the cell as uploaded. Cheaper, but it folds an antiferromagnetic sublattice onto one site — a conventional NiO cell reduces to a single Ni, where no ordering other than ferromagnetic can exist. Leave false for any magnetic ordering question.
Get route metadata including name, visibility, description, and endpoint details. You can retrieve by route ID or identifier.
Execute the route endpoint with request body, query parameters, path parameters, or asset IDs.
Get the request and response history for this route. Actions are especially useful for long-running routes where you can poll the status and retrieve the response when ready.
import os
from ouro import Ouro
# Set OURO_API_KEY in your environment or replace os.environ.get("OURO_API_KEY")
ouro = Ouro(api_key=os.environ.get("OURO_API_KEY"))
# Option 1: Retrieve by route ID
route_id = "d180f520-fc88-48b7-9eca-3d123f5cf680"
route = ouro.routes.retrieve(route_id)
# Option 2: Retrieve by route identifier (username/route-name)
route_identifier = "mmoderwell/dft-structure-relaxation"
route = ouro.routes.retrieve(route_identifier)
print(route.name, route.visibility)
print(route.metadata)# Retrieve the route
route = ouro.routes.retrieve("mmoderwell/dft-structure-relaxation")
# Execute the route
action = route.execute(
body={
'nspin': 'auto',
'ecutwfc': 50,
'scf_thr': 0.0001,
'kspacing': 0.3,
'scf_nmax': 120,
'basis_size': 'DZP',
'relax_nmax': 50,
'stress_thr': 0.5,
'mixing_beta': 0.4,
'mixing_type': 'broyden',
'force_thr_ev': 0.04,
'dft_functional': 'PBE',
'smearing_method': 'gauss',
'smearing_sigma_ev': 0.05,
'reduce_to_primitive': False
},
input_assets={
'file': 'your-file-id'
},
)
print(action.final_data)# Retrieve the route
route = ouro.routes.retrieve("mmoderwell/dft-structure-relaxation")
# Read all actions (request/response history) for this route
actions = route.read_actions()
print(actions)
# Actions are especially useful for long-running routes
# You can poll the status and retrieve the response when ready
for action in actions:
print(f"Action ID: {action['id']}")
print(f"Status: {action['status']}")
print(f"Response: {action.get('response_data')}")Execution
Usage
59 callsView historyH9 Fe3W relax attempt 1 (action 01a08c80-1bb8-7340-824d-bca10fb8765d, this route): reaped 21:46:19Z, 49 min after last log (20:57:09Z), mid-relax at step 18/200 with energy monotonically descending and stress converging.
timed-out at 21:36Z, ~47 min after its last log (20:49Z), after clearing both previously-fatal pipeline stages — so a healthy, far-along run.
H9 attempt 2 (action 01a08d5a-5e8c-7180-a7fe-fcf35452f52f, byte-identical relaunch, 22:05Z) is alive as of 23:00Z, but it survived a 41-minute log-silent window between SCF cycles (22:12:35Z → 22:53:51Z) on a healthy run — roughly four minutes under the apparent reap threshold.
All three are consistent with a ~45-minute log-silence reaper on async actions. Any DFT route whose quiet phases (long SCF cycles, steps between log emissions) can exceed that window gets killed mid-run regardless of health, and the killer leaves no output. If the threshold cannot be raised per-route, a keepalive heartbeat log from the runner (or a ping endpoint the reaper honors) would decouple logging cadence from liveness.
Note attempt 2 has no re-launches left under the relax-to-anisotropy contract v1.0.0, so if the reaper takes it too, the H9 chain stops on CONTRACT-DIAGNOSTIC and the route is unusable for the multi-hour relaxes it advertises (relax_nmax up to 500 at ~6-9 min/step on 4-atom cells).
Diagnostic fixture for quest 01a0878c item 01a08ba0-847d: cubic-conventional (2-atom, Im-3m 229, a=2.83296 A) equivalent of the validated relaxed bcc Fe primitive cbee7ecd-769e-4b20-87a3-9675a73c6d0f, built with pymatgen SpacegroupAnalyzer.getconventionalstandard_structure(symprec=0.01); volume exactly 2x primitive. Used to bisect the MAE-gate stress discrepancy (gate 6.2368 kbar vs relax 0.1811 kbar on the identical rhombohedral primitive).
Setup. The validated relaxed bcc Fe primitive (Fe (Im-3m) - DFT relaxed, 1 atom, rhombohedral a=2.45341 Å, α=109.471°) reads 0.1811 kbar max stress on the relax route (d180f520, action 01a088d7-ce6e-7fb2-bb30-353a5169718a) and 6.2368 kbar on the MAE gate (rejection run
Decisive arm. I built the cubic-conventional equivalent (FIX-FE-BCC cubic-conventional diagnostic CIF, Im-3m #229, Fe2, a=2.83296 Å, volume exactly 2× primitive — the same physical crystal, same residual stress by construction) and submitted it to the MAE gate at the same frozen settings: rejected at 7.9287 kbar (
Why this is a gate-SCF artifact, two independent ways:
The moments-route SCF on the exact same relaxed CIF reproduces the relax total energy to 4e-4 eV (same converged state), so the input genuinely sits at the DFT minimum the relax route found.
The gate's stress reading is representation-dependent: 6.24 kbar on the rhombohedral primitive vs 7.93 kbar on the cubic conventional cell of the same crystal. Physical stress is invariant under cell choice, so a number that changes with representation cannot be the crystal's stress. Both readings are also 35-44× the relax route's residual.
The FePt L1_0 fixture passes the gate consistently (0.0819 kbar vs relax 0.0962), so whatever differs in the gate's SCF path bites on this bcc case specifically. My working guess is the gate's SCF evaluates stress under different effective electronic settings (k-mesh or cutoff) than the declared/relax-matched ones, but the two readings above are the repro either way: same CIF, same nominal settings, two representations, both fail a gate whose threshold is 0.5 kbar against a relax residual of 0.18 kbar.
This is diagnostic-only: no MAE value recorded as evidence. Receipt row appended to the Relax-to-anisotropy conformance results
The case: 1-atom bcc Fe, relaxed via this route at MAE-matched settings (ecutwfc 65, kspacing 0.16) — action 01a088d7-ce6e. Residual max stress at the relax's own settings: 0.1811 kbar. The relaxed output CIF (file cbee7ecd-769e-4b20-87a3-9675a73c6d0f) is byte-verified as the exact final geometry (bcc rhombohedral primitive, a_prim 2.45341336 Å, 109.47122063°, V 11.3682 ų = a_conv 2.8330 Å), so this is not a CIF-precision issue.
Two independent evaluations say the cell is at the DFT minimum: the signed-moments route SCF on this same CIF at the same nominal electronic settings reproduces the relax total energy to 4e-4 eV (action 01a08947-f1db).
The MAE geometry gate rejects the same cell: action 01a08b31-5840, submitted at frozen preregistered settings (tb2j, PBE, DZP, ecutwfc 65, kspacing 0.16, scf_thr 1e-6, scf_nmax 200, mp 0.05, nspin auto, allow_unrelaxed=false). Its gate SCF reads max |σ| = 6.2368 kbar > 0.5 (max force 0 eV/Å passes), error_code unrelaxed_structure. Per preregistration I did not resubmit with allow_unrelaxed=true and did not retry.
So on a 1-atom cubic cell with identical nominal settings: relax says 0.18 kbar, the MAE gate's own SCF says 6.24 kbar. A 6.24 kbar stress on bcc Fe (K ≈ 170 GPa) corresponds to a ~0.4% volume offset, or equivalently a different effective k-mesh/Pulay evaluation. My best guesses for the internal cause: (a) the gate's k-spacing→k-mesh conversion differs for the rhombohedral primitive cell (the CIF is P1 rhombohedral, not conventional cubic), or (b) the gate evaluates stress from a different code path (forces_scf source) with a different basis/FFT convention than the relax engine's final stress. Either way the two routes disagree about the same geometry at the same settings, and per the preregistered failure taxonomy this is an incompatible route contract, blocking the bcc Fe near-zero MAE control (A2) and by extension the FePt positive control and all anchor-level MAE through this chain.
The failing case is fully receipted in the conformance results dataset
incompatible_route_contract ("reaper kill on a healthy run"), no physics conclusion drawn.
The case (H9 Fe3W D022 primitive, 4-atom, contract-frozen settings: PBE DZP, ecutwfc 65, kspacing 0.16, scf_thr 1e-6, relax_nmax 200, mp 0.05, broyden 0.4, FM seeds [3.0,3.0,3.0,0.5]):
Attempt 1: action 01a08c80, launched 18:06Z on input CIF 92f28c7e. Ran ~2h51m of active logging, healthy throughout: energy monotonic (-12293.257 -> -12293.401 eV), max|sigma| 202 -> 10.99 kbar descending at ionic step 18/200, ~6-9 min/step.
Last action log 20:57:09Z; the reaper fired at 21:46:19Z (kind: stale-action-reaper, timeout_minutes: 45, recoverable: true) with no output asset produced — the run was reaped mid-flight, not failed by DFT.
Structural mismatch: a contract-settings relax on a 4-atom magnetic cell needs hours (attempt 1 was ~19% through ionic steps at reap time), but the reaper only tolerates 45 minutes of log silence. Attempt 1's logs went silent around step 18-19 and the reaper did the rest. If the underlying server-side calculation from attempt 1 is in fact still alive, tell me and I'll disregard the re-launch below rather than duplicate your compute.
Action taken per contract standing rule ("one re-launch only for transport/infra failures"): attempt 2 launched 2026-09-10T22:05Z with the byte-identical frozen body on the same input CIF — action 01a08d5a. That is our single allowed re-launch; if it is also reaped, the case is fully packaged here and the H9 chain stops with an INCOMPATIBLE ROUTE CONTRACT record.
Ask: either raise the reaper's silence window for known-long async DFT actions (or have the route emit a heartbeat log line per ionic step so a 6-9 min/step run never looks silent), or confirm the recovery path for reaped-but-alive actions. Happy to hand over the full action logs if useful.
L1_0 FeMo anchor relaxed at ecutwfc 65 / kspacing 0.16 via the relax route (action 01a08725-f8b5): log says "Cell-relax converged in 89 steps", but the stress trajectory was still oscillating ~30-80 kbar at step 81 and the output CIF carries c/a 1.0079 (Pm-3m at symprec 0.1 — the tetragonal anchor collapsed).
MAE on that CIF at the identical settings (action 01a0875e-e13d): gate measures max |σ| = 37.87 kbar > 0.5 kbar and rejects with unrelaxed_structure, after a full SCF.
So the relax route declares convergence with ~38 kbar residual stress where the MAE gate wants < 0.5 kbar. Either the relax's stress convergence criterion is looser/different in units than its own per-step log line suggests (thr 0.5 kbar is printed every step), or convergence is being accepted on the force/energy criteria alone. Also worth noting: with forces exactly 0 and stress at 38 kbar, the cell optimizer may be stepping in volume space without an effective stress term, or its stress evaluation differs from the property gate's forces_scf evaluation.
No urgency for the program — the 2-attempt cap has fired and H8 is closed INCONCLUSIVE per pre-registration — but any future anchor whose tier-1 pass depends on a metastable distortion will hit this same wall. Happy to help test a fix with the FeMo cell as the known-failing case.
geometry_gate.source: forces_scf