void RobustPhy2Ref(real physnode[20][3], real xphy[3], real xref[3]) { #define _ITERNEWTON 8 #define _NTHETA 5 real dtau[3][3], codtau[3][3]; real dxref[3], dxphy[3],xphy0[3]; int ifa =- 1; // construct a point xphy0 for which we know the inverse map xref[0] = 0.5; xref[1] = 0.5; xref[2] = 0.5; Ref2Phy(physnode, xref, 0,ifa, xphy0,0,0,0,0); // homotopy path // theta=0 -> xphy0 // theta=1 -> xphy real dtheta=1./_NTHETA; for(int itheta=0;itheta<=_NTHETA;itheta++){ //printf("itheta=%d\n",itheta); real theta=itheta*dtheta; // intermediate point to find real xphy1[3]; for(int ii=0;ii<3;ii++){ xphy1[ii]=theta*xphy[ii]+(1-theta)*xphy0[ii]; } for(int iter = 0; iter < _ITERNEWTON; ++iter) { Ref2Phy(physnode, xref, 0,ifa, dxphy, dtau, codtau, 0,0); dxphy[0] -= xphy1[0]; dxphy[1] -= xphy1[1]; dxphy[2] -= xphy1[2]; real det = dot_product(dtau[0], codtau[0]); //assert(det > 0); for(int ii = 0; ii < 3; ii ++ ) { dxref[ii] = 0; for(int jj = 0; jj < 3; jj ++ ) { dxref[ii] += codtau[jj][ii] * dxphy[jj]; } xref[ii] -= dxref[ii] / det; } //printf("iter= %d dxref=%f %f %f xref=%f %f %f\n",iter,dxref[0],dxref[1],dxref[2],xref[0],xref[1],xref[2]); } } }
void GeomRef2Phy(Geom* g) { Ref2Phy(g->physnode, g->xref, g->dphiref, g->ifa, g->xphy, g->dtau, g->codtau, g->dphi, g->vnds); g->det = g->codtau[0][0] * g->dtau[0][0] + g->codtau[0][1] * g->dtau[0][1] + g->codtau[0][2] * g->dtau[0][2]; }
// TODO: do not store all diagnotics for all time, but instead just // append to the output file. void Energies(field *f, real *w, real k_energy, real e_energy, real t_energy) { k_energy = 0; e_energy = 0; t_energy = 0; for (int ie = 0; ie < f->macromesh.nbelems; ie++){ // get the physical nodes of element ie real physnode[20][3]; for(int inoloc = 0; inoloc < 20; inoloc++){ int ino = f->macromesh.elem2node[20 * ie + inoloc]; physnode[inoloc][0] = f->macromesh.node[3 * ino + 0]; physnode[inoloc][1] = f->macromesh.node[3 * ino + 1]; physnode[inoloc][2] = f->macromesh.node[3 * ino + 2]; } // loop on the glops (for numerical integration) for(int ipg = 0; ipg < NPG(f->interp_param + 1); ipg++){ real xpgref[3], xphy[3], wpg; real dtau[3][3], codtau[3][3];//,xpg[3]; // get the coordinates of the Gauss point ref_pg_vol(f->interp_param + 1, ipg, xpgref, &wpg, NULL); Ref2Phy(physnode, // phys. nodes xpgref, // xref NULL,-1, // dpsiref,ifa xphy,dtau, // xphy,dtau codtau,NULL,NULL); // codtau,dpsi,vnds real det = dtau[0][0] * codtau[0][0] + dtau[0][1] * codtau[0][1] + dtau[0][2] * codtau[0][2]; real wn[f->model.m]; for(int iv = 0; iv < _INDEX_MAX + 1; iv++){ int imem = f->varindex(f->interp_param, ie, ipg, iv); wn[iv] = w[imem]; } // get the exact value k_energy += local_kinetic_energy(f, xphy, wn) * wpg * det; e_energy += wn[_MV+1] * wn[_MV+1] * wpg * det; } } t_energy = 0.5 * (e_energy + k_energy); f->Diagnostics[f->iter_time] = 0.5 * k_energy; f->Diagnostics[f->iter_time + f->itermax] = 0.5 * e_energy; f->Diagnostics[f->iter_time + 2 * f->itermax] = t_energy; }
// compute the normalized L2 distance with the imposed data double L2error(Field* f){ //int param[8]={f->model.m,_DEGX,_DEGY,_DEGZ,_RAFX,_RAFY,_RAFZ,0}; double error=0; double moy=0; // mean value //#pragma omp parallel for for (int ie=0;ie<f->macromesh.nbelems;ie++){ // get the physical nodes of element ie double physnode[20][3]; for(int inoloc=0;inoloc<20;inoloc++){ int ino=f->macromesh.elem2node[20*ie+inoloc]; physnode[inoloc][0]=f->macromesh.node[3*ino+0]; physnode[inoloc][1]=f->macromesh.node[3*ino+1]; physnode[inoloc][2]=f->macromesh.node[3*ino+2]; } // loop on the glops (for numerical integration) for(int ipg=0;ipg<NPG(f->interp_param+1);ipg++){ double xpgref[3],xphy[3],wpg; double dtau[3][3],codtau[3][3];//,xpg[3]; // get the coordinates of the Gauss point ref_pg_vol(f->interp_param+1,ipg,xpgref,&wpg,NULL); Ref2Phy(physnode, // phys. nodes xpgref, // xref NULL,-1, // dpsiref,ifa xphy,dtau, // xphy,dtau codtau,NULL,NULL); // codtau,dpsi,vnds double det = dtau[0][0] * codtau[0][0] + dtau[0][1] * codtau[0][1] + dtau[0][2] * codtau[0][2]; double w[f->model.m],wex[f->model.m]; for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(f->interp_param,ie,ipg,iv); w[iv]=f->wn[imem]; } // get the exact value f->model.ImposedData(xphy,f->tnow,wex); for(int iv=0;iv<f->model.m;iv++){ error+=pow(w[iv]-wex[iv],2)*wpg*det; moy+=pow(w[iv],2)*wpg*det; } } } return sqrt(error)/sqrt(moy); }
// apply division by the mass matrix void* DGMass(void* mc){ MacroCell* mcell = (MacroCell*) mc; Field* f= mcell->field; // loop on the elements for (int ie=mcell->first_cell;ie<mcell->last_cell_p1;ie++){ // get the physical nodes of element ie double physnode[20][3]; for(int inoloc = 0; inoloc < 20; inoloc++) { int ino=f->macromesh.elem2node[20*ie+inoloc]; physnode[inoloc][0]=f->macromesh.node[3*ino+0]; physnode[inoloc][1]=f->macromesh.node[3*ino+1]; physnode[inoloc][2]=f->macromesh.node[3*ino+2]; } for(int ipg=0;ipg<NPG(f->interp_param+1);ipg++){ double dtau[3][3],codtau[3][3],xpgref[3],wpg; ref_pg_vol(f->interp_param+1,ipg,xpgref,&wpg,NULL); Ref2Phy(physnode, // phys. nodes xpgref, // xref NULL,-1, // dpsiref,ifa NULL,dtau, // xphy,dtau codtau,NULL,NULL); // codtau,dpsi,vnds double det = dtau[0][0]*codtau[0][0] + dtau[0][1]*codtau[0][1] + dtau[0][2]*codtau[0][2]; for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(f->interp_param,ie,ipg,iv); f->dtwn[imem]/=(wpg*det); } } } return NULL; }
real L2_Kinetic_error(field* f){ real error = 0; for (int ie = 0; ie < f->macromesh.nbelems; ie++){ // get the physical nodes of element ie real physnode[20][3]; for(int inoloc = 0; inoloc < 20; inoloc++){ int ino = f->macromesh.elem2node[20 * ie + inoloc]; physnode[inoloc][0] = f->macromesh.node[3 * ino + 0]; physnode[inoloc][1] = f->macromesh.node[3 * ino + 1]; physnode[inoloc][2] = f->macromesh.node[3 * ino + 2]; } // loop on the glops (for numerical integration) for(int ipg = 0; ipg < NPG(f->interp_param + 1); ipg++){ real xpgref[3], xphy[3], wpg; real dtau[3][3], codtau[3][3];//,xpg[3]; // get the coordinates of the Gauss point ref_pg_vol(f->interp_param + 1, ipg, xpgref, &wpg, NULL); Ref2Phy(physnode, // phys. nodes xpgref, // xref NULL, -1, // dpsiref,ifa xphy, dtau, // xphy,dtau codtau, NULL, NULL); // codtau,dpsi,vnds real det = dtau[0][0] * codtau[0][0] + dtau[0][1] * codtau[0][1] + dtau[0][2] * codtau[0][2]; real w[f->model.m]; for(int iv = 0;iv < f->model.m; iv++){ int imem = f->varindex(f->interp_param, ie, ipg, iv); w[iv] = f->wn[imem]; } // get the exact value error += L2VelError(f, xphy, w) * wpg * det; } } return sqrt(error); }
void Phy2Ref(real physnode[20][3], real xphy[3], real xref[3]) { #define ITERNEWTON 10 real dtau[3][3], codtau[3][3]; real dxref[3], dxphy[3]; int ifa =- 1; xref[0] = 0.5; xref[1] = 0.5; xref[2] = 0.5; real *codtau0 = codtau[0]; real *codtau1 = codtau[1]; real *codtau2 = codtau[2]; for(int iter = 0; iter < ITERNEWTON; ++iter) { Ref2Phy(physnode, xref, 0,ifa, dxphy, dtau, codtau, 0,0); dxphy[0] -= xphy[0]; dxphy[1] -= xphy[1]; dxphy[2] -= xphy[2]; real overdet = 1.0 / dot_product(dtau[0], codtau[0]); //assert(overdet > 0); for(int ii = 0; ii < 3; ii ++ ) { dxref[ii] = 0; dxref[ii] += codtau0[ii] * dxphy[0] + codtau1[ii] * dxphy[1] + codtau2[ii] * dxphy[2]; xref[ii] -= dxref[ii] * overdet; } } /* real eps = 1e-2; // may be to constraining... */ /* assert(xref[0] < 1 + eps && xref[0] > -eps); */ /* assert(xref[1] < 1 + eps && xref[1] > -eps); */ /* assert(xref[2] < 1 + eps && xref[2] > -eps); */ }
// Detect if the mesh is 2D and then permut the nodes so that the z // direction coincides in the reference or physical frame void Detect2DMacroMesh(MacroMesh *m) { m->is2d = true; // Do not permut the node if the connectivity is already built if(m->elem2elem != NULL) printf("Cannot permute nodes before building connectivity\n"); assert(m->elem2elem == 0); for(int ie = 0; ie < m->nbelems; ie++) { // get the physical nodes of element ie real physnode[20][3]; for(int inoloc = 0; inoloc < 20; inoloc++) { int ino = m->elem2node[20 * ie + inoloc]; physnode[inoloc][0] = m->node[3 * ino + 0]; physnode[inoloc][1] = m->node[3 * ino + 1]; physnode[inoloc][2] = m->node[3 * ino + 2]; } // We decide that the mesh is 2D if the middles of the elements // have a constant z coordinate equal to 0.5 real zmil = 0; for(int inoloc = 0; inoloc < 20; inoloc++) { zmil += physnode[inoloc][2]; } zmil /= 20; //printf("zmil: %f\n", zmil); if(fabs(zmil-0.5) > 1e-6) { // The mesh is not 2d m->is2d = false; return; } } // TODO: if the mesh is not 2D, then assert constraints on nraf[2] // and deg[2]. printf("Detection of a 2D mesh\n"); for(int ie = 0; ie < m->nbelems; ie++) { // get the physical nodes of element ie real physnode[20][3]; for(int inoloc = 0; inoloc < 20; inoloc++) { int ino=m->elem2node[20 * ie + inoloc]; physnode[inoloc][0] = m->node[3 * ino + 0]; physnode[inoloc][1] = m->node[3 * ino + 1]; physnode[inoloc][2] = m->node[3 * ino + 2]; } // If the mesh is 2d permut the nodes in order that the z^ and z // axis are the same real face_centers[6][3] = { {0.5, 0.0, 0.5}, {1.0, 0.5, 0.5}, {0.5, 1.0, 0.5}, {0.0, 0.5, 0.5}, {0.5, 0.5, 1.0}, {0.5, 0.5, 0.0} }; // Rotation of the cube around the origin at most two rotations // are needed to put the cube in a correct position for(int irot = 0; irot < 2; irot++) { // compute the normal to face 4 real vnds[3], dtau[3][3], codtau[3][3]; Ref2Phy(physnode, face_centers[4], NULL, 4, // dphiref,ifa NULL, dtau, codtau, NULL, vnds); // codtau,dphi,vnds real d = norm(vnds); // If the normal is not up or down we have to permut the nodes if(fabs(vnds[2] / d) < 0.9) { printf("irot=%d rotating the element %d\n", irot, ie); int oldnum[20]; int newnum[20] = {1, 5, 6, 2, 4, 8, 7, 3, 11, 9, 10, 17, 18, 13, 19, 12, 16, 14, 20, 15}; for(int inoloc = 0; inoloc < 20; inoloc++) { newnum[inoloc]--; oldnum[inoloc] = m->elem2node[20 * ie + inoloc]; } // Rotate the node numbering for(int inoloc = 0; inoloc < 20; inoloc++) { m->elem2node[20 * ie + inoloc] = oldnum[newnum[inoloc]]; } // Get the rotated node coordinates for(int inoloc = 0; inoloc < 20; inoloc++) { int ino = m->elem2node[20 * ie + inoloc]; physnode[inoloc][0] = m->node[3 * ino + 0]; physnode[inoloc][1] = m->node[3 * ino + 1]; physnode[inoloc][2] = m->node[3 * ino + 2]; } } } } }
void CheckMacroMesh(MacroMesh *m, int *param) { Geom g; real face_centers[6][3]={ {0.5,0.0,0.5}, {1.0,0.5,0.5}, {0.5,1.0,0.5}, {0.0,0.5,0.5}, {0.5,0.5,1.0}, {0.5,0.5,0.0} }; //real *bounds = malloc(6 * sizeof(real)); //macromesh_bounds(m, bounds); /* real refnormal[6][3]={{0,-1,0},{1,0,0}, */ /* {0,1,0},{-1,0,0}, */ /* {0,0,1},{0,0,-1}}; */ assert(m->connec_ok); for(int ie = 0; ie < m->nbelems; ie++) { // Load geometry for macro element ie: for(int inoloc = 0; inoloc < 20; inoloc++) { int ino = m->elem2node[20 * ie + inoloc]; g.physnode[inoloc][0] = m->node[3 * ino + 0]; g.physnode[inoloc][1] = m->node[3 * ino + 1]; g.physnode[inoloc][2] = m->node[3 * ino + 2]; } // Test that the ref_ipg function is compatible with ref_pg_vol //int param[7]={_DEGX,_DEGY,_DEGZ,_RAFX,_RAFY,_RAFZ,0}; for(int ipg = 0; ipg < NPG(param); ipg++) { real xref1[3], xref_in[3]; real wpg; ref_pg_vol(param, ipg, xref1, &wpg, xref_in); memcpy(g.xref, xref1, sizeof(g.xref)); g.ifa = 0; GeomRef2Phy(&g); GeomPhy2Ref(&g); // if(param[4]==1 && param[5]==1 && param[6]==1) { //printf("ipg %d ipg2 %d xref %f %f %f\n",ipg, // ref_ipg(param,xref_in),xref_in[0],xref_in[1],xref_in[2]); // Ensure that the physical coordinates give the same point: assert(ipg == ref_ipg(param, xref_in)); //} } // middle of the element g.xref[0] = 0.5; g.xref[1] = 0.5; g.xref[2] = 0.5; GeomRef2Phy(&g); real xphym[3]; memcpy(xphym, g.xphy, sizeof(xphym)); for(int ifa = 0; ifa < 6; ifa++) { // Middle of the face memcpy(g.xref, face_centers[ifa], sizeof(g.xref)); g.ifa = ifa; GeomRef2Phy(&g); // Check volume orientation assert(g.det > 0); real vec[3] = {g.xphy[0] - xphym[0], g.xphy[1] - xphym[1], g.xphy[2] - xphym[2]}; // Check face orientation assert(0 < dot_product(g.vnds, vec)); // Check compatibility between face and volume numbering for(int ipgf = 0; ipgf < NPGF(param, ifa); ipgf++) { // Get the coordinates of the Gauss point real xpgref[3]; { real wpg; ref_pg_face(param, ifa, ipgf, xpgref, &wpg, NULL); } // Recover the volume gauss point from the face index int ipgv = param[6]; real xpgref2[3]; { real wpg2; ref_pg_vol(param, ipgv, xpgref2, &wpg2, NULL); } if(m->is2d) { // in 2D do not check upper and lower face if(ifa < 4) assert(Dist(xpgref, xpgref2) < 1e-11); } else if (m->is1d){ if (ifa==1 || ifa==3) { assert(Dist(xpgref,xpgref2)<1e-11); } } // in 3D check all faces else { // in 3D check all faces if(Dist(xpgref, xpgref2) >= 1e-11) { printf("ERROR: face and vol indices give different rev points:\n"); printf("ipgv: %d\n", ipgv); printf("ipgf: %d\n", ipgf); printf("ifa: %d\n", ifa); printf("xpgref:%f %f %f\n", xpgref[0], xpgref[1], xpgref[2]); printf("xpgref2:%f %f %f\n", xpgref2[0], xpgref2[1], xpgref2[2]); } assert(Dist(xpgref, xpgref2) < 1e-11); } } } } // Check that the faces are defined by the same mapping with // opposite normals for (int ie = 0; ie < m->nbelems; ie++) { // int param[8]={1,_DEGX,_DEGY,_DEGZ,_RAFX,_RAFY,_RAFZ,0}; // Get the geometry for the macro element ie real physnode[20][3]; for(int inoloc = 0; inoloc < 20; inoloc++) { int ino = m->elem2node[20 * ie + inoloc]; physnode[inoloc][0] = m->node[3 * ino + 0]; physnode[inoloc][1] = m->node[3 * ino + 1]; physnode[inoloc][2] = m->node[3 * ino + 2]; } // Loop on the 6 faces for(int ifa = 0; ifa < 6; ifa++) { // Loop on the glops (numerical integration) of the face ifa for(int ipgf = 0; ipgf < NPGF(param, ifa); ipgf++) { // Get the right elem or the boundary id int ieR = m->elem2elem[6 * ie + ifa]; // If the right element exists and is not // the left element (may arrive in periodic cases) if(ieR >= 0 && ieR != ie) { // Get the coordinates of the Gauss point from the // face-local point index and the point slightly inside the // macrocell. real xpgref[3], xpgref_in[3]; ref_pg_face(param, ifa, ipgf, xpgref, NULL, xpgref_in); //ref_pg_face(param, ifa, ipgf, xpgref, NULL, NULL); int ipg=param[6]; /* #ifdef _PERIOD */ /* assert(m->is1d); // TODO: generalize to 2d */ /* if (xpgref_in[0] > _PERIOD) xpgref_in[0] -= _PERIOD; */ /* if (xpgref_in[0] < 0) xpgref_in[0] += _PERIOD; */ /* #endif */ // Compute the position of the point and the face normal. real xpg[3], vnds[3]; { real dtau[3][3]; real codtau[3][3]; Ref2Phy(physnode, xpgref, NULL, ifa, // dpsiref,ifa xpg, dtau, codtau, NULL, vnds); // codtau,dpsi,vnds } // Compute the "slightly inside" position real xpg_in[3]; Ref2Phy(physnode, xpgref_in, NULL, ifa, // dpsiref,ifa xpg_in, NULL, NULL, NULL, NULL); // codtau,dpsi,vnds PeriodicCorrection(xpg_in,m->period); // Load the geometry of the right macrocell real physnodeR[20][3]; for(int inoloc = 0; inoloc < 20; inoloc++) { int ino = m->elem2node[20 * ieR + inoloc]; physnodeR[inoloc][0] = m->node[3 * ino + 0]; physnodeR[inoloc][1] = m->node[3 * ino + 1]; physnodeR[inoloc][2] = m->node[3 * ino + 2]; } // Find the corresponding point in the right elem real xpgrefR_in[3];//,xpgrefR[3]; Phy2Ref(physnodeR, xpg_in, xpgrefR_in); //Phy2Ref(physnodeR, xpg, xpgrefR); int ipgR = ref_ipg(param, xpgrefR_in); // search the id of the face in the right elem // special treatment if the mesh is periodic // and contains only one elem (then ie==ieR) int neighb_count=0; for(int ifaR=0;ifaR<6;ifaR++){ if (m->elem2elem[6*ieR+ifaR] == ie) { for(int ipgfR = 0; ipgfR < NPGF(param, ifaR); ipgfR++) { real xpgrefR[3]; ref_pg_face(param, ifaR, ipgfR, xpgrefR, NULL, NULL); if (param[6] == ipgR){ real xpgR[3]; real vndsR[3]; { ref_pg_vol(param, ipgR, xpgrefR, NULL, NULL); real dtauR[3][3], codtauR[3][3]; Ref2Phy(physnodeR, xpgrefR, NULL, ifaR, // dphiref, ifa xpgR, dtauR, codtauR, NULL, vndsR); // codtau, dphi, vnds } // Ensure that the normals are opposite // if xpg and xpgR are close /* printf("xpg:%f %f %f\n", xpg_in[0], xpg_in[1], xpg_in[2]); */ /* printf("vnds: %f %f %f vndsR: %f %f %f \n", */ /* vnds[0],vnds[1],vnds[2], */ /* vndsR[0],vndsR[1],vndsR[2]); */ /* printf("xpgR:%f %f %f\n", xpgR[0], xpgR[1], xpgR[2]); */ assert(fabs(vnds[0] + vndsR[0]) < 1e-8); assert(fabs(vnds[1] + vndsR[1]) < 1e-8); assert(fabs(vnds[2] + vndsR[2]) < 1e-8); neighb_count++; } } } } //printf("neighb=%d\n",neighb_count); assert(neighb_count == 1); } } } } //free(bounds); }
void PlotParticles(PIC* pic,MacroMesh *m) { // FIXME: the output filenames should be specified. // FIXME: the output files should be closed at some point. FILE * gmshfile; FILE * gnufile; gmshfile = fopen("partplot.msh", "w" ); gnufile = fopen("partplot.dat", "w" ); float x,y,z,vx,vy,vz; fprintf(gmshfile, "$MeshFormat\n2.2 0 %d\n", (int) sizeof(real)); fprintf(gmshfile, "$EndMeshFormat\n$Nodes\n%d\n", pic->nbparts); /* fic << "$MeshFormat"<<endl; */ /* fic << "2 0 8" << endl; */ /* fic << "$EndMeshFormat"<<endl; */ /* fic << "$Nodes" << endl; */ /* fic << NbPart <<endl; */ /* cout << "NbPartFinal " << NbPart << endl; */ for(int i=0;i<pic->nbparts;i++) { int ie=pic->old_cell_id[i]; // Get the physical nodes of element ie real physnode[20][3]; for(int inoloc = 0; inoloc < 20; inoloc++) { int ino = m->elem2node[20*ie+inoloc]; physnode[inoloc][0] = m->node[3 * ino + 0]; physnode[inoloc][1] = m->node[3 * ino + 1]; physnode[inoloc][2] = m->node[3 * ino + 2]; } real xphy[3]; Ref2Phy(physnode, // phys. nodes &(pic->xv[6*i]), // xref NULL, -1, // dpsiref, ifa xphy, NULL, // xphy, dtau NULL, NULL, NULL); // codtau, dpsi, vnds /* x=pic->xv[6*i+0]; */ /* y=pic->xv[6*i+1]; */ /* z=pic->xv[6*i+2]; */ x=xphy[0]; y=xphy[1]; z=xphy[2]; vx=pic->xv[6*i+3]; vy=pic->xv[6*i+4]; vz=pic->xv[6*i+5]; fprintf(gmshfile,"%d %f %f %f \n",i+1,x,y,z); /* fic << i+1 << " "<<x<<" "<<y<<" "<<0<<endl; */ fprintf(gnufile,"%f %f %f %f %f %f \n",x,y,z,vx,vy,vz); } fprintf(gmshfile, "$EndNodes\n"); //fic << "$EndNodes"<<endl; fclose(gmshfile); fclose(gnufile); }
// compute the Discontinuous Galerkin volume terms // fast version void* DGVolume(void* mc){ MacroCell* mcell = (MacroCell*) mc; Field* f= mcell->field; // loop on the elements for (int ie=mcell->first_cell;ie<mcell->last_cell_p1;ie++){ // get the physical nodes of element ie double physnode[20][3]; for(int inoloc=0;inoloc<20;inoloc++){ int ino=f->macromesh.elem2node[20*ie+inoloc]; physnode[inoloc][0]=f->macromesh.node[3*ino+0]; physnode[inoloc][1]=f->macromesh.node[3*ino+1]; physnode[inoloc][2]=f->macromesh.node[3*ino+2]; } const int m = f->model.m; const int deg[3]={f->interp_param[1], f->interp_param[2], f->interp_param[3]}; const int npg[3] = {deg[0]+1, deg[1]+1, deg[2]+1}; const int nraf[3]={f->interp_param[4], f->interp_param[5], f->interp_param[6]}; const unsigned int sc_npg=npg[0]*npg[1]*npg[2]; int f_interp_param[8]= {f->interp_param[0], f->interp_param[1], f->interp_param[2], f->interp_param[3], f->interp_param[4], f->interp_param[5], f->interp_param[6], f->interp_param[7]}; // loop on the subcells for(int icL0 = 0; icL0 < nraf[0]; icL0++){ for(int icL1 = 0; icL1 < nraf[1]; icL1++){ for(int icL2 = 0; icL2 < nraf[2]; icL2++){ int icL[3] = {icL0,icL1,icL2}; // get the L subcell id int ncL=icL[0]+nraf[0]*(icL[1]+nraf[1]*icL[2]); // first glop index in the subcell int offsetL=npg[0]*npg[1]*npg[2]*ncL; // compute all of the xref for the subcell double *xref0 = malloc(sc_npg * sizeof(double)); double *xref1 = malloc(sc_npg * sizeof(double)); double *xref2 = malloc(sc_npg * sizeof(double)); double *omega = malloc(sc_npg * sizeof(double)); int *imems = malloc(m * sc_npg * sizeof(int)); int pos=0; for(unsigned int p=0; p < sc_npg; ++p) { double xref[3]; double tomega; ref_pg_vol(f->interp_param+1,offsetL+p,xref,&tomega,NULL); xref0[p] = xref[0]; xref1[p] = xref[1]; xref2[p] = xref[2]; omega[p] = tomega; for(int im=0; im < m; ++im) { imems[pos++] = f->varindex(f_interp_param,ie,offsetL+p,im); } } // loop in the "cross" in the three directions for(int dim0 = 0; dim0 < 3; dim0++){ // point p at which we compute the flux for(int p0 = 0; p0 < npg[0]; p0++){ for(int p1 = 0; p1 < npg[1]; p1++){ for(int p2 = 0; p2 < npg[2]; p2++){ double wL[m],flux[m]; int p[3]={p0,p1,p2}; int ipgL=offsetL+p[0]+npg[0]*(p[1]+npg[1]*p[2]); for(int iv=0; iv < m; iv++){ ///int imemL=f->varindex(f_interp_param,ie,ipgL,iv); wL[iv] = f->wn[imems[m*(ipgL-offsetL)+iv]]; /// big bug !!!! //wL[iv] = f->wn[imemL]; } int q[3]={p[0],p[1],p[2]}; // loop on the direction dim0 on the "cross" for(int iq = 0; iq < npg[dim0]; iq++){ q[dim0]=(p[dim0]+iq)%npg[dim0]; double dphiref[3]={0,0,0}; // compute grad phi_q at glop p dphiref[dim0]=dlag(deg[dim0],q[dim0],p[dim0])*nraf[dim0]; double xrefL[3]={xref0[ipgL-offsetL], xref1[ipgL-offsetL], xref2[ipgL-offsetL]}; double wpgL=omega[ipgL-offsetL]; /* double xrefL[3], wpgL; */ /* ref_pg_vol(f->interp_param+1,ipgL,xrefL,&wpgL,NULL); */ // mapping from the ref glop to the physical glop double dtau[3][3],codtau[3][3],dphiL[3]; Ref2Phy(physnode, xrefL, dphiref, // dphiref -1, // ifa NULL, // xphy dtau, codtau, dphiL, // dphi NULL); // vnds f->model.NumFlux(wL,wL,dphiL,flux); int ipgR=offsetL+q[0]+npg[0]*(q[1]+npg[1]*q[2]); for(int iv=0; iv < m; iv++){ //int imemR=f->varindex(f_interp_param,ie,ipgR,iv); f->dtwn[imems[m*(ipgR-offsetL)+iv]]+=flux[iv]*wpgL; } } // iq } // p2 } // p1 } // p0 } // dim loop free(omega); free(xref0); free(xref1); free(xref2); free(imems); } // icl2 } //icl1 } // icl0 } return NULL; }
// compute the Discontinuous Galerkin inter-macrocells boundary terms void* DGMacroCellInterface(void* mc){ MacroCell* mcell = (MacroCell*) mc; Field* f= mcell->field; int iparam[8]; for(int ip=0;ip<8;ip++) iparam[ip]=f->interp_param[ip]; // init to zero the time derivative for (int ie=mcell->first_cell;ie<mcell->last_cell_p1;ie++){ for(int ipg=0;ipg<NPG(iparam+1);ipg++){ for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(iparam,ie,ipg,iv); f->dtwn[imem]=0; } } } //assert(sizew==f->macromesh.nbelems * f->model.m * NPG(iparam+1)); // assembly of the surface terms // loop on the elements for (int ie=mcell->first_cell;ie<mcell->last_cell_p1;ie++){ // get the physical nodes of element ie double physnode[20][3]; for(int inoloc=0;inoloc<20;inoloc++){ int ino=f->macromesh.elem2node[20*ie+inoloc]; physnode[inoloc][0]=f->macromesh.node[3*ino+0]; physnode[inoloc][1]=f->macromesh.node[3*ino+1]; physnode[inoloc][2]=f->macromesh.node[3*ino+2]; } // loop on the 6 faces // or four faces for 2d computations int nbfa=6; if (f->is2d) nbfa=4; for(int ifa=0;ifa<nbfa;ifa++){ // get the right elem or the boundary id int ieR=f->macromesh.elem2elem[6*ie+ifa]; double physnodeR[20][3]; if (ieR >= 0) { for(int inoloc=0;inoloc<20;inoloc++){ int ino=f->macromesh.elem2node[20*ieR+inoloc]; physnodeR[inoloc][0]=f->macromesh.node[3*ino+0]; physnodeR[inoloc][1]=f->macromesh.node[3*ino+1]; physnodeR[inoloc][2]=f->macromesh.node[3*ino+2]; } } // loop on the glops (numerical integration) // of the face ifa for(int ipgf=0;ipgf<NPGF(f->interp_param+1,ifa);ipgf++){ // for(int ipgf=0;ipgf<NPGF(iparam+1,ifa);ipgf++){ // FIXME? double xpgref[3],xpgref_in[3],wpg; //double xpgref2[3],wpg2; // get the coordinates of the Gauss point // and coordinates of a point slightly inside the // opposite element in xref_in ref_pg_face(iparam+1,ifa,ipgf,xpgref,&wpg,xpgref_in); // recover the volume gauss point from // the face index int ipg=iparam[7]; // get the left value of w at the gauss point double wL[f->model.m],wR[f->model.m]; for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(iparam,ie,ipg,iv); wL[iv]=f->wn[imem]; } // the basis functions is also the gauss point index int ib=ipg; // normal vector at gauss point ipg double dtau[3][3],codtau[3][3],xpg[3]; double vnds[3]; Ref2Phy(physnode, xpgref, NULL,ifa, // dpsiref,ifa xpg,dtau, codtau,NULL,vnds); // codtau,dpsi,vnds double flux[f->model.m]; if (ieR >=0) { // the right element exists // find the corresponding point in the right elem double xpg_in[3]; Ref2Phy(physnode, xpgref_in, NULL,ifa, // dpsiref,ifa xpg_in,dtau, codtau,NULL,vnds); // codtau,dpsi,vnds double xref[3]; Phy2Ref(physnodeR,xpg_in,xref); int ipgR=ref_ipg(iparam+1,xref); double xpgR[3],xrefR[3],wpgR; ref_pg_vol(iparam+1, ipgR, xrefR, &wpgR,NULL); Ref2Phy(physnodeR, xrefR, NULL,-1, // dphiref,ifa xpgR,NULL, NULL,NULL,NULL); // codtau,dphi,vnds assert(Dist(xpgR,xpg)<1e-10); for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(iparam,ieR,ipgR,iv); wR[iv]=f->wn[imem]; } // int_dL F(wL,wR,grad phi_ib ) f->model.NumFlux(wL,wR,vnds,flux); } else { //the right element does not exist f->model.BoundaryFlux(xpg,f->tnow,wL,vnds,flux); } for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(iparam,ie,ib,iv); f->dtwn[imem]-=flux[iv]*wpg; } } } } return NULL; }
// inter-subcell fluxes void* DGSubCellInterface(void* mc){ MacroCell* mcell = (MacroCell*) mc; Field* f= mcell->field; // loop on the elements for (int ie=mcell->first_cell;ie<mcell->last_cell_p1;ie++){ // get the physical nodes of element ie double physnode[20][3]; for(int inoloc=0;inoloc<20;inoloc++){ int ino=f->macromesh.elem2node[20*ie+inoloc]; physnode[inoloc][0]=f->macromesh.node[3*ino+0]; physnode[inoloc][1]=f->macromesh.node[3*ino+1]; physnode[inoloc][2]=f->macromesh.node[3*ino+2]; } const int nraf[3]={f->interp_param[4], f->interp_param[5], f->interp_param[6]}; const int deg[3]={f->interp_param[1], f->interp_param[2], f->interp_param[3]}; const int npg[3] = {deg[0]+1, deg[1]+1, deg[2]+1}; const int m = f->model.m; // loop on the subcells //#pragma omp parallel for collapse(3) for(int icL0 = 0; icL0 < nraf[0]; icL0++){ for(int icL1 = 0; icL1 < nraf[1]; icL1++){ for(int icL2 = 0; icL2 < nraf[2]; icL2++){ int icL[3]={icL0,icL1,icL2}; // get the left subcell id int ncL=icL[0]+nraf[0]*(icL[1]+nraf[1]*icL[2]); // first glop index in the subcell int offsetL=npg[0]*npg[1]*npg[2]*ncL; // sweeping subcell faces in the three directions for(int dim0 = 0; dim0 < 3; dim0++){ // compute the subface flux only // if we do not touch the subcell boundary // along the current direction dim0 if (icL[dim0] != nraf[dim0]-1) { int icR[3]={icL[0],icL[1],icL[2]}; // The right cell index corresponds to an increment in // the dim0 direction icR[dim0]++; int ncR=icR[0]+nraf[0]*(icR[1]+nraf[1]*icR[2]); int offsetR=npg[0]*npg[1]*npg[2]*ncR; // FIXME: write only write to L-values (and do both // faces) to parallelise better. const int altdim1[3]={1,0,0}; const int altdim2[3]={2,2,1}; // now loop on the left glops of the subface //int dim1=(dim0+1)%3, dim2=(dim0+2)%3; int dim1=altdim1[dim0], dim2=altdim2[dim0]; int iL[3]; iL[dim0] = deg[dim0]; for(iL[dim2] = 0; iL[dim2] < npg[dim2]; iL[dim2]++){ for(iL[dim1] = 0; iL[dim1] < npg[dim1]; iL[dim1]++){ // find the right and left glops volume indices int iR[3] = {iL[0],iL[1],iL[2]}; iR[dim0] = 0; int ipgL=offsetL+iL[0]+(deg[0]+1)*(iL[1]+(deg[1]+1)*iL[2]); int ipgR=offsetR+iR[0]+(deg[0]+1)*(iR[1]+(deg[1]+1)*iR[2]); //printf("ipgL=%d ipgR=%d\n",ipgL,ipgR); // Compute the normal vector for integrating on the // face double vnds[3]; { double xref[3], wpg3; ref_pg_vol(f->interp_param+1,ipgL,xref,&wpg3,NULL); // mapping from the ref glop to the physical glop double dtau[3][3],codtau[3][3]; Ref2Phy(physnode, xref, NULL, // dphiref -1, // ifa NULL, // xphy dtau, codtau, NULL, // dphi NULL); // vnds // we compute ourself the normal vector because we // have to take into account the subcell surface double h1h2=1./nraf[dim1]/nraf[dim2]; vnds[0] = codtau[0][dim0]*h1h2; vnds[1] = codtau[1][dim0]*h1h2; vnds[2] = codtau[2][dim0]*h1h2; } // numerical flux from the left and right state and // normal vector double wL[m],wR[m],flux[m]; for(int iv=0; iv < m; iv++){ int imemL=f->varindex(f->interp_param,ie,ipgL,iv); int imemR=f->varindex(f->interp_param,ie,ipgR,iv); wL[iv] = f->wn[imemL]; wR[iv] = f->wn[imemR]; } f->model.NumFlux(wL,wR,vnds,flux); // subcell ref surface glop weight double wpg = wglop(deg[dim1],iL[dim1]) * wglop(deg[dim2],iL[dim2]); /* printf("vnds %f %f %f flux %f wpg %f\n", */ /* vnds[0],vnds[1],vnds[2], */ /* flux[0],wpg); */ // finally distribute the flux on the two sides for(int iv=0; iv < m; iv++){ int imemL = f->varindex(f->interp_param, ie, ipgL, iv); int imemR = f->varindex(f->interp_param, ie, ipgR, iv); f->dtwn[imemL] -= flux[iv] * wpg; f->dtwn[imemR] += flux[iv] * wpg; } } // face yhat loop } // face xhat loop } // endif internal face } // dim loop } // subcell icl2 loop } // subcell icl1 loop } // subcell icl0 loop } // macro elem loop return NULL; }
void InitField(Field* f){ //int param[8]={f->model.m,_DEGX,_DEGY,_DEGZ,_RAFX,_RAFY,_RAFZ,0}; double w[f->model.m]; double xpg[3]; double xref[3],omega; double physnode[20][3]; f->is2d = false; // a copy for avoiding too much "->" for(int ip=0;ip<8;ip++){ f->interp_param[ip]=f->interp.interp_param[ip]; } int nmem=f->model.m * f->macromesh.nbelems * NPG(f->interp_param+1); printf("allocate %d doubles\n",nmem); f->wn=malloc(nmem * sizeof(double)); assert(f->wn); f->wnp1=malloc(nmem * sizeof(double)); assert(f->wnp1); f->dtwn=malloc(nmem * sizeof(double)); assert(f->dtwn); f->tnow=0; for(int ie=0;ie<f->macromesh.nbelems;ie++){ for(int inoloc=0;inoloc<20;inoloc++){ int ino=f->macromesh.elem2node[20*ie+inoloc]; physnode[inoloc][0]=f->macromesh.node[3*ino+0]; physnode[inoloc][1]=f->macromesh.node[3*ino+1]; physnode[inoloc][2]=f->macromesh.node[3*ino+2]; } for(int ipg=0;ipg<NPG(f->interp_param+1);ipg++){ ref_pg_vol(f->interp_param+1, ipg, xref, &omega,NULL); double dtau[3][3]; Ref2Phy(physnode, xref, 0,-1, // dphiref,ifa xpg,dtau, NULL,NULL,NULL); // codtau,dphi,vnds // check the reverse transform at all the GLOPS double xref2[3]; Phy2Ref(physnode,xpg,xref2); assert(Dist(xref,xref2) < 1e-8); f->model.InitData(xpg,w); for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(f->interp_param,ie,ipg,iv); f->wn[imem]=w[iv]; } } } // compute cfl parameter min_i vol_i/surf_i f->hmin=1e10; for (int ie=0;ie<f->macromesh.nbelems;ie++){ double vol=0,surf=0; // get the physical nodes of element ie double physnode[20][3]; for(int inoloc=0;inoloc<20;inoloc++){ int ino=f->macromesh.elem2node[20*ie+inoloc]; physnode[inoloc][0]=f->macromesh.node[3*ino+0]; physnode[inoloc][1]=f->macromesh.node[3*ino+1]; physnode[inoloc][2]=f->macromesh.node[3*ino+2]; } // loop on the glops (for numerical integration) for(int ipg=0;ipg<NPG(f->interp_param+1);ipg++){ double xpgref[3],wpg; // get the coordinates of the Gauss point ref_pg_vol(f->interp_param+1,ipg,xpgref,&wpg,NULL); double codtau[3][3],dtau[3][3]; Ref2Phy(physnode, // phys. nodes xpgref, // xref NULL,-1, // dpsiref,ifa NULL,dtau, // xphy,dtau codtau,NULL,NULL); // codtau,dpsi,vnds double det = dtau[0][0] * codtau[0][0] + dtau[0][1] * codtau[0][1] + dtau[0][2] * codtau[0][2]; vol+=wpg*det; } for(int ifa=0;ifa<6;ifa++){ // loop on the faces for(int ipgf=0;ipgf<NPGF(f->interp_param+1,ifa);ipgf++){ double xpgref[3],wpg; //double xpgref2[3],wpg2; // get the coordinates of the Gauss point ref_pg_face(f->interp_param+1,ifa,ipgf,xpgref,&wpg,NULL); double vnds[3]; double codtau[3][3],dtau[3][3]; Ref2Phy(physnode, xpgref, NULL,ifa, // dpsiref,ifa NULL,dtau, codtau,NULL,vnds); // codtau,dpsi,vnds surf+=sqrt(vnds[0]*vnds[0]+vnds[1]*vnds[1]+vnds[2]*vnds[2])*wpg; } } f->hmin = f->hmin < vol/surf ? f->hmin : vol/surf; } // now take into account the polynomial degree and the refinement int maxd=f->interp_param[1]; maxd = maxd > f->interp_param[2] ? maxd : f->interp_param[2]; maxd = maxd > f->interp_param[3] ? maxd : f->interp_param[3]; f->hmin/=((maxd+1)*f->interp_param[4]); printf("hmin=%f\n",f->hmin); };
// save the results in the gmsh format // typplot: index of the plotted variable // int compare == true -> compare with the exact value void PlotField(int typplot,int compare,Field* f,char* filename){ const int hexa64ref[3*64]={ 0,0,3, 3,0,3, 3,3,3, 0,3,3, 0,0,0,3,0,0,3,3,0,0,3,0, 1,0,3,2,0,3,0,1,3,0,2,3,0,0,2,0,0,1,3,1,3,3,2,3, 3,0,2,3,0,1,2,3,3,1,3,3,3,3,2,3,3,1,0,3,2,0,3,1, 1,0,0,2,0,0,0,1,0,0,2,0,3,1,0,3,2,0,2,3,0,1,3,0, 1,1,3,1,2,3,2,2,3,2,1,3,1,0,2,2,0,2,2,0,1,1,0,1, 0,1,2,0,1,1,0,2,1,0,2,2,3,1,2,3,2,2,3,2,1,3,1,1, 2,3,2,1,3,2,1,3,1,2,3,1,1,1,0,2,1,0,2,2,0,1,2,0, 1,1,2,2,1,2,2,2,2,1,2,2,1,1,1,2,1,1,2,2,1,1,2,1}; int* elem2nodes = f->macromesh.elem2node; double* node = f->macromesh.node; FILE * gmshfile; gmshfile = fopen( filename, "w" ); // data plots //int param[8]={f->model.m,_DEGX,_DEGY,_DEGZ,_RAFX,_RAFY,_RAFZ,0}; int nraf[3]={f->interp_param[4],f->interp_param[5],f->interp_param[6]}; // refinement size in each direction double hh[3]={1./nraf[0],1./nraf[1],1./nraf[2]}; int npgv = NPG(f->interp_param+1); int nnodes = 20; double physnode[nnodes][3]; double Xr[3]; double Xphy[3]; // header fprintf(gmshfile,"$MeshFormat\n2.2 0 %d\n",(int) sizeof(double)); //int one=1; //fwrite((char*) &one,sizeof(int),1,gmshfile); fprintf(gmshfile,"$EndMeshFormat\n$Nodes\n%d\n", f->macromesh.nbelems*nraf[0]*nraf[1]*nraf[2]*64); int nb_plotnodes=f->macromesh.nbelems*nraf[0]*nraf[1]*nraf[2]*64; double* value=malloc(nb_plotnodes*sizeof(double)); assert(value); int nodecount=0; // nodes for(int i=0;i<f->macromesh.nbelems;i++){ // get the nodes of element L for(int ino=0;ino<nnodes;ino++){ int numnoe=elem2nodes[nnodes*i+ino]; for(int ii=0;ii<3;ii++){ physnode[ino][ii]=node[3*numnoe+ii]; } } // loop on the macro elem subcells int icL[3]; // loop on the subcells for(icL[0]=0;icL[0]<nraf[0];icL[0]++){ for(icL[1]=0;icL[1]<nraf[1];icL[1]++){ for(icL[2]=0;icL[2]<nraf[2];icL[2]++){ // get the left subcell id // first glop index in the subcell //int offsetL=(deg[0]+1)*(deg[1]+1)*(deg[2]+1)*ncL; for(int ino=0;ino<64;ino++){ Xr[0]=(double) (hexa64ref[3*ino+0]) / 3; Xr[1]=(double) (hexa64ref[3*ino+1]) / 3; Xr[2]=(double) (hexa64ref[3*ino+2]) / 3; Xr[0] = icL[0]*hh[0]+ Xr[0] * hh[0]; Xr[1] = icL[1]*hh[1]+ Xr[1] * hh[1]; Xr[2] = icL[2]*hh[2]+ Xr[2] * hh[2]; for(int ii=0;ii<3;ii++){ assert(Xr[ii]<1+1e-10 && Xr[ii]>-1e-10); } Ref2Phy(physnode, Xr, NULL, -1, Xphy, NULL, NULL, NULL, NULL); double Xplot[3]; Xplot[0]=Xphy[0]; Xplot[1]=Xphy[1]; Xplot[2]=Xphy[2]; value[nodecount]=0; double testpsi=0; for(int ib=0;ib<npgv;ib++){ double psi; psi_ref_subcell(f->interp_param+1,icL, ib, Xr, &psi, NULL); testpsi+=psi; int vi = f->varindex(f->interp_param, i, ib, typplot); value[nodecount] += psi * f->wn[vi]; } assert(fabs(testpsi-1)<1e-10); // compare with an // exact solution if (compare){ double wex[f->model.m]; f->model.ImposedData(Xphy,f->tnow,wex); value[nodecount] -= wex[typplot]; } nodecount++; // fwrite((char*) &nnoe,sizeof(int),1,gmshfile); // fwrite((char*) &(Xplot[0]),sizeof(double),1,gmshfile); // fwrite((char*) &(Xplot[1]),sizeof(double),1,gmshfile); // fwrite((char*) &(Xplot[2]),sizeof(double),1,gmshfile); fprintf(gmshfile,"%d %f %f %f\n",nodecount,Xplot[0],Xplot[1],Xplot[2]); } } } } } fprintf(gmshfile,"$EndNodes\n"); // elements fprintf(gmshfile,"$Elements\n"); fprintf(gmshfile,"%d\n",f->macromesh.nbelems*nraf[0]*nraf[1]*nraf[2]); int elm_type=92; //int num_elm_follow=f->macromesh.nbelems; int num_tags=0; // fwrite((char*) &elm_type,sizeof(int),1,gmshfile); // fwrite((char*) &num_elm_follow,sizeof(int),1,gmshfile); // fwrite((char*) &num_tags,sizeof(int),1,gmshfile); for(int i=0;i<f->macromesh.nbelems;i++){ // loop on the macro elem subcells int icL[3]; // loop on the subcells for(icL[0]=0;icL[0]<nraf[0];icL[0]++){ for(icL[1]=0;icL[1]<nraf[1];icL[1]++){ for(icL[2]=0;icL[2]<nraf[2];icL[2]++){ // get the subcell id int ncL=icL[0]+nraf[0]*(icL[1]+nraf[1]*icL[2]); // first glop index in the subcell //int offsetL=(deg[0]+1)*(deg[1]+1)*(deg[2]+1)*ncL; // global subcell id int numelem=ncL+i*nraf[0]*nraf[1]*nraf[2]+1; //fwrite((char*) &numelem,sizeof(int),1,gmshfile); fprintf(gmshfile,"%d ",numelem); fprintf(gmshfile,"%d ",elm_type); fprintf(gmshfile,"%d ",num_tags); for(int ii=0;ii<64;ii++){ int numnoe=64*(i*nraf[0]*nraf[1]*nraf[2]+ncL) + ii +1; //fwrite((char*) &numnoe,sizeof(int),1,gmshfile); fprintf(gmshfile,"%d ",numnoe); } fprintf(gmshfile,"\n"); } } } } fprintf(gmshfile,"$EndElements\n"); // now display data fprintf(gmshfile,"$NodeData\n"); fprintf(gmshfile,"1\n"); fprintf(gmshfile,"\"Field %d\"\n",typplot); double t = 0; fprintf(gmshfile,"1\n%f\n3\n0\n1\n",t); fprintf(gmshfile,"%d\n",nb_plotnodes); for(int ino=0;ino<nb_plotnodes;ino++){ //fwrite(const void *ptr, size_t size_of_elements, // size_t number_of_elements, FILE *a_file); //fwrite((char*) &nodenumber, sizeof(int),1,gmshfile); //fwrite((char*) &value, sizeof(double),1,gmshfile); //fprintf(gmshfile,"%d %f\n",nodenumber,value); fprintf(gmshfile,"%d %f\n",ino+1,value[ino]); } /* for(int i=0;i<f->macromesh.nbelems;i++){ */ /* for(int ino=0;ino<20;ino++){ */ /* int numnoe=elem2nodes[nnodes*i+ino]; */ /* for(int ii=0;ii<3;ii++){ */ /* physnode[ino][ii]=node[3*numnoe+ii]; */ /* } */ /* } */ /* // data at the eight nodes */ /* for(int ii=0;ii<64;ii++){ */ /* int nodenumber=64*i + ii +1; */ /* Xr[0]=(double) (hexa64ref[3*ii+0]) / 3; */ /* Xr[1]=(double) (hexa64ref[3*ii+1]) / 3; */ /* Xr[2]=(double) (hexa64ref[3*ii+2]) / 3; */ /* Ref2Phy(physnode, */ /* Xr, */ /* NULL, */ /* -1, */ /* Xphy, */ /* NULL, */ /* NULL, */ /* NULL, */ /* NULL); */ /* double value=0; */ /* for(int ib=0;ib<npgv;ib++){ */ /* double psi; */ /* psi_ref(f->interp_param+1, ib, Xr, &psi, NULL); */ /* int vi = f->varindex(f->interp_param, i, ib, typplot); */ /* value += psi * f->wn[vi]; */ /* } */ /* // compare with an */ /* // exact solution */ /* if (compare){ */ /* double wex[f->model.m]; */ /* f->model.ImposedData(Xphy,f->tnow,wex); */ /* value -= wex[typplot]; */ /* } */ /* //fwrite(const void *ptr, size_t size_of_elements, */ /* // size_t number_of_elements, FILE *a_file); */ /* //fwrite((char*) &nodenumber, sizeof(int),1,gmshfile); */ /* //fwrite((char*) &value, sizeof(double),1,gmshfile); */ /* //fprintf(gmshfile,"%d %f\n",nodenumber,value); */ /* fprintf(gmshfile,"%d %f\n",nodenumber,value); */ /* } */ /* } */ fprintf(gmshfile,"\n$EndNodeData\n"); fclose(gmshfile); free(value); }
// compute the Discontinuous Galerkin volume terms // slow version void DGVolumeSlow(Field* f){ // assembly of the volume terms // loop on the elements //#pragma omp parallel for for (int ie=0;ie<f->macromesh.nbelems;ie++){ // get the physical nodes of element ie double physnode[20][3]; for(int inoloc=0;inoloc<20;inoloc++){ int ino=f->macromesh.elem2node[20*ie+inoloc]; physnode[inoloc][0]=f->macromesh.node[3*ino+0]; physnode[inoloc][1]=f->macromesh.node[3*ino+1]; physnode[inoloc][2]=f->macromesh.node[3*ino+2]; } // mass matrix double masspg[NPG(f->interp_param+1)]; // loop on the glops (for numerical integration) for(int ipg=0;ipg<NPG(f->interp_param+1);ipg++){ double xpgref[3],wpg; // get the coordinates of the Gauss point ref_pg_vol(f->interp_param+1,ipg,xpgref,&wpg,NULL); // get the value of w at the gauss point double w[f->model.m]; for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(f->interp_param,ie,ipg,iv); w[iv]=f->wn[imem]; } // loop on the basis functions for(int ib=0;ib<NPG(f->interp_param+1);ib++){ // gradient of psi_ib at gauss point ipg double dpsiref[3],dpsi[3]; double dtau[3][3],codtau[3][3];//,xpg[3]; grad_psi_pg(f->interp_param+1,ib,ipg,dpsiref); Ref2Phy(physnode, // phys. nodes xpgref, // xref dpsiref,-1, // dpsiref,ifa NULL,dtau, // xphy,dtau codtau,dpsi,NULL); // codtau,dpsi,vnds // remember the diagonal mass term if (ib == ipg){ double det = dtau[0][0] * codtau[0][0] + dtau[0][1] * codtau[0][1] + dtau[0][2] * codtau[0][2]; masspg[ipg]=wpg*det; } // int_L F(w,w,grad phi_ib ) double flux[f->model.m]; f->model.NumFlux(w,w,dpsi,flux); for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(f->interp_param,ie,ib,iv); f->dtwn[imem]+=flux[iv]*wpg; } } } for(int ipg=0;ipg<NPG(f->interp_param+1);ipg++){ // apply the inverse of the diagonal mass matrix for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(f->interp_param,ie,ipg,iv); (f->dtwn[imem])/=masspg[ipg]; } } } }
// Detect if the mesh is 1D and then permut the nodes so that the y,z // direction coincides in the reference or physical frame void Detect1DMacroMesh(MacroMesh* m){ m->is1d = true; // do not permut the node if the connectivity // is already built if (m->elem2elem != NULL) printf("Cannot permut nodes before building connectivity\n"); assert(m->elem2elem == 0); for(int ie = 0; ie < m->nbelems; ie++) { // get the physical nodes of element ie real physnode[20][3]; for(int inoloc = 0; inoloc < 20; inoloc++){ int ino = m->elem2node[20 * ie + inoloc]; physnode[inoloc][0] = m->node[3 * ino + 0]; physnode[inoloc][1] = m->node[3 * ino + 1]; physnode[inoloc][2] = m->node[3 * ino + 2]; } // we decide that the mesh is 1D if the // middles of the elements have a constant y,z // coordinate equal to 0.5 real zmil = 0; real ymil = 0; for(int inoloc = 0; inoloc < 20; inoloc++){ zmil += physnode[inoloc][2]; ymil += physnode[inoloc][1]; } zmil /= 20; ymil /= 20; // the mesh is not 1d if (fabs(zmil-0.5)>1e-6 || fabs(ymil-0.5)>1e-6) { printf("The mesh is not 1D zmil=%f ymil=%f\n",zmil,ymil); m->is1d=false; return; } } printf("Detection of a 1D mesh\n"); printf("Check now hexahedrons orientation\n"); for(int ie = 0; ie < m->nbelems; ++ie){ // get the physical nodes of element ie real physnode[20][3]; for(int inoloc = 0; inoloc < 20; inoloc++){ int ino = m->elem2node[20 * ie + inoloc]; physnode[inoloc][0] = m->node[3 * ino + 0]; physnode[inoloc][1] = m->node[3 * ino + 1]; physnode[inoloc][2] = m->node[3 * ino + 2]; } // face centers coordinates in the ref frame real face_centers[6][3]={ {0.5,0.0,0.5}, {1.0,0.5,0.5}, {0.5,1.0,0.5}, {0.0,0.5,0.5}, {0.5,0.5,1.0}, {0.5,0.5,0.0}, }; // compute the normal to face 1 real vnds[3], dtau[3][3], codtau[3][3]; Ref2Phy(physnode, face_centers[1], NULL, 1, // dphiref,ifa NULL, dtau, codtau, NULL, vnds); // codtau,dphi,vnds real d = sqrt((vnds[0] - 1) * (vnds[0] - 1) + vnds[1] * vnds[1] + vnds[2] * vnds[2]); // if the mesh is not 1D exit assert(d<1e-6); } }
void AccumulateParticles(void *fv, real *w) { field *f = fv; PIC *pic = f->pic; int *raf = f->interp_param + 4; int *deg = f->interp_param + 1; int npg = NPG(raf, deg); for(int ie = 0; ie < f->macromesh.nbelems; ie++){ MacroCell *mcell = f->mcell + ie; for(int ipg = 0; ipg < npg; ipg++){ int iv = 4; int imem = f->varindex(f->interp_param, ipg, iv) + mcell->woffset; f->wn[imem]=0; iv = 5; imem = f->varindex(f->interp_param, ipg, iv) + mcell->woffset; f->wn[imem]=0; iv = 6; imem = f->varindex(f->interp_param, ipg, iv) + mcell->woffset; f->wn[imem]=0; } } for(int i=0;i<pic->nbparts;i++) { int ie=pic->old_cell_id[i]; // https://xkcd.com/292/ // FIXME: remove goto if (ie < 0) goto nexti; MacroCell *mcell = f->mcell + ie; int npg=NPG(raf, deg); real physnode[20][3]; for(int inoloc = 0; inoloc < 20; inoloc++) { int ino = f->macromesh.elem2node[20*ie+inoloc]; physnode[inoloc][0] = f->macromesh.node[3 * ino + 0]; physnode[inoloc][1] = f->macromesh.node[3 * ino + 1]; physnode[inoloc][2] = f->macromesh.node[3 * ino + 2]; } real dtau[3][3], codtau[3][3]; Ref2Phy(physnode, // phys. nodes pic->xv + 6 * i, // xref NULL, -1, // dpsiref, ifa NULL, dtau, // xphy, dtau codtau, NULL, NULL); // codtau, dpsi, vnds real det = dot_product(dtau[0], codtau[0]); for(int ib=0;ib < npg;ib++){ real wpg; int *raf = f->interp_param + 4; int *deg = f->interp_param + 1; ref_pg_vol(raf, deg, ib, NULL, &wpg, NULL); //printf("det=%f wpg=%f \n", det, wpg); wpg *= det; real psi; psi_ref(f->interp_param+1,ib,pic->xv + 6*i,&psi,NULL); int iv = 6; // rho index int imem = f->varindex(f->interp_param, ib, iv) + mcell->woffset; w[imem] += psi / wpg * pic->weight; iv = 4; // j1 index imem = f->varindex(f->interp_param, ib, iv) + mcell->woffset; w[imem] += pic->xv[6 * i + 3] * psi / wpg * pic->weight; iv = 5; // j2 index imem = f->varindex(f->interp_param, ib, iv) + mcell->woffset; w[imem] += pic->xv[6 * i + 4] * psi / wpg * pic->weight; } nexti: assert(1==1); } }
// Build other connectivity arrays void BuildConnectivity(MacroMesh* m) { printf("Build connectivity...\n"); real *bounds = malloc(6 * sizeof(real)); macromesh_bounds(m, bounds); printf("bounds: %f, %f, %f, %f, %f, %f\n", bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]); printf("bounds: %f, %f, %f, %f, %f, %f\n", m->xmin[0],m->xmax[0], m->xmin[1],m->xmax[1], m->xmin[2],m->xmax[2] ); // Build a list of faces each face is made of four corners of the // hexaedron mesh Face4Sort *face = malloc(6 * sizeof(Face4Sort) * m->nbelems); build_face(m, face); build_elem2elem(m, face); free(face); build_node2elem(m); // check /* for(int ie = 0;ie<m->nbelems;ie++) { */ /* for(int ifa = 0;ifa<6;ifa++) { */ /* printf("elem=%d face=%d, voisin=%d\n", */ /* ie,ifa,m->elem2elem[ifa+6*ie]); */ /* } */ /* } */ if(m->is2d) suppress_zfaces(m); if(m->is1d) { suppress_zfaces(m); suppress_yfaces(m); } // update connectivity if the mesh is periodic // in some directions real diag[3][3]={1,0,0, 0,1,0, 0,0,1}; for (int ie = 0; ie < m->nbelems; ie++) { real physnode[20][3]; for(int inoloc = 0; inoloc < 20; inoloc++) { int ino = m->elem2node[20 * ie + inoloc]; physnode[inoloc][0] = m->node[3 * ino + 0]; physnode[inoloc][1] = m->node[3 * ino + 1]; physnode[inoloc][2] = m->node[3 * ino + 2]; } for(int ifa = 0; ifa < 6; ifa++) { if (m->elem2elem[6 * ie + ifa] < 0){ real xpgref[3],xpgref_in[3]; int ipgf=0; int param2[7]={0,0,0,1,1,1,0}; ref_pg_face(param2, ifa, ipgf, xpgref, NULL, xpgref_in); real dtau[3][3],xpg_in[3]; real codtau[3][3],vnds[3]={0,0,0}; Ref2Phy(physnode, xpgref_in, NULL, ifa, // dpsiref,ifa xpg_in, dtau, codtau, NULL, vnds); // codtau,dpsi,vnds Normalize(vnds); vnds[0]=fabs(vnds[0]); vnds[1]=fabs(vnds[1]); vnds[2]=fabs(vnds[2]); int dim=0; while(Dist(vnds,diag[dim]) > 1e-2 && dim<3) dim++; //assert(dim < 3); //printf("xpg_in_before=%f\n",xpg_in[dim]); if (dim < 3 && m->period[dim] > 0){ //if (xpg_in[dim] > m->period[dim]){ if (xpg_in[dim] > m->xmax[dim]){ xpg_in[dim] -= m->period[dim]; //printf("xpg_in_after=%f\n",xpg_in[dim]); } //else if (xpg_in[dim] < 0){ else if (xpg_in[dim] < m->xmin[dim]){ xpg_in[dim] += m->period[dim]; //printf("xpg_in_after=%f\n",xpg_in[dim]); } else { //printf("xpg_in=%f\n",xpg_in[dim]); assert(1==2); } m->elem2elem[6 * ie + ifa] = NumElemFromPoint(m,xpg_in,NULL); /* printf("ie=%d ifa=%d numelem=%d vnds=%f %f %f xpg_in=%f %f %f \n", */ /* ie,ifa,NumElemFromPoint(m,xpg_in,NULL), */ /* vnds[0],vnds[1],vnds[2], */ /* xpg_in[0],xpg_in[1],xpg_in[2]); */ } } } } // now, update the face2elem connectivity (because elem2elem has changed) for(int ifa = 0; ifa < m->nbfaces; ifa++) { int ieL = m->face2elem[4 * ifa + 0]; int locfaL = m->face2elem[4 * ifa + 1]; int ieR = m->face2elem[4 * ifa + 2]; int locfaR = m->face2elem[4 * ifa + 3]; int ieR2=m->elem2elem[6 * ieL + locfaL]; if (ieR != ieR2){ assert(ieR == -1); int opp[6]={2,3,0,1,5,4}; if (locfaL == 0 || locfaL == 1 || locfaL == 4) { m->face2elem[4 * ifa + 2] = ieR2; m->face2elem[4 * ifa + 3] = opp[locfaL]; } else { // mark the face for suppression m->face2elem[4 * ifa + 0] = -1; } } } suppress_double_faces(m); //assert(1==5); free(bounds); m->connec_ok = true; /* #ifdef _PERIOD */ /* assert(m->is1d); // TODO : generalize to 2D */ /* assert(m->nbelems==1); */ /* // faces 1 and 3 point to the same unique macrocell */ /* m->elem2elem[1+6*0]=0; */ /* m->elem2elem[3+6*0]=0; */ /* #endif */ }
void PushParticles(field *f,PIC* pic){ for(int i=0;i<pic->nbparts;i++) { // jacobian of tau at the particle real physnode[20][3]; int ie=pic->cell_id[i]; if (ie >=0) { real w[f->model.m]; InterpField(f,pic->cell_id[i],&(pic->xv[6*i]),w); for(int inoloc = 0; inoloc < 20; inoloc++) { int ino = f->macromesh.elem2node[20*ie+inoloc]; physnode[inoloc][0] = f->macromesh.node[3 * ino + 0]; physnode[inoloc][1] = f->macromesh.node[3 * ino + 1]; physnode[inoloc][2] = f->macromesh.node[3 * ino + 2]; } real dtau[3][3],codtau[3][3]; real xphy[3]; Ref2Phy(physnode, // phys. nodes &(pic->xv[6*i]), // xref NULL, -1, // dpsiref, ifa xphy, dtau, // xphy, dtau codtau, NULL, NULL); // codtau, dpsi, vnds real det = dot_product(dtau[0], codtau[0]); //printf("w=%f %f %f %f\n",w[0],w[1],w[2],w[3]); // 2D motion real vref[3]; pic->xv[6*i+3+0] +=pic->dt * (w[0]+w[2]*pic->xv[6*i+4]); pic->xv[6*i+3+1] +=pic->dt * (w[1]-w[2]*pic->xv[6*i+3]); pic->xv[6*i+3+2] +=0; for(int ii=0;ii<3;ii++){ vref[ii]=0; for(int jj=0;jj<3;jj++){ vref[ii] += codtau[jj][ii] * pic->xv[6*i+3+jj] / det ; } } // TO DO: check if the particle is in a new element pic->xv[6*i+0]+=pic->dt * vref[0]; pic->xv[6*i+1]+=pic->dt * vref[1]; pic->xv[6*i+2]+=pic->dt * vref[2]; bool is_out = (pic->xv[6*i+0] < 0 || pic->xv[6*i+0] > 1) || (pic->xv[6*i+1] < 0 || pic->xv[6*i+1] > 1) || (pic->xv[6*i+2] < 0 || pic->xv[6*i+2] > 1); //is_out = false; if (is_out) { Ref2Phy(physnode, // phys. nodes &(pic->xv[6*i]), // xref NULL, -1, // dpsiref, ifa xphy, NULL, // xphy, dtau NULL, NULL, NULL); // codtau, dpsi, vnds int old=pic->cell_id[i]; printf("oldref=%f %f %f \n",pic->xv[6*i+0], pic->xv[6*i+1],pic->xv[6*i+2]); pic->cell_id[i]= NumElemFromPoint(&(f->macromesh), xphy, &(pic->xv[6*i])); if (pic->cell_id[i] != -1) pic->old_cell_id[i]=pic->cell_id[i]; printf("newref=%f %f %f \n",pic->xv[6*i+0], pic->xv[6*i+1],pic->xv[6*i+2]); printf("change elem: %d -> %d \n",old,pic->cell_id[i]); } } // if ie >= 0 } }
// apply the Discontinuous Galerkin approximation for computing // the time derivative of the field void dtFieldSlow(Field* f){ // interpolation params // warning: this is ugly, but the last // parameter is used for computing the volume // GLOP index from the face GLOP index... // ugly too: the first parameter is not used by all // utilities. we have sometimes to jump over : pass param+1 // instead of param... //int param[8]={f->model.m,_DEGX,_DEGY,_DEGZ,_RAFX,_RAFY,_RAFZ,0}; // init to zero the time derivative int sizew=0; //#pragma omp parallel for for(int ie=0;ie<f->macromesh.nbelems;ie++){ for(int ipg=0;ipg<NPG(f->interp_param+1);ipg++){ for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(f->interp_param,ie,ipg,iv); f->dtwn[imem]=0; sizew++; } } } assert(sizew==f->macromesh.nbelems * f->model.m * NPG(f->interp_param+1)); // assembly of the surface terms // loop on the elements //#pragma omp parallel for for (int ie=0;ie<f->macromesh.nbelems;ie++){ // get the physical nodes of element ie double physnode[20][3]; for(int inoloc=0;inoloc<20;inoloc++){ int ino=f->macromesh.elem2node[20*ie+inoloc]; physnode[inoloc][0]=f->macromesh.node[3*ino+0]; physnode[inoloc][1]=f->macromesh.node[3*ino+1]; physnode[inoloc][2]=f->macromesh.node[3*ino+2]; } // loop on the 6 faces // or four faces for 2d computations int nbfa=6; if (f->is2d) nbfa=4; for(int ifa=0;ifa<nbfa;ifa++){ // get the right elem or the boundary id int ieR=f->macromesh.elem2elem[6*ie+ifa]; double physnodeR[20][3]; if (ieR >= 0) { for(int inoloc=0;inoloc<20;inoloc++){ int ino=f->macromesh.elem2node[20*ieR+inoloc]; physnodeR[inoloc][0]=f->macromesh.node[3*ino+0]; physnodeR[inoloc][1]=f->macromesh.node[3*ino+1]; physnodeR[inoloc][2]=f->macromesh.node[3*ino+2]; } } // loop on the glops (numerical integration) // of the face ifa for(int ipgf=0;ipgf<NPGF(f->interp_param+1,ifa);ipgf++){ double xpgref[3],xpgref_in[3],wpg; //double xpgref2[3],wpg2; // get the coordinates of the Gauss point // and coordinates of a point slightly inside the // opposite element in xref_in ref_pg_face(f->interp_param+1,ifa,ipgf,xpgref,&wpg,xpgref_in); // recover the volume gauss point from // the face index int ipg=f->interp_param[7]; // get the left value of w at the gauss point double wL[f->model.m],wR[f->model.m]; for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(f->interp_param,ie,ipg,iv); wL[iv]=f->wn[imem]; } // the basis functions is also the gauss point index int ib=ipg; // normal vector at gauss point ipg double dtau[3][3],codtau[3][3],xpg[3]; double vnds[3]; Ref2Phy(physnode, xpgref, NULL,ifa, // dpsiref,ifa xpg,dtau, codtau,NULL,vnds); // codtau,dpsi,vnds double flux[f->model.m]; if (ieR >=0) { // the right element exists // find the corresponding point in the right elem double xpg_in[3]; Ref2Phy(physnode, xpgref_in, NULL,ifa, // dpsiref,ifa xpg_in,dtau, codtau,NULL,vnds); // codtau,dpsi,vnds double xref[3]; Phy2Ref(physnodeR,xpg_in,xref); int ipgR=ref_ipg(f->interp_param+1,xref); double xpgR[3],xrefR[3],wpgR; ref_pg_vol(f->interp_param+1, ipgR, xrefR, &wpgR,NULL); Ref2Phy(physnodeR, xrefR, NULL,-1, // dphiref,ifa xpgR,NULL, NULL,NULL,NULL); // codtau,dphi,vnds assert(Dist(xpgR,xpg)<1e-10); for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(f->interp_param,ieR,ipgR,iv); wR[iv]=f->wn[imem]; } // int_dL F(wL,wR,grad phi_ib ) f->model.NumFlux(wL,wR,vnds,flux); } else { //the right element does not exist f->model.BoundaryFlux(xpg,f->tnow,wL,vnds,flux); } for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(f->interp_param,ie,ib,iv); f->dtwn[imem]-=flux[iv]*wpg; } } } } // assembly of the volume terms // loop on the elements //#pragma omp parallel for for (int ie=0;ie<f->macromesh.nbelems;ie++){ // get the physical nodes of element ie double physnode[20][3]; for(int inoloc=0;inoloc<20;inoloc++){ int ino=f->macromesh.elem2node[20*ie+inoloc]; physnode[inoloc][0]=f->macromesh.node[3*ino+0]; physnode[inoloc][1]=f->macromesh.node[3*ino+1]; physnode[inoloc][2]=f->macromesh.node[3*ino+2]; } // mass matrix double masspg[NPG(f->interp_param+1)]; // loop on the glops (for numerical integration) for(int ipg=0;ipg<NPG(f->interp_param+1);ipg++){ double xpgref[3],wpg; // get the coordinates of the Gauss point ref_pg_vol(f->interp_param+1,ipg,xpgref,&wpg,NULL); // get the value of w at the gauss point double w[f->model.m]; for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(f->interp_param,ie,ipg,iv); w[iv]=f->wn[imem]; } // loop on the basis functions for(int ib=0;ib<NPG(f->interp_param+1);ib++){ // gradient of psi_ib at gauss point ipg double dpsiref[3],dpsi[3]; double dtau[3][3],codtau[3][3];//,xpg[3]; grad_psi_pg(f->interp_param+1,ib,ipg,dpsiref); Ref2Phy(physnode, // phys. nodes xpgref, // xref dpsiref,-1, // dpsiref,ifa NULL,dtau, // xphy,dtau codtau,dpsi,NULL); // codtau,dpsi,vnds // remember the diagonal mass term if (ib == ipg){ double det = dtau[0][0] * codtau[0][0] + dtau[0][1] * codtau[0][1] + dtau[0][2] * codtau[0][2]; masspg[ipg]=wpg*det; } // int_L F(w,w,grad phi_ib ) double flux[f->model.m]; f->model.NumFlux(w,w,dpsi,flux); for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(f->interp_param,ie,ib,iv); f->dtwn[imem]+=flux[iv]*wpg; } } } for(int ipg=0;ipg<NPG(f->interp_param+1);ipg++){ // apply the inverse of the diagonal mass matrix for(int iv=0;iv<f->model.m;iv++){ int imem=f->varindex(f->interp_param,ie,ipg,iv); (f->dtwn[imem])/=masspg[ipg]; } } } };