1 #include "StationaryDiffusionEquation.hxx"
2 #include "DiffusionEquation.hxx"
4 #include "SparseMatrixPetsc.hxx"
12 StationaryDiffusionEquation::StationaryDiffusionEquation(int dim, bool FECalculation, double lambda,MPI_Comm comm){
13 /* Initialisation of PETSC */
14 //check if PETSC is already initialised
15 PetscBool petscInitialized;
16 PetscInitialized(&petscInitialized);
18 {//check if MPI is already initialised
20 MPI_Initialized(&mpiInitialized);
22 PETSC_COMM_WORLD = comm;
23 PetscInitialize(NULL,NULL,0,0);//Note this is ok if MPI has been been initialised independently from PETSC
25 MPI_Comm_rank(PETSC_COMM_WORLD,&_rank);
26 MPI_Comm_size(PETSC_COMM_WORLD,&_size);
27 PetscPrintf(PETSC_COMM_WORLD,"Simulation on %d processors\n",_size);//Prints to standard out, only from the first processor in the communicator. Calls from other processes are ignored.
28 PetscSynchronizedPrintf(PETSC_COMM_WORLD,"Processor [%d] ready for action\n",_rank);//Prints synchronized output from several processors. Output of the first processor is followed by that of the second, etc.
29 PetscSynchronizedFlush(PETSC_COMM_WORLD,PETSC_STDOUT);
33 std::cout<<"Conductivity="<<lambda<<endl;
34 throw CdmathException("Error : conductivity parameter lambda cannot be negative");
38 std::cout<<"Space dimension="<<dim<<endl;
39 throw CdmathException("Error : parameter dim cannot be negative");
42 _FECalculation=FECalculation;
46 _nVar=1;//scalar prolem
48 _diffusionMatrixSet=false;
49 _initializedMemory=false;
57 _boundaryNodeIds=std::vector< int >(0);
58 _dirichletNodeIds=std::vector< int >(0);
62 _dirichletValuesSet=false;
63 _neumannValuesSet=false;
67 _precision_Newton=_precision;
68 _MaxIterLinearSolver=0;//During several newton iterations, stores the max petssc interations
72 int _PetscIts=0;//the number of iterations of the linear solver
73 _ksptype = (char*)&KSPGMRES;
74 _pctype = (char*)&PCLU;
75 _conditionNumber=false;
78 //parameters for monitoring simulation
81 _runLogFile=new ofstream;
83 //result save parameters
84 _fileName = "StationaryDiffusionProblem";
85 char result[ PATH_MAX ];//extracting current directory
86 getcwd(result, PATH_MAX );
87 _path=string( result );
89 _computationCompletedSuccessfully=false;
91 //heat transfer parameters
93 _fluidTemperatureFieldSet=false;
95 _heatPowerFieldSet=false;
96 _heatTransfertCoeff=0;
99 /* Default diffusion tensor is diagonal */
100 _DiffusionTensor=Matrix(_Ndim);
101 for(int idim=0;idim<_Ndim;idim++)
102 _DiffusionTensor(idim,idim)=_conductivity;
105 void StationaryDiffusionEquation::initialize()
107 _runLogFile->open((_fileName+".log").c_str(), ios::out | ios::trunc);;//for creation of a log file to save the history of the simulation
110 throw CdmathException("StationaryDiffusionEquation::initialize() set mesh first");
113 cout<<"!!!! Initialisation of the computation of the temperature diffusion in a solid using ";
114 *_runLogFile<<"!!!!! Initialisation of the computation of the temperature diffusion in a solid using ";
117 cout<< "Finite volumes method"<<endl<<endl;
118 *_runLogFile<< "Finite volumes method"<<endl<<endl;
122 cout<< "Finite elements method"<<endl<<endl;
123 *_runLogFile<< "Finite elements method"<<endl<<endl;
127 /**************** Field creation *********************/
129 if(!_heatPowerFieldSet){
130 _heatPowerField=Field("Heat power",_VV.getTypeOfField(),_mesh,1);
131 for(int i =0; i<_VV.getNumberOfElements(); i++)
132 _heatPowerField(i) = _heatSource;
133 _heatPowerFieldSet=true;
135 if(!_fluidTemperatureFieldSet){
136 _fluidTemperatureField=Field("Fluid temperature",_VV.getTypeOfField(),_mesh,1);
137 for(int i =0; i<_VV.getNumberOfElements(); i++)
138 _fluidTemperatureField(i) = _fluidTemperature;
139 _fluidTemperatureFieldSet=true;
142 /* Détection des noeuds frontière avec une condition limite de Dirichlet */
145 if(_NboundaryNodes==_Nnodes)
146 cout<<"!!!!! Warning : all nodes are boundary nodes !!!!!"<<endl<<endl;
148 for(int i=0; i<_NboundaryNodes; i++)
150 std::map<int,double>::iterator it=_dirichletBoundaryValues.find(_boundaryNodeIds[i]);
151 if( it != _dirichletBoundaryValues.end() )
152 _dirichletNodeIds.push_back(_boundaryNodeIds[i]);
153 else if( _mesh.getNode(_boundaryNodeIds[i]).getGroupNames().size()==0 )
155 cout<<"!!! No boundary group set for boundary node" << _boundaryNodeIds[i]<< endl;
156 *_runLogFile<< "!!! No boundary group set for boundary node" << _boundaryNodeIds[i]<<endl;
157 _runLogFile->close();
158 throw CdmathException("Missing boundary group");
160 else if(_limitField[_mesh.getNode(_boundaryNodeIds[i]).getGroupName()].bcType==NoneBCStationaryDiffusion)
162 cout<<"!!! No boundary condition set for boundary node " << _boundaryNodeIds[i]<< endl;
163 cout<<"!!! Accepted boundary conditions are DirichletStationaryDiffusion "<< DirichletStationaryDiffusion <<" and NeumannStationaryDiffusion "<< NeumannStationaryDiffusion << endl;
164 *_runLogFile<< "!!! No boundary condition set for boundary node " << _boundaryNodeIds[i]<<endl;
165 *_runLogFile<< "!!! Accepted boundary conditions are DirichletStationaryDiffusion "<< DirichletStationaryDiffusion <<" and NeumannStationaryDiffusion "<< NeumannStationaryDiffusion <<endl;
166 _runLogFile->close();
167 throw CdmathException("Missing boundary condition");
169 else if(_limitField[_mesh.getNode(_boundaryNodeIds[i]).getGroupName()].bcType==DirichletStationaryDiffusion)
170 _dirichletNodeIds.push_back(_boundaryNodeIds[i]);
171 else if(_limitField[_mesh.getNode(_boundaryNodeIds[i]).getGroupName()].bcType!=NeumannStationaryDiffusion)
173 cout<<"!!! Wrong boundary condition "<< _limitField[_mesh.getNode(_boundaryNodeIds[i]).getGroupName()].bcType<< " set for boundary node " << _boundaryNodeIds[i]<< endl;
174 cout<<"!!! Accepted boundary conditions are DirichletStationaryDiffusion "<< DirichletStationaryDiffusion <<" and NeumannStationaryDiffusion "<< NeumannStationaryDiffusion << endl;
175 *_runLogFile<< "!!! Wrong boundary condition "<< _limitField[_mesh.getNode(_boundaryNodeIds[i]).getGroupName()].bcType<< " set for boundary node " << _boundaryNodeIds[i]<<endl;
176 *_runLogFile<< "!!! Accepted boundary conditions are DirichletStationaryDiffusion "<< DirichletStationaryDiffusion <<" and NeumannStationaryDiffusion "<< NeumannStationaryDiffusion <<endl;
177 _runLogFile->close();
178 throw CdmathException("Wrong boundary condition");
181 _NdirichletNodes=_dirichletNodeIds.size();
182 _NunknownNodes=_Nnodes - _NdirichletNodes;
183 cout<<"Number of unknown nodes " << _NunknownNodes <<", Number of boundary nodes " << _NboundaryNodes<< ", Number of Dirichlet boundary nodes " << _NdirichletNodes <<endl<<endl;
184 *_runLogFile<<"Number of unknown nodes " << _NunknownNodes <<", Number of boundary nodes " << _NboundaryNodes<< ", Number of Dirichlet boundary nodes " << _NdirichletNodes <<endl<<endl;
187 //creation de la matrice
189 MatCreateSeqAIJ(PETSC_COMM_SELF, _Nmailles, _Nmailles, (1+_neibMaxNbCells), PETSC_NULL, &_A);
191 MatCreateSeqAIJ(PETSC_COMM_SELF, _NunknownNodes, _NunknownNodes, (1+_neibMaxNbNodes), PETSC_NULL, &_A);
193 VecCreate(PETSC_COMM_SELF, &_Tk);
196 VecSetSizes(_Tk,PETSC_DECIDE,_Nmailles);
198 VecSetSizes(_Tk,PETSC_DECIDE,_NunknownNodes);
200 VecSetFromOptions(_Tk);
201 VecDuplicate(_Tk, &_Tkm1);
202 VecDuplicate(_Tk, &_deltaT);
203 VecDuplicate(_Tk, &_b);//RHS of the linear system
206 KSPCreate(PETSC_COMM_SELF, &_ksp);
207 KSPSetType(_ksp, _ksptype);
208 // if(_ksptype == KSPGMRES) KSPGMRESSetRestart(_ksp,10000);
209 KSPSetTolerances(_ksp,_precision,_precision,PETSC_DEFAULT,_maxPetscIts);
210 KSPGetPC(_ksp, &_pc);
211 PCSetType(_pc, _pctype);
213 //Checking whether all boundary conditions are Neumann boundary condition
214 //if(_FECalculation) _onlyNeumannBC = _NdirichletNodes==0;
215 if(!_neumannValuesSet)//Boundary conditions set via LimitField structure
217 map<string, LimitFieldStationaryDiffusion>::iterator it = _limitField.begin();
218 while(it != _limitField.end() and (it->second).bcType == NeumannStationaryDiffusion)
220 _onlyNeumannBC = (it == _limitField.end() && _limitField.size()>0);//what if _limitField.size()==0 ???
224 _onlyNeumannBC = _neumannBoundaryValues.size()==_NboundaryNodes;
226 _onlyNeumannBC = _neumannBoundaryValues.size()==_mesh.getBoundaryFaceIds().size();
228 //If only Neumann BC, then matrix is singular and solution should be sought in space of mean zero vectors
231 std::cout<<"### Warning : all boundary conditions are Neumann. System matrix is not invertible since constant vectors are in the kernel."<<std::endl;
232 std::cout<<"### Check the compatibility condition between the right hand side and the boundary data. For homogeneous Neumann BCs, the right hand side must have integral equal to zero."<<std::endl;
233 std::cout<<"### The system matrix being singular, we seek a zero sum solution, and exact (LU and CHOLESKY) and incomplete factorisations (ILU and ICC) may fail."<<std::endl<<endl;
234 *_runLogFile<<"### Warning : all boundary condition are Neumann. System matrix is not invertible since constant vectors are in the kernel."<<std::endl;
235 *_runLogFile<<"### The system matrix being singular, we seek a zero sum solution, and exact (LU and CHOLESKY) and incomplete factorisations (ILU and ICC) may fail."<<std::endl<<endl;
236 *_runLogFile<<"### Check the compatibility condition between the right hand side and the boundary data. For homogeneous Neumann BCs, the right hand side must have integral equal to zero."<<std::endl;
239 MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_TRUE, 0, PETSC_NULL, &nullsp);
240 MatSetNullSpace(_A, nullsp);
241 MatSetTransposeNullSpace(_A, nullsp);
242 MatNullSpaceDestroy(&nullsp);
243 //PCFactorSetShiftType(_pc,MAT_SHIFT_NONZERO);
244 //PCFactorSetShiftAmount(_pc,1e-10);
246 _initializedMemory=true;
249 double StationaryDiffusionEquation::computeTimeStep(bool & stop){
250 if(!_diffusionMatrixSet)//The diffusion matrix is computed once and for all time steps
251 computeDiffusionMatrix(stop);
253 _dt_src=computeRHS(stop);
258 double StationaryDiffusionEquation::computeDiffusionMatrix(bool & stop)
263 result=computeDiffusionMatrixFE(stop);
265 result=computeDiffusionMatrixFV(stop);
267 //Contribution from the solid/fluid heat exchange with assumption of constant heat transfer coefficient
268 //update value here if variable heat transfer coefficient
269 if(_heatTransfertCoeff>_precision)
270 MatShift(_A,_heatTransfertCoeff);//Contribution from the liquit/solid heat transfer
272 if(_verbose or _system)
273 MatView(_A,PETSC_VIEWER_STDOUT_SELF);
278 double StationaryDiffusionEquation::computeDiffusionMatrixFE(bool & stop){
285 Matrix M(_Ndim+1,_Ndim+1);//cell geometry matrix
286 std::vector< Vector > GradShapeFuncs(_Ndim+1);//shape functions of cell nodes
287 std::vector< int > nodeIds(_Ndim+1);//cell node Ids
288 std::vector< Node > nodes(_Ndim+1);//cell nodes
289 int i_int, j_int; //index of nodes j and k considered as unknown nodes
290 bool dirichletCell_treated;
292 std::vector< vector< double > > values(_Ndim+1,vector< double >(_Ndim+1,0));//values of shape functions on cell node
293 for (int idim=0; idim<_Ndim+1;idim++)
294 values[idim][idim]=1;
296 /* parameters for boundary treatment */
297 vector< double > valuesBorder(_Ndim+1);
298 Vector GradShapeFuncBorder(_Ndim+1);
300 for (int j=0; j<_Nmailles;j++)
302 Cj = _mesh.getCell(j);
304 for (int idim=0; idim<_Ndim+1;idim++){
305 nodeIds[idim]=Cj.getNodeId(idim);
306 nodes[idim]=_mesh.getNode(nodeIds[idim]);
307 for (int jdim=0; jdim<_Ndim;jdim++)
308 M(idim,jdim)=nodes[idim].getPoint()[jdim];
311 for (int idim=0; idim<_Ndim+1;idim++)
312 GradShapeFuncs[idim]=DiffusionEquation::gradientNodal(M,values[idim])/DiffusionEquation::fact(_Ndim);
314 /* Loop on the edges of the cell */
315 for (int idim=0; idim<_Ndim+1;idim++)
317 if(find(_dirichletNodeIds.begin(),_dirichletNodeIds.end(),nodeIds[idim])==_dirichletNodeIds.end())//!_mesh.isBorderNode(nodeIds[idim])
318 {//First node of the edge is not Dirichlet node
319 i_int=DiffusionEquation::unknownNodeIndex(nodeIds[idim], _dirichletNodeIds);//assumes Dirichlet boundary node numbering is strictly increasing
320 dirichletCell_treated=false;
321 for (int jdim=0; jdim<_Ndim+1;jdim++)
323 if(find(_dirichletNodeIds.begin(),_dirichletNodeIds.end(),nodeIds[jdim])==_dirichletNodeIds.end())//!_mesh.isBorderNode(nodeIds[jdim])
324 {//Second node of the edge is not Dirichlet node
325 j_int= DiffusionEquation::unknownNodeIndex(nodeIds[jdim], _dirichletNodeIds);//assumes Dirichlet boundary node numbering is strictly increasing
326 MatSetValue(_A,i_int,j_int,(_DiffusionTensor*GradShapeFuncs[idim])*GradShapeFuncs[jdim]/Cj.getMeasure(), ADD_VALUES);
328 else if (!dirichletCell_treated)
329 {//Second node of the edge is a Dirichlet node
330 dirichletCell_treated=true;
331 for (int kdim=0; kdim<_Ndim+1;kdim++)
333 std::map<int,double>::iterator it=_dirichletBoundaryValues.find(nodeIds[kdim]);
334 if( it != _dirichletBoundaryValues.end() )
336 if( _dirichletValuesSet )
337 valuesBorder[kdim]=_dirichletBoundaryValues[it->second];
339 valuesBorder[kdim]=_limitField[_mesh.getNode(nodeIds[kdim]).getGroupName()].T;
342 valuesBorder[kdim]=0;
344 GradShapeFuncBorder=DiffusionEquation::gradientNodal(M,valuesBorder)/DiffusionEquation::fact(_Ndim);
345 coeff =-1.*(_DiffusionTensor*GradShapeFuncBorder)*GradShapeFuncs[idim]/Cj.getMeasure();
346 VecSetValue(_b,i_int,coeff, ADD_VALUES);
353 //Calcul de la contribution de la condition limite de Neumann au second membre
354 if( _NdirichletNodes !=_NboundaryNodes)
356 vector< int > boundaryFaces = _mesh.getBoundaryFaceIds();
357 int NboundaryFaces=boundaryFaces.size();
358 for(int i = 0; i< NboundaryFaces ; i++)//On parcourt les faces du bord
360 Face Fi = _mesh.getFace(boundaryFaces[i]);
361 for(int j = 0 ; j<_Ndim ; j++)//On parcourt les noeuds de la face
363 if(find(_dirichletNodeIds.begin(),_dirichletNodeIds.end(),Fi.getNodeId(j))==_dirichletNodeIds.end())//node j is a Neumann BC node (not a Dirichlet BC node)
365 j_int=DiffusionEquation::unknownNodeIndex(Fi.getNodeId(j), _dirichletNodeIds);//indice du noeud j en tant que noeud inconnu
366 if( _neumannValuesSet )
367 coeff =Fi.getMeasure()/_Ndim*_neumannBoundaryValues[Fi.getNodeId(j)];
369 coeff =Fi.getMeasure()/_Ndim*_limitField[_mesh.getNode(Fi.getNodeId(j)).getGroupName()].normalFlux;
370 VecSetValue(_b, j_int, coeff, ADD_VALUES);
376 MatAssemblyBegin(_A, MAT_FINAL_ASSEMBLY);
377 MatAssemblyEnd(_A, MAT_FINAL_ASSEMBLY);
378 VecAssemblyBegin(_b);
381 if(_onlyNeumannBC) //Check that the matrix is symmetric
383 PetscBool isSymetric;
384 MatIsSymmetric(_A,_precision,&isSymetric);
387 cout<<"Singular matrix is not symmetric, tolerance= "<< _precision<<endl;
388 throw CdmathException("Singular matrix should be symmetric with kernel composed of constant vectors");
392 _diffusionMatrixSet=true;
398 double StationaryDiffusionEquation::computeDiffusionMatrixFV(bool & stop){
399 long nbFaces = _mesh.getNumberOfFaces();
403 double inv_dxi, inv_dxj;
404 double barycenterDistance;
405 Vector normale(_Ndim);
408 std::vector< int > idCells;
412 for (int j=0; j<nbFaces;j++){
413 Fj = _mesh.getFace(j);
415 // compute the normal vector corresponding to face j : from idCells[0] to idCells[1]
416 idCells = Fj.getCellsId();
417 Cell1 = _mesh.getCell(idCells[0]);
419 for(int l=0; l<Cell1.getNumberOfFaces(); l++){
420 if (j == Cell1.getFacesId()[l]){
421 for (int idim = 0; idim < _Ndim; ++idim)
422 normale[idim] = Cell1.getNormalVector(l,idim);
427 //Compute velocity at the face Fj
428 dn=(_DiffusionTensor*normale)*normale;
430 // compute 1/dxi = volume of Ci/area of Fj
431 inv_dxi = Fj.getMeasure()/Cell1.getMeasure();
433 // If Fj is on the boundary
434 if (Fj.getNumberOfCells()==1) {
437 cout << "face numero " << j << " cellule frontiere " << idCells[0] << " ; vecteur normal=(";
438 for(int p=0; p<_Ndim; p++)
439 cout << normale[p] << ",";
443 std::map<int,double>::iterator it=_dirichletBoundaryValues.find(j);
444 if( it != _dirichletBoundaryValues.end() )
446 barycenterDistance=Cell1.getBarryCenter().distance(Fj.getBarryCenter());
447 MatSetValue(_A,idm,idm,dn*inv_dxi/barycenterDistance , ADD_VALUES);
448 VecSetValue(_b,idm, dn*inv_dxi/barycenterDistance*it->second, ADD_VALUES);
452 nameOfGroup = Fj.getGroupName();
454 if (_limitField[nameOfGroup].bcType==NeumannStationaryDiffusion){
455 VecSetValue(_b,idm, -dn*inv_dxi*_limitField[nameOfGroup].normalFlux, ADD_VALUES);
457 else if(_limitField[nameOfGroup].bcType==DirichletStationaryDiffusion){
458 barycenterDistance=Cell1.getBarryCenter().distance(Fj.getBarryCenter());
459 MatSetValue(_A,idm,idm,dn*inv_dxi/barycenterDistance , ADD_VALUES);
460 VecSetValue(_b,idm, dn*inv_dxi/barycenterDistance*_limitField[nameOfGroup].T, ADD_VALUES);
464 cout<<"!!!!!!!!!!!!!!! Error StationaryDiffusionEquation::computeDiffusionMatrixFV !!!!!!!!!!"<<endl;
465 cout<<"!!!!!! No boundary condition set for boundary named "<<nameOfGroup<< "!!!!!!!!!! _limitField[nameOfGroup].bcType= "<<_limitField[nameOfGroup].bcType<<endl;
466 cout<<"Accepted boundary conditions are NeumannStationaryDiffusion "<<NeumannStationaryDiffusion<< " and DirichletStationaryDiffusion "<<DirichletStationaryDiffusion<<endl;
467 *_runLogFile<<"!!!!!! Boundary condition not set for boundary named "<<nameOfGroup<< "!!!!!!!!!! _limitField[nameOfGroup].bcType= "<<_limitField[nameOfGroup].bcType<<endl;
468 _runLogFile->close();
469 throw CdmathException("Boundary condition not set");
472 // if Fj is inside the domain
473 } else if (Fj.getNumberOfCells()==2 ){
476 cout << "face numero " << j << " cellule gauche " << idCells[0] << " cellule droite " << idCells[1];
477 cout << " ; vecteur normal=(";
478 for(int p=0; p<_Ndim; p++)
479 cout << normale[p] << ",";
482 Cell2 = _mesh.getCell(idCells[1]);
485 inv_dxj = Fj.getMeasure()/Cell2.getMeasure();
487 inv_dxj = 1/Cell2.getMeasure();
489 barycenterDistance=Cell1.getBarryCenter().distance(Cell2.getBarryCenter());
491 MatSetValue(_A,idm,idm, dn*inv_dxi/barycenterDistance, ADD_VALUES);
492 MatSetValue(_A,idm,idn,-dn*inv_dxi/barycenterDistance, ADD_VALUES);
493 MatSetValue(_A,idn,idn, dn*inv_dxj/barycenterDistance, ADD_VALUES);
494 MatSetValue(_A,idn,idm,-dn*inv_dxj/barycenterDistance, ADD_VALUES);
498 *_runLogFile<<"StationaryDiffusionEquation::computeDiffusionMatrixFV(): incompatible number of cells around a face"<<endl;
499 throw CdmathException("StationaryDiffusionEquation::computeDiffusionMatrixFV(): incompatible number of cells around a face");
503 MatAssemblyBegin(_A, MAT_FINAL_ASSEMBLY);
504 MatAssemblyEnd(_A, MAT_FINAL_ASSEMBLY);
505 VecAssemblyBegin(_b);
508 if(_onlyNeumannBC) //Check that the matrix is symmetric
510 PetscBool isSymetric;
511 MatIsSymmetric(_A,_precision,&isSymetric);
514 cout<<"Singular matrix is not symmetric, tolerance= "<< _precision<<endl;
515 throw CdmathException("Singular matrix should be symmetric with kernel composed of constant vectors");
519 _diffusionMatrixSet=true;
525 double StationaryDiffusionEquation::computeRHS(bool & stop)//Contribution of the PDE RHS to the linear systemm RHS (boundary conditions do contribute to the system RHS via the function computeDiffusionMatrix
527 VecAssemblyBegin(_b);
530 for (int i=0; i<_Nmailles;i++)
531 VecSetValue(_b,i, _heatTransfertCoeff*_fluidTemperatureField(i) + _heatPowerField(i) ,ADD_VALUES);
535 std::vector< int > nodesId;
536 for (int i=0; i<_Nmailles;i++)
539 nodesId=Ci.getNodesId();
540 for (int j=0; j<nodesId.size();j++)
541 if(!_mesh.isBorderNode(nodesId[j]))
543 double coeff = _heatTransfertCoeff*_fluidTemperatureField(nodesId[j]) + _heatPowerField(nodesId[j]);
544 VecSetValue(_b,DiffusionEquation::unknownNodeIndex(nodesId[j], _dirichletNodeIds), coeff*Ci.getMeasure()/(_Ndim+1),ADD_VALUES);
551 if(_verbose or _system)
552 VecView(_b,PETSC_VIEWER_STDOUT_SELF);
558 bool StationaryDiffusionEquation::iterateNewtonStep(bool &converged)
562 //Only implicit scheme considered
563 MatAssemblyBegin(_A, MAT_FINAL_ASSEMBLY);
564 MatAssemblyEnd( _A, MAT_FINAL_ASSEMBLY);
566 #if PETSC_VERSION_GREATER_3_5
567 KSPSetOperators(_ksp, _A, _A);
569 KSPSetOperators(_ksp, _A, _A,SAME_NONZERO_PATTERN);
573 KSPSetComputeEigenvalues(_ksp,PETSC_TRUE);
574 KSPSolve(_ksp, _b, _Tk);
576 KSPConvergedReason reason;
577 KSPGetConvergedReason(_ksp,&reason);
578 KSPGetIterationNumber(_ksp, &_PetscIts);
580 KSPGetResidualNorm(_ksp,&residu);
581 if (reason!=2 and reason!=3)
583 cout<<"!!!!!!!!!!!!! Erreur système linéaire : pas de convergence de Petsc."<<endl;
584 cout<<"!!!!!!!!!!!!! Itérations maximales "<<_maxPetscIts<<" atteintes, résidu="<<residu<<", précision demandée= "<<_precision<<endl;
585 cout<<"Solver used "<< _ksptype<<", preconditioner "<<_pctype<<", Final number of iteration= "<<_PetscIts<<endl;
586 *_runLogFile<<"!!!!!!!!!!!!! Erreur système linéaire : pas de convergence de Petsc."<<endl;
587 *_runLogFile<<"!!!!!!!!!!!!! Itérations maximales "<<_maxPetscIts<<" atteintes, résidu="<<residu<<", précision demandée= "<<_precision<<endl;
588 *_runLogFile<<"Solver used "<< _ksptype<<", preconditioner "<<_pctype<<", Final number of iteration= "<<_PetscIts<<endl;
589 _runLogFile->close();
594 if( _MaxIterLinearSolver < _PetscIts)
595 _MaxIterLinearSolver = _PetscIts;
596 cout<<"## Système linéaire résolu en "<<_PetscIts<<" itérations par le solveur "<< _ksptype<<" et le preconditioneur "<<_pctype<<", précision demandée= "<<_precision<<endl<<endl;
597 *_runLogFile<<"## Système linéaire résolu en "<<_PetscIts<<" itérations par le solveur "<< _ksptype<<" et le preconditioneur "<<_pctype<<", précision demandée= "<<_precision<<endl<<endl;
598 VecCopy(_Tk, _deltaT);//ici on a deltaT=Tk
599 VecAXPY(_deltaT, -1, _Tkm1);//On obtient deltaT=Tk-Tkm1
604 VecAssemblyBegin(_Tk);
605 VecAssemblyEnd( _Tk);
606 VecAssemblyBegin(_deltaT);
607 VecAssemblyEnd( _deltaT);
610 cout<<"Début calcul de la variation relative"<<endl;
612 for(int i=0; i<_NunknownNodes; i++)
614 VecGetValues(_deltaT, 1, &i, &dTi);
615 VecGetValues(_Tk, 1, &i, &Ti);
616 if(_erreur_rel < fabs(dTi/Ti))
617 _erreur_rel = fabs(dTi/Ti);
620 cout<<"Fin calcul de la variation relative, erreur relative maximale : " << _erreur_rel <<endl;
622 converged = (_erreur_rel <= _precision) ;//converged=convergence des iterations de Newton
630 void StationaryDiffusionEquation::setMesh(const Mesh &M)
632 if(_Ndim != M.getSpaceDimension() or _Ndim!=M.getMeshDimension())//for the moment we must have space dim=mesh dim
634 cout<< "Problem : dimension defined is "<<_Ndim<< " but mesh dimension= "<<M.getMeshDimension()<<", and space dimension is "<<M.getSpaceDimension()<<endl;
635 *_runLogFile<< "Problem : dim = "<<_Ndim<< " but mesh dim= "<<M.getMeshDimension()<<", mesh space dim= "<<M.getSpaceDimension()<<endl;
636 *_runLogFile<<"StationaryDiffusionEquation::setMesh: mesh has incorrect dimension"<<endl;
637 _runLogFile->close();
638 throw CdmathException("StationaryDiffusionEquation::setMesh: mesh has incorrect dimension");
642 _Nmailles = _mesh.getNumberOfCells();
643 _Nnodes = _mesh.getNumberOfNodes();
645 cout<<"Mesh has "<< _Nmailles << " cells and " << _Nnodes << " nodes"<<endl<<endl;;
646 *_runLogFile<<"Mesh has "<< _Nmailles << " cells and " << _Nnodes << " nodes"<<endl<<endl;
648 // find maximum nb of neibourghs
651 _VV=Field ("Temperature", CELLS, _mesh, 1);
652 _neibMaxNbCells=_mesh.getMaxNbNeighbours(CELLS);
656 if(_Ndim==1 )//The 1D cdmath mesh is necessarily made of segments
657 cout<<"1D Finite element method on segments"<<endl;
660 if( _mesh.isTriangular() )//Mesh dim=2
661 cout<<"2D Finite element method on triangles"<<endl;
662 else if (_mesh.getMeshDimension()==1)//Mesh dim=1
663 cout<<"1D Finite element method on a 2D network : space dimension is "<<_Ndim<< ", mesh dimension is "<<_mesh.getMeshDimension()<<endl;
666 cout<<"Error Finite element with Space dimension "<<_Ndim<< ", and mesh dimension "<<_mesh.getMeshDimension()<< ", mesh should be either triangular either 1D network"<<endl;
667 *_runLogFile<<"StationaryDiffusionEquation::setMesh: mesh has incorrect dimension"<<endl;
668 _runLogFile->close();
669 throw CdmathException("StationaryDiffusionEquation::setMesh: mesh has incorrect cell types");
674 if( _mesh.isTetrahedral() )//Mesh dim=3
675 cout<<"3D Finite element method on tetrahedra"<<endl;
676 else if (_mesh.getMeshDimension()==2 and _mesh.isTriangular())//Mesh dim=2
677 cout<<"2D Finite element method on a 3D surface : space dimension is "<<_Ndim<< ", mesh dimension is "<<_mesh.getMeshDimension()<<endl;
678 else if (_mesh.getMeshDimension()==1)//Mesh dim=1
679 cout<<"1D Finite element method on a 3D network : space dimension is "<<_Ndim<< ", mesh dimension is "<<_mesh.getMeshDimension()<<endl;
682 cout<<"Error Finite element with Space dimension "<<_Ndim<< ", and mesh dimension "<<_mesh.getMeshDimension()<< ", mesh should be either tetrahedral, either a triangularised surface or 1D network"<<endl;
683 *_runLogFile<<"StationaryDiffusionEquation::setMesh: mesh has incorrect dimension"<<endl;
684 _runLogFile->close();
685 throw CdmathException("StationaryDiffusionEquation::setMesh: mesh has incorrect cell types");
689 _VV=Field ("Temperature", NODES, _mesh, 1);
691 _neibMaxNbNodes=_mesh.getMaxNbNeighbours(NODES);
692 _boundaryNodeIds = _mesh.getBoundaryNodeIds();
693 _NboundaryNodes=_boundaryNodeIds.size();
699 void StationaryDiffusionEquation::setLinearSolver(linearSolver kspType, preconditioner pcType)
701 //_maxPetscIts=maxIterationsPetsc;
702 // set linear solver algorithm
704 _ksptype = (char*)&KSPGMRES;
705 else if (kspType==CG)
706 _ksptype = (char*)&KSPCG;
707 else if (kspType==BCGS)
708 _ksptype = (char*)&KSPBCGS;
710 cout << "!!! Error : only 'GMRES', 'CG' or 'BCGS' is acceptable as a linear solver !!!" << endl;
711 *_runLogFile << "!!! Error : only 'GMRES', 'CG' or 'BCGS' is acceptable as a linear solver !!!" << endl;
712 _runLogFile->close();
713 throw CdmathException("!!! Error : only 'GMRES', 'CG' or 'BCGS' algorithm is acceptable !!!");
715 // set preconditioner
717 _pctype = (char*)&PCNONE;
718 else if (pcType ==LU)
719 _pctype = (char*)&PCLU;
720 else if (pcType == ILU)
721 _pctype = (char*)&PCILU;
722 else if (pcType ==CHOLESKY)
723 _pctype = (char*)&PCCHOLESKY;
724 else if (pcType == ICC)
725 _pctype = (char*)&PCICC;
727 cout << "!!! Error : only 'NOPC', 'LU', 'ILU', 'CHOLESKY' or 'ICC' preconditioners are acceptable !!!" << endl;
728 *_runLogFile << "!!! Error : only 'NOPC' or 'LU' or 'ILU' preconditioners are acceptable !!!" << endl;
729 _runLogFile->close();
730 throw CdmathException("!!! Error : only 'NOPC' or 'LU' or 'ILU' preconditioners are acceptable !!!" );
734 bool StationaryDiffusionEquation::solveStationaryProblem()
736 if(!_initializedMemory)
738 *_runLogFile<< "ProblemCoreFlows::run() call initialize() first"<< _fileName<<endl;
739 _runLogFile->close();
740 throw CdmathException("ProblemCoreFlows::run() call initialize() first");
742 bool stop=false; // Does the Problem want to stop (error) ?
743 bool converged=false; // has the newton scheme converged (end) ?
745 cout<< "!!! Running test case "<< _fileName << " using ";
746 *_runLogFile<< "!!! Running test case "<< _fileName<< " using ";
750 cout<< "Finite volumes method"<<endl<<endl;
751 *_runLogFile<< "Finite volumes method"<<endl<<endl;
755 cout<< "Finite elements method"<<endl<<endl;
756 *_runLogFile<< "Finite elements method"<< endl<<endl;
759 computeDiffusionMatrix( stop);//For the moment the conductivity does not depend on the temperature (linear LHS)
761 cout << "Error : failed computing diffusion matrix, stopping calculation"<< endl;
762 *_runLogFile << "Error : failed computing diffusion matrix, stopping calculation"<< endl;
763 _runLogFile->close();
764 throw CdmathException("Failed computing diffusion matrix");
766 computeRHS(stop);//For the moment the heat power does not depend on the unknown temperature (linear RHS)
768 cout << "Error : failed computing right hand side, stopping calculation"<< endl;
769 *_runLogFile << "Error : failed computing right hand side, stopping calculation"<< endl;
770 throw CdmathException("Failed computing right hand side");
772 stop = iterateNewtonStep(converged);
774 cout << "Error : failed solving linear system, stopping calculation"<< endl;
775 *_runLogFile << "Error : failed linear system, stopping calculation"<< endl;
776 _runLogFile->close();
777 throw CdmathException("Failed solving linear system");
780 _computationCompletedSuccessfully=true;
783 // Newton iteration loop for non linear problems
785 while(!stop and !converged and _NEWTON_its<_maxNewtonIts)
787 computeDiffusionMatrix( stop);//case when the conductivity depends on the temperature (nonlinear LHS)
788 computeRHS(stop);//case the heat power depends on the unknown temperature (nonlinear RHS)
789 stop = iterateNewtonStep(converged);
793 cout << "Error : failed solving Newton iteration "<<_NEWTON_its<<", stopping calculation"<< endl;
794 *_runLogFile << "Error : failed solving Newton iteration "<<_NEWTON_its<<", stopping calculation"<< endl;
795 throw CdmathException("Failed solving a Newton iteration");
797 else if(_NEWTON_its==_maxNewtonIts){
798 cout << "Error : no convergence of Newton scheme. Maximum Newton iterations "<<_maxNewtonIts<<" reached, stopping calculation"<< endl;
799 *_runLogFile << "Error : no convergence of Newton scheme. Maximum Newton iterations "<<_maxNewtonIts<<" reached, stopping calculation"<< endl;
800 throw CdmathException("No convergence of Newton scheme");
803 cout << "Convergence of Newton scheme at iteration "<<_NEWTON_its<<", end of calculation"<< endl;
804 *_runLogFile << "Convergence of Newton scheme at iteration "<<_NEWTON_its<<", end of calculation"<< endl;
809 *_runLogFile<< "!!!!!! Computation successful !!!!!!"<< endl;
810 _runLogFile->close();
815 void StationaryDiffusionEquation::save(){
816 cout<< "Saving numerical results"<<endl<<endl;
817 *_runLogFile<< "Saving numerical results"<< endl<<endl;
819 string resultFile(_path+"/StationaryDiffusionEquation");//Results
822 resultFile+=_fileName;
824 // create mesh and component info
825 string suppress ="rm -rf "+resultFile+"_*";
826 system(suppress.c_str());//Nettoyage des précédents calculs identiques
828 if(_verbose or _system)
829 VecView(_Tk,PETSC_VIEWER_STDOUT_SELF);
833 for(int i=0; i<_Nmailles; i++)
835 VecGetValues(_Tk, 1, &i, &Ti);
841 for(int i=0; i<_NunknownNodes; i++)
843 VecGetValues(_Tk, 1, &i, &Ti);
844 globalIndex = DiffusionEquation::globalNodeIndex(i, _dirichletNodeIds);
850 for(int i=0; i<_NdirichletNodes; i++)
852 Ni=_mesh.getNode(_dirichletNodeIds[i]);
853 nameOfGroup = Ni.getGroupName();
854 _VV(_dirichletNodeIds[i])=_limitField[nameOfGroup].T;
858 _VV.setInfoOnComponent(0,"Temperature_(K)");
862 _VV.writeVTK(resultFile);
865 _VV.writeMED(resultFile);
868 _VV.writeCSV(resultFile);
873 StationaryDiffusionEquation::getOutputTemperatureField()
875 if(!_computationCompletedSuccessfully)
876 throw("Computation not performed yet or failed. No temperature field available");
881 void StationaryDiffusionEquation::terminate()
885 VecDestroy(&_deltaT);
890 StationaryDiffusionEquation::setDirichletValues(map< int, double> dirichletBoundaryValues)
892 _dirichletValuesSet=true;
893 _dirichletBoundaryValues=dirichletBoundaryValues;
897 StationaryDiffusionEquation::setNeumannValues(map< int, double> neumannBoundaryValues)
899 _neumannValuesSet=true;
900 _neumannBoundaryValues=neumannBoundaryValues;
904 StationaryDiffusionEquation::getConditionNumber(bool isSingular, double tol) const
906 SparseMatrixPetsc A = SparseMatrixPetsc(_A);
907 return A.getConditionNumber( isSingular, tol);
909 std::vector< double >
910 StationaryDiffusionEquation::getEigenvalues(int nev, EPSWhich which, double tol) const
912 SparseMatrixPetsc A = SparseMatrixPetsc(_A);
914 if(_FECalculation)//We need to scale the FE matrix, otherwise the eigenvalues go to zero as the mesh is refined
916 Vector nodal_volumes(_NunknownNodes);
918 for(int i = 0; i< _Nmailles ; i++)//On parcourt les cellules du maillage
920 Cell Ci = _mesh.getCell(i);
921 for(int j = 0 ; j<_Ndim+1 ; j++)//On parcourt les noeuds de la cellule
923 if(find(_dirichletNodeIds.begin(),_dirichletNodeIds.end(),Ci.getNodeId(j))==_dirichletNodeIds.end())//node j is an unknown node (not a Dirichlet node)
925 j_int=DiffusionEquation::unknownNodeIndex(Ci.getNodeId(j), _dirichletNodeIds);//indice du noeud j en tant que noeud inconnu
926 nodal_volumes[j_int]+=Ci.getMeasure()/(_Ndim+1);
930 for( j_int = 0; j_int< _NunknownNodes ; j_int++)
931 nodal_volumes[j_int]=1/nodal_volumes[j_int];
932 A.leftDiagonalScale(nodal_volumes);
935 return A.getEigenvalues( nev, which, tol);
937 std::vector< Vector >
938 StationaryDiffusionEquation::getEigenvectors(int nev, EPSWhich which, double tol) const
940 SparseMatrixPetsc A = SparseMatrixPetsc(_A);
941 return A.getEigenvectors( nev, which, tol);
944 StationaryDiffusionEquation::getEigenvectorsField(int nev, EPSWhich which, double tol) const
946 SparseMatrixPetsc A = SparseMatrixPetsc(_A);
947 MEDCoupling::DataArrayDouble * d = A.getEigenvectorsDataArrayDouble( nev, which, tol);
950 my_eigenfield = Field("Eigenvectors field", _VV.getTypeOfField(), _mesh, nev);
952 my_eigenfield.setFieldByDataArrayDouble(d);
954 return my_eigenfield;