All notable changes to aimspy are documented here. The format follows Keep a Changelog, and this project adheres to Semantic Versioning.
0.2.1 - 2026-09-01
- Real-space grid data capture (
export_grid_data) — new callback (8th) exporting converged density, Kohn-Sham potential, Hartree potential, and grid geometry (coords/weights/indices) after SCF. Includes vdW potential invkswhen enabled in FHI-aims (use_vdw_correction_hirshfeld_sc/use_mbd_std/use_libmbd).GridDatadataclass with derived quantities (delta_rho,delta_vks,vxc,rho_free), npz I/O, and Gatherv-based MPI gather (root peak memory ~1x vs ~3x for pickle-based gather). - Visualization helpers (
aimspy.viz) —scatter_slice,slice_contour,radial_profile,isosurfacefor grid data analysis.radial_profileuses exact nuclear positions fromatom_coordswhen available (fallback: grid-point centroid). tests/unit/test_grid_data.py(19 tests),tests/unit/test_viz.py(20 tests),tests/test_grid_data_capture.py(integration test).tests/data/MoS2_LDA/— LDA test fixture for grid data capture.- NAO radial basis capture (
export_basis_data) — new callback (9th) exporting the full cubic-spline representation of all NAO radial basis functions (u(r), (e−v)·u(r), du/dr) plus per-species logarithmic grid parameters and outer radii, fired once insideprepare_scf(before SCF).BasisDatadataclass with spline evaluation —evaluate_u/evaluate_phi/evaluate_du_dr/evaluate_kinetic/evaluate_deriv(per-function species map attached automatically at init;evaluate_derivreads the aims-nativespline_deriv, non-zero only whenuse_basis_gradientsis active) — and incremental element-per-groupbasis.h5export for building reusable basis libraries. Registered beforeaimspy_init(capture_basis_data=True). - Basis visualization (
aimspy.viz_basis) + CLI —plot_radial_basisplots u(r) or φ(r) from abasis.h5file (runtime-free), with per-l panels, optional log-x (evenly spreads the log-grid sampling), and log-grid rug markers. New CLI commandsaimspy viz-basis(all elements,-jparallel) andaimspy viz-grid(scatter/contour/radial overGridDatanpz). tests/unit/test_basis_data.py(22 tests),tests/unit/test_viz_basis.py(39 tests),tests/test_basis_export.py(integration test, 35 checks),tests/test_basis_callback_paths.py(integration test: pre-init registration / init-time error surfacing / user-precedence).tests/unit/test_viz_basis.py::TestCLIErrorPaths— CLI error-path regression tests (corrupt h5/npz, missing datasets, NaN/negative--r-max, bad-opaths).[viz]optional dependency extra —pip install aimspy[viz]installs matplotlib + scipy for the visualization helpers; the CLI commands now fail with an install hint instead of a rawModuleNotFoundErrorwhen they are missing.docs/architecture.md— full architecture reference (layered design, ABI/callback inventory, state machine, execution timelines, conventions, memory-ownership rules, known limitations).- Makefile targets
test-grid-data-capture,test-basis-export,test-basis-callback-paths(previously runnable only by hand);test-integrationnow includes them.
- Fortran buffer lifecycle —
aimspy_export_grid_data_finalize()explicitly deallocates all 8 module-level buffers (coords, partition_tab, indices, vks, vks0, c_vdw_potential) inaimspy_finalize, preventing ~500 MB retention after Calculator close. - Basis export guards —
aimspy_export_basis_data_runis now only called whenaimspy_initialized(plain aims runs no longer allocate the ~MB export buffers), and early-returns when theexport_basis_datacallback was never registered (newexport_basis_data_registeredflag, same pattern asmodify_h0_registered). - viz defaults —
scatter_slicemarker sizes2.0 → 5.0,slice_contourlevels50 → 60 (denser atom-centred grids stay readable). The new defaults are now pinned by unit tests. - Docs refresh —
key_concepts.mdpatch-system section now describes all three bundled versions (latestv0.2.1), the callback wiring table covers all 8 config-driven callbacks (grid/dHde rows added), and the trigger-point list includesDFPT_module.f90;api_reference.rstpreamble clarifies the import path of the viz helpers; README Core Features gains the NAO basis capture entry and an[viz]install note. register_callback('export_basis_data', ...)timing — the registration semantics are: a pre-init registration is applied beforeaimspy_init(the callback fires insideaimspy_inititself, so this is the only point at which it can take effect). Registering afterinit()issues aUserWarning— by then the callback has already fired and would never be called.- Init-time callback errors — exceptions raised inside callbacks that
fire during
aimspy_init(export_basis_data) now surface asAimspyCallbackErrorfrominit()itself, instead of being deferred to the firstcalc()call.
-
CLI tracebacks on malformed input —
aimspy viz-basis/aimspy viz-gridnow wrap their entire command bodies (including file loading, plotting, and figure saving) and convert library-level exceptions (ValueError/IndexError/KeyError/OSError/TypeError/EOFError/BadZipFile) into cleanError:messages instead of raw tracebacks. Previously e.g. a corruptbasis.h5or truncated.npzcrashed with a full traceback, the interactive (no-o) path ofviz-basishad no wrapping at all, andviz-basisdid not catchKeyError/OSError.--r-max nanis now rejected (r_max must be a positive finite number) instead of silently producing an empty plot. -
aimspy_finalizeon a never-initialized runtime — two pre-init failure paths (older libaims lackingaimspy_register_export_basis_data_callback+capture_basis_data, and an invalid pending callback name) reached the init()-error handler's_defensive_finalize, callingaimspy_finalizeon a runtime whoseaimspy_initnever ran. init() now tracks whetheraimspy_initwas actually entered and skips the Fortran-side finalize when it was not (Python-side callback reset still runs). -
register_callbackin FAILED state — the docstring promised anAimspyStateError, but registration silently succeeded on the still live callback manager. FAILED now raises (matching FINALIZED); the previous operation aborted, so registration could never be useful. -
save_h5species_list duplication on h5py 3.0.x — the read-back of thespecies_listattribute did not decodebytesentries (returned by h5py 3.0.x for vlen-string attrs), making the membership test always True and accumulating duplicate entries on every save. Entries are now decoded before the test; the empty initializer is also written with an explicit string dtype (a bare[]was stored as an empty float64 attribute). -
save_h5contiguity guard — the per-species function-range computation silently assumed species-major ordering ofbasisfn_species; an interleaved ordering would have exported another species' functions under the wrong element. A loudValueErrornow fires on non-contiguous indices. -
tests/conftest.pycollect_ignorenow liststest_basis_export.py/test_basis_callback_paths.py— collectingtests/directly previously aborted the whole pytest run at import (comm.Abort(1)whenAIMSPY_TEST_AIMS_LIBPATHis unset). -
Vacuous
test_rug_respects_r_maxunit test now actually exercises the rug filter (bohr units + r_max below the outer grid points); zeta numbering is now tested through the realsave_h5path with duplicate (n, l) pairs (previously only a local re-implementation was tested). -
Process crash on second
aimspy_initreusing the same logfile (forrtl severe 104) —aimspy_initopened the logfile (unit 20) withstatus='replace'but nothing ever closed the unit; a secondCalculatorin the same process pointing at the same logfile path hit "incorrect STATUS= specifier value for connected file" and died with a native crash.aimspy_initnow defensively closes the unit before opening, andaimspy_finalizecloses it afteraims_finalize(iostat-guarded, idempotent). All existing multi-Calculator tests unknowingly avoided this by using a distinct logfile per cycle. -
Process death at close() for init-only workflows (ELSI) —
final_deallocationsunconditionally calledelsi_finalize(eh_scf)even when no SCF ran (eh_scfnever initialized), and ELSI'selsi_stopends with a bare Fortranstop— silently killing the whole Python process (exit code 0) atCalculator.close().aims_elsi_finalize_scfis now guarded by anelsi_scf_readyflag set inaims_elsi_init_scfand cleared after finalization. This affected thecapture_basis_datainit-only pattern and any init-failure cleanup path;tests/test_basis_export.pypreviously lost its final "ALL CHECKS PASSED" line to this. -
vdW potential omission —
vksnow includes vdW correction when vdW is active. Previouslyvks = V_H + v_xconly, missing the vdW contribution thatintegrate_hamiltonian_matrix_p2adds to the Hamiltonian. -
Fortran callback deregistration +
aimspy_reset_callbacks— newTAimspyCallback.reset_allclears all registered funptrs / aux pointers / input pointers / registered-flags; called insideaimspy_finalizeand exposed via the newaimspy_reset_callbacksbind(c) entry point, whichCalculator.close()/force_close()invoke (when the symbol exists). Prevents a secondCalculatorin the same process from calling a dangling Python function pointer left over from a previousCalculator. -
Serial DFPT dH/de support —
export_dHdenow accumulates the three Cartesian directions across the three serial CPSCF calls (n_dir=1, j_coord∈{1,2,3});modify_dHdeinjects only the current direction in serial mode. Previously serial mode was silently ignored. -
tests/test_dHde_serial_capture.py— self-contained serial capture test: runs full-memory (reference) + serial capture and cross-validates the dH/de tensors to within the CPSCF convergence noise (global relative difference < 1e-4; the two modes run independent CPSCF cycles converging todfpt_sc_accuracy_dm~1e-3, so they do not agree to machine precision). -
tests/test_dHde_serial_inject.py— serial warmstart test: injects the serial capture product and verifies per-direction CPSCF iteration counts are reduced (MoS2: [11,12,12] → [2,2,4]). -
tests/data/MoS2_DFPT_serial/— serial DFPT test data (electric_field_serial .true.). -
tests/test_callback_reset.py— same-process multi-Calculator test verifying no stale-callback invocation after close.
- Spin-polarized (n_spin=2) data now raises instead of silently reading
spin channel 0.
AimspyMatrix.from_aims_csr/to_aims_csrraiseAimspyErrorwhencsr_descr.n_spin != 1;DeepHData.from_directoryraisesAimspyConfigErrorwheninfo.jsonhasspinful: true. Previously spin channel 1 was silently discarded. - Callback failure now marks the Calculator as FAILED (was DONE),
so DONE-only properties (
hamiltonian,energy, …) are inaccessible after a callback error — results are untrustworthy. modify_init_ham/modify_init_first_order_hamnow raiseAimspyStateErrorif called afterinit()/do()(previously a silent no-op).
-
dHde_warmstart_serial.pybuffer layout — the flat injection buffer is now built as contiguous per-direction chunks (C-order ravel of the transposed array); the previous Fortran-order ravel interleaved the directions, injecting wrong data in serial mode. -
rs_matrix.pyCSR descriptor ABI — added the missingn_cells_arrayfield toAimsCsrMxDescr.c_struct, fixing a struct layout mismatch with the FortranTAimspyCsrMxDescr. -
DeepHData.from_directorynow validatesatom_pairsconsistency across matrix files, reordering per-pair when the same set is stored in a different order and raising when the pair sets differ. -
DeepHData.from_memory/set_first_order_hamiltonianrequire all three dH/de directions[x, y, z]to be non-empty (dH/de is only meaningful when all three are present). -
save_first_order_hamiltonianraises a clear error when the first-order chunk layout is not set. -
modify_dHdevalidates theto_aims_csroutput shape before memmove. -
Fortran:
c_loccalls infill_mx_descrare now guarded byallocated();c_f_stringguards against a NULL C pointer; the five matrix dummies in the callback entry points are declaredcontiguous. -
Calculator.hamiltonianis now cached after the first access. -
Failed
calc()/init()now release large matrices retained in the runtime aux dict (overlap, initial/converged/first-order Hamiltonian, external sources). -
DeepHData: optionalelectric_response.h5(dH/de) export — electric-response first-order Hamiltonian (DFPT) support mirroring the existing Hamiltonian warmstart path. Newfirst_order_hamiltonian_entries/_fo_chunk_boundaries/_fo_chunk_shapesdataclass fields;first_order_hamiltonian=keyword argument onfrom_aimspy/from_memory(accepts list of 3AimspyMatrix[x, y, z]in Hartree);set_first_order_hamiltonian/save_first_order_hamiltonian/to_first_order_aimspymethods; auto read/write infrom_directory/save. Theelectric_response.h5file uses the sameatom_pairsashamiltonian.h5butchunk_shapesrows are 3× (one block per Cartesian direction[y, z, x]= real spherical harmonicsm = -1, 0, +1) andentriesis 3× longer. Units are eV (converted from Hartree). -
Calculator: DFPT electric-response capture + warmstart —CalculatorConfig.capture_first_order_hamiltonian: boolflag;Calculator.first_order_hamiltonianproperty (list of 3AimspyMatrix[x, y, z]);Calculator.modify_init_first_order_ham(source=, strategy=)method (direct + deferred mode, REPLACE/ADD strategies) that injects predicted dH/de before the initial U1 computation inDFPT_cpscf, accelerating CPSCF convergence (tested: 11→4 iterations on MoS2). Two new callbacks:export_dHde(post-CPSCF) andmodify_dHde(pre-CPSCF). -
ExternalFirstOrderMatrixSourceProtocol — structural typing protocol for first-order Hamiltonian sources accepted bymodify_init_first_order_ham; implemented byDeepHData. -
tests/unit/test_deeph_data.py— 21 new unit tests for first-order functionality (roundtrip, direction order, shape validation, save/load, error cases, protocol, calculator config). -
tests/unit/test_protocol_enum.py— updated for 2 new callback names. -
DeepHData: optionalforce.h5MD-style export — newforce/energy_eVdataclass fields;force=/energy=keyword arguments onfrom_aimspy/from_memory(accepts aims-order force array + Hartree energy, auto-reorders / converts);set_force/save_forcemethods; auto read/write infrom_directory/save. Theforce.h5file uses a different layout from matrix.h5files:cell(3,3),energyscalar,force(n_atoms,3),stress(6,) zeros placeholder, withformula/natomsroot attributes. Forces are in eV/Å (matchingcalc.forces); energy is in eV (converted fromcalc.energyHartree). -
tests/unit/test_deeph_data.py— 16 new unit tests for force functionality (roundtrip, reorder, shape validation, list input, error cases). -
Integration tests:
test_baseline.pynow checks forces shape/finite;test_export_deeph.pyadds 10 force.h5 cross-validation checks;test_regression.pyadds 10 force/energy checks (total 60 checks). -
tests/unit/test_force_close.py— 9 unit tests forforce_close()andCalcStatetransitions (no MPI/libaims required). -
tests/conftest.py— prevents pytest from collecting integration test scripts (which require MPI). -
examples/continue_calc/run.py— warmstart example using DeepH data produced byfrom_scratch/run.py.
- DFPT serial mode —
electric_field_serial .true.(the default) is not supported for dH/de warmstart; only full-memory mode (electric_field_serial .false.) works correctly. Serial mode injects correctly but CPSCF converges slower (22 vs 16 iterations on MoS2) due to an unresolved interaction between the injected H1 andevaluate_U1_electric_scalapack. Themodify_dHdecallback skips injection whenn_dir != 3 or j_coord != 0(serial mode signature). - Makefile targets:
test-baseline,test-export-deeph,test-warmstart,test-capture-overlap,test-regression,test-strategies,test-integration,test-all,run-from-scratch,run-continue-calc,run-example. pyproject.toml:[tool.pytest.ini_options]withtestpaths.pyproject.toml: 4 new classifiers (License GPLv3+, OS Linux, Audience Science/Research, Python 3 :: Only)..gitignore:*.out,*.h5,tests/data/MoS2/deeph_out/,tests/data/MoS2/_regression_*/,examples/*/deeph_data/.- Integration tests now exit with code 1 on failure (previously always 0).
- CLI:
aimspy patch --versionrenamed to--patch-versionto avoid collision with Click's built-in--versionflag. forcesproperty no longer has a state guard — returnsNonewhen unavailable (beforecalc(), afterclose(), orcompute_forcesnot set) instead of raisingAimspyStateError.logging.basicConfig()at import time replaced withNullHandler— aimspy no longer configures the root logger.Makefile buildtarget now produces both sdist and wheel (matches thepublish.yamlworkflow).Makefile testtarget now runs unit tests only (pytest -v); integration tests run viamake test-integration.Makefile cleantarget now cleans test/example generated artifacts.AIMSPY_TEST_NPROCdefault unified to 8 for both tests and examples.- README:
CalculatorConfigtypes updated toPath | str; rank/opt-in descriptions forhamiltonian/overlap/initial_hamiltonianclarified; 16 previously-undocumented public exports added to "Other public symbols".
DeepHData.from_memory: empty dict{}forhamiltonian_blocks,overlap_blocks, orinitial_hamiltonian_blocksnow correctly producesNoneentries instead of mis-storing data or filling zeros.DeepHData.save_hamiltonian/save_overlap/save_initial_hamiltonian: now raiseAimspyConfigError(wasValueError) — completes the standardization claimed in v0.2.0.libloader.py: MPI CDLL now anchored at module level to prevent GC fromdlclose-ing it and removing RTLD_GLOBAL symbols.export_ovlp/export_h0callback wrappers now setwriteable=Falseon the numpy view to protect Fortranintent(in)arrays from accidental modification.AimspyInfo.from_crenamed to_from_c(internal API, was incorrectly public).- Removed dead
CallbackSpecfields:property_name,property_doc,raw_value_key. tests/test_strategies.py/test_regression.py/test_warmstart.py:deeph_warm→deeph_out(use live-generated data, not stale reference).tests/test_export_deeph.py: removed DeepH-vs-DeepH comparison against non-existentdeeph_warm/reference; replaced with cross-validation against in-memory matrices andrs_hamiltonian.out.- CLI exception handling broadened to catch
OSError/subprocess.SubprocessError(was onlyKeyError/RuntimeError).
0.2.0 - 2026-07-19
- Unified
Calculator.modify_init_ham()API supporting both direct source and deferred decorator modes (replaces the formerModifyInitialHamiltonianConfig+CalculatorConfig.modifypair). CalcState.FAILEDandCalculator.force_close()for safe recovery after SCF orinit()failure (swallows Fortran errors, clears all state).CallbackNameenum andExternalMatrixSourceProtocolfor type-safe callback registration and pluggable matrix sources.CalculatorConfig.capture_overlap=Trueflag — live overlap matrix on all ranks via theexport_ovlpcallback (no longer rank-0-only fallback).CalculatorConfig.initializerhook —fn(Calculator) -> Noneinvoked on rank 0 beforeaimspy_init.AimspyCallbackError.callback_errorsattribute — preserves(name, exception, traceback_str)tuples for post-mortem inspection.- Unit test suite in
tests/unit/(66 tests, no MPI/libaims required):test_structure,test_protocol_enum,test_poscar,test_deeph_data,test_force_close. tests/test_strategies.py—Strategy.ADD/SCALE/CUSTOMvia sub-MPI dispatch (FHI-aims is a global Fortran singleton).tests/test_capture_overlap.py— live overlap on all ranks + two-step API.AIMSPY_TEST_AIMS_LIBPATHenvironment variable for tests and examples (replaces hardcoded locallibaims.sopaths).aimspy patchCLI:--check/--dry-run,--list,--no-git,-yoptions and versioned bundled diffs.pyproject.toml:Changelogproject URL.
- Forces are now captured before the callback error check, so they are no longer lost when a callback raises.
CalcStatetransitions:init()/calc()now wrap Fortran calls in try/except to transition toFAILEDon errors.Calculator.__exit__usesforce_close()on exception body to avoid masking the original error.- Structure derived properties (
phase_factor,basis_subidx,orbit_per_atom,atom_permutation) cached via@functools.cached_property. basis_subidx/orbit_per_atomvectorized (no Python loops).- State guards added to
info,structure,overlapproperties. - Logging: INFO/WARNING emitted on rank 0 only; ERROR on all ranks.
register_callbackfromDONEstate now emits aUserWarning(the callback will not fire).- Deferred
modify_init_hamsource: explicitNonecheck raisesAimspyConfigError. - All
mpirunreferences in docs and examples replaced withmpiexec. Makefile buildtarget now produces both sdist and wheel (matches thepublish.yamlworkflow).pyproject.tomlDevelopment Statusremains3 - Alpha.
AimspyInfo.frac_coordsunits bug — was multiplied byBOHR_TO_ANG, now dimensionless.np.maximummerge in_aimspy_blocks_to_poscarsilently dropped duplicate keys — now raises on duplicate.DeepHData._build_elements_orbital_map: per-shelllis no longer duplicated for multi-atom elements.DeepHDatamethods now raiseAimspyConfigError(instead ofValueError) for consistency with the rest of the package.- Removed dead code (
_map_to_center_cell) and stale E741 lint warnings.
0.1.0 - 2026-07-10
- Full
aimspypackage (22 Python files, ~2950 LOC):calculator.py:Calculatorwithmodify_h0(),capture_h0, state machine.structure.py:AimspyStructure(shared structure + orbital descriptor).matrix.py:AimspyMatrix+ aims↔aimspy CSR conversion.data.py:AimspyInfo,CsrMatrixDescriptor._callbacks/:CallbackSpec/CallbackManager+ 5 registered callbacks (get_descr,export_ovlp,export_h0,modify_h0,python_func)._binding/: ctypes prototypes, Fortran structure mirrors,CFUNCTYPEtypes,libloader(with MPICH symbol-visibility workaround).interface/:ExternalMatrixSourceABC.interface/deeph/:DeepHData,DeepHSource, deeph↔aimspy converters.
- Bundled FHI-aims patch (
aimspy-patch_v0.1.0.diff, 1105 lines) addingsrc/aimspy_api/(5 Fortran modules) and injection points ininitialize_scf.f90/scf_solver.f90/pbc_lists.f90. aimspy patchCLI for applying / uninstalling / listing versioned patches (Click-based, withgit applyandpatch -p1backends).AimspyInfoctypes mirror of the FortranTAimspyInfostruct with automatic unit conversions (Bohr→Å, 1-based→0-based indices).- Integration tests on MoS₂:
test_baseline,test_warmstart,test_regression(50 checks),test_export_deeph. - PyPI trusted-publishing workflow
(
.github/workflows/publish.yaml, triggered on GitHub release). examples/from_scratch/run.py— H₂O baseline SCF + DeepH export.
0.0.2 - 2026-06-18
- Require Python 3.12–3.14.
- README: add PyPI badges, fix DeepX/DeepH-pack link and name.
pyproject.toml: explicitly exclude skeleton subpackages from wheel.
0.0.1 - 2026-06-18
- Minimal PyPI placeholder.
- Initial
Calculatorskeleton and ctypes binding scaffold. - PyPI publish workflow (GitHub Release triggered, trusted publishing).