Submission tutorials

MarketSolve is called from ASE the way you'd call any calculator β€” except the compute happens on the marketplace and the answer comes back verified. Four tutorials, easy to hard, matching the four reference systems in Open Problems.

Contents
  1. Bulk MgO β€” hello world
  2. Bulk Fe β€” spin-polarized metal
  3. Defected SrTiO₃ β€” constraints, relaxation, async jobs
  4. NiO β€” noncollinear + SOC + Hubbard U

1. Bulk MgO β€” hello world

The synchronous calculator form. Submit, wait, get a verified number back.

from ase.build import bulk
from marketsolve_client import MarketSolve

atoms = bulk("MgO", crystalstructure="rocksalt", a=4.21)
atoms.calc = MarketSolve(api_key="msk_...", engine="siesta", fidelity="reasonable")

e = atoms.get_potential_energy()   # submits, waits for verification, returns eV
f = atoms.get_forces()             # cached β€” same frozen problem, no second charge

print(atoms.calc.oracle_result.acceptance_json)   # the full verdict, not just numbers
If verification fails, the calculator raises β€” you can never silently receive an unverified energy. The complete raw SIESTA/QE log rides on oracle_result, hash-sealed.

2. Bulk Fe β€” spin-polarized metal

Magnetism uses ASE's standard machinery. Smearing and mixing come from the fidelity preset's parameterizer β€” you declare physics, not solver knobs.

atoms = bulk("Fe", "bcc", a=2.87)
atoms.set_initial_magnetic_moments([2.2] * len(atoms))

atoms.calc = MarketSolve(api_key="msk_...", engine="qe", fidelity="conservative")
e = atoms.get_potential_energy()

3. Defected SrTiO₃ β€” constraints, relaxation, async jobs

A marketplace bounty can take hours to clear, so use the job API instead of the blocking calculator. Constraints come straight from atoms.constraints.

from ase.constraints import FixAtoms
from marketsolve_client import Client

sto = make_srtio3_supercell(3, 3, 3)          # a = 3.905 Γ…
del sto[oxygen_vacancy_index]                  # the defect
sto.constraints = [FixAtoms(indices=outer_shell(sto))]

client = Client(api_key="msk_...")
job = client.submit(sto, engine="siesta", mode="relax",
                    fidelity="reasonable", bounty_usd=65.0)
print(job.id)                                  # persist it β€” safe to exit

# ...later, any process:
result = client.job(job.id).result(wait=True)
relaxed = result.final_geometry
Unsupported constraint types are a hard error at submit, never silently dropped β€” a dropped constraint would change the answer. Supported: FixAtoms, FixedLine, FixedPlane, FixCartesian.

4. NiO β€” noncollinear + SOC + Hubbard U

ASE has no vector-magmom channel, so the client provides one; SOC selects the fully-relativistic pseudopotential table automatically.

from marketsolve_client import Client, set_vector_magmoms, QeParams

nio = make_nio_afm2_cell()                     # rocksalt, AFM-II ordering
set_vector_magmoms(nio, afm2_vectors(nio))     # rides in atoms.info

job = Client(api_key="msk_...").submit(
    nio, mode="scf",
    params=QeParams(ecutwfc_ry=..., kgrid=..., soc=True,
                    hubbard=[{"species": "Ni", "orbital": "3d", "u_ev": ...}]),
    allow_more_accurate=True)
This is the hard-coded-parameters path: you specify the exact tuple and it is frozen verbatim into the problem record. Pseudopotentials are sponsor-selectable: pick a named set β€” pseudodojo-sr (default) or pseudodojo-fr (auto-selected here by soc=True) β€” or upload your own UPF/PSML via POST /v1/pseudos and select them by id. Either way the per-element file hashes freeze into the problem record at posting; solvers can never substitute pseudos at solve time.

The two rules every submission follows