proteus 1.9.0
C/C++/Fortran libraries
Loading...
Searching...
No Matches
ErrorResidualMethod.cpp
Go to the documentation of this file.
1#include "MeshAdaptPUMI.h"
2#include <PCU.h>
3#include <petscksp.h>
4
5#include <apf.h>
6#include <apfMesh.h>
7#include <apfShape.h>
8#include <apfDynamicMatrix.h>
9#include <apfNumbering.h>
10
11#include <iostream>
12#include <fstream>
13
19//Global variables used to make it easier to pass these variables from MeshAdaptPUMIDrvr
20int approx_order; //shape function order
21int int_order; //integration order
23double a_kl = 0.5; //flux term weight
24
25inline void getProps(double*rho,double*nu)
26//Function used to transfer MeshAdaptPUMIDrvr variables into global variables
27{
28 rho_0 = rho[0];
29 nu_0 = nu[0];
30 rho_1 = rho[1];
31 nu_1 = nu[1];
32 return;
33}
34
35double MeshAdaptPUMIDrvr::getMPvalue(double field_val,double val_0, double val_1)
46{
47 return val_0*(1-field_val)+val_1*field_val;
48}
49
50apf::Vector3 getFaceNormal(apf::Mesh* mesh, apf::MeshEntity* face)
51//Function used to get the unit vector normal to an element face
52{
53 apf::Vector3 normal;
54 apf::Adjacent verts;
55 mesh->getAdjacent(face,0,verts);
56 //apf::Vector3 vtxs[verts.getSize()];
57 apf::Vector3 vtxs[3];
58 for(int i=0;i<verts.getSize();i++){
59 mesh->getPoint(verts[i],0,vtxs[i]);
60 }
61 apf::Vector3 a,b;
62 if(mesh->getDimension()==2){
63 vtxs[2] = vtxs[0];
64 vtxs[2][2] = 1.0;
65 }
66 a = vtxs[1]-vtxs[0];
67 b = vtxs[2]-vtxs[0];
68 normal = apf::cross(a,b);
69
70 return normal.normalize();
71}
72
73double getDotProduct(apf::Vector3 a, apf::Vector3 b)
74//Function used to get the dot product between two vectors
75{
76 return (a[0]*b[0] + a[1]*b[1] + a[2]*b[2]);
77}
78
79double getDotProduct(apf::Matrix3x3 a, apf::Matrix3x3 b)
80//Overloaded function used to get the dot product analog between two matrices
81{
82 double temp =0;
83 for(int i=0;i<3;i++){
84 for(int j=0;j<3;j++){
85 temp = temp + a[i][j]*b[i][j];
86 }
87 }
88 return temp;
89}
90
91
92bool isInTet(apf::Mesh* mesh, apf::MeshEntity* ent, apf::Vector3 pt)
93//Function used to test if a point pt is inside the tetrahedron ent
94//Returns a boolean: 1 if is in tet, 0 if not
95{
96 bool isin=0;
97
98 apf::Adjacent verts;
99 mesh->getAdjacent(ent,0,verts);
100 apf::Vector3 vtxs[4]; //4 points
101 for(int i=0;i<4;i++){
102 mesh->getPoint(verts[i],0,vtxs[i]);
103 }
104 apf::Vector3 c[4];
105 c[0] = vtxs[1]-vtxs[0];
106 c[1] = vtxs[2]-vtxs[0];
107 c[2] = vtxs[3]-vtxs[0];
108 c[3] = pt-vtxs[0];
109
110 apf::Matrix3x3 K,Kinv;
111 apf::Vector3 F;
112 for(int i=0;i<3;i++){
113 for(int j=0;j<3;j++){
114 K[i][j] = getDotProduct(c[i],c[j]);
115 }
116 F[i] = getDotProduct(c[3],c[i]);
117 }
118 Kinv = invert(K);
119 apf::DynamicMatrix Kinv_dyn = apf::fromMatrix(Kinv);
120 apf::DynamicVector F_dyn = apf::fromVector(F);
121 apf::DynamicVector uvw; //result
122 apf::multiply(Kinv_dyn,F_dyn,uvw);
123 if(uvw[0] >= 0 && uvw[1] >=0 && uvw[2] >=0 && (uvw[0]+uvw[1]+uvw[2])<=1) isin = 1;
124 return isin;
125}
126
127bool isInSimplex(apf::Mesh* mesh, apf::MeshEntity* ent, apf::Vector3 pt, int dim)
128//Function used to test if a point pt is inside the tetrahedron ent
129//Returns a boolean: 1 if is in tet, 0 if not
130{
131 bool isin=0;
132 int numverts = dim+1;
133 apf::Adjacent verts;
134 mesh->getAdjacent(ent,0,verts);
135 apf::Vector3 vtxs[4];
136 for(int i=0;i<numverts;i++){
137 mesh->getPoint(verts[i],0,vtxs[i]);
138 }
139 apf::Vector3 c[4];
140 if(dim==2){
141 c[0] = vtxs[1]-vtxs[0];
142 c[1] = vtxs[2]-vtxs[0];
143 c[2] = pt-vtxs[0];
144 }
145 else if(dim==3){
146 c[0] = vtxs[1]-vtxs[0];
147 c[1] = vtxs[2]-vtxs[0];
148 c[2] = vtxs[3]-vtxs[0];
149 c[3] = pt-vtxs[0];
150 }
151
152 apf::Matrix3x3 K(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0);
153 apf::Matrix3x3 Kinv;
154 apf::Vector3 F(0.0,0.0,0.0);
155 for(int i=0;i<dim;i++){
156 for(int j=0;j<dim;j++){
157 K[i][j] = getDotProduct(c[i],c[j]);
158 }
159 F[i] = getDotProduct(c[dim],c[i]);
160 }
161 if(dim==2)
162 K[2][2] = 1.0;
163 Kinv = invert(K);
164 apf::DynamicMatrix Kinv_dyn = apf::fromMatrix(Kinv);
165 apf::DynamicVector F_dyn = apf::fromVector(F);
166 apf::DynamicVector uvw; //result
167 apf::multiply(Kinv_dyn,F_dyn,uvw);
168 if(dim==2){
169 if(uvw[0] >= 0 && uvw[1] >=0 && (uvw[0]+uvw[1])<=1) isin = 1;
170 }
171 else{
172 if(uvw[0] >= 0 && uvw[1] >=0 && uvw[2] >=0 && (uvw[0]+uvw[1]+uvw[2])<=1) isin = 1;
173 }
174 return isin;
175}
176
177
178/*
179double a_k(apf::Matrix3x3 u, apf::Matrix3x3 v,double nu){
180 //u and v are gradients of a vector
181 apf::Matrix3x3 temp_u = u+apf::transpose(u);
182 apf::Matrix3x3 temp_v = v+apf::transpose(v);
183 return nu*getDotProduct(temp_u,temp_v);
184}
185
186double b_k(double a, apf::Matrix3x3 b){
187 //b is a gradient of a vector
188 return a*(b[0][0]+b[1][1]+b[1][1]);
189}
190
191double c_k(apf::Vector3 a, apf::Matrix3x3 b, apf::Vector3 c){
192 //b is a gradient of a vector
193 return getDotProduct(b*a,c);
194}
195*/
196
197void getLHS(Mat &K,apf::NewArray <apf::DynamicVector> &shdrv,int nsd,double weight, double visc_val,int nshl)
198//Function used to get the LHS of the local error problem.
199//The LHS is entirely A(\phi,\phi) which can be decomposed into a diagonal contributions and off-diagonal contributions
200//Inputs:
201// shdrv is the set of shape function derivatives for an element evaluated at a quadrature point
202// nsd is the number of spatial dimensions
203// weight is the corresponding weight for a given quadrature point
204// visc_val is the viscosity at that quadrature point
205// nshl is the number of local shape functions in an element
206//Outputs:
207// K is the matrix representing the LHS
208{
209 PetscScalar term1[nshl][nshl], term2[nshl][nshl];
210 //Calculate LHS Diagonal Block Term
211 for(int s=0; s<nshl;s++){
212 for(int t=0; t<nshl;t++){
213 double temp=0;
214 for(int j=0;j<nsd;j++){
215 temp+=shdrv[s][j]*shdrv[t][j];
216 }
217 term1[s][t] = temp*weight*visc_val;
218 }
219 }
220 int idx[nshl]; //indices for PETSc Mat insertion
221 for(int i=0; i<nsd;i++){
222 for(int j=0;j<nshl;j++){
223 idx[j] = i*nshl+j;
224 }
225 MatSetValues(K,nshl,idx,nshl,idx,term1[0],ADD_VALUES);
226 }
227 int idxr[nshl],idxc[nshl]; //indices for PETSc rows and columns
228 for(int i = 0; i< nsd;i++){
229 for(int j=0; j< nsd;j++){
230 for(int s=0;s<nshl;s++){
231 for(int t=0;t<nshl;t++){
232 term2[s][t] = shdrv[s][j]*shdrv[t][i]*weight*visc_val;
233 }
234 }
235 for(int count=0;count<nshl;count++){
236 idxr[count] = i*nshl+count;
237 idxc[count] = j*nshl+count;
238 }
239 MatSetValues(K,nshl,idxr,nshl,idxc,term2[0],ADD_VALUES);
240 }
241 } //end 2nd term loop
242}
243
244void getRHS(Vec &F,apf::NewArray <double> &shpval,apf::NewArray <apf::DynamicVector> &shdrv,apf::Vector3 vel_vect,apf::Matrix3x3 grad_vel,
245 int nsd,double weight,int nshl,
246 double visc_val,double density,apf::Vector3 grad_density,double pressure,
247 double g[3])
248//Function used to get the RHS of the local error problem.
249//The RHS is the weak residual of the N-S equations
250//Inputs:
251// shpval are the local shape functions evaluated at a quadrature point
252// shdrv are the local shape function derivatives evaluated at a quadrature point
253// vel_vect is the velocity vector at a quadrature point
254// grad_vel is the velocity gradient at a quadrature point
255// nsd is the number of spatial dimensions
256// weight is the corresponding weight for a given quadrature point
257// nshl is the number of local shape functions in an element
258// visc_val is the viscosity at a quadrature point
259// density is the density at a quadrature point
260// grad_density is the density gradient at a quadrature point
261// pressure is the pressure at a quadrature point
262// g is the gravity vector
263//Outputs:
264// F is the vector representing the RHS
265
266{
267 int idx[nshl];
268 for( int i = 0; i<nsd; i++){
269 double temp_vect[nshl];
270 for( int s=0;s<nshl;s++){
271 idx[s] = i*nshl+s;
272
273 //forcing term
274 //temp_vect[s] = (g[i]+0.0)*shpval[s];
275 //temp_vect[s] += pressure/density*shdrv[s][i]; //pressure term
276 double force = (g[i]+0.0)*shpval[s];
277 double pressure_force = pressure/density*shdrv[s][i];
278 double a_rho_term = 0;
279 double b_rho_term = -pressure/(density*density)*grad_density[i]*shpval[s];
280 double a_term = 0;
281 double c_term = 0;
282 //a(u,v) and c(u,u,v) term
283 for(int j=0;j<nsd;j++){
284 a_term += -visc_val*shdrv[s][j]*(grad_vel[i][j]+grad_vel[j][i]);
285 a_rho_term += visc_val*(grad_vel[i][j]+grad_vel[j][i])*shpval[s]*grad_density[j]/(density);
286 c_term += -shpval[s]*grad_vel[i][j]*vel_vect[j];
287 }
288 temp_vect[s] = force+pressure_force+a_term+c_term;
289 //temp_vect[s] = force+pressure_force+a_rho_term+b_rho_term+a_term+c_term;
290 temp_vect[s] = temp_vect[s]*weight;
291 } //end loop over number of shape functions
292 VecSetValues(F,nshl,idx,temp_vect,ADD_VALUES);
293 } //end loop over spatial dimensions
294}
295
296
297void MeshAdaptPUMIDrvr::computeDiffusiveFlux(apf::Mesh*m,apf::Field* voff, apf::Field* visc,apf::Field* pref, apf::Field* velf)
312{
313 if(comm_rank==0)
314 std::cerr<<"Begin computeDiffusiveFlux()"<<std::endl;
315 int numbqpt, nshl;
316 int hier_off;
317 if(nsd==2)
318 hier_off=3;
319 else if(nsd==3)
320 hier_off=4;
321 apf::MeshEntity* bent,*ent;
322 apf::MeshIterator* iter = m->begin(nsd-1); //loop over faces
323
324 //Need to get number of bqpt
325 while(bent = m->iterate(iter)){
326 apf::MeshElement* b_elem;
327 b_elem = apf::createMeshElement(m,bent);
328 numbqpt = apf::countIntPoints(b_elem,int_order);
329 apf::destroyMeshElement(b_elem);
330 break;
331 }
332 m->end(iter);
333
334 diffFlux = m->createDoubleTag("diffFlux",numbqpt*nsd*2);
335 apf::MeshElement* tempelem; apf::Element * tempvelo,*temppres,*tempvoff;
336 apf::MeshElement* b_elem;
337 apf::Adjacent adjFaces;
338 apf::Vector3 normal,centerdir;
339 int orientation;
340 double tempflux[numbqpt*nsd];
341 double *flux; flux = (double*) calloc(numbqpt*nsd*2,sizeof(double));
342 apf::NewArray <double> shpval;
343 apf::NewArray <double> shpval_temp;
344
345 apf::FieldShape* err_shape = apf::getHierarchic(2);
346 apf::EntityShape* elem_shape;
347 apf::Vector3 bqpt,bqptl,bqptshp;
348 double weight, Jdet;
349 apf::Matrix3x3 J;
350 apf::Matrix3x3 tempgrad_velo;
351 apf::Matrix3x3 identity(1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0);
352
353 iter=m->begin(nsd-1);
354 while(ent=m->iterate(iter))
355 {
356 m->setDoubleTag(ent,diffFlux,flux);
357 }
358 m->end(iter);
359 free(flux);
360
361 if(comm_rank==0)
362 std::cerr<<"Initialized flux"<<std::endl;
363 //loop over regions
364 PCU_Comm_Begin(PCUObj);
365 iter = m->begin(nsd);
366 int ent_count=0;
367 while(ent = m->iterate(iter))
368 {
369 //Shape functions of the region and not the boundaries
370 if(ent_count==0){
371 nshl=apf::countElementNodes(err_shape,m->getType(ent));
372 shpval_temp.allocate(nshl);
373 nshl= nshl-hier_off;
374 shpval.allocate(nshl);
375 elem_shape = err_shape->getEntityShape(m->getType(ent));
376 }
377
378 m->getAdjacent(ent,nsd-1,adjFaces);
379 for(int adjcount =0;adjcount<adjFaces.getSize();adjcount++){
380 bent = adjFaces[adjcount];
381 normal=getFaceNormal(m,bent);
382 centerdir=apf::getLinearCentroid(m,ent)-apf::getLinearCentroid(m,bent);
383 //if(isInTet(m,ent,apf::project(normal,centerdir)*centerdir.getLength()+apf::getLinearCentroid(m,bent)))
384 if(isInSimplex(m,ent,apf::project(normal,centerdir).normalize()*centerdir.getLength()+apf::getLinearCentroid(m,bent),nsd)){
385 orientation = 1;
386 }
387 else{
388 orientation = 0;
389 }
390
391 //begin calculation of flux
392 b_elem = apf::createMeshElement(m,bent);
393 tempelem = apf::createMeshElement(m,ent);
394 temppres = apf::createElement(pref,tempelem);
395 tempvelo = apf::createElement(velf,tempelem);
396 tempvoff = apf::createElement(voff,tempelem);
397
398 for(int l = 0;l<numbqpt;l++)
399 {
400 apf::Vector3 bflux(0.0,0.0,0.0);
401 apf::Matrix3x3 tempbflux(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0);
402 apf::getIntPoint(b_elem,int_order,l,bqpt);
403 weight = apf::getIntWeight(b_elem,int_order,l);
404 apf::getJacobian(b_elem,bqpt,J); //evaluate the Jacobian at the quadrature point
405 Jdet=fabs(apf::getJacobianDeterminant(J,nsd-1));
406 bqptl=apf::boundaryToElementXi(m,bent,ent,bqpt);
407 apf::getVectorGrad(tempvelo,bqptl,tempgrad_velo);
408 tempgrad_velo = apf::transpose(tempgrad_velo);
409
410 apf::ModelEntity* me=m->toModel(bent);
411 int tag = m->getModelTag(me);
412 apf::ModelEntity* boundary_face = m->findModelEntity(nsd-1,tag);
413
414 if(me==boundary_face && has_gBC){
415 int BCtype[4];
416 double fluxdata[4][numbqpt];
417 //for(int i=1;i<nsd+1;i++){ //ignores 0th index because that's pressure
418 // m->getIntTag(bent,BCtag[i],&(BCtype[i]));
419 //}
420 m->getIntTag(bent,BCtag,&(BCtype[0]));
421 if((BCtype[1]+BCtype[2]+BCtype[3] != 3) && BCtype[1] == 1 ){
422 std::cerr << "diffusive flux not fully specified on face " << localNumber(bent) << '\n';
423 std::cerr << "BCtype "<<BCtype[1]<<" "<<BCtype[2]<<" "<<BCtype[3]<<std::endl;
424 abort();
425 }
426 if(BCtype[1]+BCtype[2]+BCtype[3] == 3){
427 for(int i=1;i<nsd+1;i++)
428 m->getDoubleTag(bent,fluxtag[i],&(fluxdata[i][0]));
429 bflux = apf::Vector3(fluxdata[1][l],fluxdata[2][l],fluxdata[3][l]);
430 bflux = bflux-identity*apf::getScalar(temppres,bqptl)/getMPvalue(apf::getScalar(tempvoff,bqptl),rho_0,rho_1)*normal;
431 }
432 else{
433 tempbflux = (tempgrad_velo+apf::transpose(tempgrad_velo))*getMPvalue(apf::getScalar(tempvoff,bqptl),nu_0,nu_1)
434 -identity*apf::getScalar(temppres,bqptl)/getMPvalue(apf::getScalar(tempvoff,bqptl),rho_0,rho_1);
435 bflux = tempbflux*normal;
436 }
437 }
438 else{
439 tempbflux = (tempgrad_velo+apf::transpose(tempgrad_velo))*getMPvalue(apf::getScalar(tempvoff,bqptl),nu_0,nu_1)
440 -identity*apf::getScalar(temppres,bqptl)/getMPvalue(apf::getScalar(tempvoff,bqptl),rho_0,rho_1);
441 bflux = tempbflux*normal;
442 } //end if boundary
443 bflux = bflux*weight*Jdet;
444 // bflux.toArray() always writes all 3 components of an apf::Vector3,
445 // regardless of nsd -- tempflux is only sized/strided for nsd components
446 // per quadrature point, so using toArray() here overflows it by one
447 // component per point when nsd==2 (harmless no-op when nsd==3, since the
448 // stride then matches the 3 components written).
449 for(int d = 0; d < nsd; d++) tempflux[l*nsd+d] = bflux[d];
450 }
451 flux = (double*) calloc(numbqpt*nsd*2,sizeof(double));
452 m->getDoubleTag(bent,diffFlux,flux);
453 for (int i=0;i<numbqpt*nsd;i++){
454 flux[orientation*numbqpt*nsd+i] = tempflux[i];
455 }
456 m->setDoubleTag(bent,diffFlux,flux);
457 free(flux);
458 apf::destroyMeshElement(tempelem);apf::destroyElement(tempvelo);apf::destroyElement(temppres); apf::destroyElement(tempvoff);
459
460 //Parallel Communications
461 apf::ModelEntity* me=m->toModel(bent);
462 apf::ModelEntity* boundary_face = m->findModelEntity(nsd-1,m->getModelTag(me));
463 apf::Copies remotes;
464 if(m->isShared(bent))
465 {
466 m->getRemotes(bent,remotes);
467 for(apf::Copies::iterator it=remotes.begin(); it!=remotes.end();++it)
468 {
469 PCU_COMM_PACK(PCUObj, it->first, it->second);
470 PCU_COMM_PACK(PCUObj, it->first, orientation);
471 PCU_COMM_PACK(PCUObj, it->first, tempflux);
472 }
473 } //end if
474 ent_count++;
475 } //end loop over faces
476 } //end loop over regions
477 m->end(iter);
478 if(comm_rank==0)
479 std::cerr<<"Sending flux"<<std::endl;
480 PCU_Comm_Send(PCUObj);
481 flux = (double*) calloc(numbqpt*nsd*2,sizeof(double));
482 while(PCU_Comm_Receive(PCUObj))
483 {
484 PCU_COMM_UNPACK(PCUObj, bent);
485 PCU_COMM_UNPACK(PCUObj, orientation);
486 PCU_COMM_UNPACK(PCUObj, tempflux);
487 m->getDoubleTag(bent,diffFlux,flux);
488 for (int i=0;i<numbqpt*nsd;i++){
489 flux[orientation*numbqpt*nsd+i] = flux[orientation*numbqpt*nsd+i]+tempflux[i];
490 }
491 m->setDoubleTag(bent,diffFlux,flux);
492 }
493 PCU_Barrier(PCUObj);
494 free(flux);
495 if(comm_rank==0)
496 std::cerr<<"End computeDiffusiveFlux()"<<std::endl;
497}
498
499void MeshAdaptPUMIDrvr::getBoundaryFlux(apf::Mesh* m, apf::MeshEntity* ent, double * endflux)
507{
508 int nshl;
509 apf::NewArray <double> shpval;
510 apf::NewArray <double> shpval_temp;
511 double* flux;
512
513 apf::FieldShape* err_shape = apf::getHierarchic(2);
514 apf::EntityShape* elem_shape;
515
516 //loop over element faces
517 apf::Adjacent boundaries;
518 apf::MeshEntity* bent;
519 apf::MeshElement* b_elem;
520 apf::Vector3 bqpt,bqptl,bqptshp;
521
522 apf::Vector3 normal;
523 apf::Vector3 centerdir;
524
525 //Shape functions of the region and not the boundaries
526 nshl=apf::countElementNodes(err_shape,m->getType(ent));
527 shpval_temp.allocate(nshl);
528 int hier_off;
529 if(nsd==2)
530 hier_off=3;
531 else if(nsd==3)
532 hier_off=4;
533 nshl= nshl-hier_off;
534 shpval.allocate(nshl);
535 elem_shape = err_shape->getEntityShape(m->getType(ent));
536
537 m->getAdjacent(ent,nsd-1,boundaries);
538 for(int adjcount =0;adjcount<boundaries.getSize();adjcount++){
539
540 apf::Vector3 bflux(0.0,0.0,0.0);
541 bent = boundaries[adjcount];
542
543 b_elem = apf::createMeshElement(m,bent);
544 normal=getFaceNormal(m,bent);
545 centerdir=apf::getLinearCentroid(m,ent)-apf::getLinearCentroid(m,bent);
546 int orientation = 0;
547
548 if(isInSimplex(m,ent,apf::project(normal,centerdir).normalize()*centerdir.getLength()+apf::getLinearCentroid(m,bent),nsd)){
549 normal = normal*-1.0; //normal needs to face the other direction
550 orientation=1;
551 }
552 apf::ModelEntity* me=m->toModel(bent);
553 int tag = m->getModelTag(me);
554 apf::ModelEntity* boundary_face = m->findModelEntity(nsd-1,tag);
555
556 double flux_weight[2];
557 if(me==boundary_face){
558 if(orientation==0){flux_weight[0]=1; flux_weight[1]=0;}
559 else{flux_weight[0]=0; flux_weight[1]=-1;}
560 }
561 else{
562 if(orientation==0){flux_weight[0]=(1-a_kl); flux_weight[1]=a_kl;}
563 else{ flux_weight[0]=-a_kl; flux_weight[1]=-1*(1-a_kl);}
564 }
565
566 int numbqpt = apf::countIntPoints(b_elem,int_order);
567 flux = (double*) calloc(numbqpt*nsd*2,sizeof(double));
568 m->getDoubleTag(bent,diffFlux,flux);
569 for(int l=0; l<numbqpt;l++){
570 apf::getIntPoint(b_elem,int_order,l,bqpt);
571 bqptshp=apf::boundaryToElementXi(m,bent,ent,bqpt);
572 elem_shape->getValues(NULL,NULL,bqptshp,shpval_temp);
573 for(int j=0;j<nshl;j++){shpval[j] = shpval_temp[hier_off+j];}
574 for(int i=0;i<nsd;i++){
575 for(int s=0;s<nshl;s++){
576 endflux[i*nshl+s] = endflux[i*nshl+s]+(flux_weight[0]*flux[l*nsd+i]+flux_weight[1]*flux[numbqpt*nsd+l*nsd+i])*shpval[s];
577 }
578 }
579 }//end of boundary integration loop
580 free(flux);
581 } //end for adjacent faces
582}
583
584apf::Field* MeshAdaptPUMIDrvr::getViscosityField(apf::Field* voff)
591{
592 apf::Field* visc = apf::createLagrangeField(m,"viscosity",apf::SCALAR,1);
593 apf::MeshEntity* ent;
594 apf::MeshIterator* iter = m->begin(0);
595 double vof_val, visc_val;
596 while(ent = m->iterate(iter)){ //loop through all vertices
597 vof_val=apf::getScalar(voff,ent,0);
598 visc_val = getMPvalue(vof_val,nu_0, nu_1);
599 apf::setScalar(visc, ent, 0,visc_val);
600 }
601 m->end(iter);
602 return visc;
603}
604
605
606void setErrorField(apf::Field* estimate,Vec coef,apf::MeshEntity* ent,int nsd,int nshl)
607//Function used to store the computed coefficients from the local error problem onto a field
608{
609 apf::Mesh* m = apf::getMesh(estimate);
610 double coef_ez[nshl*nsd];
611 int ez_idx[nshl*nsd];
612 for(int ez=0;ez<nshl*nsd;ez++){ez_idx[ez]=ez;}
613 VecGetValues(coef,nshl*nsd,ez_idx,coef_ez);
614
615 //Copy coefficients onto field
616 apf::Adjacent adjvert;
617 m->getAdjacent(ent,0,adjvert);
618 for(int idx=0;idx<adjvert.getSize();idx++){
619 double coef_sub[3]={0,0,0};
620 apf::setVector(estimate,adjvert[idx],0,&coef_sub[0]);
621 }
622
623 apf::Adjacent adjedg;
624 m->getAdjacent(ent,1,adjedg);
625 for(int idx=0;idx<nshl;idx++){
626 //coef_ez holds nshl*nsd values. The third component only exists in 3D:
627 //in 2D, coef_ez[nshl*2+idx] indexes past the end of the array and reads
628 //whatever the stack frame below happened to leave there. That is a real
629 //out-of-bounds read on every 2D element, and it is why
630 //test2DgmshLoadAndAdapt returned 8.567e-18 on some runs and garbage
631 //(1.2e+122, 2.4e+33, 2.1e+147) on others, host- and build-dependent,
632 //while the 3D case was always stable. A 2D velocity has no third
633 //component, so it contributes zero.
634 double coef_sub[3] ={coef_ez[idx],
635 coef_ez[nshl+idx],
636 (nsd > 2) ? coef_ez[nshl*2+idx] : 0.0};
637 apf::setVector(estimate,adjedg[idx],0,&coef_sub[0]);
638 }
639}
640
648{
649 if(comm_rank==0) std::cout<<"Start removing BC tags/data"<<std::endl;
650 apf::MeshEntity* ent;
651 apf::MeshIterator* fIter = m->begin(nsd-1);
652 while(ent=m->iterate(fIter))
653 {
654 if(has_gBC && m->hasTag(ent,BCtag)){
655 m->removeTag(ent,BCtag);
656 for(int i=0;i<4;i++)
657 {
658 if(i>0 && m->hasTag(ent,fluxtag[i]))
659 m->removeTag(ent,fluxtag[i]);
660 }
661 }
662 if(m->hasTag(ent,diffFlux))
663 m->removeTag(ent,diffFlux);
664
665 }
666 m->end(fIter);
667 if(has_gBC){
668 m->destroyTag(BCtag);
669 for(int i=0;i<4;i++)
670 {
671 if(i>0)
672 m->destroyTag(fluxtag[i]);
673 }
674 }
675 m->destroyTag(diffFlux);
676 if(comm_rank==0) std::cerr<<"Destroyed BC and flux tags"<<std::endl;
677}
678
680
688{
689 getProps(rho,nu);
692 nsd = m->getDimension();
693
694 //***** Get Solution Fields First *****//
695 apf::Field* voff = m->findField("vof");
696 assert(voff);
697 apf::Field* velf = m->findField("velocity");
698 assert(velf);
699 apf::Field* pref = m->findField("p");
700 assert(pref);
701 //***** *****//
702
703 //***** Compute the viscosity field *****//
704 apf::Field* visc = getViscosityField(voff);
705
706 //***** Compute diffusive flux *****//
707 computeDiffusiveFlux(m,voff,visc,pref,velf);
708
709 //Initialize the Error Fields
710 freeField(err_reg);
711 freeField(errRho_reg);
712 freeField(errRel_reg);
713 //err_reg = apf::createField(m,"ErrorRegion",apf::VECTOR,apf::getConstant(nsd));
714 err_reg = apf::createField(m,"ErrorRegion",apf::SCALAR,apf::getVoronoiShape(nsd,1));
715 errRho_reg = apf::createField(m,"ErrorDensity",apf::SCALAR,apf::getVoronoiShape(nsd,1));
716 errRel_reg = apf::createField(m,"RelativeError",apf::SCALAR,apf::getVoronoiShape(nsd,1));
717
718 //Start computing element quantities
719 int numqpt; //number of quadrature points
720 int nshl; //number of local shape functions
721 int elem_type; //what type of topology
722 double weight; //value container for the weight at each qpt
723 double Jdet;
724 apf::FieldShape* err_shape = apf::getHierarchic(approx_order);
725 apf::Field * estimate = apf::createField(m, "err_est", apf::VECTOR, err_shape);
726 apf::EntityShape* elem_shape;
727 apf::Vector3 qpt; //container for quadrature points
728 apf::MeshElement* element;
729 apf::Element* visc_elem, *pres_elem,*velo_elem,*vof_elem;
730 apf::Element* est_elem;
731 apf::Matrix3x3 J; //actual Jacobian matrix
732 apf::Matrix3x3 invJ; //inverse of Jacobian
733 apf::NewArray <double> shpval; //array to store shape function values at quadrature points
734 apf::NewArray <double> shpval_temp; //array to store shape function values at quadrature points temporarily
735 apf::NewArray <apf::Vector3> shgval; //array to store shape function values at quadrature points
736
737 apf::DynamicMatrix invJ_copy;
738 apf::NewArray <apf::DynamicVector> shdrv;
739 apf::NewArray <apf::DynamicVector> shgval_copy;
740
741 apf::MeshIterator* iter = m->begin(nsd); //loop over elements
742 apf::MeshEntity* ent;
743
744
745 double err_est = 0;
746 double err_est_total=0;
747 long nLocalSolveFailures=0; //element-local solves that failed outright
748 double u_norm_total=0;
749 errRho_max = 0;
750 while(ent = m->iterate(iter)){ //loop through all elements
751
752 elem_type = m->getType(ent);
753 if(!(elem_type != 4 || elem_type != 2)){ //2|TRI, 4|TET
754 std::cout<<"Not a Tri or Tet present"<<std::endl;
755 exit(0);
756 }
757 element = apf::createMeshElement(m,ent);
758 pres_elem = apf::createElement(pref,element);
759 velo_elem = apf::createElement(velf,element);
760 visc_elem = apf::createElement(visc,element); //at vof currently
761 vof_elem = apf::createElement(voff,element);
762
763 numqpt=apf::countIntPoints(element,int_order); //generally p*p maximum for shape functions
764 nshl=apf::countElementNodes(err_shape,elem_type);
765 shgval.allocate(nshl);
766 shpval_temp.allocate(nshl);
767
768 int hier_off;//there is an offset that needs to be made to isolate the hierarchic edge modes
769 if(nsd ==2)
770 hier_off=3;
771 else if(nsd==3)
772 hier_off= 4;
773 nshl = nshl - hier_off;
774
775 shpval.allocate(nshl); shgval_copy.allocate(nshl); shdrv.allocate(nshl);
776
777 //LHS Matrix Initialization
778 int ndofs = nshl*nsd;
779 Mat K; //matrix size depends on nshl, which may vary from element to element
780 MatCreate(PETSC_COMM_SELF,&K);
781 MatSetSizes(K,ndofs,ndofs,ndofs,ndofs);
782 MatSetFromOptions(K);
783 MatSetUp(K); //is this inefficient? check later
784
785 //RHS Vector Initialization
786 Vec F;
787 VecCreate(PETSC_COMM_SELF,&F);
788 VecSetSizes(F,ndofs,ndofs);
789 VecSetUp(F);
790
791 //loop through all qpts
792 for(int k=0;k<numqpt;k++){
793 apf::getIntPoint(element,int_order,k,qpt); //get a quadrature point and store in qpt
794 apf::getJacobian(element,qpt,J); //evaluate the Jacobian at the quadrature point
795 J = apf::transpose(J); //Is PUMI still defined in this way?
796 if(nsd==2)
797 J[2][2] = 1.0; //this is necessary to avoid singular matrix
798 invJ = invert(J);
799 Jdet=fabs(apf::getJacobianDeterminant(J,nsd));
800 weight = apf::getIntWeight(element,int_order,k);
801 invJ_copy = apf::fromMatrix(invJ);
802
803 //first get the shape function values for error shape functions
804 elem_shape = err_shape->getEntityShape(elem_type);
805 elem_shape->getValues(NULL,NULL,qpt,shpval_temp);
806 elem_shape->getLocalGradients(NULL,NULL,qpt,shgval);
807
808 for(int i =0;i<nshl;i++){ //get the true derivative and copy only the edge modes for use
809 shgval_copy[i] = apf::fromVector(shgval[i+hier_off]);
810 shpval[i] = shpval_temp[i+hier_off];
811 apf::multiply(shgval_copy[i],invJ_copy,shdrv[i]);
812 }
813
814 //obtain needed values
815
816 apf::Vector3 vel_vect;
817 apf::Matrix3x3 grad_vel;
818 apf::getVector(velo_elem,qpt,vel_vect);
819 apf::getVectorGrad(velo_elem,qpt,grad_vel);
820 grad_vel = apf::transpose(grad_vel);
821 apf::Vector3 grad_vof;
822 apf::getGrad(vof_elem,qpt,grad_vof);
823
824 double density = getMPvalue(apf::getScalar(vof_elem,qpt),rho_0,rho_1);
825 double pressure = apf::getScalar(pres_elem,qpt);
826 double visc_val = apf::getScalar(visc_elem,qpt);
827 apf::Vector3 grad_rho = grad_vof*(rho_1-rho_0);
828
829 //Left-Hand Side
830 getLHS(K,shdrv,nsd,weight,visc_val,nshl);
831
832 //Get RHS
833 getRHS(F,shpval,shdrv,vel_vect,grad_vel,nsd,weight,nshl,visc_val,density,grad_rho,pressure,g);
834
835 } // end quadrature loop
836
837 //to complete integration, scale by the determinant of the Jacobian
838
839 MatAssemblyBegin(K,MAT_FINAL_ASSEMBLY);
840 MatAssemblyEnd(K,MAT_FINAL_ASSEMBLY);
841 MatScale(K,Jdet); //must be done after assembly
842 VecAssemblyBegin(F);
843 VecAssemblyEnd(F);
844 VecScale(F,Jdet); //must be done after assembly
845 double* bflux;
846 int F_idx[ndofs];
847 bflux = (double*) calloc(ndofs,sizeof(double));
848
849 getBoundaryFlux(m, ent,bflux);
850 for(int s=0;s<ndofs;s++){
851 F_idx[s]=s;
852 }
853 VecSetValues(F,ndofs,F_idx,bflux,ADD_VALUES);
854 VecAssemblyBegin(F); VecAssemblyEnd(F);
855 free(bflux);
856 Vec coef;
857 VecCreate(PETSC_COMM_SELF,&coef);
858 VecSetSizes(coef,ndofs,ndofs);
859 VecSetUp(coef);
860 // VecSetUp does not define the vector's values. If the local solve below
861 // fails, KSPSolve leaves coef untouched and setErrorField() then reads
862 // uninitialized memory straight into the error field -- observed as
863 // get_local_error() returning 2.98e+21 on one run and 2.24e+98 on the next
864 // (assert errorTotal<1e-14 in test2DgmshLoadAndAdapt). Zero it so a failed
865 // solve contributes nothing rather than noise.
866 VecZeroEntries(coef);
867
868 KSP ksp; //initialize solver context
869 KSPCreate(PETSC_COMM_SELF,&ksp);
870 KSPSetOperators(ksp,K,K);
871 KSPSetType(ksp,KSPPREONLY);
872 PC pc;
873 KSPGetPC(ksp,&pc);
874 PCSetType(pc,PCLU);
875 KSPSetFromOptions(ksp);
876
877 KSPSolve(ksp,F,coef);
878
879 // The local problem is solved with PREONLY+LU, so a singular element matrix
880 // fails outright rather than converging poorly. That was silent: the return
881 // code was discarded and no reason was queried, so a failed factorization
882 // was indistinguishable from a good solve and propagated into the error
883 // estimate. Count the failures and report once after the loop -- an error
884 // estimate computed from a mesh with singular local problems is not
885 // trustworthy, and the user needs to know rather than receive a number.
886 KSPConvergedReason localReason;
887 KSPGetConvergedReason(ksp,&localReason);
888 if(localReason < 0)
889 nLocalSolveFailures++;
890
891 KSPDestroy(&ksp); //destroy ksp
892
893 setErrorField(estimate,coef,ent,nsd,nshl);
894
895 //compute the local error
896 double Acomp=0;
897 double Bcomp=0;
898 double visc_avg=0;
899 double u_norm = 0;
900 apf::Matrix3x3 phi_ij;
901 apf::Matrix3x3 vel_ij;
902 apf::Vector3 vel_vect;
903 apf::Vector3 grad_vof;
904
905 est_elem= apf::createElement(estimate,element);
906 for(int k=0; k<numqpt;k++){
907 apf::getIntPoint(element,int_order,k,qpt); //get a quadrature point and store in qpt
908 apf::getJacobian(element,qpt,J); //evaluate the Jacobian at the quadrature point
909
910 invJ = invert(J);
911 invJ = apf::transpose(invJ);
912 Jdet=fabs(apf::getJacobianDeterminant(J,nsd));
913 weight = apf::getIntWeight(element,int_order,k);
914 invJ_copy = apf::fromMatrix(invJ);
915
916 //first get the shape function values for error shape functions
917 elem_shape = err_shape->getEntityShape(elem_type);
918 elem_shape->getValues(NULL,NULL,qpt,shpval_temp);
919 elem_shape->getLocalGradients(NULL,NULL,qpt,shgval);
920
921 for(int i =0;i<nshl;i++){ //get the true derivative and copy only the edge modes for use
922 shgval_copy[i] = apf::fromVector(shgval[i+hier_off]);
923 shpval[i] = shpval_temp[i+hier_off];
924 apf::multiply(shgval_copy[i],invJ_copy,shdrv[i]);
925 }
926 double visc_val = apf::getScalar(visc_elem,qpt);
927 double pres_val = apf::getScalar(pres_elem,qpt);
928 double density = getMPvalue(apf::getScalar(vof_elem,qpt),rho_0,rho_1);
929 apf::getVectorGrad(est_elem,qpt,phi_ij);
930 apf::getGrad(vof_elem,qpt,grad_vof);
931 apf::getVector(velo_elem,qpt,vel_vect);
932 apf::getVectorGrad(velo_elem,qpt,vel_ij);
933 vel_ij = apf::transpose(vel_ij);
934 phi_ij = apf::transpose(phi_ij);
935
936 Acomp = Acomp + visc_val*getDotProduct(phi_ij,phi_ij+apf::transpose(phi_ij))*weight;
937 Bcomp = Bcomp + apf::getDiv(velo_elem,qpt)*apf::getDiv(velo_elem,qpt)*weight;
938 visc_avg = visc_avg + visc_val*weight;
939 u_norm = u_norm + visc_val*getDotProduct(vel_ij,vel_ij+apf::transpose(vel_ij))*weight;
940
941 } //end compute local error
942 visc_avg = visc_avg*Jdet/apf::measure(element);
943 Acomp = Acomp*Jdet/visc_avg; //nondimensionalize with average viscosity, Jacobians can cancel out, but this is done for clarity
944 Bcomp = Bcomp*Jdet;
945 u_norm = u_norm/visc_avg*Jdet;
946 err_est = sqrt(Acomp);
947
948 apf::Vector3 err_in(err_est,Acomp,Bcomp);
949 //apf::setVector(err_reg,ent,0,err_in);
950 apf::setScalar(err_reg,ent,0,err_est);
951 double errRho = err_est/sqrt(apf::measure(element));
952 apf::setScalar(errRho_reg,ent,0,errRho);
953 if(errRho>errRho_max)
954 errRho_max = errRho;
955
956 double err_rel = err_est/sqrt(u_norm);
957 apf::setScalar(errRel_reg,ent,0,err_rel);
958
959 err_est_total = err_est_total+(Acomp); //for tracking the upper bound
960 u_norm_total = u_norm_total + u_norm;
961
962 MatDestroy(&K); //destroy the matrix
963 VecDestroy(&F); //destroy vector
964 VecDestroy(&coef); //destroy vector
965
966 apf::destroyElement(visc_elem);apf::destroyElement(pres_elem);apf::destroyElement(velo_elem);apf::destroyElement(est_elem);apf::destroyElement(vof_elem);
967 } //end element loop
968
969 PCU_Add_Doubles(PCUObj, &err_est_total,1);
970 PCU_Add_Doubles(PCUObj, &u_norm_total,1);
971
972 if(nLocalSolveFailures > 0 && comm_rank==0)
973 std::cerr<<"WARNING: "<<nLocalSolveFailures<<" element-local error problems "
974 <<"failed to solve (singular matrix under PREONLY+LU); their "
975 <<"contribution to the error estimate is zero and the estimate "
976 <<"below should not be trusted."<<std::endl;
977
978 total_error = sqrt(err_est_total);
979 u_norm_total = sqrt(u_norm_total);
980 rel_err_total = total_error/u_norm_total;
981
982 if(comm_rank==0){
983 std::cerr<<std::setprecision(10)<<std::endl;
984 std::cerr<<"Error estimate "<<total_error<<std::endl;
985 std::cerr<<"Error density maximum "<<errRho_max<<std::endl;
986 std::cerr<<"U_norm_total "<<u_norm_total<<std::endl;
987 }
988
989 if(logging_config=="errorOnly"){ //feature to just look at the error fields without adapting the mesh
990 if(comm_rank==0)
991 std::cout<<"outputting error field\n";
992 char namebuffer[20];
993 sprintf(namebuffer,"err_reg_%i",nEstimate);
994 apf::writeVtkFiles(namebuffer, m);
995 //target_error = total_error*2; //this is a hack to prevent adapting
996 THRESHOLD = total_error*2;
997 nEstimate++;
998 removeBCData();
999 }
1000
1001 m->end(iter);
1002 apf::destroyField(visc);
1003 apf::destroyField(estimate);
1004
1005 if(comm_rank==0)
1006 std::cerr<<"It cleared the ERM function.\n";
1007}
1008
double a_kl
void getProps(double *rho, double *nu)
int approx_order
void getLHS(Mat &K, apf::NewArray< apf::DynamicVector > &shdrv, int nsd, double weight, double visc_val, int nshl)
double nu_0
bool isInTet(apf::Mesh *mesh, apf::MeshEntity *ent, apf::Vector3 pt)
void setErrorField(apf::Field *estimate, Vec coef, apf::MeshEntity *ent, int nsd, int nshl)
double rho_1
int int_order
double getDotProduct(apf::Vector3 a, apf::Vector3 b)
apf::Vector3 getFaceNormal(apf::Mesh *mesh, apf::MeshEntity *face)
void getRHS(Vec &F, apf::NewArray< double > &shpval, apf::NewArray< apf::DynamicVector > &shdrv, apf::Vector3 vel_vect, apf::Matrix3x3 grad_vel, int nsd, double weight, int nshl, double visc_val, double density, apf::Vector3 grad_density, double pressure, double g[3])
double nu_1
double rho_0
bool isInSimplex(apf::Mesh *mesh, apf::MeshEntity *ent, apf::Vector3 pt, int dim)
Double s
Definition Headers.h:84
void removeBCData()
Function used to remove the BC tags that were created during the computeDiffusiveFlux() function.
void computeDiffusiveFlux(apf::Mesh *m, apf::Field *voff, apf::Field *visc, apf::Field *pref, apf::Field *velf)
Function used to compute the diffusive flux at interelement boundaries and stores the values as tags ...
int localNumber(apf::MeshEntity *e)
double getMPvalue(double field_val, double val_0, double val_1)
Function primarily used to get the VOF-weighted average of physical properties at a given point.
apf::Field * getViscosityField(apf::Field *voff)
Function used to derive a viscosity field from a VOF field.
std::string logging_config
void getBoundaryFlux(apf::Mesh *m, apf::MeshEntity *ent, double *endflux)
This function reads in the stored tags and computes the boundary flux according to the RHS formulatio...
apf::MeshTag * fluxtag[4]
void get_local_error(double &total_error)
This function aims to compute error at each element via an Element Residual Method.
apf::MeshTag * BCtag
#define c(i)
Definition jf.h:21