1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
4 \\ / O peration | Version: 3.2
5 \\ / A nd | Web: http://www.foam-extend.org
6 \\/ M anipulation | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation, either version 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
28 Pairwise agglomerative AMG policy. Legacy implementation
31 Hrvoje Jasak, Wikki Ltd. All rights reserved
33 \*---------------------------------------------------------------------------*/
35 #include "pamgPolicy.H"
36 #include "amgMatrix.H"
38 #include "addToRunTimeSelectionTable.H"
39 #include "GAMGInterfaceField.H"
41 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
45 defineTypeNameAndDebug(pamgPolicy, 0);
47 addToRunTimeSelectionTable(amgPolicy, pamgPolicy, matrix);
49 } // End namespace Foam
52 const Foam::debug::tolerancesSwitch
53 Foam::pamgPolicy::diagFactor_
60 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
62 void Foam::pamgPolicy::calcChild()
65 // 1) Create temporary equation addressing using a double-pass algorithm.
66 // to create the offset table.
67 // 2) Loop through all equations and for each equation find the best fit
68 // neighbour. If all neighbours are grouped, add equation to best group
71 const label nEqns = matrix_.lduAddr().size();
73 const unallocLabelList& upperAddr = matrix_.lduAddr().upperAddr();
74 const unallocLabelList& lowerAddr = matrix_.lduAddr().lowerAddr();
76 // Get off-diagonal matrix coefficients
77 const scalarField& upper = matrix_.upper();
79 // For each equation calculate coeffs
80 labelList cols(upperAddr.size() + lowerAddr.size());
81 labelList rowOffsets(nEqns + 1);
85 labelList nNbrs(nEqns, 0);
87 forAll (upperAddr, coeffI)
89 nNbrs[upperAddr[coeffI]]++;
92 forAll (lowerAddr, coeffI)
94 nNbrs[lowerAddr[coeffI]]++;
101 rowOffsets[eqnI + 1] = rowOffsets[eqnI] + nNbrs[eqnI];
104 // Reset the list to use as counter
107 forAll (upperAddr, coeffI)
111 rowOffsets[upperAddr[coeffI]] + nNbrs[upperAddr[coeffI]]
114 nNbrs[upperAddr[coeffI]]++;
117 forAll (lowerAddr, coeffI)
121 rowOffsets[lowerAddr[coeffI]] + nNbrs[lowerAddr[coeffI]]
124 nNbrs[lowerAddr[coeffI]]++;
129 child_.setSize(nEqns, -1);
131 // Calculate agglomeration
133 // Get matrix coefficients
134 const scalarField& diag = matrix_.diag();
136 scalarField magOffDiag;
138 if (matrix_.asymmetric())
140 magOffDiag = Foam::max(mag(matrix_.upper()), mag(matrix_.lower()));
142 else if (matrix_.symmetric())
144 magOffDiag = mag(matrix_.upper());
148 // Diag only matrix. Reset and return
157 // Gather disconnected and weakly connected equations into cluster zero
158 // Weak connection is assumed to be the one where the off-diagonal
159 // coefficient is smaller than diagFactor_*diag
162 // Mark all cells to belong to zero cluster
163 // Go through all upper and lower coefficients and for the ones
164 // larger than threshold mark the equations out of cluster zero
166 scalarField magScaledDiag = diagFactor_()*mag(diag);
168 boolList zeroCluster(diag.size(), true);
170 forAll (magOffDiag, coeffI)
172 if (magOffDiag[coeffI] > magScaledDiag[upperAddr[coeffI]])
174 zeroCluster[upperAddr[coeffI]] = false;
177 if (magOffDiag[coeffI] > magScaledDiag[lowerAddr[coeffI]])
179 zeroCluster[lowerAddr[coeffI]] = false;
183 // Collect solo equations
184 forAll (zeroCluster, eqnI)
186 if (zeroCluster[eqnI])
188 // Found solo equation
191 child_[eqnI] = nCoarseEqns_;
197 // Found solo equations
200 if (lduMatrix::debug >= 2)
202 Pout<< "Found " << nSolo_ << " weakly connected equations."
208 // Go through the off-diagonal and create clusters, marking the child array
209 for (label eqnI = 0; eqnI < nEqns; eqnI++)
211 if (child_[eqnI] < 0)
213 label matchCoeffNo = -1;
214 scalar maxCoeff = -GREAT;
216 // Check row to find ungrouped neighbour with largest coefficient
219 label rowCoeffI = rowOffsets[eqnI];
220 rowCoeffI < rowOffsets[eqnI + 1];
224 label coeffI = cols[rowCoeffI];
226 // I don't know whether the current equation is owner
227 // or neighbour. Therefore I'll check both sides
230 child_[upperAddr[coeffI]] < 0
231 && child_[lowerAddr[coeffI]] < 0
232 && mag(upper[coeffI]) > maxCoeff
235 // Match found. Pick up all the necessary data
236 matchCoeffNo = coeffI;
237 maxCoeff = mag(upper[coeffI]);
241 if (matchCoeffNo >= 0)
244 child_[upperAddr[matchCoeffNo]] = nCoarseEqns_;
245 child_[lowerAddr[matchCoeffNo]] = nCoarseEqns_;
250 // No match. Find the best neighbouring cluster and
251 // put the equation there
252 label clusterMatchCoeffNo = -1;
253 scalar clusterMaxCoeff = -GREAT;
257 label rowCoeffI = rowOffsets[eqnI];
258 rowCoeffI < rowOffsets[eqnI + 1];
262 label coeffI = cols[rowCoeffI];
264 if (mag(upper[coeffI]) > clusterMaxCoeff)
266 clusterMatchCoeffNo = coeffI;
267 clusterMaxCoeff = mag(upper[coeffI]);
271 if (clusterMatchCoeffNo >= 0)
273 // Add the equation to the best cluster
276 child_[upperAddr[clusterMatchCoeffNo]],
277 child_[lowerAddr[clusterMatchCoeffNo]]
282 // This is a Dirichlet point: no neighbours
283 // Put it into its own cluster
284 child_[eqnI] = nCoarseEqns_;
291 // Reverse the map to facilitate later agglomeration.
292 // Keep zero cluster at zero index
293 forAll (child_, eqnI)
295 child_[eqnI] = nCoarseEqns_ - 1 - child_[eqnI];
298 // The decision on parallel agglomeration needs to be made for the
299 // whole gang of processes; otherwise I may end up with a different
300 // number of agglomeration levels on different processors.
302 if (nCoarseEqns_ > minCoarseEqns() && 3*nCoarseEqns_ <= 2*nEqns)
307 reduce(coarsen_, andOp<bool>());
309 if (lduMatrix::debug >= 2)
311 Pout << "Coarse level size: " << nCoarseEqns_;
315 Pout << ". Accepted" << endl;
319 Pout << ". Rejected" << endl;
325 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
327 Foam::pamgPolicy::pamgPolicy
329 const lduMatrix& matrix,
330 const label groupSize,
331 const label minCoarseEqns
334 amgPolicy(groupSize, minCoarseEqns),
344 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
346 Foam::pamgPolicy::~pamgPolicy()
350 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
352 Foam::autoPtr<Foam::amgMatrix> Foam::pamgPolicy::restrictMatrix
354 const FieldField<Field, scalar>& bouCoeffs,
355 const FieldField<Field, scalar>& intCoeffs,
356 const lduInterfaceFieldPtrsList& interfaceFields
361 FatalErrorIn("autoPtr<amgMatrix> pamgPolicy::restrictMatrix() const")
362 << "Requesting coarse matrix when it cannot be created"
363 << abort(FatalError);
366 // Construct the coarse matrix and ldu addressing for the next level
368 // 1) Loop through all fine coeffs. If the child labels on two sides are
369 // different, this creates a coarse coeff. Define owner and neighbour
370 // for this coeff based on cluster IDs.
371 // 2) Check if the coeff has been seen before. If yes, add the coefficient
372 // to the appropriate field (stored with the equation). If no, create
373 // a new coeff with neighbour ID and add the coefficient
374 // 3) Once all the coeffs have been created, loop through all clusters and
375 // insert the coeffs in the upper order. At the same time, collect the
376 // owner and neighbour addressing.
377 // 4) Agglomerate the diagonal by summing up the fine diagonal
380 const unallocLabelList& upperAddr = matrix_.lduAddr().upperAddr();
381 const unallocLabelList& lowerAddr = matrix_.lduAddr().lowerAddr();
383 label nFineCoeffs = upperAddr.size();
386 if (child_.size() != matrix_.lduAddr().size())
390 "autoPtr<amgMatrix> pamgPolicy::restrictMatrix() const"
391 ) << "Child array does not correspond to fine level. " << endl
392 << " Child size: " << child_.size()
393 << " number of equations: " << matrix_.lduAddr().size()
394 << abort(FatalError);
399 // Does the matrix have solo equations
400 bool soloEqns = nSolo_ > 0;
402 // Storage for block neighbours and coefficients
404 // Guess initial maximum number of neighbours in block
407 // Number of neighbours per block
408 labelList blockNnbrs(nCoarseEqns_, 0);
410 // Setup initial packed storage for neighbours and coefficients
411 labelList blockNbrsData(maxNnbrs*nCoarseEqns_);
413 scalarList blockCoeffsData(maxNnbrs*nCoarseEqns_, 0.0);
415 // Create face-restriction addressing
416 // Note: value of coeffRestrictAddr for off-diagonal coefficients
417 // touching solo cells will be invalid
419 labelList coeffRestrictAddr(nFineCoeffs);
421 // Initial neighbour array (not in upper-triangle order)
422 labelList initCoarseNeighb(nFineCoeffs);
424 // Counter for coarse coeffs
425 label nCoarseCoeffs = 0;
427 // Loop through all fine coeffs
428 forAll (upperAddr, fineCoeffI)
430 label rmUpperAddr = child_[upperAddr[fineCoeffI]];
431 label rmLowerAddr = child_[lowerAddr[fineCoeffI]];
433 // If the coefficient touches block zero and solo equations are
435 if (soloEqns && (rmUpperAddr == 0 || rmLowerAddr == 0))
440 if (rmUpperAddr == rmLowerAddr)
442 // For each fine coeff inside of a coarse cluster keep the address
443 // of the cluster corresponding to the coeff in the
444 // coeffRestrictAddr as a negative index
445 coeffRestrictAddr[fineCoeffI] = -(rmUpperAddr + 1);
449 // This coeff is a part of a coarse coeff
451 label cOwn = rmUpperAddr;
452 label cNei = rmLowerAddr;
454 // Get coarse owner and neighbour
455 if (rmUpperAddr > rmLowerAddr)
461 // Check the neighbour to see if this coeff has already been found
462 label* ccCoeffs = &blockNbrsData[maxNnbrs*cOwn];
464 bool nbrFound = false;
465 label& ccnCoeffs = blockNnbrs[cOwn];
467 for (int i = 0; i < ccnCoeffs; i++)
469 if (initCoarseNeighb[ccCoeffs[i]] == cNei)
472 coeffRestrictAddr[fineCoeffI] = ccCoeffs[i];
479 if (ccnCoeffs >= maxNnbrs)
481 label oldMaxNnbrs = maxNnbrs;
484 blockNbrsData.setSize(maxNnbrs*nCoarseEqns_);
486 forAllReverse(blockNnbrs, i)
488 label* oldCcNbrs = &blockNbrsData[oldMaxNnbrs*i];
489 label* newCcNbrs = &blockNbrsData[maxNnbrs*i];
491 for (int j=0; j<blockNnbrs[i]; j++)
493 newCcNbrs[j] = oldCcNbrs[j];
497 ccCoeffs = &blockNbrsData[maxNnbrs*cOwn];
500 ccCoeffs[ccnCoeffs] = nCoarseCoeffs;
501 initCoarseNeighb[nCoarseCoeffs] = cNei;
502 coeffRestrictAddr[fineCoeffI] = nCoarseCoeffs;
505 // New coarse coeff created
509 } // End for all fine coeffs
512 // Renumber into upper-triangular order
514 // All coarse owner-neighbour storage
515 labelList coarseOwner(nCoarseCoeffs);
516 labelList coarseNeighbour(nCoarseCoeffs);
517 labelList coarseCoeffMap(nCoarseCoeffs);
519 label coarseCoeffi = 0;
521 forAll (blockNnbrs, cci)
523 label* cCoeffs = &blockNbrsData[maxNnbrs*cci];
524 label ccnCoeffs = blockNnbrs[cci];
526 for (int i = 0; i < ccnCoeffs; i++)
528 coarseOwner[coarseCoeffi] = cci;
529 coarseNeighbour[coarseCoeffi] = initCoarseNeighb[cCoeffs[i]];
530 coarseCoeffMap[cCoeffs[i]] = coarseCoeffi;
535 forAll (coeffRestrictAddr, fineCoeffI)
537 label rmUpperAddr = child_[upperAddr[fineCoeffI]];
538 label rmLowerAddr = child_[lowerAddr[fineCoeffI]];
540 // If the coefficient touches block zero and solo equations are
542 if (soloEqns && (rmUpperAddr == 0 || rmLowerAddr == 0))
547 if (coeffRestrictAddr[fineCoeffI] >= 0)
549 coeffRestrictAddr[fineCoeffI] =
550 coarseCoeffMap[coeffRestrictAddr[fineCoeffI]];
554 // Clear the temporary storage for the coarse matrix data
555 blockNnbrs.setSize(0);
556 blockNbrsData.setSize(0);
557 initCoarseNeighb.setSize(0);
558 coarseCoeffMap.setSize(0);
561 // Create coarse-level coupled interfaces
563 // Create coarse interfaces, addressing and coefficients
565 // Set the coarse interfaces and coefficients
566 lduInterfacePtrsList* coarseInterfacesPtr =
567 new lduInterfacePtrsList(interfaceFields.size());
568 lduInterfacePtrsList& coarseInterfaces = *coarseInterfacesPtr;
570 // Set the coarse interfaceFields and coefficients
571 lduInterfaceFieldPtrsList* coarseInterfaceFieldsPtr =
572 new lduInterfaceFieldPtrsList(interfaceFields.size());
573 lduInterfaceFieldPtrsList& coarseInterfaceFields =
574 *coarseInterfaceFieldsPtr;
576 FieldField<Field, scalar>* coarseBouCoeffsPtr =
577 new FieldField<Field, scalar>(interfaceFields.size());
578 FieldField<Field, scalar>& coarseBouCoeffs = *coarseBouCoeffsPtr;
580 FieldField<Field, scalar>* coarseIntCoeffsPtr =
581 new FieldField<Field, scalar>(interfaceFields.size());
582 FieldField<Field, scalar>& coarseIntCoeffs = *coarseIntCoeffsPtr;
584 labelListList coarseInterfaceAddr(interfaceFields.size());
586 // Add the coarse level
588 // Set the coarse ldu addressing onto the list
589 lduPrimitiveMesh* coarseAddrPtr =
598 // Initialise transfer of restrict addressing on the interface
599 forAll (interfaceFields, intI)
601 if (interfaceFields.set(intI))
603 interfaceFields[intI].coupledInterface().initInternalFieldTransfer
611 // Store coefficients to avoid tangled communications
613 FieldField<Field, label> fineInterfaceAddr(interfaceFields.size());
615 forAll (interfaceFields, intI)
617 if (interfaceFields.set(intI))
619 const lduInterface& fineInterface =
620 interfaceFields[intI].coupledInterface();
622 fineInterfaceAddr.set
627 fineInterface.internalFieldTransfer
637 // Create GAMG interfaces
638 forAll (interfaceFields, intI)
640 if (interfaceFields.set(intI))
642 const lduInterface& fineInterface =
643 interfaceFields[intI].coupledInterface();
652 fineInterface.interfaceInternalField(child_),
653 fineInterfaceAddr[intI]
659 forAll (interfaceFields, intI)
661 if (interfaceFields.set(intI))
663 const GAMGInterface& coarseInterface =
664 refCast<const GAMGInterface>(coarseInterfaces[intI]);
666 coarseInterfaceFields.set
669 GAMGInterfaceField::New
672 interfaceFields[intI]
679 coarseInterface.agglomerateCoeffs(bouCoeffs[intI])
685 coarseInterface.agglomerateCoeffs(intCoeffs[intI])
688 coarseInterfaceAddr[intI] = coarseInterface.faceCells();
693 coarseAddrPtr->addInterfaces
695 *coarseInterfacesPtr,
697 matrix_.patchSchedule()
700 // Matrix restriction done!
702 // Set the coarse level matrix
703 lduMatrix* coarseMatrixPtr = new lduMatrix(*coarseAddrPtr);
704 lduMatrix& coarseMatrix = *coarseMatrixPtr;
706 // Coarse matrix diagonal initialised by restricting the
707 // finer mesh diagonal
708 scalarField& coarseDiag = coarseMatrix.diag();
709 restrictResidual(matrix_.diag(), coarseDiag);
711 // Check if matrix is assymetric and if so agglomerate both upper and lower
713 if (matrix_.hasLower())
715 // Get off-diagonal matrix coefficients
716 const scalarField& fineUpper = matrix_.upper();
717 const scalarField& fineLower = matrix_.lower();
719 // Coarse matrix upper coefficients
720 scalarField& coarseUpper = coarseMatrix.upper();
721 scalarField& coarseLower = coarseMatrix.lower();
723 forAll (coeffRestrictAddr, fineCoeffI)
725 label rmUpperAddr = child_[upperAddr[fineCoeffI]];
726 label rmLowerAddr = child_[lowerAddr[fineCoeffI]];
728 // If the coefficient touches block zero and solo equations are
730 if (soloEqns && (rmUpperAddr == 0 || rmLowerAddr == 0))
735 label cCoeff = coeffRestrictAddr[fineCoeffI];
739 coarseUpper[cCoeff] += fineUpper[fineCoeffI];
740 coarseLower[cCoeff] += fineLower[fineCoeffI];
744 // Add the fine face coefficients into the diagonal.
745 coarseDiag[-1 - cCoeff] +=
746 fineUpper[fineCoeffI] + fineLower[fineCoeffI];
750 else // ... Otherwise it is symmetric so agglomerate just the upper
752 // Get off-diagonal matrix coefficients
753 const scalarField& fineUpper = matrix_.upper();
755 // Coarse matrix upper coefficients
756 scalarField& coarseUpper = coarseMatrix.upper();
758 forAll (coeffRestrictAddr, fineCoeffI)
760 label rmUpperAddr = child_[upperAddr[fineCoeffI]];
761 label rmLowerAddr = child_[lowerAddr[fineCoeffI]];
763 // If the coefficient touches block zero and solo equations are
765 if (soloEqns && (rmUpperAddr == 0 || rmLowerAddr == 0))
770 label cCoeff = coeffRestrictAddr[fineCoeffI];
774 coarseUpper[cCoeff] += fineUpper[fineCoeffI];
778 // Add the fine face coefficient into the diagonal.
779 coarseDiag[-1 - cCoeff] += 2*fineUpper[fineCoeffI];
784 // Create and return amgMatrix
785 return autoPtr<amgMatrix>
794 coarseInterfaceFieldsPtr
800 void Foam::pamgPolicy::restrictResidual
802 const scalarField& res,
803 scalarField& coarseRes
810 coarseRes[child_[i]] += res[i];
815 void Foam::pamgPolicy::prolongateCorrection
818 const scalarField& coarseX
823 x[i] += coarseX[child_[i]];
828 // ************************************************************************* //