[DRAFT] Contract the anharmonic matrix elements one index at a time - #186
Draft
utksi wants to merge 8 commits into
Draft
[DRAFT] Contract the anharmonic matrix elements one index at a time#186utksi wants to merge 8 commits into
utksi wants to merge 8 commits into
Conversation
It is read before assignment - that's the problem. The loop hands work out with mod(ctr, mw%n) == mw%r, which only partitions it if every rank counts from the same place. They do not: one 64 rank run started them between 32764 and 32767, so some q-pairs are summed twice and others dropped. Practically, the differences are miniscule. Still... The third order free energy then depends on the rank count and varies between runs; free_energy_fourthorder_secondorder has the same omission.
The full outer product was computed and then contracted down. Peeling one index at a time is the same arithmetic: 4n^7 to 4n^4 per q-quartet for four phonons, 4n^5 to 3n^3 for three.
Same separable contraction as the scattering matrix elements, n^5 to n^3.
Same again. As two matrix multiplies it is n^5 + n^4 rather than n^6, and it stops rebuilding a 207 MB array for every mode pair.
Same separable contraction as in the scattering rates.
Same separable contraction as in the scattering rates.
Same separable contraction as in the scattering rates.
The first assignment is discarded by the next line, which recomputes the same expression. Once per G-vector per q-point.
utksi
marked this pull request as ready for review
September 3, 2026 16:31
utksi
marked this pull request as draft
September 3, 2026 16:32
Contributor
|
Great work! Thank you Utkarsh |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The PR in general is about certain speedups that seem to be on the table.
Building a three- or four-phonon matrix element is contracting a force constant tensor with one eigenvector per leg. In several places TDEP does this by forming the full outer product of the eigenvectors and then contracting the whole thing down in a single pass. The contraction is separable, so peeling one index at a time gets the same number for a lot less arithmetic. That is what most of the commits to this branch (in this PR) are about.
Per q-quartet the four-phonon element goes from 4n^7 to 4n^4 operations, and the three-phonon element from 4n^5 to 3n^3, where n is the number of modes. The same substitution goes in six places: the scattering rates, the third-and fourth-order free energies, the lineshape three- and four-phonon self energies, and the dielectric matrix elements. Two smaller commits also are there.
1. Speedup variance
How much this helps depends strongly on the cell. The matrix elements grow faster with mode count than the rest of the scattering loop does, so a six-mode cell like MgO spends most of its time outside the code being changed, while a sixty-mode one spends almost all of it inside. The tables below therefore give times rather than ratios: the ratio says as much about the cell as about the change.
2. On whether things are correct.
outfile.thermal_conductivityis identical to the current main across every case I ran: MgO fromtests/at 12^3, 20^3, 28^3, 36^3 and 44^3 for three phonons and 8^3, 12^3 for four, and a 24-mode Tl3VSe4 cell at 12^3 and 16^3.3. Setup for timings
Wall clock in seconds around each
mpiruncall, so queue and startup time are excluded. All runs were on Arrhenius (NAISS), 64 MPI ranks (unless stated otherwise) on one exclusively allocated AMD EPYC 9755 (Zen 5) node, gcc 14.3 with-O3 -march=znver5, OpenMPI 5.0.8, FlexiBLAS on the AOCL backend,OMP_NUM_THREADS=1.Everything here runs on the full q-grid. I do not quote subsampled numbers, but they are of course faster too.
The BLAS makes very little difference here, which is probably worth saying since these commits move work into
zgemm. Sweeping FlexiBLAS across AOCL, BLIS, MKL, OpenBLAS and reference NETLIB on the same case moves the runtime by about 10%, and the ordering of the two branches does not change.One thing about how to read these:
--fourthorderis cumulative rather than exclusive, so the four-phonon rows include the three-phonon scattering as well. The lineshape rows are third-order only unless marked otherwise.MgO, from
tests/thermal_conductivity:Tl3VSe4, 24 modes:
The lineshape row is somewhat soft; that runtime varies by tens of percent between repeats, so I think it should be read as clearly faster rather than some number.
How much this helps also depends on how many ranks one gives it, because the work 'removed' is a part that parallelises well. Tl3VSe4 3ph 12^3 on one node:
Past128 ranks, get slower since what is left is startup and communication rather than matrix elements.
Everything in the MgO table runs from inputs already in
tests/, so the correctness part is reproducible. Tl3VSe4 is 8 atoms in the primitive cell, 24 modes, and about 19 MB of force constants once third and fourth order are included, which is more than seems reasonable to add to atests/andexamples/that come to 35 MB between them. I put it in a branch of my fork:That branch is this branch plus a
benchmarks/Tl3VSe4/directory, so it builds and runs without fetching anything else. Tl3VSe4 force constants courtesy of Dr. Ashis Kundu (LiU).The README there should give the cell, the cutoffs, and the exact command for each row above. If you would rather have the case in-tree it could be its own PR. In general it might be good to have a 'bigger' cell for thermal conductivity in examples?
In principle, I have some numbers for CsPbBr3 (20 atom orthorhombic primitive cell) as well; but could not do a fair comparison against current upstream:main when increasing grid-size. Calculated without SOC, the idea was to just observe the scale-up.
But, if someone is experienced with this cell-size (also it's polar), then the numbers are:
I did run one upstream point after all, the 8x8x8 three-phonon case, which gives a somewhat dramatic speedup. Output is identical. The rest I left alone for now.
4. Minor:
ctrnot initialized(The changes are minuscule, but I can see some funky non-recommended compiler making a mess).
Initialise ctr in free_energy_thirdorderis a correctness fix.ctris read before it is assigned, and the loop hands work out withmod(ctr, mw%n) .ne. mw%r, which only partitions anything if every rank is counting from the same place. Uninitialised they are not. Printing the value on entry across a 64 rank run gave 32764 on eighteen ranks, 32765 on seventeen, 32766 on seventeen and 32767 on twelve, so the map from q-pair to rank is neither onto nor one to one: some pairs are summed twice, others by nobody.The third order free energy therefore depends on how many ranks you use, and two runs at the same rank count need not agree. On Tl3VSe4, free energy column of the second cumulant block in eV/atom:
A second 64 rank run of main gave -0.001495733420. One rank is right by accident because
mod(anything, 1)is 0. Adding the singlectr = 0fixes this.free_energy_fourthorder_secondorderhas the same omission, though nothing calls it at present it seems.Commits
They are independent and can be taken or dropped one at a time.
Sub-optimal runs?
It's entirely possible I ran tdep in a sub-optimal way. The thing in favor should be operation count/complexity dropping for identical outputs, so it should win. Someone more experienced with using tdep can perhaps test this (maybe also on a different compute resource).