// This is a user-supplied routine that sets the the boundary conditions // void SetBndValues_Unst(const mesh& Mesh, dTensor3* q, dTensor3* aux) { int meqn = q->getsize(2); int kmax = q->getsize(3); int maux = aux->getsize(2); int NumElems = Mesh.get_NumElems(); int NumPhysElems = Mesh.get_NumPhysElems(); int NumGhostElems = Mesh.get_NumGhostElems(); int NumNodes = Mesh.get_NumNodes(); int NumPhysNodes = Mesh.get_NumPhysNodes(); int NumEdges = Mesh.get_NumEdges(); // ---------------------------------------- // Loop over each ghost cell element and // place the correct information into // these elements // ---------------------------------------- for (int i=1; i<=NumGhostElems; i++) { int j = Mesh.get_ghost_link(i); for (int m=1; m<=meqn; m++) for (int k=1; k<=kmax; k++) { q->set(i+NumPhysElems,m,k, 0.0 ); } } }
// This is a user-supplied routine that sets the the boundary conditions // // The default routine for the 4D Vlasov code is to apply zero boundary // conditions in configuration space. This routine is identical to the one // found in the unst branch of the 2D DoGPack code, and *should* be setting // periodic boundary conditions. // void SetBndValues_Unst(const mesh& Mesh, dTensor3* q, dTensor3* aux) { // problem information (If this were to be pulled from DogParams, these // numbers would NOT be correct! The reason is that each quadrature point // was actually saved as a separate "equation") const int meqn = q->getsize(2); const int kmax = q->getsize(3); const int maux = aux->getsize(2); // Mesh information const int NumElems = Mesh.get_NumElems(); const int NumPhysElems = Mesh.get_NumPhysElems(); const int NumGhostElems = Mesh.get_NumGhostElems(); const int NumNodes = Mesh.get_NumNodes(); const int NumPhysNodes = Mesh.get_NumPhysNodes(); const int NumEdges = Mesh.get_NumEdges(); // ---------------------------------------- // Loop over each ghost cell element and // place the correct information into // these elements // ---------------------------------------- for (int i=1; i<=NumGhostElems; i++) { int j = Mesh.get_ghost_link(i); for (int m=1; m<=meqn; m++) for (int k=1; k<=kmax; k++) { q->set(i+NumPhysElems, m, k, q->get(j, m, k) ); } } }
// // Output basic mesh information to screen // void ScreenOutput(const mesh& Mesh) { // Compute mesh quality parameters double totalarea = Mesh.get_area_prim(1); double maxarea = Mesh.get_area_prim(1); double minarea = Mesh.get_area_prim(1); for (int i=2; i<=Mesh.get_NumPhysElems(); i++) { double tmp = Mesh.get_area_prim(i); totalarea = totalarea + tmp; if (tmp < minarea) { minarea = tmp; } if (tmp > maxarea) { maxarea = tmp; } } double minAngle = 180.0; for (int i=1; i<=Mesh.get_NumPhysElems(); i++) { const int i1 = Mesh.get_tnode(i,1); const int i2 = Mesh.get_tnode(i,2); const int i3 = Mesh.get_tnode(i,3); point v12, v23, v31; v12.x = Mesh.get_node(i2,1) - Mesh.get_node(i1,1); v12.y = Mesh.get_node(i2,2) - Mesh.get_node(i1,2); v23.x = Mesh.get_node(i3,1) - Mesh.get_node(i2,1); v23.y = Mesh.get_node(i3,2) - Mesh.get_node(i2,2); v31.x = Mesh.get_node(i1,1) - Mesh.get_node(i3,1); v31.y = Mesh.get_node(i1,2) - Mesh.get_node(i3,2); double angle1 = acos((v12.x*-v31.x+v12.y*-v31.y) /(sqrt(v12.x*v12.x+v12.y*v12.y)*sqrt(v31.x*v31.x+v31.y*v31.y))); double angle2 = acos((v23.x*-v12.x+v23.y*-v12.y) /(sqrt(v23.x*v23.x+v23.y*v23.y)*sqrt(v12.x*v12.x+v12.y*v12.y))); double angle3 = acos((v31.x*-v23.x+v31.y*-v23.y) /(sqrt(v31.x*v31.x+v31.y*v31.y)*sqrt(v23.x*v23.x+v23.y*v23.y))); if ((angle1*180/pi) < minAngle) { minAngle = angle1*180/pi; } if ((angle2*180/pi) < minAngle) { minAngle = angle2*180/pi; } if ((angle3*180/pi) < minAngle) { minAngle = angle3*180/pi; } } // Output summary of results to screen printf("\n"); printf(" SUMMARY OF RESULTS:\n"); printf(" -------------------\n"); printf(" Number of Elements: %8i\n",Mesh.get_NumElems()); printf(" Number of Physical Elements: %8i\n",Mesh.get_NumPhysElems()); printf(" Number of Ghost Elements: %8i\n",Mesh.get_NumGhostElems()); printf(" Number of Nodes: %8i\n",Mesh.get_NumNodes()); printf(" Number of Physical Nodes: %8i\n",Mesh.get_NumPhysNodes()); printf(" Number of Boundary Nodes: %8i\n",Mesh.get_NumBndNodes()); printf(" Number of Edges: %8i\n",Mesh.get_NumEdges()); printf(" Number of Boundary Edges: %8i\n",Mesh.get_NumBndEdges()); printf("\n"); printf(" Total Area Covered: %24.16e\n",totalarea); printf(" Area Ratio: small/large: %24.16e\n",minarea/maxarea); printf(" Angle Ratio: minAngle/60: %24.16e\n",minAngle/60.0); printf("\n"); }
void ComputeError(const int space_order, const mesh& Mesh, const dTensor1& phi, const dTensor2& E1, const dTensor2& E2, void (*PhiFunc)(const dTensor2& xpts,dTensor2& phi_ex), void (*EfieldFunc)(const dTensor2& xpts,dTensor2& Efield_ex)) { // Potential const int NumPhysNodes = phi.getsize();//Mesh.get_NumPhysNodes(); dTensor2 xpts(NumPhysNodes,2); dTensor2 phi_ex(NumPhysNodes,1); double phi_err; double phi_rel; switch(space_order) { case 2: for (int i=1; i<=NumPhysNodes; i++) { xpts.set(i,1, Mesh.get_node(i,1) ); xpts.set(i,2, Mesh.get_node(i,2) ); } PhiFunc(xpts,phi_ex); phi_err = 0.0; phi_rel = 0.0; for (int i=1; i<=NumPhysNodes; i++) { phi_rel = phi_rel + pow(phi_ex.get(i,1),2); phi_err = phi_err + pow(phi_ex.get(i,1)-phi.get(i),2); } phi_err = sqrt(phi_err/phi_rel); break; case 3: for (int i=1; i<=NumPhysNodes; i++) { xpts.set(i,1, Mesh.get_sub_node(i,1) ); xpts.set(i,2, Mesh.get_sub_node(i,2) ); } PhiFunc(xpts,phi_ex); phi_err = 0.0; phi_rel = 0.0; for (int i=1; i<=NumPhysNodes; i++) { phi_rel = phi_rel + pow(phi_ex.get(i,1),2); phi_err = phi_err + pow(phi_ex.get(i,1)-phi.get(i),2); } phi_err = sqrt(phi_err/phi_rel); break; } // Electric field components void L2Project_Unst(const int istart, const int iend, const int QuadOrder, const int BasisOrder_fout, const mesh& Mesh, dTensor3* fout, void (*Func)(const dTensor2&,dTensor2&)); const int NumElems = Mesh.get_NumElems(); const int NumPhysElems = Mesh.get_NumPhysElems(); const int kmax = E1.getsize(2); dTensor3 Efield_ex(NumElems,2,kmax); L2Project_Unst(1,NumElems,space_order,space_order, Mesh,&Efield_ex,EfieldFunc); double E1_err = 0.0; double E1_rel = 0.0; double E2_err = 0.0; double E2_rel = 0.0; for (int i=1; i<=NumPhysElems; i++) { double Area = Mesh.get_area_prim(i); double tmp1 = 0.0; double tmp2 = 0.0; double tmp1_rel = 0.0; double tmp2_rel = 0.0; for (int k=1; k<=kmax; k++) { tmp1 = tmp1 + pow((E1.get(i,k)-Efield_ex.get(i,1,k)),2); tmp2 = tmp2 + pow((E2.get(i,k)-Efield_ex.get(i,2,k)),2); tmp1_rel = tmp1_rel + pow((Efield_ex.get(i,1,k)),2); tmp2_rel = tmp2_rel + pow((Efield_ex.get(i,2,k)),2); } E1_err = E1_err + Area*tmp1; E2_err = E2_err + Area*tmp2; E1_rel = E1_rel + Area*tmp1_rel; E2_rel = E2_rel + Area*tmp2_rel; } E1_err = sqrt(E1_err/E1_rel); E2_err = sqrt(E2_err/E2_rel); // Summary printf(" |----------------------------\n"); printf(" | Errors:\n"); printf(" |----------------------------\n"); printf(" | phi_err = %e\n",phi_err); printf(" | E1_err = %e\n",E1_err); printf(" | E2_err = %e\n",E2_err); printf(" |----------------------------\n"); printf("\n"); }
// All-purpose routine for computing the L2-projection // of various functions onto the gradient of the Legendre basis // (Unstructured grid version) // void L2ProjectGrad_Unst( const dTensor2* vel_vec, const int istart, const int iend, const int QuadOrder, const int BasisOrder_qin, const int BasisOrder_auxin, const int BasisOrder_fout, const mesh& Mesh, const dTensor3* qin, const dTensor3* auxin, dTensor3* fout, void (*Func)(const dTensor2* vel_vec, const dTensor2&,const dTensor2&, const dTensor2&,dTensor3&)) { // starting and ending indeces const int NumElems = Mesh.get_NumElems(); assert_ge(istart,1); assert_le(iend,NumElems); // qin variable assert_eq(NumElems,qin->getsize(1)); const int meqn = qin->getsize(2); const int kmax_qin = qin->getsize(3); assert_eq(kmax_qin,(BasisOrder_qin*(BasisOrder_qin+1))/2); // auxin variable assert_eq(NumElems,auxin->getsize(1)); const int maux = auxin->getsize(2); const int kmax_auxin = auxin->getsize(3); assert_eq(kmax_auxin,(BasisOrder_auxin*(BasisOrder_auxin+1))/2); // fout variables assert_eq(NumElems,fout->getsize(1)); const int mcomps_out = fout->getsize(2); const int kmax_fout = fout->getsize(3); assert_eq(kmax_fout,(BasisOrder_fout*(BasisOrder_fout+1))/2); // number of quadrature points assert_ge(QuadOrder,1); assert_le(QuadOrder,5); int mpoints; switch ( QuadOrder ) { case 1: mpoints = 0; break; case 2: mpoints = 1; break; case 3: mpoints = 6; break; case 4: mpoints = 7; break; case 5: mpoints = 16; break; } // trivial case if ( QuadOrder==1 ) { for (int i=istart; i<=iend; i++) for (int m=1; m<=mcomps_out; m++) for (int k=1; k<=kmax_fout; k++) { fout->set(i,m,k, 0.0 ); } } else { const int kmax = iMax(iMax(kmax_qin,kmax_auxin),kmax_fout); dTensor2 spts(mpoints,2); dTensor1 wgts(mpoints); dTensor2 xpts(mpoints,2); dTensor2 qvals(mpoints,meqn); dTensor2 auxvals(mpoints,maux); dTensor3 fvals(mpoints,mcomps_out,2); dTensor2 mu(mpoints,kmax); // monomial basis (non-orthogonal) dTensor2 phi(mpoints,kmax); // Legendre basis (orthogonal) dTensor2 mu_xi(mpoints,kmax_fout); // xi-derivative of monomial basis (non-orthogonal) dTensor2 mu_eta(mpoints,kmax_fout); // eta-derivative of monomial basis (non-orthogonal) dTensor2 phi_xi(mpoints,kmax_fout); // xi-derivative of Legendre basis (orthogonal) dTensor2 phi_eta(mpoints,kmax_fout); // eta-derivative of Legendre basis (orthogonal) dTensor2 phi_x(mpoints,kmax_fout); // x-derivative of Legendre basis (orthogonal) dTensor2 phi_y(mpoints,kmax_fout); // y-derivative of Legendre basis (orthogonal) switch ( QuadOrder ) { case 2: spts.set(1,1, 0.0 ); spts.set(1,2, 0.0 ); wgts.set(1, 0.5 ); break; case 3: spts.set(1,1, 0.112615157582632 ); spts.set(1,2, 0.112615157582632 ); spts.set(2,1, -0.225230315165263 ); spts.set(2,2, 0.112615157582632 ); spts.set(3,1, 0.112615157582632 ); spts.set(3,2, -0.225230315165263 ); spts.set(4,1, -0.241757119823562 ); spts.set(4,2, -0.241757119823562 ); spts.set(5,1, 0.483514239647126 ); spts.set(5,2, -0.241757119823562 ); spts.set(6,1, -0.241757119823562 ); spts.set(6,2, 0.483514239647126 ); wgts.set(1, 0.1116907948390055 ); wgts.set(2, 0.1116907948390055 ); wgts.set(3, 0.1116907948390055 ); wgts.set(4, 0.0549758718276610 ); wgts.set(5, 0.0549758718276610 ); wgts.set(6, 0.0549758718276610 ); break; case 4: spts.set(1,1, 0.000000000000000 ); spts.set(1,2, 0.000000000000000 ); spts.set(2,1, 0.136808730771782 ); spts.set(2,2, 0.136808730771782 ); spts.set(3,1, -0.273617461543563 ); spts.set(3,2, 0.136808730771782 ); spts.set(4,1, 0.136808730771782 ); spts.set(4,2, -0.273617461543563 ); spts.set(5,1, -0.232046826009877 ); spts.set(5,2, -0.232046826009877 ); spts.set(6,1, 0.464093652019754 ); spts.set(6,2, -0.232046826009877 ); spts.set(7,1, -0.232046826009877 ); spts.set(7,2, 0.464093652019754 ); wgts.set(1, 0.1125000000000000 ); wgts.set(2, 0.0661970763942530 ); wgts.set(3, 0.0661970763942530 ); wgts.set(4, 0.0661970763942530 ); wgts.set(5, 0.0629695902724135 ); wgts.set(6, 0.0629695902724135 ); wgts.set(7, 0.0629695902724135 ); break; case 5: spts.set(1,1, 0.000000000000000 ); spts.set(1,2, 0.000000000000000 ); spts.set(2,1, 0.125959254959390 ); spts.set(2,2, 0.125959254959390 ); spts.set(3,1, -0.251918509918779 ); spts.set(3,2, 0.125959254959390 ); spts.set(4,1, 0.125959254959390 ); spts.set(4,2, -0.251918509918779 ); spts.set(5,1, -0.162764025581573 ); spts.set(5,2, -0.162764025581573 ); spts.set(6,1, 0.325528051163147 ); spts.set(6,2, -0.162764025581573 ); spts.set(7,1, -0.162764025581573 ); spts.set(7,2, 0.325528051163147 ); spts.set(8,1, -0.282786105016302 ); spts.set(8,2, -0.282786105016302 ); spts.set(9,1, 0.565572210032605 ); spts.set(9,2, -0.282786105016302 ); spts.set(10,1, -0.282786105016302 ); spts.set(10,2, 0.565572210032605 ); spts.set(11,1, -0.324938555923375 ); spts.set(11,2, -0.070220503698695 ); spts.set(12,1, -0.324938555923375 ); spts.set(12,2, 0.395159059622071 ); spts.set(13,1, -0.070220503698695 ); spts.set(13,2, -0.324938555923375 ); spts.set(14,1, -0.070220503698695 ); spts.set(14,2, 0.395159059622071 ); spts.set(15,1, 0.395159059622071 ); spts.set(15,2, -0.324938555923375 ); spts.set(16,1, 0.395159059622071 ); spts.set(16,2, -0.070220503698695 ); wgts.set(1, 0.0721578038388935 ); wgts.set(2, 0.0475458171336425 ); wgts.set(3, 0.0475458171336425 ); wgts.set(4, 0.0475458171336425 ); wgts.set(5, 0.0516086852673590 ); wgts.set(6, 0.0516086852673590 ); wgts.set(7, 0.0516086852673590 ); wgts.set(8, 0.0162292488115990 ); wgts.set(9, 0.0162292488115990 ); wgts.set(10, 0.0162292488115990 ); wgts.set(11, 0.0136151570872175 ); wgts.set(12, 0.0136151570872175 ); wgts.set(13, 0.0136151570872175 ); wgts.set(14, 0.0136151570872175 ); wgts.set(15, 0.0136151570872175 ); wgts.set(16, 0.0136151570872175 ); break; } // Loop over each quadrature point and construct monomial polys for (int m=1; m<=mpoints; m++) { // coordinates const double xi = spts.get(m,1); const double xi2 = xi*xi; const double xi3 = xi2*xi; const double xi4 = xi3*xi; const double eta = spts.get(m,2); const double eta2 = eta*eta; const double eta3 = eta2*eta; const double eta4 = eta3*eta; // monomial basis functions at each gaussian quadrature point switch( kmax ) { case 15: // fifth order mu.set(m, 15, eta4 ); mu.set(m, 14, xi4 ); mu.set(m, 13, xi2*eta2 ); mu.set(m, 12, eta3*xi ); mu.set(m, 11, xi3*eta ); case 10: // fourth order mu.set(m, 10, eta3 ); mu.set(m, 9, xi3 ); mu.set(m, 8, xi*eta2 ); mu.set(m, 7, eta*xi2 ); case 6: // third order mu.set(m, 6, eta2 ); mu.set(m, 5, xi2 ); mu.set(m, 4, xi*eta ); case 3: // second order mu.set(m, 3, eta ); mu.set(m, 2, xi ); case 1: // first order mu.set(m, 1, 1.0 ); break; } // Loop over each quadrature point and construct Legendre polys for (int i=1; i<=kmax; i++) { double tmp = 0.0; for (int j=1; j<=i; j++) { tmp = tmp + Mmat[i-1][j-1]*mu.get(m,j); } phi.set(m,i, tmp ); } // Gradient of monomial basis functions at each gaussian quadrature point switch( kmax_fout ) { case 15: // fifth order mu_xi.set( m,15, 0.0 ); mu_xi.set( m,14, 4.0*xi3 ); mu_xi.set( m,13, 2.0*xi*eta2 ); mu_xi.set( m,12, eta3 ); mu_xi.set( m,11, 3.0*xi2*eta ); mu_eta.set( m,15, 4.0*eta3 ); mu_eta.set( m,14, 0.0 ); mu_eta.set( m,13, 2.0*xi2*eta ); mu_eta.set( m,12, 3.0*eta2*xi ); mu_eta.set( m,11, xi3 ); case 10: // fourth order mu_xi.set( m,10, 0.0 ); mu_xi.set( m,9, 3.0*xi2 ); mu_xi.set( m,8, eta2 ); mu_xi.set( m,7, 2.0*eta*xi ); mu_eta.set( m,10, 3.0*eta2 ); mu_eta.set( m,9, 0.0 ); mu_eta.set( m,8, 2.0*eta*xi ); mu_eta.set( m,7, xi2 ); case 6: // third order mu_xi.set( m,6, 0.0 ); mu_xi.set( m,5, 2.0*xi ); mu_xi.set( m,4, eta ); mu_eta.set( m,6, 2.0*eta ); mu_eta.set( m,5, 0.0 ); mu_eta.set( m,4, xi ); case 3: // second order mu_xi.set( m,3, 0.0 ); mu_xi.set( m,2, 1.0 ); mu_eta.set( m,3, 1.0 ); mu_eta.set( m,2, 0.0 ); case 1: // first order mu_xi.set( m,1, 0.0 ); mu_eta.set( m,1, 0.0 ); break; } // Loop over each quadrature point and construct Legendre polys for (int i=1; i<=kmax_fout; i++) { double tmp1 = 0.0; double tmp2 = 0.0; for (int j=1; j<=i; j++) { tmp1 = tmp1 + Mmat[i-1][j-1]*mu_xi.get(m,j); tmp2 = tmp2 + Mmat[i-1][j-1]*mu_eta.get(m,j); } phi_xi.set(m,i, tmp1 ); phi_eta.set(m,i, tmp2 ); } } // ------------------------------------------------------------- // Loop over every grid cell indexed by user supplied parameters // described by istart...iend // ------------------------------------------------------------- #pragma omp parallel for for (int i=istart; i<=iend; i++) { // Find center of current cell const int i1 = Mesh.get_tnode(i,1); const int i2 = Mesh.get_tnode(i,2); const int i3 = Mesh.get_tnode(i,3); const double x1 = Mesh.get_node(i1,1); const double y1 = Mesh.get_node(i1,2); const double x2 = Mesh.get_node(i2,1); const double y2 = Mesh.get_node(i2,2); const double x3 = Mesh.get_node(i3,1); const double y3 = Mesh.get_node(i3,2); const double xc = (x1+x2+x3)/3.0; const double yc = (y1+y2+y3)/3.0; // Compute q, aux and fvals at each Gaussian Quadrature point // for this current cell indexed by (i,j) // Save results into dTensor2 qvals, auxvals and fvals. for (int m=1; m<=mpoints; m++) { // convert phi_xi and phi_eta derivatives // to phi_x and phi_y derivatives through Jacobian for (int k=1; k<=kmax_fout; k++) { phi_x.set(m,k, Mesh.get_jmat(i,1,1)*phi_xi.get(m,k) + Mesh.get_jmat(i,1,2)*phi_eta.get(m,k) ); phi_y.set(m,k, Mesh.get_jmat(i,2,1)*phi_xi.get(m,k) + Mesh.get_jmat(i,2,2)*phi_eta.get(m,k) ); } // point on the unit triangle const double s = spts.get(m,1); const double t = spts.get(m,2); // point on the physical triangle xpts.set(m,1, xc + (x2-x1)*s + (x3-x1)*t ); xpts.set(m,2, yc + (y2-y1)*s + (y3-y1)*t ); // Solution values (q) at each grid point for (int me=1; me<=meqn; me++) { qvals.set(m,me, 0.0 ); for (int k=1; k<=kmax_qin; k++) { qvals.set(m,me, qvals.get(m,me) + phi.get(m,k) * qin->get(i,me,k) ); } } // Auxiliary values (aux) at each grid point for (int ma=1; ma<=maux; ma++) { auxvals.set(m,ma, 0.0 ); for (int k=1; k<=kmax_auxin; k++) { auxvals.set(m,ma, auxvals.get(m,ma) + phi.get(m,k) * auxin->get(i,ma,k) ); } } } // Call user-supplied function to set fvals Func(vel_vec, xpts, qvals, auxvals, fvals); // Evaluate integral on current cell (project onto Legendre basis) // using Gaussian Quadrature for the integration for (int m1=1; m1<=mcomps_out; m1++) for (int m2=1; m2<=kmax_fout; m2++) { double tmp = 0.0; for (int k=1; k<=mpoints; k++) { tmp = tmp + wgts.get(k)* ( fvals.get(k,m1,1)*phi_x.get(k,m2) + fvals.get(k,m1,2)*phi_y.get(k,m2) ); } fout->set(i, m1, m2, 2.0*tmp ); } } } }
// Right-hand side for hyperbolic PDE in divergence form // // q_t = -( f(q,x,y,t)_x + g(q,x,y,t)_y ) + Psi(q,x,y,t) // void LaxWendroff_Unst(double dt, const mesh& Mesh, const edge_data_Unst& EdgeData, dTensor3& aux, // SetBndValues modifies ghost cells dTensor3& q, // SetBndValues modifies ghost cells dTensor3& Lstar, dTensor1& smax) { const int NumElems = Mesh.get_NumElems(); const int NumPhysElems = Mesh.get_NumPhysElems(); const int NumEdges = Mesh.get_NumEdges(); const int meqn = q.getsize(2); const int kmax = q.getsize(3); const int maux = aux.getsize(2); const int space_order = dogParams.get_space_order(); dTensor3 EdgeFluxIntegral(NumElems,meqn,kmax); dTensor3 ElemFluxIntegral(NumElems,meqn,kmax); dTensor3 Psi(NumElems,meqn,kmax); // --------------------------------------------------------- // Boundary Conditions SetBndValues_Unst(Mesh, &q, &aux); // --------------------------------------------------------- // --------------------------------------------------------------------- // // Part 0: Compute the Lax-Wendroff "flux" function: // // Here, we include the extra information about time derivatives. // --------------------------------------------------------------------- // dTensor3 F(NumElems, meqn, kmax ); F.setall(0.); dTensor3 G(NumElems, meqn, kmax ); G.setall(0.); L2ProjectLxW_Unst( dogParams.get_time_order(), 1.0, 0.5*dt, dt*dt/6.0, 1, NumElems, space_order, space_order, space_order, space_order, Mesh, &q, &aux, &F, &G, &FluxFunc, &DFluxFunc, &D2FluxFunc ); // --------------------------------------------------------- // Part I: compute source term // --------------------------------------------------------- if ( dogParams.get_source_term()>0 ) { // eprintf("error: have not implemented source term for LxW solver."); printf("Source term has not been implemented for LxW solver. Terminating program."); exit(1); } Lstar.setall(0.); // --------------------------------------------------------- // --------------------------------------------------------- // Part II: compute flux integral on element edges // --------------------------------------------------------- // Loop over all interior edges EdgeFluxIntegral.setall(0.); ElemFluxIntegral.setall(0.); #pragma omp parallel for // Loop over all interior edges for (int i=1; i<=NumEdges; i++) { // Edge coordinates double x1 = Mesh.get_edge(i,1); double y1 = Mesh.get_edge(i,2); double x2 = Mesh.get_edge(i,3); double y2 = Mesh.get_edge(i,4); // Elements on either side of edge int ileft = Mesh.get_eelem(i,1); int iright = Mesh.get_eelem(i,2); double Areal = Mesh.get_area_prim(ileft); double Arear = Mesh.get_area_prim(iright); // Scaled normal to edge dTensor1 nhat(2); nhat.set(1, (y2-y1) ); nhat.set(2, (x1-x2) ); // Variables to store flux integrals along edge dTensor2 Fr_tmp(meqn,dogParams.get_space_order()); dTensor2 Fl_tmp(meqn,dogParams.get_space_order()); // Loop over number of quadrature points along each edge for (int ell=1; ell<=dogParams.get_space_order(); ell++) { dTensor1 Ql(meqn), Qr(meqn); dTensor1 ffl(meqn), ffr(meqn); // << -- NEW PART -- >> dTensor1 Auxl(maux), Auxr(maux); // Riemann data - q for (int m=1; m<=meqn; m++) { Ql.set(m, 0.0 ); Qr.set(m, 0.0 ); // << -- NEW PART, ffl and ffr -- >> // ffl.set(m, 0.0 ); ffr.set(m, 0.0 ); for (int k=1; k<=kmax; k++) { Ql.set(m, Ql.get(m) + EdgeData.phi_left->get(i,ell,k) *q.get(ileft, m,k) ); Qr.set(m, Qr.get(m) + EdgeData.phi_right->get(i,ell,k) *q.get(iright,m,k) ); // << -- NEW PART, ffl and ffr -- >> // // Is this the correct way to use the normal vector? ffl.set(m, ffl.get(m) + EdgeData.phi_left->get (i, ell, k) * ( nhat.get(1)*F.get( ileft, m, k) + nhat.get(2)*G.get( ileft, m, k) ) ); ffr.set(m, ffr.get(m) + EdgeData.phi_right->get(i, ell, k) * ( nhat.get(1)*F.get(iright, m, k) + nhat.get(2)*G.get(iright, m, k) ) ); } } // Riemann data - aux for (int m=1; m<=maux; m++) { Auxl.set(m, 0.0 ); Auxr.set(m, 0.0 ); for (int k=1; k<=kmax; k++) { Auxl.set(m, Auxl.get(m) + EdgeData.phi_left->get(i,ell,k) * aux.get(ileft, m,k) ); Auxr.set(m, Auxr.get(m) + EdgeData.phi_right->get(i,ell,k) * aux.get(iright,m,k) ); } } // Solve Riemann problem dTensor1 xedge(2); double s = EdgeData.xpts1d->get(ell); xedge.set(1, x1 + 0.5*(s+1.0)*(x2-x1) ); xedge.set(2, y1 + 0.5*(s+1.0)*(y2-y1) ); // Solve the Riemann problem for this edge dTensor1 Fl(meqn), Fr(meqn); // Use the time-averaged fluxes to define left and right values for // the Riemann solver. const double smax_edge = RiemannSolveLxW( nhat, xedge, Ql, Qr, Auxl, Auxr, ffl, ffr, Fl, Fr); smax.set(i, Max(smax_edge,smax.get(i)) ); // Construct fluxes for (int m=1; m<=meqn; m++) { Fr_tmp.set(m,ell, Fr.get(m) ); Fl_tmp.set(m,ell, Fl.get(m) ); } } // Add edge integral to line integral around the full element for (int m=1; m<=meqn; m++) for (int k=1; k<=kmax; k++) { double Fl_sum = 0.0; double Fr_sum = 0.0; for (int ell=1; ell<=dogParams.get_space_order(); ell++) { Fl_sum = Fl_sum + 0.5*EdgeData.wgts1d->get(ell) *EdgeData.phi_left->get(i,ell,k) *Fl_tmp.get(m,ell); Fr_sum = Fr_sum + 0.5*EdgeData.wgts1d->get(ell) *EdgeData.phi_right->get(i,ell,k)*Fr_tmp.get(m,ell); } EdgeFluxIntegral.set(ileft, m,k, EdgeFluxIntegral.get(ileft, m,k) + Fl_sum/Areal ); EdgeFluxIntegral.set(iright,m,k, EdgeFluxIntegral.get(iright,m,k) - Fr_sum/Arear ); } } // --------------------------------------------------------- // --------------------------------------------------------- // Part III: compute intra-element contributions // --------------------------------------------------------- if( dogParams.get_space_order() > 1 ) { L2ProjectGradAddLegendre_Unst(1, NumPhysElems, space_order, Mesh, &F, &G, &ElemFluxIntegral ); } // --------------------------------------------------------- // --------------------------------------------------------- // Part IV: construct Lstar // --------------------------------------------------------- if (dogParams.get_source_term()==0) // Without Source Term { #pragma omp parallel for for (int i=1; i<=NumPhysElems; i++) for (int m=1; m<=meqn; m++) for (int k=1; k<=kmax; k++) { double tmp = ElemFluxIntegral.get(i,m,k) - EdgeFluxIntegral.get(i,m,k); Lstar.set(i,m,k, tmp ); } } else // With Source Term { #pragma omp parallel for for (int i=1; i<=NumPhysElems; i++) for (int m=1; m<=meqn; m++) for (int k=1; k<=kmax; k++) { // double tmp = ElemFluxIntegral.get(i,m,k) // - EdgeFluxIntegral.get(i,m,k) // + Psi.get(i,m,k); // Lstar.set(i,m,k, tmp ); printf("Source term has not been implemented for LxW solver. Terminating program."); exit(1); } } // --------------------------------------------------------- // --------------------------------------------------------- // Part V: add extra contributions to Lstar // --------------------------------------------------------- // Call LstarExtra LstarExtra_Unst(Mesh, &q, &aux, &Lstar); // --------------------------------------------------------- // --------------------------------------------------------- // Part VI: artificial viscosity limiter // --------------------------------------------------------- // if (dogParams.get_space_order()>1 && // dogParams.using_viscosity_limiter()) // { ArtificialViscosity(&aux,&q,&Lstar); } // --------------------------------------------------------- }
// This file should be identical to DogSolveRK_Unst, with the exception that all // output printing statements are silenced. // // Advance the solution qold to qnew over time interval tstart to tend. // // All local information is allocated within this function. The only part // that gets shared are time values passed through dogStateUnst2. This class // should be modified to accept the state variable, q and aux in place of only // containing time information as is currently the case. (-DS) double DogSolveRK_Unst_Quiet( const dTensor2* vel_vec, const mesh& Mesh, const edge_data_Unst& EdgeData, dTensor3& aux, dTensor3& qold, dTensor3& qnew, const double tstart, const double tend, DogStateUnst2& dogStateUnst2) { const int mx = qnew.getsize(1); const int meqn = qnew.getsize(2); const int kmax = qnew.getsize(3); const int maux = aux.getsize(2); const double* cflv = dogParams.get_cflv(); const int nv = dogParams.get_nv(); RKinfo rk; SetRKinfo(dogParams.get_time_order(),rk); // define local variables int n_step = 0; double t = tstart; double dt = dogStateUnst2.get_initial_dt(); const double CFL_max = cflv[1]; const double CFL_target = cflv[2]; double cfl = 0.0; double dtmin = dt; double dtmax = dt; const int NumElems = Mesh.get_NumElems(); // Number of total elements in mesh const int NumNodes = Mesh.get_NumNodes(); // Number of nodes in mesh const int NumEdges = Mesh.get_NumEdges(); // Number of edges in mesh dTensor3 qstar(NumElems,meqn,kmax); dTensor3 q1(NumElems,meqn,kmax); dTensor3 q2(NumElems,meqn,kmax); dTensor3 auxstar(NumElems,maux,kmax); dTensor3 Lstar(NumElems,meqn,kmax); dTensor3 Lold(NumElems,meqn,kmax); dTensor3 auxold(NumElems,maux,kmax); dTensor1 smax(NumEdges); void L2Project_Unst( const dTensor2* vel_vec, const int istart, const int iend, const int QuadOrder, const int BasisOrder_qin, const int BasisOrder_auxin, const int BasisOrder_fout, const mesh& Mesh, const dTensor3* qin, const dTensor3* auxin, dTensor3* fout, void (*Func)(const dTensor2* vel_vec, const dTensor2&,const dTensor2&, const dTensor2&,dTensor2&)); // JUNK here: void AuxFuncWrapper( const dTensor2* vel_vec, const dTensor2& xpts, const dTensor2& NOT_USED_1, const dTensor2& NOT_USED_2, dTensor2& auxvals); const int space_order = dogParams.get_space_order(); if( maux > 0 ) { printf("WARNING: maux = %d should be zero for Vlasov routines.", maux); printf(" Modify parameters.ini to remove this warning\n" ); L2Project_Unst(vel_vec,1,NumElems, space_order,space_order,space_order,space_order, Mesh,&qnew,&aux,&aux,&AuxFuncWrapper); } // Set initialize qstar and auxstar values qstar.copyfrom(qold); auxstar.copyfrom(aux); // Runge-Kutta time stepping while (t<tend) { // initialize time step int m_accept = 0; n_step = n_step + 1; // check if max number of time steps exceeded if( n_step > nv ) { eprintf(" Error in DogSolveRK_Unst.cpp: " " Exceeded allowed # of time steps \n" " n_step = %d\n" " nv = %d\n\n", n_step, nv); } // copy qnew into qold qold.copyfrom(qnew); auxold.copyfrom(aux); // keep trying until we get a dt that does not violate CFL condition while (m_accept==0) { // set current time double told = t; if (told+dt > tend) { dt = tend - told; } t = told + dt; // TODO - this needs to be performed at the 'local' level dogStateUnst2.set_time ( told ); dogStateUnst2.set_dt ( dt ); // Set initial maximum wave speed to zero smax.setall(0.); // Take a full time step of size dt switch ( dogParams.get_time_order() ) { case 1: // First order in time (1-stage) // ----------------------------------------------- // Stage #1 (the only one in this case) rk.mstage = 1; BeforeStep_Unst(dt,Mesh,aux,qnew); ConstructL_Unst(told, vel_vec,Mesh,EdgeData,aux,qnew,Lstar,smax); UpdateSoln_Unst(rk.alpha1->get(rk.mstage),rk.alpha2->get(rk.mstage), rk.beta->get(rk.mstage),dt,Mesh,aux, qnew, Lstar, qnew); AfterStep_Unst(dt,Mesh,aux,qnew); // ----------------------------------------------- break; case 2: // Second order in time (2-stages) // ----------------------------------------------- // Stage #1 rk.mstage = 1; dogStateUnst2.set_time(told); BeforeStep_Unst(dt,Mesh,aux,qnew); ConstructL_Unst(told,vel_vec,Mesh,EdgeData,aux,qnew,Lstar,smax); UpdateSoln_Unst( rk.alpha1->get(rk.mstage),rk.alpha2->get(rk.mstage), rk.beta->get(rk.mstage), dt, Mesh, aux, qnew, Lstar, qstar); AfterStep_Unst(dt, Mesh, auxstar, qstar); // ------------------------------------------------ // Stage #2 rk.mstage = 2; dogStateUnst2.set_time(told+dt); BeforeStep_Unst(dt, Mesh, auxstar, qstar); ConstructL_Unst(told+1.0*dt, vel_vec, Mesh, EdgeData, aux, qstar, Lstar, smax); UpdateSoln_Unst(rk.alpha1->get(rk.mstage), rk.alpha2->get(rk.mstage), rk.beta->get(rk.mstage), dt, Mesh, auxstar, qstar, Lstar, qnew); AfterStep_Unst(dt, Mesh, aux, qnew); // ------------------------------------------------ break; case 3: // Third order in time (3-stages) // qnew = alpha1 * qstar + alpha2 * qnew + beta * dt * L( qstar ) // alpha1 = 1.0 // alpha2 = 0.0 // beta = 1.0 // ------------------------------------------------ // Stage #1 rk.mstage = 1; dogStateUnst2.set_time(told); BeforeStep_Unst(dt,Mesh,aux,qnew); ConstructL_Unst(told, vel_vec,Mesh,EdgeData,aux,qnew,Lstar,smax); Lold.copyfrom(Lstar); UpdateSoln_Unst(rk.alpha1->get(rk.mstage),rk.alpha2->get(rk.mstage), rk.beta->get(rk.mstage),dt,Mesh,aux,qnew,Lstar,qstar); AfterStep_Unst(dt,Mesh,aux,qstar); // ------------------------------------------------- // alpha1 = 0.75 // alpha2 = 0.25 // beta = 0.25 // Stage #2 rk.mstage = 2; dogStateUnst2.set_time(told+0.5*dt); BeforeStep_Unst(dt,Mesh,aux,qstar); ConstructL_Unst(told+dt, vel_vec,Mesh,EdgeData,aux,qstar,Lstar,smax); UpdateSoln_Unst(rk.alpha1->get(rk.mstage),rk.alpha2->get(rk.mstage), rk.beta->get(rk.mstage),dt,Mesh,aux,qnew,Lstar,qstar); AfterStep_Unst(dt,Mesh,aux,qstar); // -------------------------------------------------- // alpha1 = 2/3 // alpha2 = 1/3 // beta = 2/3 // Stage #3 rk.mstage = 3; dogStateUnst2.set_time(told+dt); BeforeStep_Unst(dt,Mesh,auxstar,qstar); ConstructL_Unst(told+0.5*dt,vel_vec,Mesh,EdgeData,auxstar,qstar,Lstar,smax); UpdateSoln_Unst(rk.alpha1->get(rk.mstage),rk.alpha2->get(rk.mstage), rk.beta->get(rk.mstage),dt,Mesh,aux,qstar,Lstar,qnew); AfterStep_Unst(dt,Mesh,aux,qnew); // -------------------------------------------------- break; default: unsupported_value_error(dogParams.get_time_order()); } // compute cfl number cfl = GetCFL_Unst(dt,Mesh,aux,smax); // output time step information // if (dogParams.get_verbosity()>0) // { // printf(" In DogSolveRK_Quiet: DogSolve2D ... Step %5d" // " CFL =%6.3f" // " dt =%11.3e" // " t =%11.3e\n", // n_step, cfl, dt, t); // } // choose new time step if (cfl>0.0) { dt = Min(dogParams.get_max_dt(), dt*CFL_target/cfl); dtmin = Min(dt,dtmin); dtmax = Max(dt,dtmax); } else { dt = dogParams.get_max_dt(); } // see whether to accept or reject this step if (cfl<=CFL_max) // accept { m_accept = 1; dogStateUnst2.set_time(t); // do any extra work // AfterFullTimeStep_Unst(dogStateUnst2.get_dt(),Mesh, // auxold,qold,Lold,aux,qnew); } else //reject { t = told; dogStateUnst2.set_time(told); // if( dogParams.get_verbosity() > 0 ) // { // printf("DogSolve2D rejecting step..." // "CFL number too large\n"); // } // copy qold into qnew qnew.copyfrom(qold); aux.copyfrom(auxold); // after reject function // AfterReject_Unst(Mesh,dt,aux,qnew); } } } // printf(" Finished! t = %2.3e and nsteps = %d\n", t, n_step ); // set initial time step for next call to DogSolveRK dogStateUnst2.set_initial_dt(dt); void DeleteRKInfo(RKinfo& rk); DeleteRKInfo(rk); return cfl; }
// This routine simply glues together many of the routines that are already // written in the Poisson solver library // // phi( 1:SubNumPhysNodes ) is a scalar quantity. // // E1 ( 1:NumElems, 1:kmax2d ) is a vector quantity. // E2 ( 1:NumElems, 1:kmax2d ) is a vector quantity. // // See also: ConvertEfieldOntoDGbasis void ComputeElectricField( const double t, const mesh& Mesh, const dTensorBC5& q, dTensor2& E1, dTensor2& E2) { // const int mx = q.getsize(1); assert_eq(mx,dogParamsCart2.get_mx()); const int my = q.getsize(2); assert_eq(my,dogParamsCart2.get_my()); const int NumElems = q.getsize(3); const int meqn = q.getsize(4); const int kmax = q.getsize(5); const int space_order = dogParams.get_space_order(); // unstructured parameters: const int kmax2d = E2.getsize(2); const int NumBndNodes = Mesh.get_NumBndNodes(); const int NumPhysNodes = Mesh.get_NumPhysNodes(); // Quick error check if( !Mesh.get_is_submesh() ) { printf("ERROR: mesh needs to have subfactor set to %d\n", space_order); printf("Go to Unstructured mesh and remesh the problem\n"); exit(-1); } const int SubFactor = Mesh.get_SubFactor(); assert_eq( NumElems, Mesh.get_NumElems() ); // -- Step 1: Compute rho -- // dTensor3 rho(NumElems, 1, kmax2d ); void ComputeDensity( const mesh& Mesh, const dTensorBC5& q, dTensor3& rho ); ComputeDensity( Mesh, q, rho ); // -- Step 2: Figure out how large phi needs to be int SubNumPhysNodes = 0; int SubNumBndNodes = 0; switch( dogParams.get_space_order() ) { case 1: SubNumPhysNodes = NumPhysNodes; SubNumBndNodes = NumBndNodes; break; case 2: SubNumPhysNodes = Mesh.get_SubNumPhysNodes(); SubNumBndNodes = Mesh.get_SubNumBndNodes(); if(SubFactor!=2) { printf("\n"); printf(" Error: for space_order = %i, need SubFactor = %i\n",space_order,2); printf(" SubFactor = %i\n",SubFactor); printf("\n"); exit(1); } break; case 3: SubNumPhysNodes = Mesh.get_SubNumPhysNodes(); SubNumBndNodes = Mesh.get_SubNumBndNodes(); if(SubFactor!=3) { printf("\n"); printf(" Error: for space_order = %i, need SubFactor = %i\n",space_order,3); printf(" SubFactor = %i\n",SubFactor); printf("\n"); exit(1); } break; default: printf("\n"); printf(" ERROR in RunDogpack_unst.cpp: space_order value not supported.\n"); printf(" space_order = %i\n",space_order); printf("\n"); exit(1); } // local storage: dTensor1 rhs(SubNumPhysNodes); dTensor1 phi(SubNumPhysNodes); // Get Cholesky factorization matrix R // // TODO - this should be saved earlier in the code rather than reading // from file every time we with to run a Poisson solve! // SparseCholesky R(SubNumPhysNodes); string outputdir = dogParams.get_outputdir(); R.init(outputdir); R.read(outputdir); // Create right-hand side vector void Rhs2D_unst(const int space_order, const mesh& Mesh, const dTensor3& rhs_dg, dTensor1& rhs); Rhs2D_unst(space_order, Mesh, rho, rhs); // Call Poisson solver void PoissonSolver2D_unst(const int space_order, const mesh& Mesh, const SparseCholesky& R, const dTensor1& rhs, dTensor1& phi, dTensor2& E1, dTensor2& E2); PoissonSolver2D_unst(space_order, Mesh, R, rhs, phi, E1, E2); // Compare errors with the exact Electric field: // void L2Project_Unst( const double time, const dTensor2* vel_vec, const int istart, const int iend, const int QuadOrder, const int BasisOrder_qin, const int BasisOrder_auxin, const int BasisOrder_fout, const mesh& Mesh, const dTensor3* qin, const dTensor3* auxin, dTensor3* fout, void (*Func)(const double t, const dTensor2* vel_vec, const dTensor2&,const dTensor2&, const dTensor2&,dTensor2&)); const int sorder = dogParams.get_space_order(); dTensor3 qtmp (NumElems, 2, kmax2d ); qtmp.setall(0.); dTensor3 auxtmp (NumElems, 0, kmax2d ); dTensor3 ExactE (NumElems, 2, kmax2d ); L2Project_Unst( t, NULL, 1, NumElems, sorder, sorder, sorder, sorder, Mesh, &qtmp, &auxtmp, &ExactE, &ExactElectricField ); // Compute errors on these two: // double err = 0.; for( int n=1; n <= NumElems; n++ ) for( int k=1; k <= kmax2d; k++ ) { err += Mesh.get_area_prim(n)*pow( ExactE.get(n,1,k) - E1.get(n,k), 2 ); err += Mesh.get_area_prim(n)*pow( ExactE.get(n,2,k) - E2.get(n,k), 2 ); } printf("error = %2.15e\n", err ); }
// This is the positivity preserving limiter proposed in // "Maximum-Principle-Satisfying and Positivity-Preserving // High Order Discontinuous Galerkin Schemes // for Conservation Laws on Triangular Meshes", Zhang, Xia and Shu // J. Sci. Comput. (2012). // // THIS METHOD ASSUMES THAT EVERY COMPONENT OF CONSERVED VARIABLES SHOULD STAY // POSITIVE. // // In order to implement this for a different scheme, one should rewrite, or // redefine what components should remain positiive. This will require // reworking the control flow logic for how time step lengths are chosen. void ApplyPosLimiter_Unst(const mesh& Mesh, const dTensor3& aux, dTensor3& q) { const int NumElems = Mesh.get_NumElems(); const int NumPhysElems = Mesh.get_NumPhysElems(); const int NumEdges = Mesh.get_NumEdges(); const int meqn = q.getsize(2); const int kmax = q.getsize(3); const int maux = aux.getsize(2); const int space_order = dogParams.get_space_order(); // Do nothing in the case of piecewise constants if( space_order == 1 ) { return; } // ------------------------------------------------ // // number of points where we want to check solution // // ------------------------------------------------ // const int space_order_sq = space_order*space_order; const int mpts_vec[] = {0, 3*space_order_sq, 18, 3*space_order_sq, 3*space_order_sq }; // TODO - FILL IN 2ND-ORDER CASE const int mpoints = mpts_vec[space_order-1]; // ---------------------------------------------------------- // // sample basis at all points where we want to check solution // // ---------------------------------------------------------- // dTensor2 spts(mpoints, 2); void SetPositivePoints_Unst(const int& space_order, dTensor2& spts); SetPositivePoints_Unst(space_order, spts); void SamplePhiAtPositivePoints_Unst(const int& space_order, const dTensor2& spts, dTensor2& phi); dTensor2 phi(mpoints, kmax); SamplePhiAtPositivePoints_Unst(space_order, spts, phi); // -------------------------------------------------------------- // // q_limited = Q1 + \theta ( q(xi,eta) - Q1 ) // // where theta = min(1, |Q1| / |Q1-m|; m = min_{i} q(xi_i, eta_i) // // -------------------------------------------------------------- // #pragma omp parallel for for(int i=1; i <= NumPhysElems; i++) for(int me=1; me <= meqn; me++) { double m = 0.0; for(int mp=1; mp <= mpoints; mp++) { // evaluate q at spts(mp) // double qnow = 0.0; for( int k=1; k <= kmax; k++ ) { qnow += q.get(i,me,k) * phi.get(mp,k); } m = Min(m, qnow); } double theta = 0.0; double Q1 = q.get(i,me,1); assert_ge( Q1, -1e-13 ); if( fabs( Q1 - m ) < 1.0e-14 ){ theta = 1.0; } else{ theta = Min( 1.0, fabs( Q1 / (Q1 - m) ) ); } // limit q // for( int k=2; k <= kmax; k++ ) { q.set(i,me,k, q.get(i,me,k) * theta ); } } }
// Modified version of the all purpose routine L2Project specifically written // for projecting the "time-averaged" flux function onto the basis function. // // This routine also returns the coefficients of the Lax Wendroff Flux // Function when expanded with legendre basis functions, and therefore the // basis expansions produced by this routine can be used for all of the // Riemann solves. // // --------------------------------------------------------------------- // Inputs should have the following sizes: // TODO - document the inputs here // --------------------------------------------------------------------- void L2ProjectLxW_Unst( const int mterms, const double alpha, const double beta_dt, const double charlie_dt, const int istart, const int iend, // Start-stop indices const int QuadOrder, const int BasisOrder_qin, const int BasisOrder_auxin, const int BasisOrder_fout, const mesh& Mesh, const dTensor3* qin, const dTensor3* auxin, // state vector dTensor3* F, dTensor3* G, // time-averaged Flux function void FluxFunc (const dTensor2& xpts, const dTensor2& Q, const dTensor2& Aux, dTensor3& flux), void DFluxFunc (const dTensor2& xpts, const dTensor2& Q, const dTensor2& aux, dTensor4& Dflux), void D2FluxFunc (const dTensor2& xpts, const dTensor2& Q, const dTensor2& aux, dTensor5& D2flux) ) { if( fabs( alpha ) < 1e-14 && fabs( beta_dt ) < 1e-14 && fabs( charlie_dt ) < 1e-14 ) { F->setall(0.); G->setall(0.); return; } // starting and ending indices const int NumElems = Mesh.get_NumElems(); assert_ge(istart,1); assert_le(iend,NumElems); // qin variable assert_eq(NumElems,qin->getsize(1)); const int meqn = qin->getsize(2); const int kmax_qin = qin->getsize(3); assert_eq(kmax_qin,(BasisOrder_qin*(BasisOrder_qin+1))/2); // auxin variable assert_eq(NumElems,auxin->getsize(1)); const int maux = auxin->getsize(2); const int kmax_auxin = auxin->getsize(3); assert_eq(kmax_auxin,(BasisOrder_auxin*(BasisOrder_auxin+1))/2); // fout variables assert_eq(NumElems, F->getsize(1)); const int mcomps_out = F->getsize(2); const int kmax_fout = F->getsize(3); assert_eq(kmax_fout, (BasisOrder_fout*(BasisOrder_fout+1))/2 ); // number of quadrature points assert_ge(QuadOrder, 1); assert_le(QuadOrder, 5); // Number of quadrature points int mpoints; switch( QuadOrder ) { case 1: mpoints = 1; break; case 2: mpoints = 3; break; case 3: mpoints = 6; break; case 4: mpoints = 12; break; case 5: mpoints = 16; break; } const int kmax = iMax(iMax(kmax_qin, kmax_auxin), kmax_fout); dTensor2 phi(mpoints, kmax); // Legendre basis (orthogonal) dTensor2 spts(mpoints, 2); // List of quadrature points dTensor1 wgts(mpoints); // List of quadrature weights setQuadPoints_Unst( QuadOrder, wgts, spts ); // ---------------------------------------------------------------------- // // Evaluate the basis functions at each point SetLegendreAtPoints_Unst(spts, phi); // ---------------------------------------------------------------------- // // ---------------------------------------------------------------------- // // First-order derivatives dTensor2 phi_xi (mpoints, kmax ); dTensor2 phi_eta(mpoints, kmax ); SetLegendreGrad_Unst( spts, phi_xi, phi_eta ); // ---------------------------------------------------------------------- // // ---------------------------------------------------------------------- // // Second-order derivatives dTensor2 phi_xi2 (mpoints, kmax ); dTensor2 phi_xieta(mpoints, kmax ); dTensor2 phi_eta2 (mpoints, kmax ); LegendreDiff2_Unst(spts, &phi_xi2, &phi_xieta, &phi_eta2 ); // ---------------------------------------------------------------------- // // ------------------------------------------------------------- // // Loop over every grid cell indexed by user supplied parameters // // described by istart...iend, jstart...jend // // ------------------------------------------------------------- // #pragma omp parallel for for (int i=istart; i<=iend; i++) { // These need to be defined locally. Each mesh element carries its // own change of basis matrix, so these need to be recomputed for // each element. The canonical derivatives, phi_xi, and phi_eta can // be computed and shared for each element. // First-order derivatives dTensor2 phi_x(mpoints, kmax_fout); // x-derivative of Legendre basis (orthogonal) dTensor2 phi_y(mpoints, kmax_fout); // y-derivative of Legendre basis (orthogonal) // Second-order derivatives dTensor2 phi_xx(mpoints, kmax_fout); // xx-derivative of Legendre basis (orthogonal) dTensor2 phi_xy(mpoints, kmax_fout); // xy-derivative of Legendre basis (orthogonal) dTensor2 phi_yy(mpoints, kmax_fout); // yy-derivative of Legendre basis (orthogonal) //find center of current cell const int i1 = Mesh.get_tnode(i,1); const int i2 = Mesh.get_tnode(i,2); const int i3 = Mesh.get_tnode(i,3); // Corners: const double x1 = Mesh.get_node(i1,1); const double y1 = Mesh.get_node(i1,2); const double x2 = Mesh.get_node(i2,1); const double y2 = Mesh.get_node(i2,2); const double x3 = Mesh.get_node(i3,1); const double y3 = Mesh.get_node(i3,2); // Center of current cell: const double xc = (x1+x2+x3)/3.0; const double yc = (y1+y2+y3)/3.0; // Variables that need to be written to, and therefore are // created for each thread dTensor2 xpts (mpoints, 2); dTensor2 qvals (mpoints, meqn); dTensor2 auxvals(mpoints, maux); // local storage for Flux function its Jacobian, and the Hessian: dTensor3 fvals(mpoints, meqn, 2); // flux function (vector) dTensor4 A(mpoints, meqn, meqn, 2); // Jacobian of flux dTensor5 H(mpoints, meqn, meqn, meqn, 2); // Hessian of flux // Compute q, aux and fvals at each Gaussian Quadrature point // for this current cell indexed by (i,j) // Save results into dTensor2 qvals, auxvals and fvals. for (int m=1; m<= mpoints; m++) { // convert phi_xi and phi_eta derivatives // to phi_x and phi_y derivatives through Jacobian // // Note that: // // pd_x = J11 pd_xi + J12 pd_eta and // pd_y = J21 pd_xi + J22 pd_eta. // // Squaring these operators yields the second derivatives. for (int k=1; k<=kmax_fout; k++) { phi_x.set(m,k, Mesh.get_jmat(i,1,1)*phi_xi.get(m,k) + Mesh.get_jmat(i,1,2)*phi_eta.get(m,k) ); phi_y.set(m,k, Mesh.get_jmat(i,2,1)*phi_xi.get(m,k) + Mesh.get_jmat(i,2,2)*phi_eta.get(m,k) ); phi_xx.set(m,k, Mesh.get_jmat(i,1,1)*Mesh.get_jmat(i,1,1)*phi_xi2.get(m,k) + Mesh.get_jmat(i,1,1)*Mesh.get_jmat(i,1,2)*phi_xieta.get(m,k) + Mesh.get_jmat(i,1,2)*Mesh.get_jmat(i,1,2)*phi_eta2.get(m,k) ); phi_xy.set(m,k, Mesh.get_jmat(i,1,1)*Mesh.get_jmat(i,2,1)*phi_xi2.get(m,k) +(Mesh.get_jmat(i,1,2)*Mesh.get_jmat(i,2,1) + Mesh.get_jmat(i,1,1)*Mesh.get_jmat(i,2,2))*phi_xieta.get(m,k) + Mesh.get_jmat(i,1,2)*Mesh.get_jmat(i,2,2)*phi_eta2.get(m,k) ); phi_yy.set(m,k, Mesh.get_jmat(i,2,1)*Mesh.get_jmat(i,2,1)*phi_xi2.get(m,k) + Mesh.get_jmat(i,2,1)*Mesh.get_jmat(i,2,2)*phi_xieta.get(m,k) + Mesh.get_jmat(i,2,2)*Mesh.get_jmat(i,2,2)*phi_eta2.get(m,k) ); } // point on the unit triangle const double s = spts.get(m,1); const double t = spts.get(m,2); // point on the physical triangle xpts.set(m,1, xc + (x2-x1)*s + (x3-x1)*t ); xpts.set(m,2, yc + (y2-y1)*s + (y3-y1)*t ); // Solution values (q) at each grid point for (int me=1; me<=meqn; me++) { qvals.set(m,me, 0.0 ); for (int k=1; k<=kmax_qin; k++) { qvals.set(m,me, qvals.get(m,me) + phi.get(m,k) * qin->get(i,me,k) ); } } // Auxiliary values (aux) at each grid point for (int ma=1; ma<=maux; ma++) { auxvals.set(m,ma, 0.0 ); for (int k=1; k<=kmax_auxin; k++) { auxvals.set(m,ma, auxvals.get(m,ma) + phi.get(m,k) * auxin->get(i,ma,k) ); } } } // ----------------------------------------------------------------- // // // Part I: // // Project the flux function onto the basis // functions. This is the term of order O( 1 ) in the // "time-averaged" Taylor expansion of f and g. // // ----------------------------------------------------------------- // // Call user-supplied function to set fvals FluxFunc(xpts, qvals, auxvals, fvals); // Evaluate integral on current cell (project onto Legendre basis) // using Gaussian Quadrature for the integration // // TODO - do we want to optimize this by looking into using transposes, // as has been done in the 2d/cart code? (5/14/2014) -DS for (int me=1; me<=mcomps_out; me++) for (int k=1; k<=kmax; k++) { double tmp1 = 0.0; double tmp2 = 0.0; for (int mp=1; mp <= mpoints; mp++) { tmp1 += wgts.get(mp)*fvals.get(mp, me, 1)*phi.get(mp, k); tmp2 += wgts.get(mp)*fvals.get(mp, me, 2)*phi.get(mp, k); } F->set(i, me, k, 2.0*tmp1 ); G->set(i, me, k, 2.0*tmp2 ); } // ----------------------------------------------------------------- // // // Part II: // // Project the derivative of the flux function onto the basis // functions. This is the term of order O( \dt ) in the // "time-averaged" Taylor expansion of f and g. // // ----------------------------------------------------------------- // // ----------------------------------------------------------------- // // Compute pointwise values for fx+gy: // // We can't multiply fvals of f, and g, // by alpha, otherwise we compute the wrong derivative here! // dTensor2 fx_plus_gy( mpoints, meqn ); fx_plus_gy.setall(0.); for( int mp=1; mp <= mpoints; mp++ ) for( int me=1; me <= meqn; me++ ) { double tmp = 0.; for( int k=2; k <= kmax; k++ ) { tmp += F->get( i, me, k ) * phi_x.get( mp, k ); tmp += G->get( i, me, k ) * phi_y.get( mp, k ); } fx_plus_gy.set( mp, me, tmp ); } // Call user-supplied Jacobian to set f'(q) and g'(q): DFluxFunc( xpts, qvals, auxvals, A ); // place-holders for point values of // f'(q)( fx + gy ) and g'(q)( fx + gy ): dTensor2 dt_times_fdot( mpoints, meqn ); dTensor2 dt_times_gdot( mpoints, meqn ); // Compute point values for f'(q) * (fx+gy) and g'(q) * (fx+gy): for( int mp=1; mp <= mpoints; mp++ ) for( int m1=1; m1 <= meqn; m1++ ) { double tmp1 = 0.; double tmp2 = 0.; for( int m2=1; m2 <= meqn; m2++ ) { tmp1 += A.get(mp, m1, m2, 1 ) * fx_plus_gy.get(mp, m2); tmp2 += A.get(mp, m1, m2, 2 ) * fx_plus_gy.get(mp, m2); } dt_times_fdot.set( mp, m1, -beta_dt*tmp1 ); dt_times_gdot.set( mp, m1, -beta_dt*tmp2 ); } // --- Third-order terms --- // // // These are the terms that are O( \dt^2 ) in the "time-averaged" // flux function. dTensor2 f_tt( mpoints, meqn ); f_tt.setall(0.); dTensor2 g_tt( mpoints, meqn ); g_tt.setall(0.); if( mterms > 2 ) { // Construct the Hessian at each (quadrature) point D2FluxFunc( xpts, qvals, auxvals, H ); // Second-order derivative terms dTensor2 qx_vals (mpoints, meqn); qx_vals.setall(0.); dTensor2 qy_vals (mpoints, meqn); qy_vals.setall(0.); dTensor2 fxx_vals(mpoints, meqn); fxx_vals.setall(0.); dTensor2 gxx_vals(mpoints, meqn); gxx_vals.setall(0.); dTensor2 fxy_vals(mpoints, meqn); fxy_vals.setall(0.); dTensor2 gxy_vals(mpoints, meqn); gxy_vals.setall(0.); dTensor2 fyy_vals(mpoints, meqn); fyy_vals.setall(0.); dTensor2 gyy_vals(mpoints, meqn); gyy_vals.setall(0.); for( int m=1; m <= mpoints; m++ ) for( int me=1; me <= meqn; me++ ) { // Can start at k=1, because derivative of a constant is // zero. double tmp_qx = 0.; double tmp_qy = 0.; for( int k=2; k <= kmax; k++ ) { tmp_qx += phi_x.get(m,k) * qin->get(i,me,k); tmp_qy += phi_y.get(m,k) * qin->get(i,me,k); } qx_vals.set(m,me, tmp_qx ); qy_vals.set(m,me, tmp_qy ); // First non-zero terms start at third-order. for( int k=4; k <= kmax; k++ ) { fxx_vals.set(m,me, fxx_vals.get(m,me) + phi_xx.get(m,k)*F->get(i,me,k) ); gxx_vals.set(m,me, gxx_vals.get(m,me) + phi_xx.get(m,k)*G->get(i,me,k) ); fxy_vals.set(m,me, fxy_vals.get(m,me) + phi_xy.get(m,k)*F->get(i,me,k) ); gxy_vals.set(m,me, gxy_vals.get(m,me) + phi_xy.get(m,k)*G->get(i,me,k) ); fyy_vals.set(m,me, fyy_vals.get(m,me) + phi_yy.get(m,k)*F->get(i,me,k) ); gyy_vals.set(m,me, gyy_vals.get(m,me) + phi_yy.get(m,k)*G->get(i,me,k) ); } } // ----------------------------------- // // Part I: Compute (f_x + g_y)_{,t} // ----------------------------------- // // Compute terms that get multiplied by \pd2{ f }{ q } and \pd2{ g }{ q }. dTensor2 fx_plus_gy_t( mpoints, meqn ); for( int m = 1; m <= mpoints; m++ ) for( int me = 1; me <= meqn; me++ ) { double tmp = 0.; // Terms that get multiplied by the Hessian: for( int m1=1; m1 <= meqn; m1++ ) for( int m2=1; m2 <= meqn; m2++ ) { tmp += H.get(m,me,m1,m2,1)*qx_vals.get(m,m1)*fx_plus_gy.get(m,m2); tmp += H.get(m,me,m1,m2,2)*qy_vals.get(m,m1)*fx_plus_gy.get(m,m2); } // Terms that get multiplied by f'(q) and g'(q): for( int m1=1; m1 <= meqn; m1++ ) { tmp += A.get(m,me,m1,1)*( fxx_vals.get(m,m1)+gxy_vals.get(m,m1) ); tmp += A.get(m,me,m1,2)*( fxy_vals.get(m,m1)+gyy_vals.get(m,m1) ); } fx_plus_gy_t.set( m, me, tmp ); } // ----------------------------------- // // Part II: Compute // f'(q) * fx_plus_gy_t and // g'(q) * fx_plus_gy_t // ----------------------------------- // // Add in the third term that gets multiplied by A: for( int m=1; m <= mpoints; m++ ) for( int m1=1; m1 <= meqn; m1++ ) { double tmp1 = 0.; double tmp2 = 0.; for( int m2=1; m2 <= meqn; m2++ ) { tmp1 += A.get(m,m1,m2,1)*fx_plus_gy_t.get(m,m2); tmp2 += A.get(m,m1,m2,2)*fx_plus_gy_t.get(m,m2); } f_tt.set( m, m1, tmp1 ); g_tt.set( m, m1, tmp2 ); } // ----------------------------------------------- // // Part III: Add in contributions from // f''(q) * (fx_plus_gy, fx_plus_gy ) and // g''(q) * (fx_plus_gy, fx_plus_gy ). // ----------------------------------------------- // for( int m =1; m <= mpoints; m++ ) for( int me =1; me <= meqn; me++ ) { double tmp1 = 0.; double tmp2 = 0.; // Terms that get multiplied by the Hessian: for( int m1=1; m1 <= meqn; m1++ ) for( int m2=1; m2 <= meqn; m2++ ) { tmp1 += H.get(m,me,m1,m2,1)*fx_plus_gy.get(m,m1)*fx_plus_gy.get(m,m2); tmp2 += H.get(m,me,m1,m2,2)*fx_plus_gy.get(m,m1)*fx_plus_gy.get(m,m2); } f_tt.set( m, me, f_tt.get(m,me) + tmp1 ); g_tt.set( m, me, g_tt.get(m,me) + tmp2 ); } } // End of computing "third"-order terms // ---------------------------------------------------------- // // // Construct basis coefficients (integrate_on_current_cell) // // ---------------------------------------------------------- // for (int me=1; me<=mcomps_out; me++) for (int k=1; k<=kmax; k++) { double tmp1 = 0.0; double tmp2 = 0.0; for (int mp=1; mp<=mpoints; mp++) { tmp1 += wgts.get(mp)*phi.get(mp,k)*( dt_times_fdot.get(mp, me) + charlie_dt*f_tt.get(mp, me) ); tmp2 += wgts.get(mp)*phi.get(mp,k)*( dt_times_gdot.get(mp, me) + charlie_dt*g_tt.get(mp, me) ); } F->set(i,me,k, F->get(i,me,k) + 2.0*tmp1 ); G->set(i,me,k, G->get(i,me,k) + 2.0*tmp2 ); } } }
void ConstructL_Unst( const double t, const dTensor2* vel_vec, const mesh& Mesh, const edge_data_Unst& EdgeData, dTensor3& aux, // SetBndValues_Unst modifies ghost cells dTensor3& q, // SetBndValues_Unst modifies ghost cells dTensor3& Lstar, dTensor1& smax) { const int NumElems = Mesh.get_NumElems(); const int NumPhysElems = Mesh.get_NumPhysElems(); const int NumEdges = Mesh.get_NumEdges(); const int meqn = q.getsize(2); const int kmax = q.getsize(3); const int maux = aux.getsize(2); const int space_order = dogParams.get_space_order(); dTensor3 EdgeFluxIntegral(NumElems,meqn,kmax); dTensor3 ElemFluxIntegral(NumElems,meqn,kmax); dTensor3 Psi(NumElems,meqn,kmax); // --------------------------------------------------------- // Boundary Conditions SetBndValues_Unst(Mesh,&q,&aux); // Positivity limiter void ApplyPosLimiter_Unst(const mesh& Mesh, const dTensor3& aux, dTensor3& q); if( dogParams.using_moment_limiter() ) { ApplyPosLimiter_Unst(Mesh, aux, q); } // --------------------------------------------------------- // --------------------------------------------------------- // Part I: compute flux integral on element edges // --------------------------------------------------------- // Loop over all interior edges and solve Riemann problems // dTensor1 nvec(2); // Loop over all interior edges EdgeFluxIntegral.setall(0.); ElemFluxIntegral.setall(0.); // Loop over all interior edges #pragma omp parallel for for (int i=1; i<=NumEdges; i++) { // Edge coordinates double x1 = Mesh.get_edge(i,1); double y1 = Mesh.get_edge(i,2); double x2 = Mesh.get_edge(i,3); double y2 = Mesh.get_edge(i,4); // Elements on either side of edge int ileft = Mesh.get_eelem(i,1); int iright = Mesh.get_eelem(i,2); double Areal = Mesh.get_area_prim(ileft); double Arear = Mesh.get_area_prim(iright); // Scaled normal to edge dTensor1 nhat(2); nhat.set(1, (y2-y1) ); nhat.set(2, (x1-x2) ); // Variables to store flux integrals along edge dTensor2 Fr_tmp(meqn,dogParams.get_space_order()); dTensor2 Fl_tmp(meqn,dogParams.get_space_order()); // Loop over number of quadrature points along each edge for (int ell=1; ell<=dogParams.get_space_order(); ell++) { dTensor1 Ql(meqn),Qr(meqn); dTensor1 Auxl(maux),Auxr(maux); // Riemann data - q for (int m=1; m<=meqn; m++) { Ql.set(m, 0.0 ); Qr.set(m, 0.0 ); for (int k=1; k<=kmax; k++) { Ql.set(m, Ql.get(m) + EdgeData.phi_left->get(i,ell,k) *q.get(ileft, m,k) ); Qr.set(m, Qr.get(m) + EdgeData.phi_right->get(i,ell,k) *q.get(iright,m,k) ); } } // Riemann data - aux for (int m=1; m<=maux; m++) { Auxl.set(m, 0.0 ); Auxr.set(m, 0.0 ); for (int k=1; k<=kmax; k++) { Auxl.set(m, Auxl.get(m) + EdgeData.phi_left->get(i,ell,k) *aux.get(ileft, m,k) ); Auxr.set(m, Auxr.get(m) + EdgeData.phi_right->get(i,ell,k) *aux.get(iright,m,k) ); } } // Solve Riemann problem dTensor1 xedge(2); double s = EdgeData.xpts1d->get(ell); xedge.set(1, x1 + 0.5*(s+1.0)*(x2-x1) ); xedge.set(2, y1 + 0.5*(s+1.0)*(y2-y1) ); dTensor1 Fl(meqn),Fr(meqn); const double smax_edge = RiemannSolve(vel_vec, nhat, xedge, Ql, Qr, Auxl, Auxr, Fl, Fr); smax.set(i, Max(smax_edge,smax.get(i)) ); // Construct fluxes for (int m=1; m<=meqn; m++) { Fr_tmp.set(m,ell, Fr.get(m) ); Fl_tmp.set(m,ell, Fl.get(m) ); } } // Add edge integral to line integral around the full element for (int m=1; m<=meqn; m++) for (int k=1; k<=kmax; k++) { double Fl_sum = 0.0; double Fr_sum = 0.0; for (int ell=1; ell<=dogParams.get_space_order(); ell++) { Fl_sum = Fl_sum + 0.5*EdgeData.wgts1d->get(ell) *EdgeData.phi_left->get(i,ell,k) *Fl_tmp.get(m,ell); Fr_sum = Fr_sum + 0.5*EdgeData.wgts1d->get(ell) *EdgeData.phi_right->get(i,ell,k)*Fr_tmp.get(m,ell); } EdgeFluxIntegral.set(ileft, m,k, EdgeFluxIntegral.get(ileft, m,k) + Fl_sum/Areal ); EdgeFluxIntegral.set(iright,m,k, EdgeFluxIntegral.get(iright,m,k) - Fr_sum/Arear ); } } // --------------------------------------------------------- // --------------------------------------------------------- // Part II: compute intra-element contributions // --------------------------------------------------------- L2ProjectGrad_Unst(vel_vec, 1,NumPhysElems, space_order,space_order,space_order,space_order, Mesh,&q,&aux,&ElemFluxIntegral,&FluxFunc); // --------------------------------------------------------- // --------------------------------------------------------- // Part III: compute source term // --------------------------------------------------------- if ( dogParams.get_source_term()>0 ) { // Set source term on computational grid // Set values and apply L2-projection L2Project_Unst(t, vel_vec, 1,NumPhysElems, space_order,space_order,space_order,space_order, Mesh,&q,&aux,&Psi,&SourceTermFunc); } // --------------------------------------------------------- // --------------------------------------------------------- // Part IV: construct Lstar // --------------------------------------------------------- if (dogParams.get_source_term()==0) // Without Source Term { #pragma omp parallel for for (int i=1; i<=NumPhysElems; i++) for (int m=1; m<=meqn; m++) for (int k=1; k<=kmax; k++) { double tmp = ElemFluxIntegral.get(i,m,k) - EdgeFluxIntegral.get(i,m,k); Lstar.set(i,m,k, tmp ); } } else // With Source Term { #pragma omp parallel for for (int i=1; i<=NumPhysElems; i++) for (int m=1; m<=meqn; m++) for (int k=1; k<=kmax; k++) { double tmp = ElemFluxIntegral.get(i,m,k) - EdgeFluxIntegral.get(i,m,k) + Psi.get(i,m,k); Lstar.set(i,m,k, tmp ); } } // --------------------------------------------------------- // --------------------------------------------------------- // Part V: add extra contributions to Lstar // --------------------------------------------------------- // Call LstarExtra LstarExtra_Unst(Mesh,&q,&aux,&Lstar); // --------------------------------------------------------- }