TOML migration (2026-05)
🤖 Auto-edited by Claude (Opus 4.7) — verify before relying on details. This page and the TOML-migration banners across ecaljdoc were drafted in a single Claude Code session (commit
86e7646onmain). The shape is right (file roles, conversion command, sample dirs, gotchas all reflect the actual ecalj state at master7ee2d7c13/c4bf09418plus the nvfortran cherry-pickf693848ce), but specific commit hashes, line counts, and English wording may need a human pass. Flag anything that reads off and either fix it directly or open an issue.
As of May 2026, the Fortran binaries (lmf, lmfa, lmchk, gwsc, hsfp0, eps_lmfh, epsPP_lmfh, epsPP0, mlo, ...) read structured TOML only. Legacy ctrl.<sname> and GWinput text files are no longer parsed by Fortran — they are kept around as developer references but must be converted before any binary is invoked.
The one TOML file
| file | role |
|---|---|
ctrlg.<sname>.toml | ctrl + GW driver sections ([gw] [mlo] [blocks]) + [product_basis] last, with the cut-offs and the per-atom tables (nlx, valence, core) |
The per-atom tables are for the GW path only — normally no hand editing. They feed the mixed-product-basis generator (
hbasfp0/hvccfp0etc.) used bygwsc, the eps tools andmlo's W-side, and are written bygwinit(throughctrlgenToml.pyorLegacy2toml.py); leave them as generated. Until 2026-09 they were a separatePB.<sname>.toml; such a file is not read any more — the binaries abort and point atctrlg_absorb.py <sname>, which moves the tables intoctrlg.If you have a pure DFT / no-GW workflow (just
lmf/lmfa/ band plots) and don't want the GW sections at all, pass--skipgwtoctrlgenToml.py— that skips thelmfa → lmf --jobgw=0 → gwinitsub-step, omits[gw]/[mlo]/[blocks]/[product_basis]from the output.Adding GW sections later (preserving your hand-edits): use
ctrlgenToml.py <sname> --addgw. This appends[gw]/[mlo]/[blocks]/[product_basis]without regenerating the ctrl-side keys — your edits to[bz],[ham],[[spec]]etc. are preserved as is. It refuses to run if[gw]is already present (to avoid silent duplication). Do not plain re-runctrlgenToml.py <sname>for this purpose: the default flow overwrites the wholectrlg.<sname>.tomlfromctrls.<sname>(the previous file is moved toctrlg.<sname>.toml.bakup, one-generation backup only).
<sname> is the user-defined material extension (e.g. si, cu, gaas, fe, ...). One working directory normally has exactly one ctrlg.<sname>.toml; lmf auto-detects it without a positional argument when run inside the directory.
Migration of an existing legacy directory
# inside an old directory containing ctrl.<sname> [+ GWinput]
Legacy2toml.py <sname>
# produces ctrlg.<sname>.toml
# resume normal workflow (lmf, gwsc, eps_lmfh, ...) unchangedLegacy2toml.py lives at ecalj/SRC/exec/Legacy2toml.py (installed in ~/bin/ along with the binaries by InstallAll.py). It is idempotent and prints [INFO] / [WARN] / [ERROR] diagnostics for any command-line -v overrides that would not survive the conversion.
esm_input.dat (slabs)
The separate positional esm_input.dat was retired on 2026-09-16 and became the [esm] section of ctrlg.<sname>.toml. The Fortran does not read the file: a leftover one makes lmf abort and name the converter, ctrlg_absorb.py <sname>, which appends [esm] to the TOML if it is not there yet and moves the original to esm_input.dat.bk with a header saying where the settings went. Legacy2toml.py does the same during a legacy conversion. The same script folds a leftover PB.<sname>.toml (the per-atom product-basis tables, a separate file until 2026-09) into [product_basis]; the GW binaries abort on that file likewise. See ESM in lmf.md.
Run-time --ctrlg: overrides
%const constants embedded in the legacy ctrl.<sname> are baked into ctrlg.<sname>.toml at conversion time. Run-time overrides use --ctrlg:<dotted.path>=<value>, processed in-memory by m_toml_override.f90 (no on-disk rewrite):
# OLD (no longer parsed — silently ignored or aborted)
lmf si -vnk=8 -vmetal=3
lmf si -v[bz.nkabc]=[8,8,8] -v[bz.metal]=3
lmf si --toml.bz.nkabc=[8,8,8] --toml.bz.metal=3
# NEW
lmf si --ctrlg:bz.nkabc=[8,8,8] --ctrlg:bz.metal=3The text after --ctrlg: is the dotted TOML path (section.key); the value parses as TOML ([8,8,8] for an integer vector, true/false lowercase for bools, quoted strings, etc.). The override layer auto-quotes a bare identifier value (find → "find") so that shell quote-stripping is forgiving.
⚠️ Retired override forms (all abort with a hint). Earlier iterations of the migration accepted several override spellings (
-v<NAME>=<VAL>,-v[<path>]=<VAL>,--[<path>]=<VAL>,--<a.b.c>=<VAL>,--toml.<path>=<VAL>,--pr=N,--time=N,M,--phispinsym). They are all gone; any of them on the cmdline triggers a one-line abort pointing at the canonical--ctrlg:<path>=<value>form.
--ctrlg:bz.nkabc=[8,8,8]— directly set the value at TOML path[bz].nkabcinctrlg.<sname>.tomlfor this run, in memory. No%constindirection; no on-disk rewrite. The path must match a real key in the schema. Each applied override is logged on rank 0 so it appears inllmfetc.Practical consequences:
- Old habits like
-vmetal=3,-vnspin=2 -vso=0abort with a migration hint. Use--ctrlg:bz.metal=3,--ctrlg:ham.nspin=2 --ctrlg:ham.so=0.Legacy2toml.pyprints[INFO] / [WARN] / [ERROR]diagnostics for any%constoverrides it encounters during conversion to help spot silent breakages.
Migrated samples (use these as templates)
The following directories under ecalj/Samples/ are fully TOML-migrated and pass testecalj:
| dir | role |
|---|---|
| Samples/MLOsamples/ | MuffinTin Localized Orbitals (Wannier replacement). 17 samples — see MLOsamples/README.md |
| Samples/TestInstall/ | install validation suite. 23 samples driven by testecalj --all |
| Samples/EPS/ | dielectric function ε(q,ω) — EPS_Cu, EPS_GaAs, EPS_Ag (epsPP0) |
| Samples/PROCAR/ | fat-band weight — MgO_PROCAR, Ni2MnGa_L21_PROCAR |
The catch-all index for the sample tree is Samples/README.md.
Legacy (awaiting migration)
Everything else lives under Samples/Legacy/ and still uses ctrl.<sname> + GWinput. To run any of those, first Legacy2toml.py <sname> inside a working copy of the directory, then proceed as usual. The Legacy/ subgroups include Magnon/, AFsymmetry/ (with test.py) plus 24 example/educational directories.
Result summaries (worked-out reference values)
- MLOsamples/README.md § Reference value: bcc Fe Fe-3d on-site W — bcc Fe Fe-3d on-site screened W ~1.5 eV (job_mloW reproduction, 2×2×2 vs 4×4×4 BZ)
- MLOsamples/README.md § Regression test (testecalj Fe) — automated diagonal V/W-V check against inline reference values
- MLOsamples/README_SOC.md — SOC-as-perturbation variant (
job_mlo_soc)
Auto-job runner (ecalj_auto)
For batch QSGW / GW jobs across many materials, see ecalj_auto/README.md and ecalj_auto/README_slot_scheduler.md. The auto/ driver consumes ctrlg.<sname>.toml directly (no legacy fallback) and dispatches to jobtemplate.{kugui,ohtaka,ucgw,...} for SLURM/PBS clusters.
Common gotchas
- Retired override forms —
lmfaborts on-vnspin=2 -vso=0,-v[ham.so]=1,--toml.ham.so=1,--pr=50,--time=5,5,--phispinsym. Each abort prints the canonical--ctrlg:<path>=<value>replacement. - Stale
Worbblocks —gwinput2toml.py(the GWinput leg ofLegacy2toml.py) used to last-write-wins on duplicate<Worb>blocks; now keeps the first. mlo_emax = 0literal — TOML reader now honours an explicit0; the historical "0 means default" sentinel was retired.- gfortran 13.3 / 14.2 codegen — a documented codegen bug was worked around in
m_HamPMT.f90(one-line baitaaa = trim(aaa) // ' '); see commits35ee9f225andf693848ceon master.
For nvfortran 26.1 specifically: findloc runtime crashes on logical arrays were fixed by adding use m_nvfortran, only: findloc to the seven affected source files (commit f693848ce).