示例#1
0
文件: mesh.cpp 项目: uranix/mcubes
void EdgeMesh::update_edge(const dim3 &cube, const edge &e, int edgeid,
        bool cb, bool ce, int patchid)
{
    int dir = edgeid >> 2;
    int ab = edgeid & 3;
    int ccb = cb ? 1 : 0;
    int cce = ce ? 1 : 0;
    int sig = (ccb << 3) | (cce << 2) | patchid;
    if (dir == 0)
        xedge(cube + e.beg)[ab] = sig;
    else if (dir == 1)
        yedge(cube + e.beg)[ab] = sig;
    else
        zedge(cube + e.beg)[ab] = sig;
}
示例#2
0
    MyVertex::Index GetRepVertex_TRI(MyEdge::Index edgeId, Triangle* polygon)
    {
        MyEdge& edge = xedge(edgeId);

        int edgeIndexInFace = -1;
        for (size_t i = 0; i < polygon->degree(); i++)
        {
            if (polygon->edgeId(i) == edgeId)
            {
                edgeIndexInFace = i;
                break;
            }
        }
        assert(edgeIndexInFace != -1);
        return polygon->vertexId(edgeIndexInFace);
    }
示例#3
0
    void calcEdgeIndicatorByExtremity(MyVertex::Index seedId, SSeed& seed, FullIndicatorVector& inds, size_t nMesh)
    {
        FullIndicatorVector vInds(nMesh);
        MyVertex& seedV = xvertex(seedId);
        for (int i = 0; i < nMesh; i++)
            vInds[i] = REL_OUTSIDE;

        // 找到那些on boundary的mesh,通过遍历所有的边上的面,查看它们的meshId
        for (auto &edgeId : seedV.edges)
        {
            MyEdge& eRef = xedge(edgeId);
            MyVertex& theOther = eRef.theOtherVertex(seedId);
            if (theOther.isPlaneRep()) continue; // 过滤掉非原始边

            auto fItr = MyEdge::ConstFaceIterator(eRef);
            for (; fItr; ++fItr)
            {
                if (fItr.face()->getType() == IPolygon::SUBPOLYGON) continue; // 过滤掉非原始面
                vInds[fItr.face()->meshId()] = REL_ON_BOUNDARY;
            }
        }

        // 找到一个包含有效面,且周围面的不共面的边,赋值edgeId
        // 这里会有隐含的假设:这个极点必须是原始点(应该是对的)
        bool flag = false;
        for (auto &edgeId : seedV.edges)
        {
            MyEdge& eRef = xedge(edgeId);
            MyVertex& theOther = eRef.theOtherVertex(seedId);

            auto fItr = MyEdge::FaceIterator(eRef);
            if (fItr.face()->getType() == IPolygon::TRIANGLE)
            {
                reinterpret_cast<Triangle*>(fItr.face())->calcSupportingPlane();
            }
            XPlane basePlane = fItr.face()->supportingPlane();
            flag = false; // 是否有不共面的相邻面
            for (; fItr; ++fItr)
            {
                if (!fItr.face()->isValid()) continue;

                if (fItr.face()->getType() == IPolygon::TRIANGLE)
                {
                    Triangle* pTri = (Triangle*)fItr.face();
                    pTri->calcSupportingPlane();
                }
                
                if (!basePlane.coplanar(fItr.face()->supportingPlane()))
                {
                    seed.edgeId = edgeId;
                    flag = true;
                    break;
                }
            }
            if (flag) break;
        }
        assert(flag);
        assert(xedge(seed.edgeId).ends[0] == seedId || xedge(seed.edgeId).ends[1] == seedId);

        // 赋值pFace
        MyEdge& eRef = xedge(seed.edgeId);
        auto fItr = MyEdge::FaceIterator(eRef);
        flag = false;
        for (; fItr; ++fItr)
        {
            if (fItr.face()->isValid())
            {
                flag = true;
                seed.pFace = fItr.face();
                break;
            }
        }
        assert(flag);

        calcEdgeIndicator(seedId, seed.edgeId, vInds, inds);
    }
示例#4
0
    void doClassification(Octree* pOctree, CSGTree<RegularMesh>* pCSG,
        std::vector<RegularMesh*>& meshList, RegularMesh* result,
        MyVertex::Index seedId)
    {
        uint32_t nMesh = meshList.size();
        CSGTreeOld* tree = pCSG->auxiliary();
        CSGTreeNode** curTreeLeaves = new CSGTreeNode*[nMesh];

        SSeed tmpSeed;
        MyVertex& seedV = xvertex(seedId);
        tmpSeed.edgeId = *seedV.edges.begin();
        MyEdge& seedE = xedge(tmpSeed.edgeId);

        tmpSeed.eIndicators.reset(new FullIndicatorVector(nMesh));
        calcEdgeIndicatorByExtremity(seedId, tmpSeed, 
            *reinterpret_cast<FullIndicatorVector*>(tmpSeed.eIndicators.get()), nMesh);

        MeshId curMeshId;
        std::queue<IPolygon*> faceQueue;
        std::queue<SSeed> intraQueue, interQueue;
        bool added, inverse;
        std::pair<uint32_t, uint32_t> inversePair;
        Relation relation;
        TestTree dummyForest;
        std::vector<Relation> relTab(nMesh);
        std::vector<MyEdge::Index> edges;

        interQueue.push(tmpSeed);
        tmpSeed.pFace->mark = SEEDED0;
        assert(tmpSeed.pFace->isValid());
        while (!interQueue.empty())
        {
            SSeed curSeed = interQueue.front();
            interQueue.pop();

            if (curSeed.pFace->mark == VISITED)
                continue;

            assert(intraQueue.empty());
            curSeed.pFace->mark = SEEDED1;
            intraQueue.push(curSeed);

            curMeshId = curSeed.pFace->meshId();
            inverse = meshList[curMeshId]->inverse();
            if (inverse)
                inversePair.first = result->faces().size();

            bool seedFlag = true;
            while (!intraQueue.empty())
            {
                curSeed = intraQueue.front();
                intraQueue.pop();

                if (curSeed.pFace->mark == VISITED)
                    continue;

                CSGTreeNode* tree0 = copy2(tree->pRoot, curTreeLeaves);

                calcFaceIndicator(curSeed, relTab, seedFlag?false:true);
                if (seedFlag) seedFlag = false; // only for debug use

                relation = ParsingCSGTree(meshList[curMeshId], relTab.data(), 
                    nMesh, tree0, curTreeLeaves, dummyForest);
                assert(relation != REL_NOT_AVAILABLE || relation != REL_UNKNOWN);

                added = (relation == REL_SAME); 
                faceQueue.push(curSeed.pFace);
                while (!faceQueue.empty())
                {
                    IPolygon* curFace = faceQueue.front();
                    faceQueue.pop();
                    if (curFace->mark == VISITED)
                        continue;

                    curFace->mark = VISITED;
                    if (added)
                        result->faces().push_back(curFace);

                    edges.clear();
                    curFace->getEdges(edges);
                    assert(edges.size() == curFace->degree());

                    // flood filling, bfs
                    for (int i = 0; i < curFace->degree(); i++)
                    {
                        MyEdge& curEdge = xedge(edges[i]);
                        MyEdge::FaceIterator fItr(curEdge);
                        if (curEdge.neighbor)
                        {
                            // 但因为共面的存在,有些neigh可能记录的相邻,但不在相邻面当中,这没关系!
                            //assert(curEdge.faceCount() >= 4); // 如果这是一个相交而成的边,那么一定会有超过4个polygon在周围
                            for (; fItr; ++fItr)
                            {
                                if (!fItr.face())
                                {
                                    XLOG_ERROR << "Edge with less than two neighboring faces.";
                                    continue;
                                }

                                if (!fItr.face()->isValid()) continue;

                                tmpSeed.edgeId = edges[i];
                                tmpSeed.pFace = fItr.face();

                                for (int i = 0; i < nMesh; i++)
                                {
                                    Indicator tmpInd = REL_NOT_AVAILABLE;
                                    switch (relTab[i])
                                    {
                                    case REL_SAME:
                                    case REL_OPPOSITE:
                                        tmpInd = REL_ON_BOUNDARY;
                                        break;
                                    default:
                                        tmpInd = relTab[i];
                                        break;
                                    }
                                    tmpSeed.eIndicators->at(i) = tmpInd;
                                }

                                for (NeighborInfo& nInfo : *curEdge.neighbor)
                                    tmpSeed.eIndicators->at(nInfo.neighborMeshId) = REL_ON_BOUNDARY;

                                if (fItr.face()->meshId() == curMeshId)
                                {
                                    if (fItr.face()->mark < SEEDED1)
                                    {
                                        tmpSeed.pFace->mark = SEEDED1;
                                        intraQueue.push(tmpSeed);
                                    }
                                }
                                else
                                {
                                    if (fItr.face()->mark < SEEDED0)
                                    {
                                        tmpSeed.pFace->mark = SEEDED0;
                                        interQueue.push(tmpSeed);
                                    }
                                }
                            }
                        }
                        else
                        {
                            for (; fItr; ++fItr)
                            {
                                if (!fItr.face())
                                {
                                    XLOG_ERROR << "Edge with less than two neighboring faces.";
                                    continue;
                                }
                                if (!fItr.face()->isValid() || fItr.face()->meshId() != curMeshId) continue;
                                if (fItr.face()->mark < SEEDED2)
                                {
                                    faceQueue.push(fItr.face());
                                    fItr.face()->mark = SEEDED2;
                                }
                            }
                        }
                    }
                }
                //break;
            }
            if (inverse)
            {
                inversePair.second = result->faces().size();
                result->inverseMap.push_back(inversePair);
            }
            //break;
        }
        SAFE_DELETE_ARRAY(curTreeLeaves);
    }
示例#5
0
    void calcFaceIndicator(SSeed& seed, std::vector<Relation>& relTab, bool hasNeighbor)
    {
        MyEdge& edge = xedge(seed.edgeId);
        IPolygon* polygon = seed.pFace;

        // copy the relation
        const size_t nMesh = ((FullIndicatorVector*)seed.eIndicators.get())->getNumber();
        for (size_t i = 0; i < nMesh; i++)
        {
            relTab[i] = (Relation)seed.eIndicators.get()->at(i);
#ifdef XR_DEBUG
            bool flag = true;
            if (i != polygon->meshId() && relTab[i] == REL_ON_BOUNDARY)
            {
                flag = false;
                for (auto& neigh : *edge.neighbor)
                {
                    if (neigh.neighborMeshId == i)
                    {
                        flag = true;
                        break;
                    }
                }
            }
            assert(flag);
#endif
        }

        relTab[polygon->meshId()] = REL_SAME;

        assert(!hasNeighbor || edge.neighbor->size());
        if (!edge.neighbor || edge.neighbor->empty()) return;

        // remove repetitive neighborInfo, must before <find repVertex>
        // because subpolygon use neighborInfo to find a splitPlane
        if (!edge.noOverlapNeighbor)
        {
            edge.noOverlapNeighbor = true;
            std::set<MeshId> meshSets;
            std::vector<NeighborInfo> newNeighbor;
            for (auto &neigh : *edge.neighbor)
            {
                if (meshSets.find(neigh.neighborMeshId) == meshSets.end())
                {
                    newNeighbor.push_back(neigh);
                    meshSets.insert(neigh.neighborMeshId);
                }
            }
            edge.neighbor->swap(newNeighbor);
        }

        // find the represented vertex
        MyVertex::Index repVertexId;
        if (seed.pFace->getType() == IPolygon::TRIANGLE)
            repVertexId = GetRepVertex_TRI(seed.edgeId, (Triangle*)seed.pFace);
        else
            repVertexId = GetRepVertex_SPOLY(seed.edgeId, (SubPolygon*)seed.pFace);
        MyVertex& repVertex = xvertex(repVertexId);

        // correct the relation
        for (auto &neigh: *edge.neighbor)
        {
            Oriented_side side;

            if (neigh.neighborMeshId == polygon->meshId())
                continue;

            if (neigh.type == NeighborInfo::Edge)
            {
                std::vector<IPolygon*> faces;
                auto fItr = MyEdge::FaceIterator(xedge(neigh.neighborEdgeId), true);
                for (; fItr; fItr.incrementToTriangle())
                {
                    assert(fItr.face()->getType() == IPolygon::TRIANGLE);
                    faces.push_back(fItr.face());
                }

                BSPTree bsp; XPlane bspPlane;
                bsp.buildNoCross(faces);
                side = bsp.classify(repVertex, &bspPlane);

                relTab[neigh.neighborMeshId] = vRelation2fRelation(side,
                    bspPlane, polygon->supportingPlane());
            }
            else
            {
                assert(neigh.type == NeighborInfo::Face);
                Oriented_side side = orientation(neigh.pTrangle->supportingPlane(), repVertex);

                relTab[neigh.neighborMeshId] = vRelation2fRelation(side,
                    neigh.pTrangle->supportingPlane(), polygon->supportingPlane());
            }
        }
    }
示例#6
0
    MyVertex::Index GetRepVertex_SPOLY(MyEdge::Index edgeId, SubPolygon* polygon)
    {
        assert(orientation(polygon->supportingPlane(),(polygon->vertex(0))) == ON_ORIENTED_BOUNDARY);
        assert(orientation(polygon->supportingPlane(),(polygon->vertex(1))) == ON_ORIENTED_BOUNDARY);
        assert(orientation(polygon->supportingPlane(),(polygon->vertex(2))) == ON_ORIENTED_BOUNDARY);

        MyEdge& edge = xedge(edgeId);

        // find init point
        int edgeIndexInFace = -1;
        for (int i = 0; i < polygon->degree(); i++)
        {
            if (polygon->edgeId(i) == edgeId)
            {
                edgeIndexInFace = i;
                break;
            }
        }
        assert(edgeIndexInFace != -1);

        MyVertex::Index vIdInPlane;
        XPlane boundPlane, tmpPlane;
        for (int i = 2; i < polygon->degree(); i++)
        {
            vIdInPlane = polygon->vertexId((edgeIndexInFace + i) % polygon->degree());

            // find a bounding plane
            bool flag = false;
            for (auto &neigh : *edge.neighbor)
            {
                if (neigh.type == NeighborInfo::Edge)
                {
                    for (auto fItr = MyEdge::FaceIterator(xedge(neigh.neighborEdgeId), true);
                        fItr; fItr.incrementToTriangle())
                    {
                        assert(fItr.face()->getType() == IPolygon::TRIANGLE);
                        tmpPlane = ((Triangle*)fItr.face())->supportingPlane();
                        if (orientation(tmpPlane, xvertex(vIdInPlane)) != ON_ORIENTED_BOUNDARY)
                        {
                            boundPlane = tmpPlane;
                            flag = true;
                            break;
                        }
                    }
                }
                else
                {
                    assert(neigh.type == NeighborInfo::Face);
                    XPlane tmpPlane = neigh.pTrangle->supportingPlane();
                    if (orientation(tmpPlane, xvertex(vIdInPlane)) != ON_ORIENTED_BOUNDARY)
                    {
                        boundPlane = tmpPlane;
                        break;
                    }
                }
                if (flag) break;
            }
            if (boundPlane.isValid()) break;
        }
        assert(boundPlane.isValid());

        // correct the direction of bounding plane
        XLine edgeLine(polygon->supportingPlane(), boundPlane);
        assert(!polygon->supportingPlane().idEquals(boundPlane));
        int tmpSide = linearOrder(edgeLine, xvertex(polygon->vertexId((edgeIndexInFace + 1) % polygon->degree())),
            xvertex(polygon->vertexId(edgeIndexInFace)));

        assert(tmpSide != 0);
        if (tmpSide < 0)
            boundPlane.inverse();

        // pick a correct rep vertex
        MyVertex::Index repVertexId = INVALID_UINT32;
        for (size_t i = 0; i < polygon->degree(); i++)
        {
            if (orientation(boundPlane, xvertex(polygon->vertexId(i))) == ON_POSITIVE_SIDE)
            {
                repVertexId = polygon->vertexId(i);
                break;
            }
        }
        assert(repVertexId != INVALID_UINT32);
        return repVertexId;
    }
示例#7
0
// Right-hand side of the Lax-Wendroff discretization:
//
//      ( q(t+dt) - q(t) )/dt = -F_{x} - G_{y}.
//
// This routine constructs the 'time-integrated' flux functions F and G using the
// Cauchy-Kowalewski procedure.
//
// First, consider time derivatives of q
//
//    q^{n+1} = q^n + dt * (q^n_t + dt/2 * q^n_tt + dt^3/6 q^n_ttt ).
//
// Formally, these are given by
//
//   q_{t}    = ( -f(q) )_x + ( -g(q) )_y, 
//   q_{tt}   = ( f'(q) * ( f_x + g_y )_x + ( g'(q) * ( f_x + g_y )_y
//   q_{ttt}  = ...
//
// Now, considering Taylor expansions of f and g, centered about t = t^n
//
//  F = f^n + (t-t^n) \dot{f^n} + \cdots
//  G = g^n + (t-t^n) \dot{g^n} + \cdots
//
// We have the following form, after integrating in time
//
//  F: = ( f - dt/2 * ( f'(q)*( f_x+g_y ) 
//           + dt^2/6 ( \pd2{f}{q} \cdot (f_x+g_y,f_x+g_y) +
//                        \pd{f}{q} (  f_x + g_y )_t  ) + \cdots
//
//  G: = ( g - dt/2 * ( g'(q)*( f_x+g_y )
//           + dt^2/6 ( \pd2{g}{q} \cdot (f_x+g_y,f_x+g_y) +
//                        \pd{g}{q} (  f_x + g_y )_t ) + \cdots
//
// where the final ingredient is
//
//  (f_x+g_y)_t = \pd2{f}{q} \cdot (q_x, f_x+g_y ) + \pd{f}{q} ( f_xx + g_xy ) +
//                \pd2{g}{q} \cdot (q_y, f_x+g_y ) + \pd{g}{q} ( f_xy + g_yy ).
//
// At the end of the day, we set
//
//    L(q) := -F_x - G_y.
//
// See also: ConstructL.
void LaxWendroff(double dt, 
    const dTensorBC4& aux, const dTensorBC4& q,    // set bndy values modifies these
    dTensorBC4& Lstar, dTensorBC3& smax)
{

printf("This call hasn't been tested \n");

    if ( !dogParams.get_flux_term() )
    {  return;  }

    const edge_data& edgeData = Legendre2d::get_edgeData();
    const int space_order = dogParams.get_space_order();
    const int mx   = q.getsize(1);
    const int my   = q.getsize(2);
    const int meqn = q.getsize(3);
    const int kmax = q.getsize(4);
    const int mbc  = q.getmbc();
    const int maux = aux.getsize(3);

    // Flux values
    //
    // Space-order = number of quadrature points needed for 1D integration
    // along cell edges.
    //
    dTensorBC4 Fm(mx, my, meqn, space_order, mbc);
    dTensorBC4 Fp(mx, my, meqn, space_order, mbc);
    dTensorBC4 Gm(mx, my, meqn, space_order, mbc);
    dTensorBC4 Gp(mx, my, meqn, space_order, mbc);

    // Flux function
    void FluxFunc(const dTensor2& xpts,
                  const dTensor2& Q,
                  const dTensor2& Aux,
                  dTensor3& flux);

    // Jacobian of the flux function:
    void DFluxFunc(const dTensor2& xpts, 
                   const dTensor2& Q,
                   const dTensor2& Aux, 
                   dTensor4& Dflux );

    // Hessian of the flux function:
    void D2FluxFunc(const dTensor2& xpts, 
                   const dTensor2& Q,
                   const dTensor2& Aux, 
                   dTensor5& D2flux );


    // Riemann solver that relies on the fact that we already have 
    // f(ql) and f(qr) already computed:
    double RiemannSolveLxW(const dTensor1& nvec,
        const dTensor1& xedge,
        const dTensor1& Ql,   const dTensor1& Qr,
        const dTensor1& Auxl, const dTensor1& Auxr,
        const dTensor1& ffl,  const dTensor1& ffr,
        dTensor1& Fl, dTensor1& Fr);

    void LstarExtra(const dTensorBC4*,
            const dTensorBC4*,
            dTensorBC4*);
    void ArtificialViscosity(const dTensorBC4* aux, 
            const dTensorBC4* q, 
            dTensorBC4* Lstar);

    // Grid information
    const double xlower = dogParamsCart2.get_xlow();
    const double ylower = dogParamsCart2.get_ylow();
    const double dx = dogParamsCart2.get_dx();
    const double dy = dogParamsCart2.get_dy();

    // --------------------------------------------------------------------- //
    // Boundary data:
    // --------------------------------------------------------------------- //
    // TODO - call this routine before calling this function.
//  void SetBndValues(dTensorBC4& q, dTensorBC4& aux);
//  SetBndValues(q, aux);
    // ---------------------------------------------------------

    // --------------------------------------------------------------------- //
    // Part 0: Compute the Lax-Wendroff "flux" function:
    //
    // Here, we include the extra information about time derivatives.
    // --------------------------------------------------------------------- //
    dTensorBC4 F(mx, my, meqn, kmax, mbc);  F.setall(0.);
    dTensorBC4 G(mx, my, meqn, kmax, mbc);  G.setall(0.);
    void L2ProjectLxW( const int mterms, 
        const double alpha, const double beta_dt, const double charlie_dt,
        const int istart, const int iend, 
        const int jstart, const int jend,
        const int QuadOrder,
        const int BasisOrder_auxin,
        const int BasisOrder_fout,    
        const dTensorBC4* qin,
        const dTensorBC4* auxin,
        dTensorBC4* F,
        dTensorBC4* G,
        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) );
printf("hello\n");
    L2ProjectLxW( 3, 1.0, 0.5*dt, dt*dt/6.0,
        1-mbc, mx+mbc, 1-mbc, my+mbc,
        space_order, space_order, space_order,
        &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 inter-element interaction fluxes
    //
    //   N = int( F(q,x,t) * phi_x, x ) / dA
    //
    // ---------------------------------------------------------

    // 1-direction: loop over interior edges and solve Riemann problems
    dTensor1 nvec(2);
    nvec.set(1, 1.0e0 );
    nvec.set(2, 0.0e0 );

#pragma omp parallel for
    for (int i=(2-mbc); i<=(mx+mbc); i++)
    {

        dTensor1 Ql(meqn),   Qr(meqn);
        dTensor1 ffl(meqn),  ffr(meqn);
        dTensor1 Fl(meqn),   Fr(meqn);
        dTensor1 DFl(meqn),  DFr(meqn);
        dTensor1 Auxl(maux), Auxr(maux);

        dTensor1 xedge(2);

        for (int j=(2-mbc); j<=(my+mbc-1); j++)
        {
            // ell indexes Riemann point along the edge
            for (int ell=1; ell<=space_order; ell++)
            {
                // Riemann data - q and f (from basis functions/q)
                for (int m=1; m<=meqn; m++)
                {
                    Ql.set (m, 0.0 );
                    Qr.set (m, 0.0 );
                    ffl.set(m, 0.0 );
                    ffr.set(m, 0.0 );

                    for (int k=1; k<=kmax; k++)
                    {
                        // phi_xl( xi=1.0, eta ), phi_xr( xi=-1.0, eta )
                        Ql.fetch(m)  += edgeData.phi_xl->get(ell,k)*q.get(i-1, j, m, k );
                        Qr.fetch(m)  += edgeData.phi_xr->get(ell,k)*q.get(i,   j, m, k );
                        ffl.fetch(m) += edgeData.phi_xl->get(ell,k)*F.get(i-1, j, m, k );
                        ffr.fetch(m) += edgeData.phi_xr->get(ell,k)*F.get(i,   j, 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.fetch(m) += edgeData.phi_xl->get(ell,k)*aux.get(i-1, j, m, k);
                        Auxr.fetch(m) += edgeData.phi_xr->get(ell,k)*aux.get(i,   j, m, k);
                    }
                }

                // Solve Riemann problem
                xedge.set(1, xlower + (double(i)-1.0)*dx );
                xedge.set(2, ylower + (double(j)-0.5)*dy );

                const double smax_edge = RiemannSolveLxW(
                    nvec, xedge, Ql, Qr, Auxl, Auxr, ffl, ffr, Fl, Fr);

                smax.set(i-1, j, 1, Max(dy*smax_edge,smax.get(i-1, j, 1)) );
                smax.set(i,   j, 1, Max(dy*smax_edge,smax.get(i,   j, 1)) );

                // Construct fluxes
                for (int m=1; m<=meqn; m++)
                {
                    Fm.set(i  , j, m, ell,  Fr.get(m) );
                    Fp.set(i-1, j, m, ell,  Fl.get(m) );
                }
            }
        }
    }


    // 2-direction: loop over interior edges and solve Riemann problems
    nvec.set(1, 0.0e0 );
    nvec.set(2, 1.0e0 );

#pragma omp parallel for
    for (int i=(2-mbc); i<=(mx+mbc-1); i++)
    {
        dTensor1  Ql(meqn),   Qr(meqn);
        dTensor1  Fl(meqn),   Fr(meqn);
        dTensor1 ffl(meqn),  ffr(meqn);
        dTensor1 Auxl(maux),Auxr(maux);
        dTensor1 xedge(2);

        for (int j=(2-mbc); j<=(my+mbc); j++)
        for (int ell=1; ell<=space_order; ell++)
        {
            // Riemann data - q
            for (int m=1; m<=meqn; m++)
            {

                Ql.set  (m, 0.0 );
                Qr.set  (m, 0.0 );
                ffl.set (m, 0.0 );
                ffr.set (m, 0.0 );

                for (int k=1; k<=kmax; k++)
                {
                    Ql.fetch(m)  += edgeData.phi_yl->get(ell, k)*q.get(i, j-1, m, k );
                    Qr.fetch(m)  += edgeData.phi_yr->get(ell, k)*q.get(i, j,   m, k );
                    ffl.fetch(m) += edgeData.phi_yl->get(ell, k)*G.get(i, j-1, m, k );
                    ffr.fetch(m) += edgeData.phi_yr->get(ell, k)*G.get(i,   j, 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.fetch(m) += edgeData.phi_yl->get(ell,k)*aux.get(i,j-1,m,k);
                    Auxr.fetch(m) += edgeData.phi_yr->get(ell,k)*aux.get(i,j,m,k);
                }
            }

            // Solve Riemann problem
            xedge.set(1, xlower + (double(i)-0.5)*dx );
            xedge.set(2, ylower + (double(j)-1.0)*dy );

            const double smax_edge = RiemannSolveLxW(
                nvec, xedge, Ql, Qr, Auxl, Auxr, ffl, ffr, Fl, Fr);

            smax.set(i, j-1, 2,  Max(dx*smax_edge, smax.get(i, j-1, 2)) );
            smax.set(i, j,   2,  Max(dx*smax_edge, smax.get(i, j,   2)) );

            // Construct fluxes
            for (int m=1; m<=meqn; m++)
            {
                Gm.set(i, j,   m, ell, Fr.get(m) );
                Gp.set(i, j-1, m, ell, Fl.get(m) );
            }
        }
    }

    // Compute ``flux differences'' dF and dG    
    const double half_dx_inv = 0.5/dx;
    const double half_dy_inv = 0.5/dy;
    const int mlength = Lstar.getsize(3);   assert_eq( meqn, mlength );

    // Use the four values, Gm, Gp, Fm, Fp to construct the boundary integral:
#pragma omp parallel for
    for (int i=(2-mbc); i<=(mx+mbc-1); i++)    
    for (int j=(2-mbc); j<=(my+mbc-1); j++)
    for (int m=1; m<=mlength; m++)
    for (int k=1; k<=kmax; k++)
    {
        // 1-direction: dF
        double F1 = 0.0;
        double F2 = 0.0;
        for (int ell=1; ell<=space_order; ell++)
        {
            F1 = F1 + edgeData.wght_phi_xr->get(ell,k)*Fm.get(i,j,m,ell);
            F2 = F2 + edgeData.wght_phi_xl->get(ell,k)*Fp.get(i,j,m,ell);
        }

        // 2-direction: dG
        double G1 = 0.0;
        double G2 = 0.0;
        for (int ell=1; ell<=space_order; ell++)
        {
            G1 = G1 + edgeData.wght_phi_yr->get(ell,k)*Gm.get(i,j,m,ell);
            G2 = G2 + edgeData.wght_phi_yl->get(ell,k)*Gp.get(i,j,m,ell);
        }

        Lstar.fetch(i,j,m,k) -= (half_dx_inv*(F2-F1) + half_dy_inv*(G2-G1));

    }
    // ---------------------------------------------------------


    // ---------------------------------------------------------
    // Part III: compute intra-element contributions
    // ---------------------------------------------------------
    // No need to call this if first-order in space
    if(dogParams.get_space_order()>1)
    {

        dTensorBC4 Ltmp( mx, my, meqn, kmax, mbc );
        void L2ProjectGradAddLegendre(const int istart, 
                const int iend, 
                const int jstart, 
                const int jend,
                const int QuadOrder, 
                const dTensorBC4* F, 
                const dTensorBC4* G, 
                dTensorBC4* fout );
        L2ProjectGradAddLegendre( 1-mbc, mx+mbc, 1-mbc, my+mbc,
            space_order, &F, &G, &Lstar );

    }
    // ---------------------------------------------------------  

    // ---------------------------------------------------------
    // Part IV: add extra contributions to Lstar
    // ---------------------------------------------------------
    // Call LstarExtra
    LstarExtra(&q,&aux,&Lstar);
    // ---------------------------------------------------------


    // ---------------------------------------------------------
    // Part V: artificial viscosity limiter
    // ---------------------------------------------------------  
    if (dogParams.get_space_order()>1  &&
            dogParams.using_viscosity_limiter())
    {  ArtificialViscosity(&aux,&q,&Lstar);  }
    // ---------------------------------------------------------

}
// 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);  }
    // ---------------------------------------------------------

}
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);
    // ---------------------------------------------------------

}