Port the magnetic exchange workflow from atomate1 (Heisenberg fit + Vampire Tc) - #1528
Port the magnetic exchange workflow from atomate1 (Heisenberg fit + Vampire Tc)#1528Luguza wants to merge 12 commits into
Conversation
added a ex_mat field to the ExchangeDocument
The pymatgen fork's HeisenbergModel replaced the ground-state unique_site_ids dict with per-ordering site_labels and dropped _get_j_exc, which crashed run_vampire (TypeError in _create_mat). _create_mat now groups sites into materials by (sublattice, spin sign) straight from site_labels[0], and _create_ucf reads the per-bond J_ij from the igraph edge weights. Also fixes the material-element lookup (it indexed the structure with a sublattice id instead of a site index) and drops the unused HeisenbergMapper construction path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…atgen and update ExchangeDocument to include vampire_settings; add tests for VampireCaller input-file writers.
…ler; update ExchangeDocument schema to include residuals and remove avg option.
|
@Luguza thanks! That's great! I am on vacation at the moment and back at the beginning of September. In case I don't react then, could you please ping me again? |
|
Thanks @Luguza I'll also try to take a look soon. Is this dependent on the pymatgen PR? I would be curious to know if the maintainers want to add vampire support back |
| # The strict forcefield groups have mutually incompatible pins (e.g. numpy>=2 | ||
| # via mattersim vs numpy==1.26.4). They are meant to be installed one at a time. | ||
| # uv builds a single universal lockfile, so these must be declared as conflicting | ||
| # extras or `uv sync` fails to resolve them together. | ||
| [tool.uv] | ||
| conflicts = [ | ||
| [ | ||
| { extra = "strict-forcefields-generic" }, | ||
| { extra = "strict-forcefields-torch-limited" }, | ||
| { extra = "strict-forcefields-e3nn-limited" }, | ||
| { extra = "strict-forcefields-numpy-limited" }, | ||
| ], | ||
| ] | ||
|
|
There was a problem hiding this comment.
I'm not sure this is necessary since these are all optional dependencies, can you remove this?
| class VampireOutput(MSONable): | ||
| """Process results from a Vampire Monte Carlo simulation. |
There was a problem hiding this comment.
Can you make this a pydantic BaseModel in keeping with our other schema definitions? Also, since this is such a lightweight schema, could this be folded into the exchange schemas?
| for idx, structure in enumerate(structures): | ||
| if not structure.site_properties.get("magmom"): |
There was a problem hiding this comment.
Condense:
if any(not structure.site_properties.get("magmom") for structure in structures):
...| @job(name="build exchange doc") | ||
| def build_exchange_doc( | ||
| heisenberg_model: HeisenbergModel, | ||
| parent_structure: Structure | None = None, | ||
| vampire_output: VampireOutput | None = None, | ||
| vampire_settings: dict | None = None, | ||
| ) -> ExchangeDocument: |
There was a problem hiding this comment.
I understand why a separate job is necessary, but maybe to reduce boilerplate, can you take a look at how the MPMorph workflow has an optional return_as_job kwarg? This could be added to ExchangeDocument.from_model, or you could directly wrap jobflow.Job(ExchangeDocument.from_model(...)) in the workflow (I think)
| # vampire_caller.py is vendored verbatim from pymatgen (removed there in 2026.3.23); | ||
| # exclude it from lint/format so it stays a faithful copy of the upstream source. |
There was a problem hiding this comment.
If the vampire caller remains in atomate2 (depends on pmg maintainer decision), remove these comments since we would have to maintain the interface to vampire moving forward
This is dependent on the pymatgen PR since I refactored the underlying HeisenbergMapper. It was silently producing wrong results for mixed supercell systems, which are the default inputs from the magnet orderings workflow. But I have not been implementing a vampire caller into the pymatgen side and instead vendored the vampire caller into atomate2 and then molded it to the new workflow. |
|
OK can you ask the PMG devs if they want to restore the vampire caller? The removal was (I think) unintentional |
…llow customization via ATOMATE2_VAMPIRE_CMD and improve error messaging.
Summary
Port of atomate1's
ExchangeWF. Closes #1404.However, this PR is part of a large refactor I did for the underlying HeisenbergMapper from pymatgen.
The following PRs are connected and potentially still ongoing:
ferri_by_motif_{wyckoff_symbol}_flipstrategy; fix dedup never-pruning bug pymatgen#4699HeisenbergMappervia a shared parent-cell sublattice definition pymatgen#4700Introduces
ExchangeMakerflow (atomate2/common/flows/exchange.py): port of atomate1'sExchangeWF. Given magnetic structures with different spin configurations andtheir energies it fits a classical Heisenberg Hamiltonian with pymatgen's
HeisenbergMapperand, optionally, runs Vampire Monte-Carlo for the critical temperature.atomate2/common/jobs/exchange.py):heisenberg_mappingandbuild_exchange_doc, replacing the atomate1HeisenbergModelMapping,HeisenbergModelToDbandVampireToDbfiretasks.ExchangeMaker.make_from_ordering_docconvenience constructor for chaining onto themagnetic-orderings workflow: it pulls structures, per-atom energies and the parent
structure out of a computed
MagneticOrderingsDocumentand re-attaches themagmomsiteproperty that
HeisenbergMapperrequires.make: structure/energy counts must match, every structure mustcarry a
magmomsite property, and orderings are sorted so the ground state is index 0.ExchangeDocumentschema (atomate2/common/schemas/exchange.py) with formula,parent structure, the
{cutoff, tol}Heisenberg settings, the Vampire settings actuallyused, the fit
residual(RMS, meV per magnetic ion - intensive in cell size and number oforderings, so comparable between materials),
ex_params,ex_mat, the serializedHeisenbergModel, andcritical_temp/ fullVampireOutputwhen Vampire was run.atomate2.vampiresubpackage vendoring pymatgen'sVampireCaller/VampireOutput, which were removed from pymatgen in 2026.3.23 (Major reorganization of pymatgen repo pymatgen#4595).Provenance and every local change are documented in the module docstring.
atomate2/vampire/jobs/run_vampire.pywraps it as a jobflow job.HeisenbergModelAPI: per-orderingsublattice_idsand theigraphinteraction graph replace the oldunique_site_idsdictand
_get_j_exclookup, and the ground-state cell is read frommagnetic_structures[0]rather than
structures[0], which now retains the non-magnetic ions. Theavgoption isgone along with the
<J>estimate (HeisenbergModel.javg) it read - the model now fitsshell-resolved
J_ijover every ordering andigraphcarries them per bond in VAMPIRE'snormalized-spin meV convention.
.ucfno longer contains non-magnetic ions, which previously entered the file asatoms and de-synchronised the interaction block's node indices.
unit-cell-category. Without that binding every material kept Vampire's default category0, all atoms collapsed into material 1, the magnetic sublattices vanished and the system
never ordered.
igraph, magnetic structure andsublattice_idsdisagree in length, instead of silently writing a broken.ucf.vampire-serialnow raises instead of being ignored.tests/vampire/test_vampire_caller.py): four tests over the.mat/.ucfwriters, guarding the
HeisenbergModelcontract the caller relies on (magnetic-only cell,in-range interaction indices,
.mat/.ucfmaterial agreement, rejection of a misalignedmodel). They monkeypatch the
vampire-serialsubprocess, so no external binary is needed.pyproject.toml: exclude the vendoredvampire_caller.pyfrom ruff so it stays afaithful copy of the upstream source, and declare the mutually incompatible
strict-forcefields-*extras as[tool.uv] conflictssouv synccan resolve thelockfile. The uv change is unrelated to the exchange workflow and can be split out if
preferred.
Additional dependencies introduced (if any)
vampire_caller.pyimportspandas(usedonly to parse Vampire's stdout), which already arrives transitively via
pymatgen; it isnot added to
dependencieshere. Say the word if it should be declared explicitly.vampire-serialbinary onPATH(https://vampire.york.ac.uk/download/). It is guarded bymonty.dev.requires, and the rest of the flow runs without it viarun_vampire=False.TODO
This is a work-in-progress; the main blocker is upstream.
reworked
HeisenbergMapper/HeisenbergModelAPI (magnetic_structures,per-ordering
sublattice_ids,igraph,residual). The pymatgen change has to land and bereleased first, and the
pymatgen>=pin here bumped accordingly, before this can merge.tests yet for
ExchangeMaker, the two jobs,make_from_ordering_doc, orExchangeDocument.from_model.VampireCallerwrites its input files into the current working directory - upstream'sScratchDirblock is still commented out, as it was in pymatgen.make_from_ordering_docneeds a resolvedMagneticOrderingsDocument(it inspects thestructures while building the flow), so it cannot yet be chained dynamically onto a running
magnetic-orderings flow via an output reference.
Checklist
for the flow, jobs and schema - two small vendored-adjacent modules are still missing
module-level docstrings.
upstream's annotations as-is).
mypyhas not been run yet.are not yet covered.
branch above).