DIAGPT profiling#

Complete the environment-manager setup and MPPT installation first. Run the commands below from the repository root with the mppt environment active. The first example uses the configured build/ tree.

Set DIAGPT_PROFILE=1 to print machine-readable records after the off-diagonal OpenMP region. Profiling is disabled by default.

DIAGPT_PROFILE=1 DIAGPT_PROFILE_SAMPLE_STRIDE=1024 \
OMP_NUM_THREADS=16 OMP_DYNAMIC=FALSE build/bin/diagpt < input.inp

Records use the stable form:

[DIAGPT PROFILE] key=value

Exact counters include rows, determinant pairs, row/advance/direct HNTD calls, nonzero row couplings, diagram IDs and families, MPPT2 matrix writes, and per-thread row/pair load. The serial wall timer covers packed-index setup. Worker timers cover worker-local backplay reset and redefinition, row fill, and complete pair loops. The workspace_wall_s field is always zero.

Calling a clock around every pair phase would materially perturb large runs. Therefore advance-backplay, classification, diagram dispatch, and direct-HNTD subphase timers sample every DIAGPT_PROFILE_SAMPLE_STRIDE pair handled by each worker. Their values are sampled worker-seconds, not elapsed wall time and not automatically extrapolated totals. sampled_pairs and the stride are always printed with them.

The profile validates these invariants before printing invariants_ok=1:

  • redef_calls == ncf - 1;

  • rows == ncf - 1;

  • sparse row candidates equal row HNTD calls, while intersection candidates bound advance HNTD calls;

  • classified_pairs == pairs;

  • pairs == direct_hntd_calls == h2_writes;

  • the sum of diagram-ID dispatch counts equals pairs;

  • all reported durations are nonnegative.

Use benchmarks/diagpt/run_benchmark.py for checksum-validated Ce3+/Pr3+ scaling runs and structured JSON/CSV output. Do not compare exact timing values in regression tests; compare absolute energies against the versioned tolerance, MPPT2 matrix provenance, actual OpenMP team size, and global counter invariants. Timer records use wide scientific notation so production-scale aggregate CPU values remain parseable.

The benchmark runner accepts --profile-mode off|base|dj. Use separate output roots for matched modes because each invocation replaces prior results for that case. The selected mode and both profiling environment switches are retained in JSON and CSV provenance. Pass the prior mode’s result.json through --reference-result to make cross-mode energy, normalized off-diagonal MPPT2 matrix elements, and shared exact-work equality part of harness acceptance rather than a manual comparison.

D/J detail profiling#

Exact inner D/J counters are a build-time opt-in so their hot-loop branches and contained helper wrappers are absent from ordinary optimized binaries. The small capability/provenance state remains available in every build. Configure a dedicated profiling build, then set DIAGPT_PROFILE_DJ=1 at runtime. The runtime switch also enables the base profile, so DIAGPT_PROFILE is not needed.

export CC="$CONDA_PREFIX/bin/x86_64-conda-linux-gnu-gcc"
export CXX="$CONDA_PREFIX/bin/x86_64-conda-linux-gnu-g++"
export FC="$CONDA_PREFIX/bin/x86_64-conda-linux-gnu-gfortran"

cmake -S . -B build-dj-profile \
  -DCMAKE_PREFIX_PATH="$CONDA_PREFIX" \
  -DHDF5_ROOT="$CONDA_PREFIX" \
  -DOpenMP_gomp_LIBRARY="$CONDA_PREFIX/lib/libgomp.so.1" \
  -DMPPT_COPY_TO_HOME_BIN=OFF \
  -DINSTALL_PYSCF2MPPT=OFF \
  -DWITH_LIBGRPP=OFF \
  -DMPPT_ENABLE_DIAGPT_DJ_PROFILE=ON
cmake --build build-dj-profile --target diagpt
DIAGPT_PROFILE_DJ=1 OMP_NUM_THREADS=16 OMP_DYNAMIC=FALSE \
build-dj-profile/bin/diagpt < input.inp

An ordinary build fails explicitly if DIAGPT_PROFILE_DJ=1 is requested. The benchmark harness’s dj mode therefore requires the dedicated binary.

The dj_* extension reports exact D/J dispatch and routine calls, aggregate worker time, formal ibeq/ipeq and open-orbital loop visits, rejection stages, tested terms, nonzero contributions, hainij updates, D/J-scoped ai, cont, and deco4 outcomes, and selected PPAA/PAPA/PACV/CVCV integral routes. The j0001 duration is inclusive of its nested j0002; separate nested and direct j0002 times allow an exclusive value to be derived.

Reverse D/J calls intentionally exchange occupied and virtual arrays. Therefore *_ibeq_visits and *_ipeq_visits name formal kernel arguments rather than a fixed physical orbital side. Zero-map rejection totals remain exact across both directions.

For every redefined row, detail mode also prepares stable compact metadata for nonzero equivalence entries and existing open-hole/open-particle lists. Entries retain source position, spatial and spin-orbital identity, spin, irrep, selected ERI class, and packed self-pair index. This metadata is measured but is not used to change D/J traversal, sorting, screening, or accumulation order. Equivalence-map entries represent paired spatial orbitals, so their spin_orbital and spin fields use the documented sentinels 0 and -1; open-hole and open-particle entries retain explicit spin-orbital identity.

Detail counters use one record per OpenMP worker and are merged after the parallel join. Their conservation checks cover D/J routine multiplicities, nested/direct j0002, ai route outcomes, cont spin cases, deco4 branches, selected ERI classes, compact-list bounds, and finite nonnegative durations. Exact inner-loop counters and clocks intentionally add overhead in the dedicated build. Measure its profile-off overhead separately; production overhead is gated with an adjacent ordinary optimized build compiled with the option left OFF.

Routine timers and selected-integral access census#

Build with MPPT_ENABLE_DIAGPT_ACCESS_PROFILE=ON, then set DIAGPT_PROFILE_ACCESS=1 to identify which routine actually reads which integrals. This mode is independent of the existing base and D/J profiles. It covers the selected route (MPPT_SELECTED=true) and fails explicitly if requested in an ordinary build or on the full route.

cmake -S . -B build -DMPPT_ENABLE_DIAGPT_ACCESS_PROFILE=ON
cmake --build build --parallel 1 --target diagpt
MPPT_SELECTED=true MPPT_HDF5_FILE=/path/to/selected.h5 \
DIAGPT_PROFILE_ACCESS=1 OMP_NUM_THREADS=4 OMP_DYNAMIC=FALSE \
build/bin/diagpt < input.inp > diagpt.log 2> diagpt.err

The first command updates an already configured build. For a fresh build, use the installation configuration and add the access-profile option. Ordinary optimized builds omit the access hooks, cached-pair witness fields, and routine scopes at preprocessing time. An instrumented binary with the runtime switch off retains some instrumentation overhead; use a separate ordinary build for production timing comparisons.

The final report contains two record types:

[DIAGPT ROUTINE] routine=a0006 timing=inclusive_invocation_sum calls=1 serial_calls=1 seconds= ...
[DIAGPT ACCESS] routine=cvcv_contribution storage=cvcv_pair api=scalar class=CVCV calls=18 first=1,7,1,7 value= ...

Field

Meaning

routine

Innermost instrumented physical routine; helpers such as ai, cont and sumaij retain their caller’s attribution

storage

Actual pair family, J/K slice, packed J/K array, or the indexed infinity-zero placeholder

api

scalar, indexed, compact, or tile; these are disjoint counts

class

Chemical (pq|rs) orbital classes, independent of the storage family

calls

Exact invocation/read count, including zero-valued integrals and repeated reads

first and value are one successful read’s one-based spatial indices and returned value. The witness comes from the first occupied worker slot at merge; it can change with scheduling. It is not a complete per-call trace. XXXX identifies a quartet containing a nonphysical index, such as infinity.

Classes use C for core below the selected window, A for the selected window, and V for external virtuals. Each pair and then the pair pair are canonicalized using CC, CA, AA, CV, AV, VV. Thus CVCV, CVVC, VCCV, and VCVC share one class, while CCVV is distinct. For example, Fock reads (vc\|v'c) from k_slice_full; that is a CVCV access without a cvcv_pair read.

Pure CVCV reads are attributed to the isolated cvcv_contribution scope inside a0006. For factorized inputs, storage=cvcv_factors api=tile counts the direct and exchange integral values consumed from reconstructed tiles, not individual factor elements read from disk. The [DIAGPT CVCV DF] records separately report factor-read bytes and I/O/GEMM durations.

Timing and concurrency#

Routine seconds sum the elapsed duration of every invocation, including nested calls. A serial call containing an OpenMP region measures the whole region’s wall time. Calls made by workers contribute worker elapsed time. serial_calls distinguishes the two invocation contexts. Do not sum routine durations into a total runtime: nested calls and parallel work overlap. Existing phase wall timers remain the elapsed-runtime reference.

Counters and first witnesses are worker-owned and merged after the final join. There are no clocks, atomics, or log writes in the access hooks. A serial a0004/a0006 scope supplies attribution to its internal workers; nested j0002 restores its caller on return. Nested OpenMP teams are rejected in this mode. Memory is bounded by the routine/source/class registry and maximum team size, not by the number of ERI calls.

The indexed parity replays made by DIAGPT_PROFILE_DJ=1 are excluded from the access census so enabling both modes does not count validation reads twice. scopes_closed=1 confirms balanced routine scopes. unscoped records expose successful reads outside the current routine registry rather than hiding them.

Small reproducible fixtures#

ctest --test-dir build -R '^diagpt_integration_selected_eri$' --output-on-failure

The tests create a 2-core / 4-active / 2-virtual HDF5 file with 36 CAS(4,4) determinants. Two deterministic seeds produce unequal, symmetry-consistent ERIs; no SCF or selector calculation is needed. Inputs, namelists, logs, and CASCI/MPPT2 handoffs remain in build/access-profile-tests/.

For one inspectable run and CSV tables outside pytest:

PYTHONPATH=.:diagpt/tests/integration/access_profile \
python -m access_fixture --binary build/bin/diagpt \
  --output build/access-profile-report --seed 19 --threads 4

access.csv contains every aggregate access row, routines.csv contains the timers, and families.csv is the routine-by-storage count matrix. Reusing an output directory replaces that fixture run’s files.

Checks compare first-read values with an independent dense eight-orbital oracle, exact counters across 1/2/4 workers, profile-on/off CASCI/MPPT2 matrices, and coexistence with D/J profiling when compiled. A separate case zeros only cvcv_pair and verifies that its accesses remain visible. The fixture is for access auditing, not a physical molecular energy reference or a performance benchmark.

To compare an adjacent ordinary build too, set MPPT_DIAGPT_PLAIN to its absolute diagpt path before the CTest command. That optional check also verifies that the ordinary binary rejects DIAGPT_PROFILE_ACCESS=1.

On these fixtures, general cvcv_pair reads occur in cvcv_contribution called by a0006, while compact CVCV and CCVV slices also occur in Fock construction. An absent row means not observed on this input, not proof that a routine can never use that family. One witness does not establish coverage of all spin, index-equality, symmetry, ionization, or model-space branches.