1 #include "DiffusionEquation.hxx"
9 int DiffusionEquation::fact(int n)
11 return (n == 1 || n == 0) ? 1 : fact(n - 1) * n;
13 int DiffusionEquation::unknownNodeIndex(int globalIndex, std::vector< int > dirichletNodes)
14 {//assumes Dirichlet node numbering is strictly increasing
15 int j=0;//indice de parcours des noeuds frontière avec CL Dirichlet
16 int boundarySize=dirichletNodes.size();
17 while(j<boundarySize and dirichletNodes[j]<globalIndex)
20 return globalIndex-boundarySize;
21 else if (dirichletNodes[j]>globalIndex)
24 throw CdmathException("DiffusionEquation::unknownNodeIndex : Error : node is a Dirichlet boundary node");
27 int DiffusionEquation::globalNodeIndex(int unknownNodeIndex, std::vector< int > dirichletNodes)
28 {//assumes Dirichlet boundary node numbering is strictly increasing
29 int boundarySize=dirichletNodes.size();
30 /* trivial case where all boundary nodes are Neumann BC */
32 return unknownNodeIndex;
34 double unknownNodeMax=-1;//max unknown node number in the interval between jth and (j+1)th Dirichlet boundary nodes
35 int j=0;//indice de parcours des noeuds frontière
36 //On cherche l'intervale [j,j+1] qui contient le noeud de numéro interieur unknownNodeIndex
37 while(j+1<boundarySize and unknownNodeMax<unknownNodeIndex)
39 unknownNodeMax += dirichletNodes[j+1]-dirichletNodes[j]-1;
44 return unknownNodeIndex+boundarySize;
45 else //unknownNodeMax>=unknownNodeIndex, hence our node global number is between dirichletNodes[j-1] and dirichletNodes[j]
46 return unknownNodeIndex - unknownNodeMax + dirichletNodes[j]-1;
49 Vector DiffusionEquation::gradientNodal(Matrix M, vector< double > values)
52 throw CdmathException("DiffusionEquation::gradientNodal Matrix M should be square !!!");
54 int Ndim = M.getNumberOfRows()-1;
55 vector< Matrix > matrices(Ndim);
57 for (int idim=0; idim<Ndim;idim++){
58 matrices[idim]=M.deepCopy();
59 for (int jdim=0; jdim<Ndim+1;jdim++)
60 matrices[idim](jdim,idim) = values[jdim] ;
64 for (int idim=0; idim<Ndim;idim++)
65 result[idim] = matrices[idim].determinant();
70 DiffusionEquation::DiffusionEquation(int dim, bool FECalculation,double rho,double cp, double lambda, MPI_Comm comm ):ProblemCoreFlows(comm)
72 /* Control input value are acceptable */
73 if(rho<_precision or cp<_precision)
75 PetscPrintf(PETSC_COMM_WORLD,"rho = %.2f, cp = %.2f, precision = %.2e\n",rho,cp,_precision);
76 throw CdmathException("Error : parameters rho and cp should be strictly positive");
80 PetscPrintf(PETSC_COMM_WORLD,"Conductivity = %.2f\n",lambda);
81 throw CdmathException("Error : conductivity parameter lambda cannot be negative");
85 PetscPrintf(PETSC_COMM_WORLD,"Space dimension = %.2f\n",dim);
86 throw CdmathException("Error : parameter dim cannot be negative");
89 PetscPrintf(PETSC_COMM_WORLD,"\n Diffusion problem with density %.2e, specific heat %.2e, conductivity %.2e", rho,cp,lambda);
91 PetscPrintf(PETSC_COMM_WORLD," and finite elements method\n\n");
93 PetscPrintf(PETSC_COMM_WORLD," and finite volumes method\n\n");
95 _FECalculation=FECalculation;
97 /* Finite element data */
98 _boundaryNodeIds=std::vector< int >(0);
99 _dirichletNodeIds=std::vector< int >(0);
103 _dirichletValuesSet=false;
104 _neumannValuesSet=false;
106 /* Physical parameters */
107 _conductivity=lambda;
110 _diffusivity=_conductivity/(_rho*_cp);
111 _fluidTemperatureFieldSet=false;
114 /* Numerical parameters */
119 _diffusionMatrixSet=false;
121 _fileName = "SolverlabDiffusionProblem";
123 /* Default diffusion tensor is diagonal */
124 _DiffusionTensor=Matrix(_Ndim);
125 for(int idim=0;idim<_Ndim;idim++)
126 _DiffusionTensor(idim,idim)=_diffusivity;
129 void DiffusionEquation::initialize()
133 _runLogFile->open((_fileName+".log").c_str(), ios::out | ios::trunc);;//for creation of a log file to save the history of the simulation
135 if(_Ndim != _mesh.getSpaceDimension() or _Ndim!=_mesh.getMeshDimension())//for the moment we must have space dim=mesh dim
137 PetscPrintf(PETSC_COMM_SELF,"Problem : dimension defined is %d but mesh dimension= %d, and space dimension is %d",_Ndim,_mesh.getMeshDimension(),_mesh.getSpaceDimension());
138 *_runLogFile<< "Problem : dim = "<<_Ndim<< " but mesh dim= "<<_mesh.getMeshDimension()<<", mesh space dim= "<<_mesh.getSpaceDimension()<<endl;
139 *_runLogFile<<"DiffusionEquation::initialize: mesh has incorrect dimension"<<endl;
140 _runLogFile->close();
141 throw CdmathException("!!!!!!!!DiffusionEquation::initialize: mesh has incorrect dimension");
145 throw CdmathException("!!!!!!!!DiffusionEquation::initialize() set initial data first");
148 PetscPrintf(PETSC_COMM_SELF,"\n Initialising the diffusion of a solid temperature using ");
149 *_runLogFile<<"Initialising the diffusion of a solid temperature using ";
152 PetscPrintf(PETSC_COMM_SELF,"Finite volumes method\n\n");
153 *_runLogFile<< "Finite volumes method"<<endl<<endl;
157 PetscPrintf(PETSC_COMM_SELF,"Finite elements method\n\n");
158 *_runLogFile<< "Finite elements method"<<endl<<endl;
162 /**************** Field creation *********************/
164 if(!_heatPowerFieldSet){
165 _heatPowerField=Field("Heat power",_VV.getTypeOfField(),_mesh,1);
166 for(int i =0; i<_VV.getNumberOfElements(); i++)
167 _heatPowerField(i) = _heatSource;
168 _heatPowerFieldSet=true;
170 if(!_fluidTemperatureFieldSet){
171 _fluidTemperatureField=Field("Fluid temperature",_VV.getTypeOfField(),_mesh,1);
172 for(int i =0; i<_VV.getNumberOfElements(); i++)
173 _fluidTemperatureField(i) = _fluidTemperature;
174 _fluidTemperatureFieldSet=true;
177 /* Détection des noeuds frontière avec une condition limite de Dirichlet */
180 if(_Ndim==1 )//The 1D cdmath mesh is necessarily made of segments
181 PetscPrintf(PETSC_COMM_SELF,"1D Finite element method on segments\n");
184 if( _mesh.isTriangular() )//Mesh dim=2
185 PetscPrintf(PETSC_COMM_SELF,"2D Finite element method on triangles\n");
186 else if (_mesh.getMeshDimension()==1)//Mesh dim=1
187 PetscPrintf(PETSC_COMM_SELF,"1D Finite element method on a 2D network : space dimension is %d, mesh dimension is %d\n",_Ndim,_mesh.getMeshDimension());
190 PetscPrintf(PETSC_COMM_SELF,"Error Finite element with space dimension %, and mesh dimension %d, mesh should be either triangular either 1D network\n",_Ndim,_mesh.getMeshDimension());
191 *_runLogFile<<"DiffusionEquation::initialize mesh has incorrect dimension"<<endl;
192 _runLogFile->close();
193 throw CdmathException("DiffusionEquation::initialize: mesh has incorrect cell types");
198 if( _mesh.isTetrahedral() )//Mesh dim=3
199 PetscPrintf(PETSC_COMM_SELF,"3D Finite element method on tetrahedra\n");
200 else if (_mesh.getMeshDimension()==2 and _mesh.isTriangular())//Mesh dim=2
201 PetscPrintf(PETSC_COMM_SELF,"2D Finite element method on a 3D surface : space dimension is %d, mesh dimension is %d\n",_Ndim,_mesh.getMeshDimension());
202 else if (_mesh.getMeshDimension()==1)//Mesh dim=1
203 PetscPrintf(PETSC_COMM_SELF,"1D Finite element method on a 3D network : space dimension is %d, mesh dimension is %d\n",_Ndim,_mesh.getMeshDimension());
206 PetscPrintf(PETSC_COMM_SELF,"Error Finite element with space dimension %d, and mesh dimension %d, mesh should be either tetrahedral, either a triangularised surface or 1D network",_Ndim,_mesh.getMeshDimension());
207 *_runLogFile<<"DiffusionEquation::initialize mesh has incorrect dimension"<<endl;
208 _runLogFile->close();
209 throw CdmathException("DiffusionEquation::initialize: mesh has incorrect cell types");
213 _boundaryNodeIds = _mesh.getBoundaryNodeIds();
214 _NboundaryNodes=_boundaryNodeIds.size();
216 if(_NboundaryNodes==_Nnodes)
217 PetscPrintf(PETSC_COMM_SELF,"!!!!! Warning : all nodes are boundary nodes !!!!!");
219 for(int i=0; i<_NboundaryNodes; i++)
220 if(_limitField[(_mesh.getNode(_boundaryNodeIds[i])).getGroupName()].bcType==DirichletDiffusion)
221 _dirichletNodeIds.push_back(_boundaryNodeIds[i]);
222 _NdirichletNodes=_dirichletNodeIds.size();
223 _NunknownNodes=_Nnodes - _NdirichletNodes;
224 PetscPrintf(PETSC_COMM_SELF,"Number of unknown nodes %d, Number of boundary nodes %d, Number of Dirichlet boundary nodes %d\n\n", _NunknownNodes,_NboundaryNodes, _NdirichletNodes);
225 *_runLogFile<<"Number of unknown nodes " << _NunknownNodes <<", Number of boundary nodes " << _NboundaryNodes<< ", Number of Dirichlet boundary nodes " << _NdirichletNodes <<endl<<endl;
230 _globalNbUnknowns = _Nmailles*_nVar;
232 MPI_Bcast(&_NunknownNodes, 1, MPI_INT, 0, PETSC_COMM_WORLD);
233 _globalNbUnknowns = _NunknownNodes*_nVar;
236 /* Vectors creations */
237 VecCreate(PETSC_COMM_WORLD, &_Tk);//main unknown
238 VecSetSizes(_Tk,PETSC_DECIDE,_globalNbUnknowns);
239 VecSetFromOptions(_Tk);
240 VecGetLocalSize(_Tk, &_localNbUnknowns);
242 VecDuplicate(_Tk, &_Tn);
243 VecDuplicate(_Tk, &_Tkm1);
244 VecDuplicate(_Tk, &_deltaT);
245 VecDuplicate(_Tk, &_b);//RHS of the linear system: _b=Tn/dt + _b0 + puisance volumique + couplage thermique avec le fluide
246 VecDuplicate(_Tk, &_b0);//part of the RHS that comes from the boundary conditions. Computed only once at the first time step
248 if(_mpi_rank == 0)//Process 0 reads and distributes initial data
250 for(int i = 0; i<_NunknownNodes; i++)
252 int globalIndex = globalNodeIndex(i, _dirichletNodeIds);
253 VecSetValue(_Tn,i,_VV(globalIndex), INSERT_VALUES);
256 for(int i = 0; i<_Nmailles; i++)
257 VecSetValue( _Tn, i, _VV(i), INSERT_VALUES);
258 VecAssemblyBegin(_Tn);
261 /* Matrix creation */
262 MatCreateAIJ(PETSC_COMM_WORLD, _localNbUnknowns, _localNbUnknowns, _globalNbUnknowns, _globalNbUnknowns, _d_nnz, PETSC_NULL, _o_nnz, PETSC_NULL, &_A);
264 /* Local sequential vector creation */
265 if(_mpi_size>1 && _mpi_rank == 0)
266 VecCreateSeq(PETSC_COMM_SELF, _globalNbUnknowns, &_Tn_seq);//For saving results on proc 0
267 VecScatterCreateToZero(_Tn,&_scat,&_Tn_seq);
271 _initializedMemory=true;
272 save();//save initial data
275 double DiffusionEquation::computeTimeStep(bool & stop){
276 if(!_diffusionMatrixSet)//The diffusion matrix is computed once and for all time steps
277 _dt_diffusion=computeDiffusionMatrix(stop);
279 //reset right hand side
282 _dt_src=computeRHS(stop);
284 VecAssemblyBegin(_b);
287 if(_verbose or _system)
289 PetscPrintf(PETSC_COMM_WORLD,"Right hand side of the linear system\n");
290 VecView(_b,PETSC_VIEWER_STDOUT_WORLD);
294 return min(_dt_diffusion,_dt_src);
297 double DiffusionEquation::computeDiffusionMatrix(bool & stop)
305 result=computeDiffusionMatrixFE(stop);
307 result=computeDiffusionMatrixFV(stop);
309 PetscPrintf(PETSC_COMM_WORLD,"Maximum diffusivity is %.2e, CFL = %.2f, Delta x = %.2e\n",_maxvp,_cfl,_minl);
311 MatAssemblyBegin(_A, MAT_FINAL_ASSEMBLY);
312 MatAssemblyEnd( _A, MAT_FINAL_ASSEMBLY);
313 VecAssemblyBegin(_b0);
314 VecAssemblyEnd( _b0);
317 //Contribution from the solid/fluid heat exchange with assumption of constant heat transfer coefficient
318 //update value here if variable heat transfer coefficient
319 if(_timeScheme == Implicit and _heatTransfertCoeff/(_rho*_cp)>_precision)
320 MatShift(_A,_heatTransfertCoeff/(_rho*_cp));//Contribution from the liquit/solid heat transfer
322 if(_verbose or _system)
323 MatView(_A,PETSC_VIEWER_STDOUT_WORLD);
328 double DiffusionEquation::computeDiffusionMatrixFE(bool & stop){
334 double coeff;//Diffusion coefficients between nodes i and j
336 Matrix M(_Ndim+1,_Ndim+1);//cell geometry matrix
337 std::vector< Vector > GradShapeFuncs(_Ndim+1);//shape functions of cell nodes
338 std::vector< int > nodeIds(_Ndim+1);//cell node Ids
339 std::vector< Node > nodes(_Ndim+1);//cell nodes
340 int i_int, j_int; //index of nodes j and k considered as unknown nodes
341 bool dirichletCell_treated;
343 std::vector< vector< double > > values(_Ndim+1,vector< double >(_Ndim+1,0));//values of shape functions on cell node
344 for (int idim=0; idim<_Ndim+1;idim++)
345 values[idim][idim]=1;
347 /* parameters for boundary treatment */
348 vector< double > valuesBorder(_Ndim+1);
349 Vector GradShapeFuncBorder(_Ndim+1);
351 for (int j=0; j<_Nmailles;j++)
353 Cj = _mesh.getCell(j);
355 for (int idim=0; idim<_Ndim+1;idim++){
356 nodeIds[idim]=Cj.getNodeId(idim);
357 nodes[idim]=_mesh.getNode(nodeIds[idim]);
358 for (int jdim=0; jdim<_Ndim;jdim++)
359 M(idim,jdim)=nodes[idim].getPoint()[jdim];
362 for (int idim=0; idim<_Ndim+1;idim++)
363 GradShapeFuncs[idim]=gradientNodal(M,values[idim])/fact(_Ndim);
365 /* Loop on the edges of the cell */
366 for (int idim=0; idim<_Ndim+1;idim++)
368 if(find(_dirichletNodeIds.begin(),_dirichletNodeIds.end(),nodeIds[idim])==_dirichletNodeIds.end())//!_mesh.isBorderNode(nodeIds[idim])
369 {//First node of the edge is not Dirichlet node
370 i_int=unknownNodeIndex(nodeIds[idim], _dirichletNodeIds);//assumes Dirichlet boundary node numbering is strictly increasing
371 dirichletCell_treated=false;
372 for (int jdim=0; jdim<_Ndim+1;jdim++)
374 if(find(_dirichletNodeIds.begin(),_dirichletNodeIds.end(),nodeIds[jdim])==_dirichletNodeIds.end())//!_mesh.isBorderNode(nodeIds[jdim])
375 {//Second node of the edge is not Dirichlet node
376 j_int= unknownNodeIndex(nodeIds[jdim], _dirichletNodeIds);//assumes Dirichlet boundary node numbering is strictly increasing
377 MatSetValue(_A,i_int,j_int,(_DiffusionTensor*GradShapeFuncs[idim])*GradShapeFuncs[jdim]/Cj.getMeasure(), ADD_VALUES);
379 else if (!dirichletCell_treated)
380 {//Second node of the edge is a Dirichlet node
381 dirichletCell_treated=true;
382 for (int kdim=0; kdim<_Ndim+1;kdim++)
384 std::map<int,double>::iterator it=_dirichletBoundaryValues.find(nodeIds[kdim]);
385 if( it != _dirichletBoundaryValues.end() )
387 if( _dirichletValuesSet )
388 valuesBorder[kdim]=_dirichletBoundaryValues[it->second];
390 valuesBorder[kdim]=_limitField[_mesh.getNode(nodeIds[kdim]).getGroupName()].T;
393 valuesBorder[kdim]=0;
395 GradShapeFuncBorder=gradientNodal(M,valuesBorder)/fact(_Ndim);
396 coeff =-1.*(_DiffusionTensor*GradShapeFuncBorder)*GradShapeFuncs[idim]/Cj.getMeasure();
397 VecSetValue(_b0,i_int,coeff, ADD_VALUES);
404 //Calcul de la contribution de la condition limite de Neumann au second membre
405 if( _NdirichletNodes !=_NboundaryNodes)
407 vector< int > boundaryFaces = _mesh.getBoundaryFaceIds();
408 int NboundaryFaces=boundaryFaces.size();
409 for(int i = 0; i< NboundaryFaces ; i++)//On parcourt les faces du bord
411 Face Fi = _mesh.getFace(boundaryFaces[i]);
412 for(int j = 0 ; j<_Ndim ; j++)//On parcourt les noeuds de la face
414 if(find(_dirichletNodeIds.begin(),_dirichletNodeIds.end(),Fi.getNodeId(j))==_dirichletNodeIds.end())//node j is a Neumann BC node (not a Dirichlet BC node)
416 j_int=unknownNodeIndex(Fi.getNodeId(j), _dirichletNodeIds);//indice du noeud j en tant que noeud inconnu
417 if( _neumannValuesSet )
418 coeff =Fi.getMeasure()/_Ndim*_neumannBoundaryValues[Fi.getNodeId(j)];
420 coeff =Fi.getMeasure()/_Ndim*_limitField[_mesh.getNode(Fi.getNodeId(j)).getGroupName()].normalFlux;
421 VecSetValue(_b0, j_int, coeff, ADD_VALUES);
428 _diffusionMatrixSet=true;
430 _maxvp=_diffusivity;//To do : optimise value with the mesh while respecting stability
431 if(fabs(_maxvp)<_precision)
432 throw CdmathException("DiffusionEquation::computeDiffusionMatrixFE(): Error computing time step ! Maximum diffusivity is zero => division by zero");
434 return _cfl*_minl*_minl/_maxvp;
437 double DiffusionEquation::computeDiffusionMatrixFV(bool & stop){
440 long nbFaces = _mesh.getNumberOfFaces();
444 double inv_dxi, inv_dxj;
445 double barycenterDistance;
446 Vector normale(_Ndim);
449 std::vector< int > idCells;
451 for (int j=0; j<nbFaces;j++){
452 Fj = _mesh.getFace(j);
454 // compute the normal vector corresponding to face j : from idCells[0] to idCells[1]
455 idCells = Fj.getCellsId();
456 Cell1 = _mesh.getCell(idCells[0]);
458 for(int l=0; l<Cell1.getNumberOfFaces(); l++){
459 if (j == Cell1.getFacesId()[l]){
460 for (int idim = 0; idim < _Ndim; ++idim)
461 normale[idim] = Cell1.getNormalVector(l,idim);
466 //Compute velocity at the face Fj
467 dn=(_DiffusionTensor*normale)*normale;
471 // compute 1/dxi = volume of Ci/area of Fj
472 inv_dxi = Fj.getMeasure()/Cell1.getMeasure();
474 // If Fj is on the boundary
475 if (Fj.getNumberOfCells()==1) {
478 cout << "face numero " << j << " cellule frontiere " << idCells[0] << " ; vecteur normal=(";
479 for(int p=0; p<_Ndim; p++)
480 cout << normale[p] << ",";
483 nameOfGroup = Fj.getGroupName();
485 if (_limitField[nameOfGroup].bcType==NeumannDiffusion){
486 VecSetValue(_b0,idm, -dn*inv_dxi*_limitField[nameOfGroup].normalFlux, ADD_VALUES);
488 else if(_limitField[nameOfGroup].bcType==DirichletDiffusion){
489 barycenterDistance=Cell1.getBarryCenter().distance(Fj.getBarryCenter());
490 MatSetValue(_A,idm,idm,dn*inv_dxi/barycenterDistance , ADD_VALUES);
491 VecSetValue(_b0,idm, dn*inv_dxi/barycenterDistance*_limitField[nameOfGroup].T, ADD_VALUES);
495 cout<<"!!!!!!!!!!!!!!!!! Error DiffusionEquation::computeDiffusionMatrixFV !!!!!!!!!!"<<endl;
496 cout<<"Boundary condition not accepted for boundary named "<<nameOfGroup<< ", _limitField[nameOfGroup].bcType= "<<_limitField[nameOfGroup].bcType<<endl;
497 cout<<"Accepted boundary conditions are NeumannDiffusion "<<NeumannDiffusion<< " and DirichletDiffusion "<<DirichletDiffusion<<endl;
498 *_runLogFile<<"Boundary condition not accepted for boundary named "<<nameOfGroup<< ", _limitField[nameOfGroup].bcType= "<<_limitField[nameOfGroup].bcType<<endl;
499 throw CdmathException("Boundary condition not accepted");
502 // if Fj is inside the domain
503 } else if (Fj.getNumberOfCells()==2 ){
506 cout << "face numero " << j << " cellule gauche " << idCells[0] << " cellule droite " << idCells[1];
507 cout << " ; vecteur normal=(";
508 for(int p=0; p<_Ndim; p++)
509 cout << normale[p] << ",";
512 Cell2 = _mesh.getCell(idCells[1]);
515 inv_dxj = Fj.getMeasure()/Cell2.getMeasure();
517 inv_dxj = 1/Cell2.getMeasure();
519 barycenterDistance=Cell1.getBarryCenter().distance(Cell2.getBarryCenter());
521 MatSetValue(_A,idm,idm, dn*inv_dxi/barycenterDistance, ADD_VALUES);
522 MatSetValue(_A,idm,idn,-dn*inv_dxi/barycenterDistance, ADD_VALUES);
523 MatSetValue(_A,idn,idn, dn*inv_dxj/barycenterDistance, ADD_VALUES);
524 MatSetValue(_A,idn,idm,-dn*inv_dxj/barycenterDistance, ADD_VALUES);
527 throw CdmathException("DiffusionEquation::computeDiffusionMatrixFV(): incompatible number of cells around a face");
531 _diffusionMatrixSet=true;
533 MPI_Bcast(&_maxvp, 1, MPI_DOUBLE, 0, PETSC_COMM_WORLD);//The determination of _maxvp is optimal, unlike in the FE case
535 if(fabs(_maxvp)<_precision)
536 throw CdmathException("DiffusionEquation::computeDiffusionMatrixFV(): Error computing time step ! Maximum diffusivity is zero => division by zero");
538 return _cfl*_minl*_minl/_maxvp;
541 double DiffusionEquation::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
547 for (int i=0; i<_Nmailles;i++)
548 //Contribution due to fluid/solide heat exchange + Contribution of the volumic heat power
549 if(_timeScheme == Explicit)
551 VecGetValues(_Tn, 1, &i, &Ti);
552 VecSetValue(_b,i,(_heatTransfertCoeff*(_fluidTemperatureField(i)-Ti)+_heatPowerField(i))/(_rho*_cp),ADD_VALUES);
554 else//Implicit scheme
555 VecSetValue(_b,i,(_heatTransfertCoeff* _fluidTemperatureField(i) +_heatPowerField(i))/(_rho*_cp) ,ADD_VALUES);
559 std::vector< int > nodesId;
560 for (int i=0; i<_Nmailles;i++)
563 nodesId=Ci.getNodesId();
564 for (int j=0; j<nodesId.size();j++)
565 if(!_mesh.isBorderNode(nodesId[j])) //or for better performance nodeIds[idim]>dirichletNodes.upper_bound()
567 double coeff = (_heatTransfertCoeff*_fluidTemperatureField(nodesId[j]) + _heatPowerField(nodesId[j]))/(_rho*_cp);
568 VecSetValue(_b,unknownNodeIndex(nodesId[j], _dirichletNodeIds), coeff*Ci.getMeasure()/(_Ndim+1),ADD_VALUES);
574 if(_heatTransfertCoeff>_precision)
575 return _rho*_cp/_heatTransfertCoeff;
580 bool DiffusionEquation::initTimeStep(double dt){
582 /* tricky because of code coupling */
583 if(_dt>0 and dt>0)//Previous time step was set and used
585 //Remove the contribution from dt to prepare for new time step. The diffusion matrix is not recomputed
586 if(_timeScheme == Implicit)
587 MatShift(_A,-1/_dt+1/dt);
588 //No need to remove the contribution to the right hand side since it is recomputed from scratch at each time step
590 else if(dt>0)//_dt==0, first time step
592 if(_timeScheme == Implicit)
597 PetscPrintf(PETSC_COMM_WORLD,"DiffusionEquation::initTimeStep %.2e = \n",dt);
598 throw CdmathException("Error DiffusionEquation::initTimeStep : cannot set time step to zero");
600 //At this stage _b contains _b0 + power + heat exchange
601 VecAXPY(_b, 1/dt, _Tn);
605 if(_verbose && (_nbTimeStep-1)%_freqSave ==0)
607 PetscPrintf(PETSC_COMM_WORLD,"Matrix of the linear system\n");
608 MatView(_A,PETSC_VIEWER_STDOUT_WORLD);
614 void DiffusionEquation::abortTimeStep(){
615 //Remove contribution of dt to the RHS
616 VecAXPY(_b, -1/_dt, _Tn);
617 //Remove contribution of dt to the matrix
618 if(_timeScheme == Implicit)
623 bool DiffusionEquation::iterateTimeStep(bool &converged)
627 if(_NEWTON_its>0){//Pas besoin de computeTimeStep à la première iteration de Newton
629 computeTimeStep(stop);
636 if(_timeScheme == Explicit)
638 MatMult(_A, _Tn, _Tk);
639 VecAXPY(_Tk, -1, _b);
647 #if PETSC_VERSION_GREATER_3_5
648 KSPSetOperators(_ksp, _A, _A);
650 KSPSetOperators(_ksp, _A, _A,SAME_NONZERO_PATTERN);
654 KSPSetComputeEigenvalues(_ksp,PETSC_TRUE);
655 KSPSolve(_ksp, _b, _Tk);
657 KSPGetIterationNumber(_ksp, &_PetscIts);
658 if( _MaxIterLinearSolver < _PetscIts)
659 _MaxIterLinearSolver = _PetscIts;
660 if(_PetscIts>=_maxPetscIts)
662 PetscPrintf(PETSC_COMM_WORLD,"Systeme lineaire : pas de convergence de Petsc. Itérations maximales %d atteintes \n",_maxPetscIts);
663 *_runLogFile<<"Systeme lineaire : pas de convergence de Petsc. Itérations maximales "<<_maxPetscIts<<" atteintes"<<endl;
669 VecCopy(_Tk, _deltaT);//ici on a deltaT=Tk
670 VecAXPY(_deltaT, -1, _Tkm1);//On obtient deltaT=Tk-Tkm1
671 VecNorm(_deltaT,NORM_INFINITY,&_erreur_rel);
672 converged = (_erreur_rel <= _precision) ;//converged=convergence des iterations de Newton
680 void DiffusionEquation::validateTimeStep()
682 VecCopy(_Tk, _deltaT);//ici Tk=Tnp1 donc on a deltaT=Tnp1
683 VecAXPY(_deltaT, -1, _Tn);//On obtient deltaT=Tnp1-Tn
684 VecNorm(_deltaT,NORM_INFINITY,&_erreur_rel);
686 _isStationary =(_erreur_rel <_precision);
694 if ((_nbTimeStep-1)%_freqSave ==0 || _isStationary || _time>=_timeMax || _nbTimeStep>=_maxNbOfTimeStep)
698 void DiffusionEquation::save(){
699 PetscPrintf(PETSC_COMM_WORLD,"Saving numerical results at time step number %d \n\n", _nbTimeStep);
700 *_runLogFile<< "Saving numerical results at time step number "<< _nbTimeStep << endl<<endl;
702 string resultFile(_path+"/DiffusionEquation");//Results
705 resultFile+=_fileName;
708 VecScatterBegin(_scat,_Tn,_Tn_seq,INSERT_VALUES,SCATTER_FORWARD);
709 VecScatterEnd( _scat,_Tn,_Tn_seq,INSERT_VALUES,SCATTER_FORWARD);
712 if(_verbose or _system)
714 PetscPrintf(PETSC_COMM_WORLD,"Unknown of the linear system :\n");
715 VecView(_Tn,PETSC_VIEWER_STDOUT_WORLD);
719 //On remplit le champ
722 for(int i =0; i<_Nmailles;i++)
725 VecGetValues(_Tn_seq, 1, &i, &Ti);
727 VecGetValues(_Tn , 1, &i, &Ti);
734 for(int i=0; i<_NunknownNodes; i++)
737 VecGetValues(_Tn_seq, 1, &i, &Ti);
739 VecGetValues(_Tk , 1, &i, &Ti);
740 globalIndex = globalNodeIndex(i, _dirichletNodeIds);
746 for(int i=0; i<_NdirichletNodes; i++)//Assumes node numbering starts with border nodes
748 Ni=_mesh.getNode(_dirichletNodeIds[i]);
749 nameOfGroup = Ni.getGroupName();
750 _VV(_dirichletNodeIds[i])=_limitField[nameOfGroup].T;
753 _VV.setTime(_time,_nbTimeStep);
755 // create mesh and component info
756 if (_nbTimeStep ==0 || _restartWithNewFileName){
757 if (_restartWithNewFileName)
758 _restartWithNewFileName=false;
759 string suppress ="rm -rf "+resultFile+"_*";
760 system(suppress.c_str());//Nettoyage des précédents calculs identiques
762 _VV.setInfoOnComponent(0,"Temperature_(K)");
766 _VV.writeVTK(resultFile);
769 _VV.writeMED(resultFile);
772 _VV.writeCSV(resultFile);
776 else{ // do not create mesh
780 _VV.writeVTK(resultFile,false);
783 _VV.writeMED(resultFile,false);
786 _VV.writeCSV(resultFile);
797 _VV.writeVTK(resultFile);
800 _VV.writeMED(resultFile);
803 _VV.writeCSV(resultFile);
810 void DiffusionEquation::terminate(){
814 VecDestroy(&_deltaT);
818 if(_mpi_size>1 && _mpi_rank == 0)
819 VecDestroy(&_Tn_seq);
823 DiffusionEquation::setDirichletValues(map< int, double> dirichletBoundaryValues)
825 _dirichletValuesSet=true;
826 _dirichletBoundaryValues=dirichletBoundaryValues;
830 DiffusionEquation::setNeumannValues(map< int, double> neumannBoundaryValues)
832 _neumannValuesSet=true;
833 _neumannBoundaryValues=neumannBoundaryValues;
838 DiffusionEquation::getOutputTemperatureField()
840 if(!_initializedMemory)
841 throw("Computation not initialized. No temperature field available");
847 DiffusionEquation::getRodTemperatureField()
849 return getOutputTemperatureField();
853 DiffusionEquation::getInputFieldsNames()
855 vector<string> result(2);
857 result[0]="FluidTemperature";
858 result[1]="HeatPower";
863 DiffusionEquation::getOutputFieldsNames()
865 vector<string> result(1);
867 result[0]="RodTemperature";
873 DiffusionEquation::getOutputField(const string& nameField )
875 if(nameField=="RodTemperature" || nameField=="RODTEMPERATURE" || nameField=="TEMPERATURECOMBUSTIBLE" || nameField=="TemperatureCombustible" )
876 return getRodTemperatureField();
879 cout<<"Error : Field name "<< nameField << " does not exist, call getOutputFieldsNames first" << endl;
880 throw CdmathException("DiffusionEquation::getOutputField error : Unknown Field name");
885 DiffusionEquation::setInputField(const string& nameField, Field& inputField )
888 throw CdmathException("!!!!!!!! DiffusionEquation::setInputField set initial field first");
890 if(nameField=="FluidTemperature" || nameField=="FLUIDTEMPERATURE" || nameField=="TemperatureFluide" || nameField=="TEMPERATUREFLUIDE")
891 return setFluidTemperatureField( inputField) ;
892 else if(nameField=="HeatPower" || nameField=="HEATPOWER" || nameField=="PuissanceThermique" || nameField=="PUISSANCETHERMIQUE" )
893 return setHeatPowerField( inputField );
896 cout<<"Error : Field name "<< nameField << " is not an input field name, call getInputFieldsNames first" << endl;
897 throw CdmathException("DiffusionEquation::setInputField error : Unknown Field name");
902 DiffusionEquation::setFluidTemperatureField(Field coupledTemperatureField){
904 throw CdmathException("!!!!!!!! DiffusionEquation::setFluidTemperatureField set initial field first");
906 coupledTemperatureField.getMesh().checkFastEquivalWith(_mesh);
907 _fluidTemperatureField=coupledTemperatureField;
908 _fluidTemperatureFieldSet=true;
912 DiffusionEquation::setDirichletBoundaryCondition(string groupName, string fileName, string fieldName, int timeStepNumber, int order, int meshLevel, int field_support_type){
913 if(_FECalculation && field_support_type != NODES)
914 cout<<"Warning : finite element simulation should have boundary field on nodes!!! Change parameter field_support_type"<<endl;
915 else if(!_FECalculation && field_support_type == NODES)
916 cout<<"Warning : finite volume simulation should not have boundary field on nodes!!! Change parameter field_support_type"<<endl;
920 switch(field_support_type)
923 VV = Field(fileName, CELLS, fieldName, timeStepNumber, order, meshLevel);
926 VV = Field(fileName, NODES, fieldName, timeStepNumber, order, meshLevel);
929 VV = Field(fileName, FACES, fieldName, timeStepNumber, order, meshLevel);
932 std::ostringstream message;
933 message << "Error DiffusionEquation::setDirichletBoundaryCondition \n Accepted field support integers are "<< CELLS <<" (for CELLS), "<<NODES<<" (for NODES), and "<< FACES <<" (for FACES)" ;
934 throw CdmathException(message.str().c_str());
936 /* For the moment the boundary value is taken constant equal to zero */
937 _limitField[groupName]=LimitFieldDiffusion(DirichletDiffusion,0,-1);//This line will be deleted when variable BC are properly treated in solverlab
941 DiffusionEquation::setNeumannBoundaryCondition(string groupName, string fileName, string fieldName, int timeStepNumber, int order, int meshLevel, int field_support_type){
942 if(_FECalculation && field_support_type != NODES)
943 cout<<"Warning : finite element simulation should have boundary field on nodes!!! Change parameter field_support_type"<<endl;
944 else if(!_FECalculation && field_support_type == NODES)
945 cout<<"Warning : finite volume simulation should not have boundary field on nodes!!! Change parameter field_support_type"<<endl;
949 switch(field_support_type)
952 VV = Field(fileName, CELLS, fieldName, timeStepNumber, order, meshLevel);
955 VV = Field(fileName, NODES, fieldName, timeStepNumber, order, meshLevel);
958 VV = Field(fileName, FACES, fieldName, timeStepNumber, order, meshLevel);
961 std::ostringstream message;
962 message << "Error DiffusionEquation::setNeumannBoundaryCondition \n Accepted field support integers are "<< CELLS <<" (for CELLS), "<<NODES<<" (for NODES), and "<< FACES <<" (for FACES)" ;
963 throw CdmathException(message.str().c_str());
965 /* For the moment the boundary value is taken constant equal to zero */
966 _limitField[groupName]=LimitFieldDiffusion(NeumannDiffusion,-1,0);//This line will be deleted when variable BC are properly treated in solverlab