MarketSolve is called from ASE like any other calculator. The calculation happens on the marketplace and the result comes back verified. Seven examples, easy to hard, matching the reference problems.
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)
oracle_result.Magnetism uses ASE's standard channel. Smearing and mixing come from the fidelity preset. You declare the physics, not the solver settings.
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()
A bounty can take hours to clear, so use the job API instead of the blocking calculator.
Constraints come 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]
sto.constraints = [FixAtoms(indices=outer_shell(sto))]
client = Client(api_key="msk_...")
job = client.submit(sto, engine="siesta", mode="relax",
fidelity="reasonable", bounty_usd=0.047)
print(job.id) # persist it; safe to exit
# later, in any process:
result = client.job(job.id).result(wait=True)
print(result.energy_ev, result.final_geometry)
FixAtoms,
FixedLine, FixedPlane, FixCartesian.ASE has no vector-magmom channel, so the client adds one. Setting soc=True
selects the fully relativistic pseudopotential set.
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)) # stored in atoms.info
job = Client(api_key="msk_...").submit(
nio, mode="scf", soc=True,
params=QeParams(ecutwfc_ry=..., kgrid=...,
hubbard=[{"species": "Ni", "orbital": "3d", "u_ev": ...}]),
allow_more_accurate=True)
pseudodojo-sr by default, pseudodojo-fr when
soc=True), or your own files uploaded via POST /v1/pseudos and
selected by id. Per-element hashes are frozen at posting. A solver cannot substitute
pseudos.A surface needs a vacuum gap, which sends the k-grid to Γ along that axis. This one also relaxes in-plane only, and uses noncollinear spin with spin-orbit coupling.
from ase.build import fcc110
from ase.constraints import FixCartesian
from marketsolve_client import Client, set_vector_magmoms
slab = fcc110("Co", size=(3, 3, 7), a=3.544, vacuum=6.0) # 12 Å between images
set_vector_magmoms(slab, [(0.0, 0.0, 1.7)] * len(slab))
# mask marks the FIXED directions: freeze z, relax x and y
slab.constraints = [FixCartesian(range(len(slab)), mask=(False, False, True))]
job = Client(api_key="msk_...").submit(
slab, engine="qe", mode="relax", fidelity="reasonable",
soc=True, bounty_usd=9.531)
record = job.result(wait=True) # a bounty returns the public settlement record
fix_cartesian entries
and are re-checked on the delivered geometry. A slab whose z coordinates moved fails
regardless of its forces. soc=True requires noncollinear spin and rejects
scalar-relativistic pseudos at posting. Species that are usually magnetic (Fe, Co, Ni, Mn,
Cr, V and others) must declare a spin state; the platform never picks an ordering for
you.A magnetic tunnel junction has two self-consistent states at the same geometry, electrodes parallel or antiparallel. Which one a calculation lands in is decided by the starting density. Forces cannot tell them apart.
from marketsolve_client import Client
mtj = build_fe_mgo_fe(n_fe=6, n_mgo=5) # 88 atoms, 5.73 Å in-plane
moments = [+2.2 if is_bottom_fe(a) else -2.2 if is_top_fe(a) else 0.0 for a in mtj]
mtj.set_initial_magnetic_moments(moments) # antiparallel
bounty = Client(api_key="msk_...").submit(
mtj, engine="siesta", mode="scf", fidelity="conservative", bounty_usd=1.605)
A monolayer MoS₂ memristor switches when an Au atom from the electrode drops into a
sulfur vacancy. The number you want is the energy difference between the two states. That
is two solves that must be comparable, which is the case for turning
allowMoreAccurate off.
from marketsolve_client import Client
client = Client(api_key="msk_...")
hrs = build_au_mos2_au(vacancy=True, filled=False) # 48 atoms: bare S vacancy
lrs = build_au_mos2_au(vacancy=True, filled=True) # 49 atoms: Au in the vacancy
common = dict(engine="siesta", mode="relax", fidelity="conservative",
allow_more_accurate=False) # both members on one frozen spec
bounties = [client.submit(s, bounty_usd=0.216, **common) for s in (hrs, lrs)]
e_hrs, e_lrs = (b.result(wait=True)["result"]["energy_ev"] for b in bounties)
print(f"filament formation energy: {e_lrs - e_hrs:+.3f} eV")
allowMoreAccurate on, a solver may deliver either
member converged tighter than the frozen floor. That is fine for one number and wrong for a
difference, because the subtraction then includes the gap between two different
convergence levels. Off, both members are pinned to the same spec. The two cells differ by
one atom, so this is a formation energy and needs a chemical potential reference.FixAtoms; everything near the interface relaxes.allowMoreAccurate is on by default. A solution converged
tighter than the frozen floor settles on the same terms. Turn it off for benchmarks,
convergence studies, matched pairs, and target metastable states; an energy window then
applies.