@@ -51,6 +51,8 @@ void PhScatteringMatrix::builder(std::shared_ptr<VectorBTE> linewidth,
5151
5252 Crystal crystal = innerBandStructure.getPoints ().getCrystal ();
5353
54+ bool usePhElScattering = !context.getElphFileName ().empty ();
55+
5456 // here we call the function to add ph-ph scattering
5557 if (!context.getPhFC3FileName ().empty ()) {
5658 // read this in and let it go out of scope afterwards
@@ -158,7 +160,14 @@ void PhScatteringMatrix::builder(std::shared_ptr<VectorBTE> linewidth,
158160
159161 // recalculate the phonon linewidths from the off diagonals
160162 // we should do this if phel is not involved, otherwise it wipes out phel
161- // enforceDetailedBalance();
163+ if (context.getEnforceDetailedBalance ()) {
164+ if (usePhElScattering) {
165+ Warning (" Ignoring request to enforce detailed balance on the scattering matrix,"
166+ " as this will erase the ph-el contribution along the diagonal!" );
167+ } else {
168+ enforceDetailedBalance ();
169+ }
170+ }
162171
163172 // some phonons like acoustic modes at the gamma, with omega = 0,
164173 // might have zero frequencies, and infinite populations. We set those
@@ -246,5 +255,102 @@ void PhScatteringMatrix::builder(std::shared_ptr<VectorBTE> linewidth,
246255 }
247256}
248257
258+ void PhScatteringMatrix::enforceDetailedBalance () {
259+
260+ // kill the function if it's used inappropriately
261+ if (isMatrixOmega) { // If this matrix has not been symmetrized, this function won't work
262+ DeveloperError (" enforceDetailedBalance should not be called on a matrix without symmetrization factors in the ph only case." );
263+ }
264+ if (!highMemory) return ; // must be high mem, we explicitly use iCalc=1 here
265+ if (context.getUseSymmetries ()) return ; // this is not designed for BTE syms, would need to
266+ // change the way we are indexing this
267+ if (context.getUseUpperTriangle ()) { // TODO this can be implemented without too much difficulty
268+ Warning (" Cannot run enforceDetailedBalance with only upper triangle for now." );
269+ return ;
270+ };
271+
272+ if (mpi->mpiHead ()) std::cout << " \n Enforcing detailed balance, summing off-diagonals to compute diagonal." << std::endl;
273+
274+ // this only happens when we have one iCalc, and no symmetries -- VectorBTE = an array,
275+ // which we use here because of some trouble with coupledVectorBTE object
276+ Eigen::MatrixXd newLinewidths (1 ,numStates); // copy matrix which is the same as internal diag
277+ newLinewidths.setZero ();
278+
279+ // rebuild the diagonal ---------------------------------
280+
281+ LoopPrint loopPrint (" Recalculating the diagonal" ," matrix elements" ,getAllLocalStates ().size ());
282+
283+ // NOTE: if later we want to use symmetries here,
284+ // these would actually be iBTE instead of iState, and we would convert
285+ // sum over the v' states owned by this process
286+ // TODO may want to add OMP here as well as MPI
287+ for (auto [ibte1, ibte2] : getAllLocalStates ()) {
288+
289+ loopPrint.update ();
290+
291+ // throw out lower triangle states
292+ // FIXME DOESNT WORK!
293+ if (context.getUseUpperTriangle () && ibte1 > ibte2) continue ;
294+
295+ // we are computing the diagonal using the off diagonal,
296+ // so these elements won't matter
297+ if (ibte1 == ibte2) continue ;
298+
299+ // NOTE state and bte indices are here the same, as we are blocking the use of symmetries
300+ // BteIndex bteIdx1(ibte1);
301+ // BteIndex bteIdx2(ibte2);
302+ StateIndex is1 (ibte1);
303+ StateIndex is2 (ibte2);
304+
305+ double initialEn = innerBandStructure.getEnergy (is1);
306+ double finalEn = outerBandStructure.getEnergy (is2);
307+
308+ // remove accoustic phonons
309+ if (initialEn < 1e-9 || finalEn < 1e-9 ) continue ;
310+
311+ // calculate the new linewidths
312+ newLinewidths (0 ,ibte1) -= theMatrix (ibte1,ibte2) * finalEn / initialEn;
313+ }
314+ loopPrint.close ();
315+
316+ // sum the element contributions from all processes
317+ mpi->allReduceSum (&newLinewidths);
318+
319+ if (mpi->mpiHead ()) {
320+ std::cout << " \n Checking the quality of recalculated ph diagonal elements." << std::endl;
321+
322+ for (int i = 0 ; i<numStates; i++) {
323+
324+ // don't print zeros
325+ if (newLinewidths (0 ,i) < 1e-15 && internalDiagonal->data (0 ,i) < 1e-15 ) continue ;
326+
327+ if (newLinewidths (0 ,i) < 0 || std::isnan (newLinewidths (0 ,i))) {
328+ StateIndex sIdx (i);
329+ std::cout << std::setprecision (4 ) << " Found a negative ph linewidth for state: " << i << " " << innerBandStructure.getEnergy (sIdx ) << " " << innerBandStructure.getPoints ().cartesianToCrystal (innerBandStructure.getWavevector (sIdx )).transpose () << " " << internalDiagonal->data (0 ,i) << " " << newLinewidths (0 ,i) << std::endl;
330+ // replace with the standard one to avoid definite issues
331+ newLinewidths (0 ,i) = internalDiagonal->data (0 , i);
332+ }
333+ // flag bad linewidth ratios
334+ else if (newLinewidths (0 ,i)/internalDiagonal->data (0 ,i) < 0.25 || newLinewidths (0 ,i)/internalDiagonal->data (0 ,i) > 1.75 ) {
335+ StateIndex sIdx (i);
336+ std::cout << std::setprecision (4 ) << " Found a bad ph linewidth for state: " << i << " " << innerBandStructure.getEnergy (sIdx ) << " " << innerBandStructure.getPoints ().cartesianToCrystal (innerBandStructure.getWavevector (sIdx )).transpose () << " " << newLinewidths (0 ,i) << " " << internalDiagonal->data (0 ,i) << " " << newLinewidths (0 ,i)/internalDiagonal->data (0 ,i) << std::endl;
337+ newLinewidths (0 ,i) = std::max (newLinewidths (0 ,i),internalDiagonal->data (0 ,i));
338+ }
339+ }
340+ }
341+
342+ // reinsert the linewidths in the scattering matrix
343+ internalDiagonal->data = newLinewidths;
344+
345+ // TODO figure out why this function doesn't work right now
346+ // replaceMatrixLinewidths();
347+
348+ // replace the linewidths on the scattering matrix diagonal
349+ int iCalc = 0 ;
350+ for (int iMat = 0 ; iMat < numStates; iMat++) {
351+ // zero the diagonal of the matrix
352+ if (theMatrix.indicesAreLocal (iMat,iMat)) theMatrix (iMat, iMat) = newLinewidths (iCalc, iMat);
353+ }
354+ }
249355
250356
0 commit comments