External PySCF#
Use the external-reference API when your Python program, notebook, or another
package already owns the PySCF SCF/CASCI/CASSCF calculation. The API exports the
completed orbitals and final Hamiltonian to the same HDF5 contract as the full
pyscf2mppt YAML route. The downstream workflow can then run without rebuilding
the molecule or rerunning PySCF.
your PySCF code
-> completed SCF/CASCI/CASSCF object
-> ReferenceData
-> MPPT HDF5
-> CIPSIXX -> DIAGPT -> HEFFSO
This page covers both supported ways to continue after export:
run a workflow-only YAML with
pyscf2mppt --from-hdf5;call
run_workflow_from_hdf5()from the same Python program.
Runnable Repository Cases#
The maintained examples live in test_suite/workflows/external_reference/:
Case |
Run from its directory |
Coverage |
|---|---|---|
|
|
RHF export, Python continuation, full-space FCI comparison |
|
|
CASSCF export, CLI continuation and restart checks |
|
|
PySCF + LIBGRPP, including full Ce GRPP, embedding and spin-orbit integrals, through all downstream stages |
|
|
Optional Block DMRG-CASSCF and complete downstream workflow |
Each case owns workflow.yaml, a README and test.sh. Use --dump-only to stop
after export, then run pyscf2mppt ../../workflow.yaml --from-hdf5 reference.h5
from the generated output directory (runs/default/ for H2,
runs/compact-150/ for a direct YPO4 run). Keep the HDF5 and workflow outputs
together. The YPO4 default is a small integration model space, not a converged
excitation-spectrum calculation.
The Ce:YPO4 example shows how an ordinary PySCF program uses native LIBGRPP
through libgrpppy.libgrpp4pyscf.GRPPMole, then hands its completed reference to
MPPT. LIBGRPP evaluates Ce’s full scalar GRPP, including outer-core terms, the
embedding ECPs, point-charge attraction and the spin-orbit operator exported
for HEFFSO. PySCF provides SCF and the remaining ordinary AO integrals.
The model has 150 spherical AO, all-electron oxygen and 40 selected MOs,
including 20 occupied and 20 virtual MOs above HOMO. bash test.sh runs SCF,
export, CIPSIXX, DIAGPT and HEFFSO, then checks the HDF5 contract and final
artifacts. Its README gives the complete launch instructions; validation.json
records the verification status. The log identifies the native LIBGRPP library.
Install MPPT with WITH_LIBGRPP=ON and INSTALL_PYSCF2MPPT=ON
(both enabled by default), using the bundled third-party package as supplied.
See Block DMRG-CASSCF References for optional installation, the solver substitution and the boundary between a Block MPS and CIPSIXX.
What Each Layer Owns#
The boundary is deliberately explicit:
Your Python code builds the molecule, installs any external potentials, converges SCF/CASCI/CASSCF, and chooses the final orbitals.
reference_from_scf()orreference_from_casscf()takes a validated snapshot of those results. It does not run or converge PySCF.dump_reference()applies configured orbital generation, performs the AO-to-MO transformation and publishes one HDF5 artifact atomically.The workflow runs enabled downstream stages in dependency order: CIPSIXX, DIAGPT, then HEFFSO. It does not call PySCF again.
The exported integral and reference datasets are the stable physical input. The HDF5 file itself is not read-only: selection adds determinants and model-space data, and stages that reach checkpointing add restart records. Disabled stages and some preflight failures create no stage record. DIAGPT and HEFFSO also write prefix-qualified numerical outputs beside the workflow logs.
Before The First Run#
Install the Python package and build cipsixx_select, diagpt, and heffso as
described in the installation guide. Make the executables
available on PATH:
export PATH="$MPPT_ROOT/build/bin:$PATH"
command -v cipsixx_select diagpt heffso
Alternatively, pass explicit executable paths to the CLI or Python API. Only executables for enabled stages are required.
For a large transformation, put PySCF scratch on a filesystem with enough space before starting Python:
export PYSCF_TMPDIR=/path/to/large/scratch
First Complete CASSCF Export#
This complete water example exports optimized CASSCF orbitals to
water-reference.h5. It uses CAS(2,2), a 6-31G working basis and the selected
route with MOs 1–9. spin_orbit=False is intentional: the continuation below
stops after scalar DIAGPT.
from pathlib import Path
from pyscf import gto, mcscf, scf
from pyscf2mppt import ExportConfig, dump_reference, reference_from_casscf
mol = gto.M(
atom="O 0 0 0; H 0 -0.757 0.587; H 0 0.757 0.587",
basis="6-31g",
unit="angstrom",
charge=0,
spin=0,
symmetry=False,
)
mf = scf.RHF(mol)
mf.kernel()
if not mf.converged:
raise RuntimeError("SCF did not converge")
mc = mcscf.CASSCF(mf, ncas=2, nelecas=2)
mc.kernel()
if not mc.converged:
raise RuntimeError("CASSCF did not converge")
# Use this same selection definition when starting the downstream workflow.
selection = {
"enabled": True,
"initial_space": "cas",
"core_orbitals": [1, 2, 3, 4],
"cas_orbitals": [5, 6],
"cas_electrons": 2,
"max_selected": 100,
}
dump_path = dump_reference(
reference_from_casscf(mc),
ExportConfig(
output_path=Path("water-reference.h5"),
prefix="water",
spin_orbit=False,
n_start=1,
n_select=9,
cipsixx=selection,
),
)
print(f"MPPT reference: {dump_path}")
Run it as an ordinary Python program:
python external_casscf.py
dump_reference() returns an absolute Path. Keep and pass that returned path
instead of reconstructing a relative path in later Python code.
For an SCF-only reference, import and use reference_from_scf(mf) instead.
Choose the downstream selection settings for that reference explicitly.
The adapter calls mc.get_hcore() exactly once and mc.energy_nuc() exactly
once when explicit values are not supplied. The exporter uses the captured
matrix and scalar; it does not reconstruct the one-electron Hamiltonian from
individual PySCF integrals and does not call get_hcore() again.
The adapter also borrows the completed object’s own native with_df without
building it. Both YAML and Python inherit exact/DF from the selected reference.
integral_mode="exact" or "df" checks that choice and rejects a mismatch.
An exact CASSCF object remains exact even if its parent SCF used DF.
The Same CASSCF Export With Orbital Generation#
This is the same calculation with occupied and virtual generation enabled
directly in ExportConfig. The inactive MOs 1–4 are projected first; virtual
generation starts at MO 7, preserving the CAS MOs 5–6. Each stage uses its own
reference AO basis. The working 6-31G basis remains the integral basis.
Save as external_casscf_projected.py and run
python external_casscf_projected.py:
from pathlib import Path
from pyscf import gto, mcscf, scf
from pyscf2mppt import ExportConfig, dump_reference, reference_from_casscf
mol = gto.M(
atom="O 0 0 0; H 0 -0.757 0.587; H 0 0.757 0.587",
basis="6-31g",
unit="angstrom",
charge=0,
spin=0,
symmetry=False,
)
mf = scf.RHF(mol)
mf.kernel()
if not mf.converged:
raise RuntimeError("SCF did not converge")
mc = mcscf.CASSCF(mf, ncas=2, nelecas=2)
mc.kernel()
if not mc.converged:
raise RuntimeError("CASSCF did not converge")
selection = {
"enabled": True,
"initial_space": "cas",
"core_orbitals": [1, 2, 3, 4],
"cas_orbitals": [5, 6],
"cas_electrons": 2,
"max_selected": 100,
}
dump_path = dump_reference(
reference_from_casscf(mc),
ExportConfig(
output_path=Path("water-projected-reference.h5"),
prefix="water",
spin_orbit=False,
n_start=1,
n_select=9,
cipsixx=selection,
generate_projected_occupieds=True,
generate_occupieds_from=1,
generate_occupieds_to=4,
avas_labels_occupied=("O 2s",),
avas_basis_occupied={"O": ("library", "sto-3g")},
occupied_orbital_map={"O 2s": 4},
generate_projected_virtuals=True,
generate_virtuals_from=7,
avas_labels_virtual=("O 2p",),
avas_basis_virtual={"O": ("library", "sto-3g")},
),
)
print(f"MPPT reference: {dump_path}")
Generation uses the same numerical routines as YAML. It leaves the supplied
mc and reference arrays intact and recomputes populations in the final basis
from the full correlated one-particle density, including its off-diagonal terms.
It does not rerun CASSCF or transform its CI coefficients into a downstream
wavefunction. CASSCF generation requires density information; old checkpoints
without it can still export already-prepared orbitals.
YAML saves the prepared orbitals at its existing orbital checkpoint. Resuming export or inspecting that checkpoint uses the saved coefficients and populations without running either generator again. Reported source orbital energies remain source metadata; the generators do not recanonicalize the final orbitals.
Default Changes For Existing Python Callers#
integral_mode now defaults to None (inherit the reference), and
write_property_integrals defaults to True, matching YAML export. To export
an exact reference, supply an exact source/provider; an explicit mode cannot
silently replace its approximation. Set write_property_integrals=False when
property datasets are intentionally unnecessary.
External export still requires overwrite=True to replace an existing file.
The YAML runner retains its replacement default; an explicit mppt.overwrite
overrides it. YAML molecule.so remains the fallback for spin_orbit.
Run From A Workflow YAML#
Create a workflow-only YAML. It needs only the mppt section; molecule, basis,
SCF, and CASSCF sections are neither needed nor rerun.
mppt:
prefix: water
cipsixx:
enabled: true
initial_space: cas
core_orbitals: [1, 2, 3, 4]
cas_orbitals: [5, 6]
cas_electrons: 2
max_selected: 100
diagpt:
enabled: true
integral_space: auto
heffso:
enabled: false
Run the enabled stages against the exported file:
# Continue from the saved reference; SCF and integral export are already complete.
pyscf2mppt workflow.yaml --from-hdf5 water-reference.h5
For executables that are not on PATH, use explicit paths:
pyscf2mppt workflow.yaml \
--from-hdf5 water-reference.h5 \
--selection-executable "$MPPT_ROOT/build/bin/cipsixx_select" \
--diagpt-executable "$MPPT_ROOT/build/bin/diagpt"
The CLI creates runs/workflow/ by default; use --output-dir for another
location. Generated inputs are in input/generated/, raw stage logs in logs/,
native binary outputs in data/, and analysis tables in reports/heffso/.
run.log contains the complete pipeline stream. mppt.prefix is a filename
prefix, not a path. The explicit --from-hdf5 path is authoritative and writable:
mppt.output_hdf5 does not override it, and the source is updated in place.
No implicit HDF5 copy is made; an explicit archive includes a verified copy.
Matching completed stages are reused automatically. Use --force-workflow
only when the enabled stages must run again despite valid fingerprints:
pyscf2mppt workflow.yaml \
--from-hdf5 water-reference.h5 \
--resume --force-workflow
--from-hdf5 cannot be combined with --stop-after. Downstream
stage fingerprints live in the HDF5 file, while DIAGPT and HEFFSO reuse also
depends on their prefix-qualified outputs. With a relative prefix, those
outputs live under data/ and reports/heffso/ in the run directory. HEFFSO reuse
additionally requires logs/heffso.log.
Run From The PySCF Program#
The same workflow can start directly after dump_reference() in the Python
program. This low-level API writes raw stage logs and forwards complete native
output to the caller’s stdout; the host program owns its top-level log. Add this
after either CASSCF export example:
from pyscf2mppt.workflow import run_workflow_from_hdf5
# Reuse the selection settings used for export and enable the desired stages.
workflow = {
"mppt": {
"prefix": "water",
"cipsixx": selection,
"diagpt": {"enabled": True, "integral_space": "auto"},
"heffso": {"enabled": False},
}
}
# Launch the enabled MPPT stages from the absolute HDF5 path returned by export.
run_workflow_from_hdf5(
workflow,
dump_path,
workdir=Path("water-workflow"),
)
config_input may be any of the following:
a YAML-like mapping, as above;
a path to a full or workflow-only YAML file;
a
WorkflowConfigreturned bybuild_workflow_config().
Explicit executable paths are available as
selection_executable=, diagpt_executable=, and heffso_executable=. The
Python force=True argument is equivalent to CLI --force-workflow.
The Python API creates workdir while resolving the HEFFSO stage even when all
stages are disabled. Disabled HEFFSO also removes stale prefix-owned sovec,
analysis-log, and properties outputs so they cannot be mistaken for current
results. An all-disabled call therefore performs cleanup; it is not a read-only
no-op. A relative hdf5_path is resolved under workdir, while a relative YAML
config_input path is resolved under the caller’s current directory. Passing
the absolute path returned by dump_reference() avoids the common HDF5-path
mistake.
Export CASSCF Or CASCI#
Use reference_from_casscf() after kernel() has completed. The adapter uses
mc.mo_coeff. PySCF commonly leaves mc.mo_occ unset, so the adapter computes
populations from the final-basis spin-summed CASSCF/CASCI one-particle density.
State-averaged references use their actual solver weights and spin sectors.
An explicitly supplied mc.mo_occ remains authoritative for unmodified export;
generation derives the final populations from the correlated density.
CASCI objects use the same adapter. The adapter does not infer a downstream
CIPSIXX initial space from mc.ncas or mc.nelecas. Configure
initial_space, active orbitals, and active electron counts explicitly in both
the exported cipsixx metadata and the workflow configuration. Reusing one
Python selection mapping, as above, keeps the two definitions synchronized.
Workflow orbital lists are human-facing, 1-based indices in the orbital space
written for CIPSIXX. If n_select > 0, they index the selected space, not the
original full PySCF MO list. See the selection guide for
the HF, CAS, and determinant initial-space contracts.
External Potentials And Final Operators#
Patched get_hcore() implementations are supported. For example, PySCF QM/MM,
point-charge, electric-field, embedding, and custom mean-field objects can
provide the final AO Hamiltonian through their normal public method. The adapter
captures that result once.
Pass hcore_ao explicitly when another layer already owns or caches the final
matrix:
# Supply the complete cached operator and scalar, including all external terms.
reference = reference_from_scf(
mf,
hcore_ao=final_hcore_ao,
constant_energy=final_constant,
)
Providing hcore_ao causes zero get_hcore() calls. It must be the complete
final AO one-electron Hamiltonian, including the base Hamiltonian and every
external contribution exactly once. Do not pass a correction-only matrix, and
do not add a potential again if get_hcore() already includes it.
constant_energy has the same rule: it is the complete additive scalar for the
exported Hamiltonian. The default is one call to energy_nuc(). Supply an
explicit value when the external model has a scalar term that energy_nuc()
does not include, or when the patched provider already uses a different energy
partition. Do not include a scalar both in hcore_ao and constant_energy.
For CASSCF/CASCI, reference_from_casscf() calls the public mc.get_hcore().
This preserves an override installed directly on mc and PySCF’s normal
delegation to mc._scf.get_hcore().
The LIBGRPP example wraps mf.get_hcore with functools.cache before SCF.
This is particularly useful if the same mean-field object is used for CASSCF:
its macro-iteration code repeatedly requests the core Hamiltonian. At fixed
geometry, basis, GRPP parameters and external potentials, the AO operator does
not depend on density or orbital rotations, so repeated calls can reuse it.
The Fock matrix and MO-basis transformations still change during optimization.
Call mf.get_hcore.cache_clear() or create a new mean-field object when modifying
the molecule or potentials in place.
Spin-Orbit And Property Integrals#
ExportConfig.spin_orbit defaults to True. Set it to False only when HEFFSO
will not run; this deliberately writes a zero spin-orbit tensor and leaves no
physical coupling for a later HEFFSO calculation. The current exporter still
evaluates the AO ECPso provider in both modes; False replaces its result with
zeros rather than avoiding that call. Setting spin_orbit=True retains and
transforms the evaluated operator, but cannot guarantee that the chosen
molecule, ECP/GRPP, and basis produce the intended nonzero data. Verify that
scientific input explicitly.
When a separate molecule supplies spin-orbit integrals, pass it through the adapter:
# Keep the SCF reference, but obtain SO integrals from an AO-compatible provider.
reference = reference_from_scf(mf, mol_for_so=spin_orbit_mol)
mol_for_so must preserve the AO count, labels, order, and overlap of mf.mol.
An equal matrix size alone is insufficient.
heffso.properties defaults to true. For an external reference, export the
required full-space property integrals before running HEFFSO with properties:
# Attach final AO property operators to the completed reference.
reference = reference_from_scf(
mf,
one_body_operators_ao={
"dipole": dipole_ao,
"angular_momentum": angular_momentum_ao,
},
)
# Include the property and physical SO datasets required by HEFFSO.
dump_path = dump_reference(
reference,
ExportConfig(
output_path=Path("reference.h5"),
spin_orbit=True,
write_property_integrals=True,
),
)
Explicit property tensors have shape (3, nao, nao). dipole components must
be symmetric and angular_momentum components skew-symmetric. Either name may
be supplied independently. Omitted property tensors are computed from mol
when write_property_integrals=True. Supplying a tensor while leaving
write_property_integrals=False is rejected instead of silently ignoring it.
Set heffso.properties: false in the downstream workflow only when the
four-column energy report without <prefix>heffso_properties.h5 is intended.
Explicit tensors must also follow the conventions recorded in HDF5. dipole
is the bare position operator r, not -r, at the fixed origin (0, 0, 0)
Bohr. angular_momentum contains the real skew coefficients from
-int1e_cg_irxp; the physical complex Hermitian orbital-angular-momentum
operator is 1j * angular_momentum. Shape and symmetry checks cannot detect a
wrong sign, origin, or operator convention.
Choose The Export Route#
Export inherits exact Coulomb integrals or the DF-expanded approximation from the selected source. Memory and scratch settings do not switch the approximation.
ExportConfig.avas_basis_occupied and avas_basis_virtual select independent
reference AO bases for the corresponding diagnostic labels. Python values are
atom-label dictionaries with ("library", name) or ("file", path) tuples;
unspecified atoms inherit the working basis. These settings drive diagnostics
and the enabled generators in both YAML and Python. Labels alone do not rotate
orbitals: set the corresponding generate_projected_* flag. Final
orbital analysis preserves the per-label reference-basis specifications.
The most important ExportConfig controls are:
Setting |
Meaning |
|---|---|
|
Destination |
|
Prefix embedded for downstream MPPT artifacts. Keep it consistent with |
|
Inherit the source’s own exact/DF provider. An explicit |
|
Keep every supplied reference MO; no automatic virtual-tail truncation. |
|
Export the full-ERI route, including |
|
Export compact downstream families for exactly this many consecutive MOs, starting at |
|
Exact positive one-based first selected MO. The window must contain a downstream occupied orbital. For example, |
|
Override the occupied-orbital count used by downstream selected-space tools when the default physical count is not the intended quasi-restricted vacuum. |
|
Automatic workspace from |
|
Keep the monolithic exact selected half-transform; a positive integer optionally bounds its first MO side. Requires |
|
Force disk-backed AO-to-MO transformation only on the exact full route. It has no effect on selected export or DF MO-factor storage. |
|
Atomically replace an existing artifact; defaults to |
To retain all reference MOs with a selected window, use delete_from=0 and
n_select>0. The window is inside the full space, not added to it; n_select=0
requests full-ERI materialization rather than preserving extra orbitals.
Both routes retain the complete final Hamiltonian and requested SO/property
operators described above, with their established HDF5 meanings and indexing.
If delete_from exceeds the number of supplied MO columns, export logs a
[WARN] before orbital analysis and ignores the cutoff, exactly as with
delete_from=0. This includes MO count plus one and applies to both exact and
DF export. The comparison uses the MO count, not the AO basis size. A cutoff
equal to the MO count removes the last MO; negative values and 1 remain
errors. Selected-window settings still apply when the cutoff is ignored.
The default full route can require large temporary and final two-electron
integral storage. For a scientifically appropriate compact selected route,
review its required datasets, indexing rules and consumer limits in
Selected Route. Export success alone does not establish
that a downstream calculation fits. Set PYSCF_TMPDIR before importing the
package to suitable scratch even when max_mem_bytes is bounded.
overwrite=True replaces the entire HDF5 artifact. It therefore removes any
downstream results and workflow checkpoints stored in the old file. It is an
export operation, not a workflow restart option.
What Gets Reused#
Each enabled stage fingerprints its normalized configuration, executable, and
consumed HDF5 inputs. DIAGPT and HEFFSO also validate their prefix-qualified
sidecar outputs. HEFFSO additionally validates its captured primary log. With
the recommended relative prefix, all of these files are in the same workdir.
On a later call:
a matching complete stage with valid outputs is skipped;
a changed stage configuration reruns that stage;
changed upstream selection invalidates DIAGPT and HEFFSO;
force=Trueor--force-workflowreruns all enabled stages.
Workflow-only changes do not rerun PySCF or repeat the AO-to-MO export. They can,
however, replace downstream groups and outputs. With a relative prefix, moving
only a post-workflow HDF5 file to a different workdir preserves the physical
inputs but normally does not preserve DIAGPT/HEFFSO reuse because sidecar
outputs and the HEFFSO primary log are left behind.
With a relative prefix, treat one HDF5 file plus its workflow directory as one restart unit. An absolute prefix adds its external output paths to that unit. Do not launch concurrent workflows that write the same artifact. For independent parameter studies whose results must coexist, copy the freshly exported reference before the first downstream run and use one copy and one working directory per study. For example, run these commands from the project directory:
mkdir -p runs/config-a runs/config-b
cp reference.h5 runs/config-a/reference.h5
cp reference.h5 runs/config-b/reference.h5
(cd runs/config-a && pyscf2mppt ../../config-a.yaml --from-hdf5 reference.h5)
(cd runs/config-b && pyscf2mppt ../../config-b.yaml --from-hdf5 reference.h5)
Inspect a completed artifact read-only and only while no workflow is writing it:
from pathlib import Path
import h5py
with h5py.File(Path("h2-reference.h5"), "r") as h5file:
print("reference schema:", h5file["/mppt/reference"].attrs["schema_version"])
print("workflow stages:", list(h5file.get("/mppt/workflow/stages", {})))
Reference provenance is stored as JSON under /mppt/reference. Stage status is
stored under /mppt/workflow/stages; selection also writes
/cipsixx/selected_determinants and the quasi-restricted model-space handoff.
See Outputs And Checkpoints for the complete file and log layout.
Validation And Current Limits#
ReferenceData detaches its arrays from PySCF and validates the export boundary:
mo_coeffmust be finite, real, AO-by-MO, and orthonormal in the AO overlap metric;mo_occand optionalmo_energymust match the MO count;hcore_aomust be a finite real Hermitian(nao, nao)matrix;constant_energymust be finite;provenance must be JSON-compatible.
The current contract supports C1 calculations with real restricted spatial orbitals and one-electron spin-orbit integrals. Complex orbitals and unrestricted alpha/beta coefficient pairs are rejected rather than coerced. The exporter does not preserve a non-C1 symmetry-block contract.
ReferenceData validates the completed arrays but does not decide whether an
SCF or CASSCF solution is scientifically converged. Check mf.converged or
mc.converged before adapting the object, as in the examples.
ExportConfig is strict. Use Path for output_path and correctly typed values
for all controls. The destination is published only after a complete temporary
artifact has been written beside it. Existing destinations are rejected unless
overwrite=True; a failed overwrite leaves the previous artifact unchanged.
Common Problems#
Symptom |
Cause and fix |
|---|---|
|
|
Unexpected external-potential energy |
Verify that |
|
The destination already exists. Reuse it for workflow restart, choose a new path, or deliberately set |
No downstream computation starts |
All three downstream |
|
Put the built executables on |
CAS orbitals are rejected or select the wrong space |
Lists are 1-based and refer to the exported CIPSIXX orbital space. With |
DIAGPT starts without model-space data |
A fresh export needs CIPSIXX selection first. Enable |
HEFFSO gives zero or unexpected spin-orbit splitting |
Export with |
HEFFSO reports missing property inputs |
|
Relative HDF5 path works in CLI but not Python |
The Python API resolves a relative HDF5 path under |
Export rejects its memory budget or buffer cap |
Use the reported byte counts and shared memory guidance. |
Full-route export runs out of memory or scratch |
Check the molecule total hint and scratch capacity. |
Workflow-only parsing currently ignores unknown top-level keys and unknown keys
directly under mppt; nested cipsixx, diagpt, and heffso sections are
strict. Check spelling because a typo such as mppt.prefx silently selects the
default prefix.
For production settings, continue with the unified workflow guide and the exact YAML input reference. Selection targets, active spaces, root counts, and state weights are scientific inputs; do not copy the small H2 demonstration values to another system.