<commit_before>/*-----------------------------------------------------------------------------
 This source file is part of Hopsan NG

 Copyright (c) 2011
    Mikael Axin, Robert Braun, Alessandro Dell'Amico, Björn Eriksson,
    Peter Nordin, Karl Pettersson, Petter Krus, Ingo Staack

 This file is provided "as is", with no guarantee or warranty for the
 functionality or reliability of the contents. All contents in this file is
 the original work of the copyright holders at the Division of Fluid and
 Mechatronic Systems (Flumes) at Linköping University. Modifying, using or
 redistributing any part of this file is prohibited without explicit
 permission from the copyright holders.
-----------------------------------------------------------------------------*/

//!
//! @file   ClassFactory.hpp
//! @author <peter.nordin@liu.se>
//! @date   2009-12-26
//!
//! @brief Contains template class ClassFactory for automatic object instantiation through key value
//!
//$Id$

#ifndef CLASFACTORY_HPP_INCLUDED
#define CLASFACTORY_HPP_INCLUDED

#include <map>
#include <vector>
#include <iostream>

namespace hopsan {

    //! @brief Template class for automatic object instantiation by key-value.
    //!
    //! This code is based on:
    //! http://www.codeproject.com/KB/architecture/SimpleDynCreate.aspx
    template <typename _Key, typename _Base, typename _Predicator = std::less<_Key> >
    class ClassFactory
    {
    public:
        typedef std::vector< std::pair<_Key, int> > RegStatusVectorT;
        enum {REGISTEREDOK, ALLREADYREGISTERED, NOTREGISTERED};

    protected:
        typedef _Base* (*CreatorFunctionT) (void);
        typedef std::map<_Key, CreatorFunctionT, _Predicator> FactoryMapT;
        typedef std::pair<_Key, CreatorFunctionT> FactoryPairT;

        //Map where the construction info is stored
        FactoryMapT mFactoryMap;
        //Error status map
        RegStatusVectorT mRegStatusVector;

    public:
        //! @brief Used to register creator functions
        _Key registerCreatorFunction(_Key idKey, CreatorFunctionT classCreator)
        {
            //std::cout << "Registering: " << idKey << std::endl;
            std::cout << "BeforeInsert: Size: " << mFactoryMap.size() << std::endl;
            std::pair<typename FactoryMapT::iterator, bool> rc;
            rc = mFactoryMap.insert(FactoryPairT(idKey, classCreator));
            if (!rc.second)
            {
                std::cout << "Warning! You are trying to register a Key value that already exist. This registration will be ignored, Key: " << idKey << std::endl;
                mRegStatusVector.push_back(std::pair<_Key, int>(idKey, ALLREADYREGISTERED));
            }
            else
            {
                mRegStatusVector.push_back(std::pair<_Key, int>(idKey, REGISTEREDOK));
            }
            std::cout << "AfterInsert: Size: " << mFactoryMap.size() << std::endl;
            return idKey;
        }

        //! @brief Creates an instance based on the key using creator function (if registered)
        _Base* createInstance(_Key idKey)
        {
            //std::cout << "Create: Size: " << mFactoryMap.size() << std::endl;
            typename FactoryMapT::iterator it = mFactoryMap.find(idKey);
            if (it != mFactoryMap.end())
            {
                if (it->second)
                {
                    return it->second();
                }
            }
            mRegStatusVector.push_back(std::pair<_Key, int>(idKey, NOTREGISTERED));
            //std::cout << "Warning key: " << idKey << " not found!" << std::endl;
            return NULL;
        }

        //! @brief Check if the factory has key registerd
        bool hasKey(_Key idKey)
        {
            if (mFactoryMap.count(idKey) > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        //! @brief Unregister creator functions for given key
        void unRegisterCreatorFunction(_Key idKey)
        {
            size_t rc;
            rc = mFactoryMap.erase(idKey);
            if (rc > 0)
            {
                std::cout << "Sucessfully unregistered: " << idKey << std::endl;
                //! @todo Do we need a status message here to ??
            }
            else
            {
                mRegStatusVector.push_back(std::pair<_Key, int>(idKey, NOTREGISTERED));
                std::cout << "Failed to unregister: " << idKey << std::endl;
            }
        }

        //! @brief Get a copy of the internal error map, it maps key values agains error codes, error codes come from registration or unregistration
        RegStatusVectorT getRegisterStatusMap()
        {
            return mRegStatusVector;
        }

        //! @brief Clears the internal error status map
        void clearRegisterStatusMap()
        {
            mRegStatusVector.clear();
        }

        //! @brief Clear the entire factory map (unregister everything)
        void clearFactory()
        {
            mFactoryMap.clear();
            mRegStatusVector.clear();
        }
    };
}

#endif // CLASFACTORY_HPP_INCLUDED
<commit_msg>Commented some debug couts in classfactory<commit_after>/*-----------------------------------------------------------------------------
 This source file is part of Hopsan NG

 Copyright (c) 2011
    Mikael Axin, Robert Braun, Alessandro Dell'Amico, Björn Eriksson,
    Peter Nordin, Karl Pettersson, Petter Krus, Ingo Staack

 This file is provided "as is", with no guarantee or warranty for the
 functionality or reliability of the contents. All contents in this file is
 the original work of the copyright holders at the Division of Fluid and
 Mechatronic Systems (Flumes) at Linköping University. Modifying, using or
 redistributing any part of this file is prohibited without explicit
 permission from the copyright holders.
-----------------------------------------------------------------------------*/

//!
//! @file   ClassFactory.hpp
//! @author <peter.nordin@liu.se>
//! @date   2009-12-26
//!
//! @brief Contains template class ClassFactory for automatic object instantiation through key value
//!
//$Id$

#ifndef CLASFACTORY_HPP_INCLUDED
#define CLASFACTORY_HPP_INCLUDED

#include <map>
#include <vector>
//#include <iostream>

namespace hopsan {

    //! @brief Template class for automatic object instantiation by key-value.
    //!
    //! This code is based on:
    //! http://www.codeproject.com/KB/architecture/SimpleDynCreate.aspx
    template <typename _Key, typename _Base, typename _Predicator = std::less<_Key> >
    class ClassFactory
    {
    public:
        typedef std::vector< std::pair<_Key, int> > RegStatusVectorT;
        enum {REGISTEREDOK, ALLREADYREGISTERED, NOTREGISTERED};

    protected:
        typedef _Base* (*CreatorFunctionT) (void);
        typedef std::map<_Key, CreatorFunctionT, _Predicator> FactoryMapT;
        typedef std::pair<_Key, CreatorFunctionT> FactoryPairT;

        //Map where the construction info is stored
        FactoryMapT mFactoryMap;
        //Error status map
        RegStatusVectorT mRegStatusVector;

    public:
        //! @brief Used to register creator functions
        _Key registerCreatorFunction(_Key idKey, CreatorFunctionT classCreator)
        {
            //std::cout << "Registering: " << idKey << std::endl;
            //std::cout << "BeforeInsert: Size: " << mFactoryMap.size() << std::endl;
            std::pair<typename FactoryMapT::iterator, bool> rc;
            rc = mFactoryMap.insert(FactoryPairT(idKey, classCreator));
            if (!rc.second)
            {
                //std::cout << "Warning! You are trying to register a Key value that already exist. This registration will be ignored, Key: " << idKey << std::endl;
                mRegStatusVector.push_back(std::pair<_Key, int>(idKey, ALLREADYREGISTERED));
            }
            else
            {
                mRegStatusVector.push_back(std::pair<_Key, int>(idKey, REGISTEREDOK));
            }
            //std::cout << "AfterInsert: Size: " << mFactoryMap.size() << std::endl;
            return idKey;
        }

        //! @brief Creates an instance based on the key using creator function (if registered)
        _Base* createInstance(_Key idKey)
        {
            //std::cout << "Create: Size: " << mFactoryMap.size() << std::endl;
            typename FactoryMapT::iterator it = mFactoryMap.find(idKey);
            if (it != mFactoryMap.end())
            {
                if (it->second)
                {
                    return it->second();
                }
            }
            mRegStatusVector.push_back(std::pair<_Key, int>(idKey, NOTREGISTERED));
            //std::cout << "Warning key: " << idKey << " not found!" << std::endl;
            return NULL;
        }

        //! @brief Check if the factory has key registerd
        bool hasKey(_Key idKey)
        {
            if (mFactoryMap.count(idKey) > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        //! @brief Unregister creator functions for given key
        void unRegisterCreatorFunction(_Key idKey)
        {
            size_t rc;
            rc = mFactoryMap.erase(idKey);
            if (rc > 0)
            {
                //std::cout << "Sucessfully unregistered: " << idKey << std::endl;
                //! @todo Do we need a status message here to ??
            }
            else
            {
                mRegStatusVector.push_back(std::pair<_Key, int>(idKey, NOTREGISTERED));
                //std::cout << "Failed to unregister: " << idKey << std::endl;
            }
        }

        //! @brief Get a copy of the internal error map, it maps key values agains error codes, error codes come from registration or unregistration
        RegStatusVectorT getRegisterStatusMap()
        {
            return mRegStatusVector;
        }

        //! @brief Clears the internal error status map
        void clearRegisterStatusMap()
        {
            mRegStatusVector.clear();
        }

        //! @brief Clear the entire factory map (unregister everything)
        void clearFactory()
        {
            mFactoryMap.clear();
            mRegStatusVector.clear();
        }
    };
}

#endif // CLASFACTORY_HPP_INCLUDED
<|endoftext|>


<commit_before>//                       MFEM Example 3 - Parallel Version
//
// Compile with: make ex3p
//
// Sample runs:  mpirun -np 4 ex3p -m ../data/star.mesh
//               mpirun -np 4 ex3p -m ../data/square-disc.mesh -o 2
//               mpirun -np 4 ex3p -m ../data/beam-tet.mesh
//               mpirun -np 4 ex3p -m ../data/beam-hex.mesh
//               mpirun -np 4 ex3p -m ../data/beam-hex.mesh -o 2 -pa
//               mpirun -np 4 ex3p -m ../data/escher.mesh
//               mpirun -np 4 ex3p -m ../data/escher.mesh -o 2
//               mpirun -np 4 ex3p -m ../data/fichera.mesh
//               mpirun -np 4 ex3p -m ../data/fichera-q2.vtk
//               mpirun -np 4 ex3p -m ../data/fichera-q3.mesh
//               mpirun -np 4 ex3p -m ../data/square-disc-nurbs.mesh
//               mpirun -np 4 ex3p -m ../data/beam-hex-nurbs.mesh
//               mpirun -np 4 ex3p -m ../data/amr-quad.mesh -o 2
//               mpirun -np 4 ex3p -m ../data/amr-hex.mesh
//               mpirun -np 4 ex3p -m ../data/ref-prism.mesh -o 1
//               mpirun -np 4 ex3p -m ../data/octahedron.mesh -o 1
//               mpirun -np 4 ex3p -m ../data/star-surf.mesh -o 2
//               mpirun -np 4 ex3p -m ../data/mobius-strip.mesh -o 2 -f 0.1
//               mpirun -np 4 ex3p -m ../data/klein-bottle.mesh -o 2 -f 0.1
//
// Device sample runs:
//               mpirun -np 4 ex3p -m ../data/star.mesh -pa -d cuda
//               mpirun -np 4 ex3p -m ../data/star.mesh -no-pa -d cuda
//               mpirun -np 4 ex3p -m ../data/star.mesh -pa -d raja-cuda
//               mpirun -np 4 ex3p -m ../data/star.mesh -pa -d raja-omp
//               mpirun -np 4 ex3p -m ../data/beam-hex.mesh -pa -d cuda
//
// Description:  This example code solves a simple electromagnetic diffusion
//               problem corresponding to the second order definite Maxwell
//               equation curl curl E + E = f with boundary condition
//               E x n = <given tangential field>. Here, we use a given exact
//               solution E and compute the corresponding r.h.s. f.
//               We discretize with Nedelec finite elements in 2D or 3D.
//
//               The example demonstrates the use of H(curl) finite element
//               spaces with the curl-curl and the (vector finite element) mass
//               bilinear form, as well as the computation of discretization
//               error when the exact solution is known. Static condensation is
//               also illustrated.
//
//               We recommend viewing examples 1-2 before viewing this example.

#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace std;
using namespace mfem;

// Exact solution, E, and r.h.s., f. See below for implementation.
void E_exact(const Vector &, Vector &);
void f_exact(const Vector &, Vector &);
double freq = 1.0, kappa;
int dim;

int main(int argc, char *argv[])
{
   // 1. Initialize MPI and HYPRE.
   Mpi::Init(argc, argv);
   int num_procs = Mpi::WorldSize();
   int myid = Mpi::WorldRank();
   Hypre::Init();

   // 2. Parse command-line options.
   const char *mesh_file = "../data/beam-tet.mesh";
   int order = 1;
   bool static_cond = false;
   bool pa = false;
   const char *device_config = "cpu";
   bool visualization = true;
#ifdef MFEM_USE_AMGX
   bool useAmgX = false;
#endif

   OptionsParser args(argc, argv);
   args.AddOption(&mesh_file, "-m", "--mesh",
                  "Mesh file to use.");
   args.AddOption(&order, "-o", "--order",
                  "Finite element order (polynomial degree).");
   args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
                  " solution.");
   args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
                  "--no-static-condensation", "Enable static condensation.");
   args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
                  "--no-partial-assembly", "Enable Partial Assembly.");
   args.AddOption(&device_config, "-d", "--device",
                  "Device configuration string, see Device::Configure().");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
#ifdef MFEM_USE_AMGX
   args.AddOption(&useAmgX, "-amgx", "--useAmgX", "-no-amgx",
                  "--no-useAmgX",
                  "Enable or disable AmgX in MatrixFreeAMS.");
#endif

   args.Parse();
   if (!args.Good())
   {
      if (myid == 0)
      {
         args.PrintUsage(cout);
      }
      return 1;
   }
   if (myid == 0)
   {
      args.PrintOptions(cout);
   }
   kappa = freq * M_PI;

   // 3. Enable hardware devices such as GPUs, and programming models such as
   //    CUDA, OCCA, RAJA and OpenMP based on command line options.
   Device device(device_config);
   if (myid == 0) { device.Print(); }

   // 4. Read the (serial) mesh from the given mesh file on all processors.  We
   //    can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
   //    and volume meshes with the same code.
   Mesh *mesh = new Mesh(mesh_file, 1, 1);
   dim = mesh->Dimension();
   int sdim = mesh->SpaceDimension();

   // 5. Refine the serial mesh on all processors to increase the resolution. In
   //    this example we do 'ref_levels' of uniform refinement. We choose
   //    'ref_levels' to be the largest number that gives a final mesh with no
   //    more than 1,000 elements.
   {
      int ref_levels = (int)floor(log(1000./mesh->GetNE())/log(2.)/dim);
      for (int l = 0; l < ref_levels; l++)
      {
         mesh->UniformRefinement();
      }
   }

   // 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
   //    this mesh further in parallel to increase the resolution. Once the
   //    parallel mesh is defined, the serial mesh can be deleted.
   ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
   delete mesh;
   {
      int par_ref_levels = 2;
      for (int l = 0; l < par_ref_levels; l++)
      {
         pmesh->UniformRefinement();
      }
   }

   // 7. Define a parallel finite element space on the parallel mesh. Here we
   //    use the Nedelec finite elements of the specified order.
   FiniteElementCollection *fec = new ND_FECollection(order, dim);
   ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
   HYPRE_BigInt size = fespace->GlobalTrueVSize();
   if (myid == 0)
   {
      cout << "Number of finite element unknowns: " << size << endl;
   }

   // 8. Determine the list of true (i.e. parallel conforming) essential
   //    boundary dofs. In this example, the boundary conditions are defined
   //    by marking all the boundary attributes from the mesh as essential
   //    (Dirichlet) and converting them to a list of true dofs.
   Array<int> ess_tdof_list;
   Array<int> ess_bdr;
   if (pmesh->bdr_attributes.Size())
   {
      ess_bdr.SetSize(pmesh->bdr_attributes.Max());
      ess_bdr = 1;
      fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
   }

   // 9. Set up the parallel linear form b(.) which corresponds to the
   //    right-hand side of the FEM linear system, which in this case is
   //    (f,phi_i) where f is given by the function f_exact and phi_i are the
   //    basis functions in the finite element fespace.
   VectorFunctionCoefficient f(sdim, f_exact);
   ParLinearForm *b = new ParLinearForm(fespace);
   b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
   b->Assemble();

   // 10. Define the solution vector x as a parallel finite element grid function
   //     corresponding to fespace. Initialize x by projecting the exact
   //     solution. Note that only values from the boundary edges will be used
   //     when eliminating the non-homogeneous boundary condition to modify the
   //     r.h.s. vector b.
   ParGridFunction x(fespace);
   VectorFunctionCoefficient E(sdim, E_exact);
   x.ProjectCoefficient(E);

   // 11. Set up the parallel bilinear form corresponding to the EM diffusion
   //     operator curl muinv curl + sigma I, by adding the curl-curl and the
   //     mass domain integrators.
   Coefficient *muinv = new ConstantCoefficient(1.0);
   Coefficient *sigma = new ConstantCoefficient(1.0);
   ParBilinearForm *a = new ParBilinearForm(fespace);
   if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
   a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
   a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));

   // 12. Assemble the parallel bilinear form and the corresponding linear
   //     system, applying any necessary transformations such as: parallel
   //     assembly, eliminating boundary conditions, applying conforming
   //     constraints for non-conforming AMR, static condensation, etc.
   if (static_cond) { a->EnableStaticCondensation(); }
   a->Assemble();

   OperatorPtr A;
   Vector B, X;
   a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);

   // 13. Solve the system AX=B using PCG with an AMS preconditioner.
   if (pa)
   {
#ifdef MFEM_USE_AMGX
      MatrixFreeAMS ams(*a, *A, *fespace, muinv, sigma, NULL, ess_bdr, useAmgX);
#else
      MatrixFreeAMS ams(*a, *A, *fespace, muinv, sigma, NULL, ess_bdr);
#endif
      CGSolver cg(MPI_COMM_WORLD);
      cg.SetRelTol(1e-12);
      cg.SetMaxIter(1000);
      cg.SetPrintLevel(1);
      cg.SetOperator(*A);
      cg.SetPreconditioner(ams);
      cg.Mult(B, X);
   }
   else
   {
      if (myid == 0)
      {
         cout << "Size of linear system: "
              << A.As<HypreParMatrix>()->GetGlobalNumRows() << endl;
      }

      ParFiniteElementSpace *prec_fespace =
         (a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
      HypreAMS ams(*A.As<HypreParMatrix>(), prec_fespace);
      HyprePCG pcg(*A.As<HypreParMatrix>());
      pcg.SetTol(1e-12);
      pcg.SetMaxIter(500);
      pcg.SetPrintLevel(2);
      pcg.SetPreconditioner(ams);
      pcg.SetOperator(*A.As<HypreParMatrix>());
      pcg.Mult(B, X);

      pcg.SetOperator(*A.As<HypreParMatrix>());
      pcg.Mult(B, X);
   }

   // 14. Recover the parallel grid function corresponding to X. This is the
   //     local finite element solution on each processor.
   a->RecoverFEMSolution(X, *b, x);

   // 15. Compute and print the L^2 norm of the error.
   {
      double error = x.ComputeL2Error(E);
      if (myid == 0)
      {
         cout << "\n|| E_h - E ||_{L^2} = " << error << '\n' << endl;
      }
   }

   // 16. Save the refined mesh and the solution in parallel. This output can
   //     be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
   {
      ostringstream mesh_name, sol_name;
      mesh_name << "mesh." << setfill('0') << setw(6) << myid;
      sol_name << "sol." << setfill('0') << setw(6) << myid;

      ofstream mesh_ofs(mesh_name.str().c_str());
      mesh_ofs.precision(8);
      pmesh->Print(mesh_ofs);

      ofstream sol_ofs(sol_name.str().c_str());
      sol_ofs.precision(8);
      x.Save(sol_ofs);
   }

   // 17. Send the solution by socket to a GLVis server.
   if (visualization)
   {
      char vishost[] = "localhost";
      int  visport   = 19916;
      socketstream sol_sock(vishost, visport);
      sol_sock << "parallel " << num_procs << " " << myid << "\n";
      sol_sock.precision(8);
      sol_sock << "solution\n" << *pmesh << x << flush;
   }

   // 18. Free the used memory.
   delete a;
   delete sigma;
   delete muinv;
   delete b;
   delete fespace;
   delete fec;
   delete pmesh;

   return 0;
}

void E_exact(const Vector &x, Vector &E)
{
   if (dim == 3)
   {
      E(0) = sin(kappa * x(1));
      E(1) = sin(kappa * x(2));
      E(2) = sin(kappa * x(0));
   }
   else
   {
      E(0) = sin(kappa * x(1));
      E(1) = sin(kappa * x(0));
      if (x.Size() == 3) { E(2) = 0.0; }
   }
}

void f_exact(const Vector &x, Vector &f)
{
   if (dim == 3)
   {
      f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
      f(1) = (1. + kappa * kappa) * sin(kappa * x(2));
      f(2) = (1. + kappa * kappa) * sin(kappa * x(0));
   }
   else
   {
      f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
      f(1) = (1. + kappa * kappa) * sin(kappa * x(0));
      if (x.Size() == 3) { f(2) = 0.0; }
   }
}
<commit_msg>Revert test of memory leak in ex3p<commit_after>//                       MFEM Example 3 - Parallel Version
//
// Compile with: make ex3p
//
// Sample runs:  mpirun -np 4 ex3p -m ../data/star.mesh
//               mpirun -np 4 ex3p -m ../data/square-disc.mesh -o 2
//               mpirun -np 4 ex3p -m ../data/beam-tet.mesh
//               mpirun -np 4 ex3p -m ../data/beam-hex.mesh
//               mpirun -np 4 ex3p -m ../data/beam-hex.mesh -o 2 -pa
//               mpirun -np 4 ex3p -m ../data/escher.mesh
//               mpirun -np 4 ex3p -m ../data/escher.mesh -o 2
//               mpirun -np 4 ex3p -m ../data/fichera.mesh
//               mpirun -np 4 ex3p -m ../data/fichera-q2.vtk
//               mpirun -np 4 ex3p -m ../data/fichera-q3.mesh
//               mpirun -np 4 ex3p -m ../data/square-disc-nurbs.mesh
//               mpirun -np 4 ex3p -m ../data/beam-hex-nurbs.mesh
//               mpirun -np 4 ex3p -m ../data/amr-quad.mesh -o 2
//               mpirun -np 4 ex3p -m ../data/amr-hex.mesh
//               mpirun -np 4 ex3p -m ../data/ref-prism.mesh -o 1
//               mpirun -np 4 ex3p -m ../data/octahedron.mesh -o 1
//               mpirun -np 4 ex3p -m ../data/star-surf.mesh -o 2
//               mpirun -np 4 ex3p -m ../data/mobius-strip.mesh -o 2 -f 0.1
//               mpirun -np 4 ex3p -m ../data/klein-bottle.mesh -o 2 -f 0.1
//
// Device sample runs:
//               mpirun -np 4 ex3p -m ../data/star.mesh -pa -d cuda
//               mpirun -np 4 ex3p -m ../data/star.mesh -no-pa -d cuda
//               mpirun -np 4 ex3p -m ../data/star.mesh -pa -d raja-cuda
//               mpirun -np 4 ex3p -m ../data/star.mesh -pa -d raja-omp
//               mpirun -np 4 ex3p -m ../data/beam-hex.mesh -pa -d cuda
//
// Description:  This example code solves a simple electromagnetic diffusion
//               problem corresponding to the second order definite Maxwell
//               equation curl curl E + E = f with boundary condition
//               E x n = <given tangential field>. Here, we use a given exact
//               solution E and compute the corresponding r.h.s. f.
//               We discretize with Nedelec finite elements in 2D or 3D.
//
//               The example demonstrates the use of H(curl) finite element
//               spaces with the curl-curl and the (vector finite element) mass
//               bilinear form, as well as the computation of discretization
//               error when the exact solution is known. Static condensation is
//               also illustrated.
//
//               We recommend viewing examples 1-2 before viewing this example.

#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace std;
using namespace mfem;

// Exact solution, E, and r.h.s., f. See below for implementation.
void E_exact(const Vector &, Vector &);
void f_exact(const Vector &, Vector &);
double freq = 1.0, kappa;
int dim;

int main(int argc, char *argv[])
{
   // 1. Initialize MPI and HYPRE.
   Mpi::Init(argc, argv);
   int num_procs = Mpi::WorldSize();
   int myid = Mpi::WorldRank();
   Hypre::Init();

   // 2. Parse command-line options.
   const char *mesh_file = "../data/beam-tet.mesh";
   int order = 1;
   bool static_cond = false;
   bool pa = false;
   const char *device_config = "cpu";
   bool visualization = true;
#ifdef MFEM_USE_AMGX
   bool useAmgX = false;
#endif

   OptionsParser args(argc, argv);
   args.AddOption(&mesh_file, "-m", "--mesh",
                  "Mesh file to use.");
   args.AddOption(&order, "-o", "--order",
                  "Finite element order (polynomial degree).");
   args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
                  " solution.");
   args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
                  "--no-static-condensation", "Enable static condensation.");
   args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
                  "--no-partial-assembly", "Enable Partial Assembly.");
   args.AddOption(&device_config, "-d", "--device",
                  "Device configuration string, see Device::Configure().");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
#ifdef MFEM_USE_AMGX
   args.AddOption(&useAmgX, "-amgx", "--useAmgX", "-no-amgx",
                  "--no-useAmgX",
                  "Enable or disable AmgX in MatrixFreeAMS.");
#endif

   args.Parse();
   if (!args.Good())
   {
      if (myid == 0)
      {
         args.PrintUsage(cout);
      }
      return 1;
   }
   if (myid == 0)
   {
      args.PrintOptions(cout);
   }
   kappa = freq * M_PI;

   // 3. Enable hardware devices such as GPUs, and programming models such as
   //    CUDA, OCCA, RAJA and OpenMP based on command line options.
   Device device(device_config);
   if (myid == 0) { device.Print(); }

   // 4. Read the (serial) mesh from the given mesh file on all processors.  We
   //    can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
   //    and volume meshes with the same code.
   Mesh *mesh = new Mesh(mesh_file, 1, 1);
   dim = mesh->Dimension();
   int sdim = mesh->SpaceDimension();

   // 5. Refine the serial mesh on all processors to increase the resolution. In
   //    this example we do 'ref_levels' of uniform refinement. We choose
   //    'ref_levels' to be the largest number that gives a final mesh with no
   //    more than 1,000 elements.
   {
      int ref_levels = (int)floor(log(1000./mesh->GetNE())/log(2.)/dim);
      for (int l = 0; l < ref_levels; l++)
      {
         mesh->UniformRefinement();
      }
   }

   // 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
   //    this mesh further in parallel to increase the resolution. Once the
   //    parallel mesh is defined, the serial mesh can be deleted.
   ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
   delete mesh;
   {
      int par_ref_levels = 2;
      for (int l = 0; l < par_ref_levels; l++)
      {
         pmesh->UniformRefinement();
      }
   }

   // 7. Define a parallel finite element space on the parallel mesh. Here we
   //    use the Nedelec finite elements of the specified order.
   FiniteElementCollection *fec = new ND_FECollection(order, dim);
   ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
   HYPRE_BigInt size = fespace->GlobalTrueVSize();
   if (myid == 0)
   {
      cout << "Number of finite element unknowns: " << size << endl;
   }

   // 8. Determine the list of true (i.e. parallel conforming) essential
   //    boundary dofs. In this example, the boundary conditions are defined
   //    by marking all the boundary attributes from the mesh as essential
   //    (Dirichlet) and converting them to a list of true dofs.
   Array<int> ess_tdof_list;
   Array<int> ess_bdr;
   if (pmesh->bdr_attributes.Size())
   {
      ess_bdr.SetSize(pmesh->bdr_attributes.Max());
      ess_bdr = 1;
      fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
   }

   // 9. Set up the parallel linear form b(.) which corresponds to the
   //    right-hand side of the FEM linear system, which in this case is
   //    (f,phi_i) where f is given by the function f_exact and phi_i are the
   //    basis functions in the finite element fespace.
   VectorFunctionCoefficient f(sdim, f_exact);
   ParLinearForm *b = new ParLinearForm(fespace);
   b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
   b->Assemble();

   // 10. Define the solution vector x as a parallel finite element grid function
   //     corresponding to fespace. Initialize x by projecting the exact
   //     solution. Note that only values from the boundary edges will be used
   //     when eliminating the non-homogeneous boundary condition to modify the
   //     r.h.s. vector b.
   ParGridFunction x(fespace);
   VectorFunctionCoefficient E(sdim, E_exact);
   x.ProjectCoefficient(E);

   // 11. Set up the parallel bilinear form corresponding to the EM diffusion
   //     operator curl muinv curl + sigma I, by adding the curl-curl and the
   //     mass domain integrators.
   Coefficient *muinv = new ConstantCoefficient(1.0);
   Coefficient *sigma = new ConstantCoefficient(1.0);
   ParBilinearForm *a = new ParBilinearForm(fespace);
   if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
   a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
   a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));

   // 12. Assemble the parallel bilinear form and the corresponding linear
   //     system, applying any necessary transformations such as: parallel
   //     assembly, eliminating boundary conditions, applying conforming
   //     constraints for non-conforming AMR, static condensation, etc.
   if (static_cond) { a->EnableStaticCondensation(); }
   a->Assemble();

   OperatorPtr A;
   Vector B, X;
   a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);

   // 13. Solve the system AX=B using PCG with an AMS preconditioner.
   if (pa)
   {
#ifdef MFEM_USE_AMGX
      MatrixFreeAMS ams(*a, *A, *fespace, muinv, sigma, NULL, ess_bdr, useAmgX);
#else
      MatrixFreeAMS ams(*a, *A, *fespace, muinv, sigma, NULL, ess_bdr);
#endif
      CGSolver cg(MPI_COMM_WORLD);
      cg.SetRelTol(1e-12);
      cg.SetMaxIter(1000);
      cg.SetPrintLevel(1);
      cg.SetOperator(*A);
      cg.SetPreconditioner(ams);
      cg.Mult(B, X);
   }
   else
   {
      if (myid == 0)
      {
         cout << "Size of linear system: "
              << A.As<HypreParMatrix>()->GetGlobalNumRows() << endl;
      }

      ParFiniteElementSpace *prec_fespace =
         (a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
      HypreAMS ams(*A.As<HypreParMatrix>(), prec_fespace);
      HyprePCG pcg(*A.As<HypreParMatrix>());
      pcg.SetTol(1e-12);
      pcg.SetMaxIter(500);
      pcg.SetPrintLevel(2);
      pcg.SetPreconditioner(ams);
      pcg.Mult(B, X);
   }

   // 14. Recover the parallel grid function corresponding to X. This is the
   //     local finite element solution on each processor.
   a->RecoverFEMSolution(X, *b, x);

   // 15. Compute and print the L^2 norm of the error.
   {
      double error = x.ComputeL2Error(E);
      if (myid == 0)
      {
         cout << "\n|| E_h - E ||_{L^2} = " << error << '\n' << endl;
      }
   }

   // 16. Save the refined mesh and the solution in parallel. This output can
   //     be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
   {
      ostringstream mesh_name, sol_name;
      mesh_name << "mesh." << setfill('0') << setw(6) << myid;
      sol_name << "sol." << setfill('0') << setw(6) << myid;

      ofstream mesh_ofs(mesh_name.str().c_str());
      mesh_ofs.precision(8);
      pmesh->Print(mesh_ofs);

      ofstream sol_ofs(sol_name.str().c_str());
      sol_ofs.precision(8);
      x.Save(sol_ofs);
   }

   // 17. Send the solution by socket to a GLVis server.
   if (visualization)
   {
      char vishost[] = "localhost";
      int  visport   = 19916;
      socketstream sol_sock(vishost, visport);
      sol_sock << "parallel " << num_procs << " " << myid << "\n";
      sol_sock.precision(8);
      sol_sock << "solution\n" << *pmesh << x << flush;
   }

   // 18. Free the used memory.
   delete a;
   delete sigma;
   delete muinv;
   delete b;
   delete fespace;
   delete fec;
   delete pmesh;

   return 0;
}

void E_exact(const Vector &x, Vector &E)
{
   if (dim == 3)
   {
      E(0) = sin(kappa * x(1));
      E(1) = sin(kappa * x(2));
      E(2) = sin(kappa * x(0));
   }
   else
   {
      E(0) = sin(kappa * x(1));
      E(1) = sin(kappa * x(0));
      if (x.Size() == 3) { E(2) = 0.0; }
   }
}

void f_exact(const Vector &x, Vector &f)
{
   if (dim == 3)
   {
      f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
      f(1) = (1. + kappa * kappa) * sin(kappa * x(2));
      f(2) = (1. + kappa * kappa) * sin(kappa * x(0));
   }
   else
   {
      f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
      f(1) = (1. + kappa * kappa) * sin(kappa * x(0));
      if (x.Size() == 3) { f(2) = 0.0; }
   }
}
<|endoftext|>


<commit_before>#include "xchainer/shape.h"

#include <tuple>
#include <vector>

#include <gtest/gtest.h>
#include <gsl/gsl>

#include "xchainer/axes.h"
#include "xchainer/dtype.h"
#include "xchainer/strides.h"

namespace xchainer {
namespace {

void CheckSpanEqual(std::initializer_list<int64_t> expect, gsl::span<const int64_t> actual) {
    EXPECT_EQ(gsl::make_span(expect.begin(), expect.end()), actual);
}

TEST(ShapeTest, Ctor) {
    {  // Default ctor
        const Shape shape{};
        EXPECT_EQ(0, shape.ndim());
        EXPECT_EQ(size_t{0}, shape.size());
    }
    {  // From std::initializer_list
        const Shape shape{2, 3, 4};
        EXPECT_EQ(3, shape.ndim());
        EXPECT_EQ(size_t{3}, shape.size());
        CheckSpanEqual({2, 3, 4}, shape.span());
        EXPECT_EQ(2 * 3 * 4, shape.GetTotalSize());
    }
    {  // From gsl::span
        const std::array<int64_t, 3> dims{2, 3, 4};
        const Shape shape{gsl::make_span(dims)};
        EXPECT_EQ(3, shape.ndim());
        CheckSpanEqual({2, 3, 4}, shape.span());
    }
    {  // From iterators
        const std::vector<int64_t> dims{2, 3, 4};
        const Shape shape{dims.begin(), dims.end()};
        EXPECT_EQ(3, shape.ndim());
        CheckSpanEqual({2, 3, 4}, shape.span());
    }
    {  // From empty std::initializer_list
        const Shape shape(std::initializer_list<int64_t>{});
        EXPECT_EQ(0, shape.ndim());
        CheckSpanEqual({}, shape.span());
    }
    {  // From empty gsl::span
        const std::array<int64_t, 0> dims{};
        const Shape shape{gsl::make_span(dims)};
        EXPECT_EQ(0, shape.ndim());
        CheckSpanEqual({}, shape.span());
    }
    {  // From empty iterators
        const std::vector<int64_t> dims{};
        const Shape shape{dims.begin(), dims.end()};
        EXPECT_EQ(0, shape.ndim());
        CheckSpanEqual({}, shape.span());
    }
    {  // Too long std::initializer_list
        EXPECT_THROW(Shape({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), DimensionError);
    }
    {  // Too long gsl::span
        const std::array<int64_t, kMaxNdim + 1> too_long{1};
        EXPECT_THROW(Shape{gsl::make_span(too_long)}, DimensionError);
    }
    {  // Too long iterators
        std::vector<int64_t> dims{};
        dims.resize(kMaxNdim + 1);
        std::iota(dims.begin(), dims.end(), int64_t{1});
        EXPECT_THROW(Shape({dims.begin(), dims.end()}), DimensionError);
    }
}

TEST(ShapeTest, Subscript) {
    const Shape shape = {2, 3, 4};
    EXPECT_EQ(2, shape[0]);
    EXPECT_EQ(3, shape[1]);
    EXPECT_EQ(4, shape[2]);
    EXPECT_THROW(shape[-1], DimensionError);
    EXPECT_THROW(shape[3], DimensionError);
}

TEST(ShapeTest, Compare) {
    {
        const Shape shape = {2, 3, 4};
        const Shape shape2 = {2, 3, 4};
        EXPECT_TRUE(shape == shape2);
    }
    {
        const Shape shape = {2, 3, 4};
        const Shape shape2 = {2, 3};
        EXPECT_TRUE(shape != shape2);
    }
    {
        const Shape shape = {2, 3, 4};
        const Shape shape2 = {1, 2, 3};
        EXPECT_TRUE(shape != shape2);
    }
}

TEST(ShapeTest, CheckEqual) {
    {
        const Shape shape = {2, 3, 4};
        const Shape shape2 = {2, 3, 4};
        EXPECT_NO_THROW(CheckEqual(shape, shape2));
    }
    {
        const Shape shape = {2, 3, 4};
        const Shape shape2 = {};
        EXPECT_THROW(CheckEqual(shape, shape2), DimensionError);
    }
}

TEST(ShapeTest, Iterator) {
    const Shape shape = {2, 3, 4};
    CheckSpanEqual({2, 3, 4}, gsl::make_span(shape.begin(), shape.end()));
    CheckSpanEqual({4, 3, 2}, gsl::make_span(std::vector<int64_t>{shape.rbegin(), shape.rend()}));
}

TEST(ShapeTest, ToString) {
    {
        const Shape shape = {};
        EXPECT_EQ(shape.ToString(), "()");
    }
    {
        const Shape shape = {1};
        EXPECT_EQ(shape.ToString(), "(1,)");
    }
    {
        const Shape shape = {2, 3, 4};
        EXPECT_EQ(shape.ToString(), "(2, 3, 4)");
    }
}

TEST(StridesTest, SpanFromShape) {
    const Shape shape = {2, 3, 4};
    CheckSpanEqual({2, 3, 4}, gsl::make_span(shape));
}

TEST(ShapeTest, IsContiguous) {
    {
        Shape shape{2, 3, 4};
        Strides strides{shape, GetItemSize(Dtype::kFloat32)};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{2, 3, 4};
        Strides strides{shape, GetItemSize(Dtype::kFloat32)};
        EXPECT_FALSE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat64)));
    }
    {
        Shape shape{2, 3};
        Strides strides{12, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{2, 3, 4};
        Strides strides{48, 16, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{2, 3, 4};
        Strides strides{32, 16, 4};
        EXPECT_FALSE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{};
        Strides strides{};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{0};
        Strides strides{8};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{0, 0};
        Strides strides{24, 8};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 2};
        Strides strides{12, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 3};
        Strides strides{-12, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 2, 4};
        Strides strides{48, 16, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 3};
        Strides strides{0, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 1, 2, 3};
        Strides strides{0, 0, 12, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 1, 2, 3};
        Strides strides{240, 80, 20, 4};
        EXPECT_FALSE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 1, 2};
        Strides strides{48, 16, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 2};
        Strides strides{0, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{2, 2};
        Strides strides{16, 4};
        EXPECT_FALSE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{2, 1, 2};
        Strides strides{48, 16, 4};
        EXPECT_FALSE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
}

TEST(ShapeTest, IsValidReductionShape) {
    {
        Shape in_shape{};
        Shape out_shape{};
        Axes axes{};
        bool allow_keepdims = true;
        EXPECT_TRUE(internal::IsValidReductionShape(in_shape, axes, out_shape, allow_keepdims));
    }
    {
        Shape in_shape{};
        Shape out_shape{};
        Axes axes{};
        bool allow_keepdims = false;
        EXPECT_TRUE(internal::IsValidReductionShape(in_shape, axes, out_shape, allow_keepdims));
    }
    {
        Shape in_shape{2, 3, 4};
        Shape out_shape{2};
        Axes axes{1, 2};
        bool allow_keepdims = false;
        EXPECT_TRUE(internal::IsValidReductionShape(in_shape, axes, out_shape, allow_keepdims));
    }
    {
        Shape in_shape{2, 3, 4};
        Shape out_shape{2};
        Axes axes{1, 2};
        bool allow_keepdims = false;
        EXPECT_TRUE(internal::IsValidReductionShape(in_shape, axes, out_shape, allow_keepdims));
    }
    {
        Shape in_shape{2, 3, 4};
        Shape out_shape{2, 1, 1};
        Axes axes{1, 2};
        bool allow_keepdims = true;
        EXPECT_TRUE(internal::IsValidReductionShape(in_shape, axes, out_shape, allow_keepdims));
    }
}

TEST(ShapeTest, IsValidReductionShapeInvalid) {
    {
        Shape in_shape{1};
        Shape out_shape{};
        Axes axes{};
        bool allow_keepdims = true;
        EXPECT_FALSE(internal::IsValidReductionShape(in_shape, axes, out_shape, allow_keepdims));
    }
    {
        Shape in_shape{1};
        Shape out_shape{};
        Axes axes{};
        bool allow_keepdims = false;
        EXPECT_FALSE(internal::IsValidReductionShape(in_shape, axes, out_shape, allow_keepdims));
    }
    {
        Shape in_shape{1};
        Shape out_shape{1, 2};
        Axes axes{1};
        bool allow_keepdims = true;
        EXPECT_FALSE(internal::IsValidReductionShape(in_shape, axes, out_shape, allow_keepdims));
    }
    {
        Shape in_shape{1};
        Shape out_shape{1, 2};
        Axes axes{1};
        bool allow_keepdims = false;
        EXPECT_FALSE(internal::IsValidReductionShape(in_shape, axes, out_shape, allow_keepdims));
    }
    {
        Shape in_shape{2, 3, 4};
        Shape out_shape{2, 1, 1};
        Axes axes{1, 2};
        bool allow_keepdims = false;
        EXPECT_FALSE(internal::IsValidReductionShape(in_shape, axes, out_shape, allow_keepdims));
    }
}

class BroadcastShapesCorrectTest : public ::testing::TestWithParam<std::tuple<Shape, Shape, Shape>> {
public:
    const Shape& shape0() const { return std::get<0>(GetParam()); }
    const Shape& shape1() const { return std::get<1>(GetParam()); }
    const Shape& expected() const { return std::get<2>(GetParam()); }
};

TEST_P(BroadcastShapesCorrectTest, Check) {
    Shape result = internal::BroadcastShapes(shape0(), shape1());
    EXPECT_EQ(expected(), result);
}

INSTANTIATE_TEST_CASE_P(
        Cases,
        BroadcastShapesCorrectTest,
        ::testing::Values(
                std::make_tuple(Shape{2, 3, 4}, Shape{2, 3, 4}, Shape{2, 3, 4}),
                std::make_tuple(Shape{1, 3, 1}, Shape{2, 3, 4}, Shape{2, 3, 4}),
                std::make_tuple(Shape{2, 3, 4}, Shape{1, 3, 1}, Shape{2, 3, 4}),
                std::make_tuple(Shape{1, 3, 4}, Shape{2, 3, 1}, Shape{2, 3, 4}),
                std::make_tuple(Shape{3, 4}, Shape{2, 3, 1}, Shape{2, 3, 4})));

TEST(BroadcastShapesInvalidTest, Check) {
    EXPECT_THROW(internal::BroadcastShapes({2, 3}, {3, 2}), DimensionError);
    EXPECT_THROW(internal::BroadcastShapes({2, 3}, {2, 3, 1}), DimensionError);
    EXPECT_THROW(internal::BroadcastShapes({3, 2, 1, 3}, {3, 2, 3}), DimensionError);
}

class ReduceShapesCorrectTest : public ::testing::TestWithParam<std::tuple<Shape, Axes, bool, Shape>> {
public:
    const Shape& shape() const { return std::get<0>(GetParam()); }
    const Axes& axes() const { return std::get<1>(GetParam()); }
    bool keepdims() const { return std::get<2>(GetParam()); }
    const Shape& expected() const { return std::get<3>(GetParam()); }
};

TEST_P(ReduceShapesCorrectTest, Check) {
    Shape result = internal::ReduceShape(shape(), axes(), keepdims());
    EXPECT_EQ(expected(), result);
}

INSTANTIATE_TEST_CASE_P(
        Cases,
        ReduceShapesCorrectTest,
        ::testing::Values(
                std::make_tuple(Shape{}, Axes{}, true, Shape{}),
                std::make_tuple(Shape{}, Axes{}, false, Shape{}),
                std::make_tuple(Shape{2, 3}, Axes{}, true, Shape{2, 3}),
                std::make_tuple(Shape{2, 3}, Axes{}, false, Shape{2, 3}),
                std::make_tuple(Shape{2, 3}, Axes{1}, true, Shape{2, 1}),
                std::make_tuple(Shape{2, 3}, Axes{1}, false, Shape{2}),
                std::make_tuple(Shape{2, 3, 4}, Axes{0, 2}, true, Shape{1, 3, 1}),
                std::make_tuple(Shape{2, 3, 4}, Axes{0, 2}, false, Shape{3})));

TEST(TransposeShapeTest, Normal) {
    Shape actual = internal::TransposeShape(Shape{2, 3, 4, 5}, {2, 0, 3, 1});
    Shape expected{4, 2, 5, 3};
    EXPECT_EQ(expected, actual);
}

TEST(TransposeShapeTest, LongAxis) {
    Shape actual = internal::TransposeShape(Shape{128, 256, 65536}, {1, 2, 0});
    Shape expected{256, 65536, 128};
    EXPECT_EQ(expected, actual);
}

}  // namespace
}  // namespace xchainer
<commit_msg>Remove tests for internal functions<commit_after>#include "xchainer/shape.h"

#include <tuple>
#include <vector>

#include <gtest/gtest.h>
#include <gsl/gsl>

#include "xchainer/dtype.h"
#include "xchainer/strides.h"

namespace xchainer {
namespace {

void CheckSpanEqual(std::initializer_list<int64_t> expect, gsl::span<const int64_t> actual) {
    EXPECT_EQ(gsl::make_span(expect.begin(), expect.end()), actual);
}

TEST(ShapeTest, Ctor) {
    {  // Default ctor
        const Shape shape{};
        EXPECT_EQ(0, shape.ndim());
        EXPECT_EQ(size_t{0}, shape.size());
    }
    {  // From std::initializer_list
        const Shape shape{2, 3, 4};
        EXPECT_EQ(3, shape.ndim());
        EXPECT_EQ(size_t{3}, shape.size());
        CheckSpanEqual({2, 3, 4}, shape.span());
        EXPECT_EQ(2 * 3 * 4, shape.GetTotalSize());
    }
    {  // From gsl::span
        const std::array<int64_t, 3> dims{2, 3, 4};
        const Shape shape{gsl::make_span(dims)};
        EXPECT_EQ(3, shape.ndim());
        CheckSpanEqual({2, 3, 4}, shape.span());
    }
    {  // From iterators
        const std::vector<int64_t> dims{2, 3, 4};
        const Shape shape{dims.begin(), dims.end()};
        EXPECT_EQ(3, shape.ndim());
        CheckSpanEqual({2, 3, 4}, shape.span());
    }
    {  // From empty std::initializer_list
        const Shape shape(std::initializer_list<int64_t>{});
        EXPECT_EQ(0, shape.ndim());
        CheckSpanEqual({}, shape.span());
    }
    {  // From empty gsl::span
        const std::array<int64_t, 0> dims{};
        const Shape shape{gsl::make_span(dims)};
        EXPECT_EQ(0, shape.ndim());
        CheckSpanEqual({}, shape.span());
    }
    {  // From empty iterators
        const std::vector<int64_t> dims{};
        const Shape shape{dims.begin(), dims.end()};
        EXPECT_EQ(0, shape.ndim());
        CheckSpanEqual({}, shape.span());
    }
    {  // Too long std::initializer_list
        EXPECT_THROW(Shape({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), DimensionError);
    }
    {  // Too long gsl::span
        const std::array<int64_t, kMaxNdim + 1> too_long{1};
        EXPECT_THROW(Shape{gsl::make_span(too_long)}, DimensionError);
    }
    {  // Too long iterators
        std::vector<int64_t> dims{};
        dims.resize(kMaxNdim + 1);
        std::iota(dims.begin(), dims.end(), int64_t{1});
        EXPECT_THROW(Shape({dims.begin(), dims.end()}), DimensionError);
    }
}

TEST(ShapeTest, Subscript) {
    const Shape shape = {2, 3, 4};
    EXPECT_EQ(2, shape[0]);
    EXPECT_EQ(3, shape[1]);
    EXPECT_EQ(4, shape[2]);
    EXPECT_THROW(shape[-1], DimensionError);
    EXPECT_THROW(shape[3], DimensionError);
}

TEST(ShapeTest, Compare) {
    {
        const Shape shape = {2, 3, 4};
        const Shape shape2 = {2, 3, 4};
        EXPECT_TRUE(shape == shape2);
    }
    {
        const Shape shape = {2, 3, 4};
        const Shape shape2 = {2, 3};
        EXPECT_TRUE(shape != shape2);
    }
    {
        const Shape shape = {2, 3, 4};
        const Shape shape2 = {1, 2, 3};
        EXPECT_TRUE(shape != shape2);
    }
}

TEST(ShapeTest, CheckEqual) {
    {
        const Shape shape = {2, 3, 4};
        const Shape shape2 = {2, 3, 4};
        EXPECT_NO_THROW(CheckEqual(shape, shape2));
    }
    {
        const Shape shape = {2, 3, 4};
        const Shape shape2 = {};
        EXPECT_THROW(CheckEqual(shape, shape2), DimensionError);
    }
}

TEST(ShapeTest, Iterator) {
    const Shape shape = {2, 3, 4};
    CheckSpanEqual({2, 3, 4}, gsl::make_span(shape.begin(), shape.end()));
    CheckSpanEqual({4, 3, 2}, gsl::make_span(std::vector<int64_t>{shape.rbegin(), shape.rend()}));
}

TEST(ShapeTest, ToString) {
    {
        const Shape shape = {};
        EXPECT_EQ(shape.ToString(), "()");
    }
    {
        const Shape shape = {1};
        EXPECT_EQ(shape.ToString(), "(1,)");
    }
    {
        const Shape shape = {2, 3, 4};
        EXPECT_EQ(shape.ToString(), "(2, 3, 4)");
    }
}

TEST(StridesTest, SpanFromShape) {
    const Shape shape = {2, 3, 4};
    CheckSpanEqual({2, 3, 4}, gsl::make_span(shape));
}

TEST(ShapeTest, IsContiguous) {
    {
        Shape shape{2, 3, 4};
        Strides strides{shape, GetItemSize(Dtype::kFloat32)};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{2, 3, 4};
        Strides strides{shape, GetItemSize(Dtype::kFloat32)};
        EXPECT_FALSE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat64)));
    }
    {
        Shape shape{2, 3};
        Strides strides{12, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{2, 3, 4};
        Strides strides{48, 16, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{2, 3, 4};
        Strides strides{32, 16, 4};
        EXPECT_FALSE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{};
        Strides strides{};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{0};
        Strides strides{8};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{0, 0};
        Strides strides{24, 8};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 2};
        Strides strides{12, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 3};
        Strides strides{-12, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 2, 4};
        Strides strides{48, 16, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 3};
        Strides strides{0, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 1, 2, 3};
        Strides strides{0, 0, 12, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 1, 2, 3};
        Strides strides{240, 80, 20, 4};
        EXPECT_FALSE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 1, 2};
        Strides strides{48, 16, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{1, 2};
        Strides strides{0, 4};
        EXPECT_TRUE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{2, 2};
        Strides strides{16, 4};
        EXPECT_FALSE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
    {
        Shape shape{2, 1, 2};
        Strides strides{48, 16, 4};
        EXPECT_FALSE(internal::IsContiguous(shape, strides, GetItemSize(Dtype::kFloat32)));
    }
}

class BroadcastShapesCorrectTest : public ::testing::TestWithParam<std::tuple<Shape, Shape, Shape>> {
public:
    const Shape& shape0() const { return std::get<0>(GetParam()); }
    const Shape& shape1() const { return std::get<1>(GetParam()); }
    const Shape& expected() const { return std::get<2>(GetParam()); }
};

TEST_P(BroadcastShapesCorrectTest, Check) {
    Shape result = internal::BroadcastShapes(shape0(), shape1());
    EXPECT_EQ(expected(), result);
}

INSTANTIATE_TEST_CASE_P(
        Cases,
        BroadcastShapesCorrectTest,
        ::testing::Values(
                std::make_tuple(Shape{2, 3, 4}, Shape{2, 3, 4}, Shape{2, 3, 4}),
                std::make_tuple(Shape{1, 3, 1}, Shape{2, 3, 4}, Shape{2, 3, 4}),
                std::make_tuple(Shape{2, 3, 4}, Shape{1, 3, 1}, Shape{2, 3, 4}),
                std::make_tuple(Shape{1, 3, 4}, Shape{2, 3, 1}, Shape{2, 3, 4}),
                std::make_tuple(Shape{3, 4}, Shape{2, 3, 1}, Shape{2, 3, 4})));

TEST(BroadcastShapesInvalidTest, Check) {
    EXPECT_THROW(internal::BroadcastShapes({2, 3}, {3, 2}), DimensionError);
    EXPECT_THROW(internal::BroadcastShapes({2, 3}, {2, 3, 1}), DimensionError);
    EXPECT_THROW(internal::BroadcastShapes({3, 2, 1, 3}, {3, 2, 3}), DimensionError);
}

TEST(TransposeShapeTest, Normal) {
    Shape actual = internal::TransposeShape(Shape{2, 3, 4, 5}, {2, 0, 3, 1});
    Shape expected{4, 2, 5, 3};
    EXPECT_EQ(expected, actual);
}

TEST(TransposeShapeTest, LongAxis) {
    Shape actual = internal::TransposeShape(Shape{128, 256, 65536}, {1, 2, 0});
    Shape expected{256, 65536, 128};
    EXPECT_EQ(expected, actual);
}

}  // namespace
}  // namespace xchainer
<|endoftext|>


<commit_before>#include "Alembic.h"
#include "AlembicMax.h"
#include "AlembicPolyMsh.h"
#include "AlembicXForm.h"
#include "SceneEnumProc.h"
#include "Utility.h"
#include "AlembicMetadataUtils.h"
#include "AlembicPointsUtils.h"
#include "AlembicIntermediatePolyMesh3DSMax.h"
#include "CommonMeshUtilities.h"

// From the SDK
// How to calculate UV's for face mapped materials.
//static Point3 basic_tva[3] = {
//	Point3(0.0,0.0,0.0),Point3(1.0,0.0,0.0),Point3(1.0,1.0,0.0)
//};
//static Point3 basic_tvb[3] = {
//	Point3(1.0,1.0,0.0),Point3(0.0,1.0,0.0),Point3(0.0,0.0,0.0)
//};
//static int nextpt[3] = {1,2,0};
//static int prevpt[3] = {2,0,1};

AlembicPolyMesh::AlembicPolyMesh(const SceneEntry &in_Ref, AlembicWriteJob *in_Job)
: AlembicObject(in_Ref, in_Job)
{
    std::string meshName = EC_MCHAR_to_UTF8( in_Ref.node->GetName() );
    std::string xformName = meshName + "Xfo";

    AbcG::OXform xform(GetOParent(), xformName.c_str(), GetCurrentJob()->GetAnimatedTs());
    AbcG::OPolyMesh mesh(xform, meshName.c_str(), GetCurrentJob()->GetAnimatedTs());

    // JSS - I'm not sure if this is require under 3DSMAx
    // AddRef(prim.GetParent3DObject().GetKinematics().GetGlobal().GetRef());

    // create the generic properties
    mOVisibility = CreateVisibilityProperty(mesh,GetCurrentJob()->GetAnimatedTs());

    mXformSchema = xform.getSchema();
    mMeshSchema = mesh.getSchema();
}

AlembicPolyMesh::~AlembicPolyMesh()
{
    // we have to clear this prior to destruction this is a workaround for issue-171
    mOVisibility.reset();
}

Abc::OCompoundProperty AlembicPolyMesh::GetCompound()
{
    return mMeshSchema;
}

void AlembicPolyMesh::SaveMaterialsProperty(bool bFirstFrame, bool bLastFrame)
{
	if(bLastFrame){
		std::vector<std::string> materialNames;
		mergedMeshMaterialsMap& matMap = materialsMerge.groupMatMap;

		for ( mergedMeshMaterialsMap_it it=matMap.begin(); it != matMap.end(); it++)
		{
			meshMaterialsMap& map = it->second;
			for( meshMaterialsMap_it it2 =map.begin(); it2 != map.end(); it2++)
			{
				std::stringstream nameStream;
				int nMaterialId = it2->second.matId+1;
				nameStream<<it2->second.name<<"_"<<nMaterialId;
				materialNames.push_back(nameStream.str());
			}
		}

		if(materialNames.size() > 0){
			if(!mMatNamesProperty.valid())
			{
				mMatNamesProperty = Abc::OStringArrayProperty(mMeshSchema, ".materialnames", mMeshSchema.getMetaData(), GetCurrentJob()->GetAnimatedTs());
			}
			Abc::StringArraySample sample = Abc::StringArraySample(materialNames);
			mMatNamesProperty.set(sample);
		}
	}
}

bool AlembicPolyMesh::Save(double time, bool bLastFrame)
{
	//this call is here to avoid reading pointers that are only valid on a single frame
	mMeshSample.reset();

	const bool bFirstFrame = mNumSamples == 0;

	TimeValue ticks = GetTimeValueFromFrame(time);

	Object *obj = GetRef().node->EvalWorldState(ticks).obj;
	const bool bIsParticleSystem = obj->IsParticleSystem() == 1;

	if(bIsParticleSystem){
		bForever = false;
	}
	else{
		if(mNumSamples == 0){
			bForever = CheckIfObjIsValidForever(obj, ticks);
		}
		else{
			bool bNewForever = CheckIfObjIsValidForever(obj, ticks);
			if(bForever && bNewForever != bForever){
				ESS_LOG_INFO( "bForever has changed" );
			}
		}
	}

	const bool bFlatten = GetCurrentJob()->GetOption("flattenHierarchy");

    // Store the transformation
    SaveXformSample(GetRef(), mXformSchema, mXformSample, time, bFlatten);

	SaveMetaData(GetRef().node, this);

    // store the metadata
    //IMetaDataManager mng;
    // mng.GetMetaData(GetRef().node, 0);
    // SaveMetaData(prim.GetParent3DObject().GetRef(),this);

    // set the visibility
    if(!bForever || mNumSamples == 0)
    {
        float flVisibility = GetRef().node->GetLocalVisibility(ticks);
        mOVisibility.set(flVisibility > 0 ? AbcG::kVisibilityVisible : AbcG::kVisibilityHidden);
    }

    // check if the mesh is animated (Otherwise, no need to export)
    if(mNumSamples > 0)
    {
        if(bForever)
        {
			ESS_LOG_INFO( "Node is not animated, not saving topology on subsequent frames." );
            return true;
        }
    }

	IntermediatePolyMesh3DSMax finalPolyMesh;

	if(bIsParticleSystem){

		bool bSuccess = getParticleSystemMesh(ticks, obj, GetRef().node, &finalPolyMesh, &materialsMerge, mJob, mNumSamples);
		if(!bSuccess){
			ESS_LOG_INFO( "Error. Could not get particle system mesh. Time: "<<time );
			return false;
		}
	}
	else
	{
		PolyObject *polyObj = NULL;
		TriObject *triObj = NULL;
		Mesh *triMesh = NULL;
		MNMesh* polyMesh = NULL;

		if (obj->CanConvertToType(Class_ID(POLYOBJ_CLASS_ID, 0)))
		{
			polyObj = reinterpret_cast<PolyObject *>(obj->ConvertToType(ticks, Class_ID(POLYOBJ_CLASS_ID, 0)));
			polyMesh = &polyObj->GetMesh();
		}
		else if (obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0)))
		{
			triObj = reinterpret_cast<TriObject *>(obj->ConvertToType(ticks, Class_ID(TRIOBJ_CLASS_ID, 0)));
			triMesh = &triObj->GetMesh();
		}

		// Make sure we have a poly or a tri object
		if (polyMesh == NULL && triMesh == NULL)
		{
			return false;
		}

		//keep the orignal material IDs, since we are not saving out a single nonmerged mesh
		materialsMerge.bPreserveIds = true;

		Matrix3 worldTrans;
		worldTrans.IdentityMatrix();
		finalPolyMesh.Save(mJob->mOptions, triMesh, polyMesh, worldTrans, GetRef().node->GetMtl(), -1, mNumSamples == 0, &materialsMerge);

	   // Note that the TriObject should only be deleted
	   // if the pointer to it is not equal to the object
	   // pointer that called ConvertToType()
	   if (polyObj != NULL && polyObj != obj)
	   {
		   delete polyObj;
		   polyObj = NULL;
	   }

	   if (triObj != NULL && triObj != obj)
	   {
		   delete triObj;
		   triObj = NULL;
	   }
	}

	bool dynamicTopology = static_cast<bool>(GetCurrentJob()->GetOption("exportDynamicTopology"));

	// Extend the archive bounding box
	if (mJob){
		Abc::M44d wm;
		ConvertMaxMatrixToAlembicMatrix(GetRef().node->GetObjTMAfterWSM(ticks), wm);

		Abc::Box3d bbox = finalPolyMesh.bbox;

		//ESS_LOG_INFO( "Archive bbox: min("<<bbox.min.x<<", "<<bbox.min.y<<", "<<bbox.min.z<<") max("<<bbox.max.x<<", "<<bbox.max.y<<", "<<bbox.max.z<<")" );

		bbox.min = finalPolyMesh.bbox.min * wm;
		bbox.max = finalPolyMesh.bbox.max * wm;

		//ESS_LOG_INFO( "Archive bbox: min("<<bbox.min.x<<", "<<bbox.min.y<<", "<<bbox.min.z<<") max("<<bbox.max.x<<", "<<bbox.max.y<<", "<<bbox.max.z<<")" );

		mJob->GetArchiveBBox().extendBy(bbox);

		Abc::Box3d box = mJob->GetArchiveBBox();
		//ESS_LOG_INFO( "Archive bbox: min("<<box.min.x<<", "<<box.min.y<<", "<<box.min.z<<") max("<<box.max.x<<", "<<box.max.y<<", "<<box.max.z<<")" );
	}

	mMeshSample.setPositions(Abc::P3fArraySample(finalPolyMesh.posVec));

	mMeshSample.setSelfBounds(finalPolyMesh.bbox);
	mMeshSample.setChildBounds(finalPolyMesh.bbox);

    // abort here if we are just storing points
    if(mJob->GetOption("exportPurePointCache"))
    {
        if(mNumSamples == 0)
        {
            // store a dummy empty topology
			mMeshSample.setFaceCounts(Abc::Int32ArraySample(NULL, 0));
			mMeshSample.setFaceIndices(Abc::Int32ArraySample(NULL, 0));
        }

        mMeshSchema.set(mMeshSample);
        mNumSamples++;
        return true;
    }

	if(mJob->GetOption("validateMeshTopology")){
		mJob->mMeshErrors += validateAlembicMeshTopo(finalPolyMesh.mFaceCountVec, finalPolyMesh.mFaceIndicesVec, EC_MCHAR_to_UTF8(GetRef().node->GetName()));
	}

	Abc::Int32ArraySample faceCountSample(finalPolyMesh.mFaceCountVec);
	Abc::Int32ArraySample faceIndicesSample(finalPolyMesh.mFaceIndicesVec);
	mMeshSample.setFaceCounts(faceCountSample);
	mMeshSample.setFaceIndices(faceIndicesSample);

	if(mJob->GetOption("exportNormals")){
		AbcG::ON3fGeomParam::Sample normalSample;
		normalSample.setScope(AbcG::kFacevaryingScope);
		normalSample.setVals(Abc::N3fArraySample(finalPolyMesh.mIndexedNormals.values));
		normalSample.setIndices(Abc::UInt32ArraySample(finalPolyMesh.mIndexedNormals.indices));
		mMeshSample.setNormals(normalSample);
	}

	//write out the texture coordinates if necessary
	if(mJob->GetOption("exportUVs"))
	{

		if(mNumSamples == 0 && finalPolyMesh.mIndexedUVSet.size() > 0){
			Abc::OStringArrayProperty uvSetNamesProperty = Abc::OStringArrayProperty(
				mMeshSchema, ".uvSetNames", mMeshSchema.getMetaData(), mJob->GetAnimatedTs() );
			finalPolyMesh.mIndexedUVNames.clear();
			for( int i = 0; i < finalPolyMesh.mIndexedUVSet.size(); i ++ ) {
				finalPolyMesh.mIndexedUVNames.push_back( finalPolyMesh.mIndexedUVSet[i].name );
			}
			Abc::StringArraySample uvSetNamesSample( finalPolyMesh.mIndexedUVNames );
			uvSetNamesProperty.set(uvSetNamesSample);
		}

		for(int i=0; i<finalPolyMesh.mIndexedUVSet.size(); i++){
			std::vector<Abc::V2f>& uvVec = finalPolyMesh.mIndexedUVSet[i].values;
			std::vector<Abc::uint32_t>& uvIndexVec = finalPolyMesh.mIndexedUVSet[i].indices;

			int uvIndexSize = 0;
			AbcG::OV2fGeomParam::Sample uvSample =
				AbcG::OV2fGeomParam::Sample(Abc::V2fArraySample(uvVec), AbcG::kFacevaryingScope);
			uvIndexSize = (int)uvIndexVec.size();
			uvSample.setIndices(Abc::UInt32ArraySample(uvIndexVec));

			if(i == 0){
				mMeshSample.setUVs(uvSample);
			}
			else{
				// create the uv param if required
				if(mNumSamples == 0)
				{
					std::stringstream storedUVSetNameStream;
					storedUVSetNameStream<<"uv"<<i;
					mUvParams.push_back(AbcG::OV2fGeomParam( mMeshSchema, storedUVSetNameStream.str().c_str(), uvIndexSize > 0,
									 AbcG::kFacevaryingScope, 1, mJob->GetAnimatedTs()));
				}
				mUvParams[i-1].set(uvSample);
			}
		}
	}

	// sweet, now let's have a look at face sets (really only for first sample)
	// for 3DS Max, we are mapping this to the material ids
	if(GetCurrentJob()->GetOption("exportMaterialIds"))
	{
		if(!mMatIdProperty.valid())
		{
			mMatIdProperty = Abc::OUInt32ArrayProperty(mMeshSchema, ".materialids", mMeshSchema.getMetaData(), GetCurrentJob()->GetAnimatedTs());
		}

		Abc::UInt32ArraySample sample = Abc::UInt32ArraySample(finalPolyMesh.mMatIdIndexVec);
		mMatIdProperty.set(sample);

		SaveMaterialsProperty(bFirstFrame, bLastFrame || bForever);

		size_t numMatId = finalPolyMesh.mFaceSetsMap.size();
		// For sample zero, export the material ids as face sets
		if (bFirstFrame && numMatId > 1)
		{
			for ( facesetmap_it it=finalPolyMesh.mFaceSetsMap.begin(); it != finalPolyMesh.mFaceSetsMap.end(); it++)
			{
				std::stringstream nameStream;
				int nMaterialId = it->first+1;
				nameStream<<it->second.name<<"_"<<nMaterialId;

				std::vector<Abc::int32_t>& faceSetVec = it->second.faceIds;

				AbcG::OFaceSet faceSet = mMeshSchema.createFaceSet(nameStream.str());
				AbcG::OFaceSetSchema::Sample faceSetSample(Abc::Int32ArraySample(&faceSetVec.front(), faceSetVec.size()));
				faceSet.getSchema().set(faceSetSample);
			}
		}

	}

   /*if(bFirstFrame || (dynamicTopology))
   {
      // check if we need to export the bindpose (also only for first frame)

      if(GetJob()->GetOption(L"exportBindPose") && prim.GetParent3DObject().GetEnvelopes().GetCount() > 0 && mNumSamples == 0)
      {
         mBindPoseProperty = OV3fArrayProperty(mMeshSchema, ".bindpose", mMeshSchema.getMetaData(), GetJob()->GetAnimatedTs());

         // store the positions of the modeling stack into here
         PolygonMesh bindPoseGeo = prim.GetGeometry(time, siConstructionModeModeling);
         CVector3Array bindPosePos = bindPoseGeo.GetPoints().GetPositionArray();
         mBindPoseVec.resize((size_t)bindPosePos.GetCount());
         for(LONG i=0;i<bindPosePos.GetCount();i++)
         {
            mBindPoseVec[i].x = (float)bindPosePos[i].GetX();
            mBindPoseVec[i].y = (float)bindPosePos[i].GetY();
            mBindPoseVec[i].z = (float)bindPosePos[i].GetZ();
         }

         Abc::V3fArraySample sample;
         if(mBindPoseVec.size() > 0)
            sample = Abc::V3fArraySample(&mBindPoseVec.front(),mBindPoseVec.size());
         mBindPoseProperty.set(sample);
      }

   }*/

   // check if we should export the velocities
   // TODO: support velocity property for nonparticle system meshes if possible
	//TODO: add export velocities option
	if(bIsParticleSystem)
	{
		if(finalPolyMesh.posVec.size() != finalPolyMesh.mVelocitiesVec.size()){
			ESS_LOG_INFO("mVelocitiesVec has wrong size.");
		}
		mMeshSample.setVelocities(Abc::V3fArraySample(finalPolyMesh.mVelocitiesVec));
	}

   // save the sample
   mMeshSchema.set(mMeshSample);

   mNumSamples++;

	///////////////////////////////////////////////////////////////////////////////////////////////

    return true;
}
<commit_msg>fixed #300 - unified UV exporter.<commit_after>#include "Alembic.h"
#include "AlembicMax.h"
#include "AlembicPolyMsh.h"
#include "AlembicXForm.h"
#include "SceneEnumProc.h"
#include "Utility.h"
#include "AlembicMetadataUtils.h"
#include "AlembicPointsUtils.h"
#include "AlembicIntermediatePolyMesh3DSMax.h"
#include "CommonMeshUtilities.h"

// From the SDK
// How to calculate UV's for face mapped materials.
//static Point3 basic_tva[3] = {
//	Point3(0.0,0.0,0.0),Point3(1.0,0.0,0.0),Point3(1.0,1.0,0.0)
//};
//static Point3 basic_tvb[3] = {
//	Point3(1.0,1.0,0.0),Point3(0.0,1.0,0.0),Point3(0.0,0.0,0.0)
//};
//static int nextpt[3] = {1,2,0};
//static int prevpt[3] = {2,0,1};

AlembicPolyMesh::AlembicPolyMesh(const SceneEntry &in_Ref, AlembicWriteJob *in_Job)
: AlembicObject(in_Ref, in_Job)
{
    std::string meshName = EC_MCHAR_to_UTF8( in_Ref.node->GetName() );
    std::string xformName = meshName + "Xfo";

    AbcG::OXform xform(GetOParent(), xformName.c_str(), GetCurrentJob()->GetAnimatedTs());
    AbcG::OPolyMesh mesh(xform, meshName.c_str(), GetCurrentJob()->GetAnimatedTs());

    // JSS - I'm not sure if this is require under 3DSMAx
    // AddRef(prim.GetParent3DObject().GetKinematics().GetGlobal().GetRef());

    // create the generic properties
    mOVisibility = CreateVisibilityProperty(mesh,GetCurrentJob()->GetAnimatedTs());

    mXformSchema = xform.getSchema();
    mMeshSchema = mesh.getSchema();
}

AlembicPolyMesh::~AlembicPolyMesh()
{
    // we have to clear this prior to destruction this is a workaround for issue-171
    mOVisibility.reset();
}

Abc::OCompoundProperty AlembicPolyMesh::GetCompound()
{
    return mMeshSchema;
}

void AlembicPolyMesh::SaveMaterialsProperty(bool bFirstFrame, bool bLastFrame)
{
	if(bLastFrame){
		std::vector<std::string> materialNames;
		mergedMeshMaterialsMap& matMap = materialsMerge.groupMatMap;

		for ( mergedMeshMaterialsMap_it it=matMap.begin(); it != matMap.end(); it++)
		{
			meshMaterialsMap& map = it->second;
			for( meshMaterialsMap_it it2 =map.begin(); it2 != map.end(); it2++)
			{
				std::stringstream nameStream;
				int nMaterialId = it2->second.matId+1;
				nameStream<<it2->second.name<<"_"<<nMaterialId;
				materialNames.push_back(nameStream.str());
			}
		}

		if(materialNames.size() > 0){
			if(!mMatNamesProperty.valid())
			{
				mMatNamesProperty = Abc::OStringArrayProperty(mMeshSchema, ".materialnames", mMeshSchema.getMetaData(), GetCurrentJob()->GetAnimatedTs());
			}
			Abc::StringArraySample sample = Abc::StringArraySample(materialNames);
			mMatNamesProperty.set(sample);
		}
	}
}

bool AlembicPolyMesh::Save(double time, bool bLastFrame)
{
	//this call is here to avoid reading pointers that are only valid on a single frame
	mMeshSample.reset();

	const bool bFirstFrame = mNumSamples == 0;

	TimeValue ticks = GetTimeValueFromFrame(time);

	Object *obj = GetRef().node->EvalWorldState(ticks).obj;
	const bool bIsParticleSystem = obj->IsParticleSystem() == 1;

	if(bIsParticleSystem){
		bForever = false;
	}
	else{
		if(mNumSamples == 0){
			bForever = CheckIfObjIsValidForever(obj, ticks);
		}
		else{
			bool bNewForever = CheckIfObjIsValidForever(obj, ticks);
			if(bForever && bNewForever != bForever){
				ESS_LOG_INFO( "bForever has changed" );
			}
		}
	}

	const bool bFlatten = GetCurrentJob()->GetOption("flattenHierarchy");

    // Store the transformation
    SaveXformSample(GetRef(), mXformSchema, mXformSample, time, bFlatten);

	SaveMetaData(GetRef().node, this);

    // store the metadata
    //IMetaDataManager mng;
    // mng.GetMetaData(GetRef().node, 0);
    // SaveMetaData(prim.GetParent3DObject().GetRef(),this);

    // set the visibility
    if(!bForever || mNumSamples == 0)
    {
        float flVisibility = GetRef().node->GetLocalVisibility(ticks);
        mOVisibility.set(flVisibility > 0 ? AbcG::kVisibilityVisible : AbcG::kVisibilityHidden);
    }

    // check if the mesh is animated (Otherwise, no need to export)
    if(mNumSamples > 0)
    {
        if(bForever)
        {
			ESS_LOG_INFO( "Node is not animated, not saving topology on subsequent frames." );
            return true;
        }
    }

	IntermediatePolyMesh3DSMax finalPolyMesh;

	if(bIsParticleSystem){

		bool bSuccess = getParticleSystemMesh(ticks, obj, GetRef().node, &finalPolyMesh, &materialsMerge, mJob, mNumSamples);
		if(!bSuccess){
			ESS_LOG_INFO( "Error. Could not get particle system mesh. Time: "<<time );
			return false;
		}
	}
	else
	{
		PolyObject *polyObj = NULL;
		TriObject *triObj = NULL;
		Mesh *triMesh = NULL;
		MNMesh* polyMesh = NULL;

		if (obj->CanConvertToType(Class_ID(POLYOBJ_CLASS_ID, 0)))
		{
			polyObj = reinterpret_cast<PolyObject *>(obj->ConvertToType(ticks, Class_ID(POLYOBJ_CLASS_ID, 0)));
			polyMesh = &polyObj->GetMesh();
		}
		else if (obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0)))
		{
			triObj = reinterpret_cast<TriObject *>(obj->ConvertToType(ticks, Class_ID(TRIOBJ_CLASS_ID, 0)));
			triMesh = &triObj->GetMesh();
		}

		// Make sure we have a poly or a tri object
		if (polyMesh == NULL && triMesh == NULL)
		{
			return false;
		}

		//keep the orignal material IDs, since we are not saving out a single nonmerged mesh
		materialsMerge.bPreserveIds = true;

		Matrix3 worldTrans;
		worldTrans.IdentityMatrix();
		finalPolyMesh.Save(mJob->mOptions, triMesh, polyMesh, worldTrans, GetRef().node->GetMtl(), -1, mNumSamples == 0, &materialsMerge);

	   // Note that the TriObject should only be deleted
	   // if the pointer to it is not equal to the object
	   // pointer that called ConvertToType()
	   if (polyObj != NULL && polyObj != obj)
	   {
		   delete polyObj;
		   polyObj = NULL;
	   }

	   if (triObj != NULL && triObj != obj)
	   {
		   delete triObj;
		   triObj = NULL;
	   }
	}

	bool dynamicTopology = static_cast<bool>(GetCurrentJob()->GetOption("exportDynamicTopology"));

	// Extend the archive bounding box
	if (mJob){
		Abc::M44d wm;
		ConvertMaxMatrixToAlembicMatrix(GetRef().node->GetObjTMAfterWSM(ticks), wm);

		Abc::Box3d bbox = finalPolyMesh.bbox;

		//ESS_LOG_INFO( "Archive bbox: min("<<bbox.min.x<<", "<<bbox.min.y<<", "<<bbox.min.z<<") max("<<bbox.max.x<<", "<<bbox.max.y<<", "<<bbox.max.z<<")" );

		bbox.min = finalPolyMesh.bbox.min * wm;
		bbox.max = finalPolyMesh.bbox.max * wm;

		//ESS_LOG_INFO( "Archive bbox: min("<<bbox.min.x<<", "<<bbox.min.y<<", "<<bbox.min.z<<") max("<<bbox.max.x<<", "<<bbox.max.y<<", "<<bbox.max.z<<")" );

		mJob->GetArchiveBBox().extendBy(bbox);

		Abc::Box3d box = mJob->GetArchiveBBox();
		//ESS_LOG_INFO( "Archive bbox: min("<<box.min.x<<", "<<box.min.y<<", "<<box.min.z<<") max("<<box.max.x<<", "<<box.max.y<<", "<<box.max.z<<")" );
	}

	mMeshSample.setPositions(Abc::P3fArraySample(finalPolyMesh.posVec));

	mMeshSample.setSelfBounds(finalPolyMesh.bbox);
	mMeshSample.setChildBounds(finalPolyMesh.bbox);

    // abort here if we are just storing points
    if(mJob->GetOption("exportPurePointCache"))
    {
        if(mNumSamples == 0)
        {
            // store a dummy empty topology
			mMeshSample.setFaceCounts(Abc::Int32ArraySample(NULL, 0));
			mMeshSample.setFaceIndices(Abc::Int32ArraySample(NULL, 0));
        }

        mMeshSchema.set(mMeshSample);
        mNumSamples++;
        return true;
    }

	if(mJob->GetOption("validateMeshTopology")){
		mJob->mMeshErrors += validateAlembicMeshTopo(finalPolyMesh.mFaceCountVec, finalPolyMesh.mFaceIndicesVec, EC_MCHAR_to_UTF8(GetRef().node->GetName()));
	}

	Abc::Int32ArraySample faceCountSample(finalPolyMesh.mFaceCountVec);
	Abc::Int32ArraySample faceIndicesSample(finalPolyMesh.mFaceIndicesVec);
	mMeshSample.setFaceCounts(faceCountSample);
	mMeshSample.setFaceIndices(faceIndicesSample);

	if(mJob->GetOption("exportNormals")){
		AbcG::ON3fGeomParam::Sample normalSample;
		normalSample.setScope(AbcG::kFacevaryingScope);
		normalSample.setVals(Abc::N3fArraySample(finalPolyMesh.mIndexedNormals.values));
		normalSample.setIndices(Abc::UInt32ArraySample(finalPolyMesh.mIndexedNormals.indices));
		mMeshSample.setNormals(normalSample);
	}

	AbcG::OV2fGeomParam::Sample uvSample;

	//write out the texture coordinates if necessary
	if(mJob->GetOption("exportUVs"))
	{
		saveIndexedUVs( mMeshSchema, mMeshSample, uvSample, mUvParams, mJob->GetAnimatedTs(), mNumSamples, finalPolyMesh.mIndexedUVSet );
	}

	// sweet, now let's have a look at face sets (really only for first sample)
	// for 3DS Max, we are mapping this to the material ids
	if(GetCurrentJob()->GetOption("exportMaterialIds"))
	{
		if(!mMatIdProperty.valid())
		{
			mMatIdProperty = Abc::OUInt32ArrayProperty(mMeshSchema, ".materialids", mMeshSchema.getMetaData(), GetCurrentJob()->GetAnimatedTs());
		}

		Abc::UInt32ArraySample sample = Abc::UInt32ArraySample(finalPolyMesh.mMatIdIndexVec);
		mMatIdProperty.set(sample);

		SaveMaterialsProperty(bFirstFrame, bLastFrame || bForever);

		size_t numMatId = finalPolyMesh.mFaceSetsMap.size();
		// For sample zero, export the material ids as face sets
		if (bFirstFrame && numMatId > 1)
		{
			for ( facesetmap_it it=finalPolyMesh.mFaceSetsMap.begin(); it != finalPolyMesh.mFaceSetsMap.end(); it++)
			{
				std::stringstream nameStream;
				int nMaterialId = it->first+1;
				nameStream<<it->second.name<<"_"<<nMaterialId;

				std::vector<Abc::int32_t>& faceSetVec = it->second.faceIds;

				AbcG::OFaceSet faceSet = mMeshSchema.createFaceSet(nameStream.str());
				AbcG::OFaceSetSchema::Sample faceSetSample(Abc::Int32ArraySample(&faceSetVec.front(), faceSetVec.size()));
				faceSet.getSchema().set(faceSetSample);
			}
		}

	}

   /*if(bFirstFrame || (dynamicTopology))
   {
      // check if we need to export the bindpose (also only for first frame)

      if(GetJob()->GetOption(L"exportBindPose") && prim.GetParent3DObject().GetEnvelopes().GetCount() > 0 && mNumSamples == 0)
      {
         mBindPoseProperty = OV3fArrayProperty(mMeshSchema, ".bindpose", mMeshSchema.getMetaData(), GetJob()->GetAnimatedTs());

         // store the positions of the modeling stack into here
         PolygonMesh bindPoseGeo = prim.GetGeometry(time, siConstructionModeModeling);
         CVector3Array bindPosePos = bindPoseGeo.GetPoints().GetPositionArray();
         mBindPoseVec.resize((size_t)bindPosePos.GetCount());
         for(LONG i=0;i<bindPosePos.GetCount();i++)
         {
            mBindPoseVec[i].x = (float)bindPosePos[i].GetX();
            mBindPoseVec[i].y = (float)bindPosePos[i].GetY();
            mBindPoseVec[i].z = (float)bindPosePos[i].GetZ();
         }

         Abc::V3fArraySample sample;
         if(mBindPoseVec.size() > 0)
            sample = Abc::V3fArraySample(&mBindPoseVec.front(),mBindPoseVec.size());
         mBindPoseProperty.set(sample);
      }

   }*/

   // check if we should export the velocities
   // TODO: support velocity property for nonparticle system meshes if possible
	//TODO: add export velocities option
	if(bIsParticleSystem)
	{
		if(finalPolyMesh.posVec.size() != finalPolyMesh.mVelocitiesVec.size()){
			ESS_LOG_INFO("mVelocitiesVec has wrong size.");
		}
		mMeshSample.setVelocities(Abc::V3fArraySample(finalPolyMesh.mVelocitiesVec));
	}

   // save the sample
   mMeshSchema.set(mMeshSample);

   mNumSamples++;

	///////////////////////////////////////////////////////////////////////////////////////////////

    return true;
}
<|endoftext|>


<commit_before>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2010, Willow Garage, Inc.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of Willow Garage, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef PCL_SURFACE_IMPL_MARCHING_CUBES_RBF_H_
#define PCL_SURFACE_IMPL_MARCHING_CUBES_RBF_H_

#include <pcl/surface/marching_cubes_rbf.h>
#include <pcl/common/common.h>
#include <pcl/common/vector_average.h>
#include <pcl/Vertices.h>
#include <pcl/kdtree/kdtree_flann.h>

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointNT>
pcl::MarchingCubesRBF<PointNT>::MarchingCubesRBF ()
  : MarchingCubes<PointNT> (),
    off_surface_epsilon_ (0.1f)
{
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointNT>
pcl::MarchingCubesRBF<PointNT>::~MarchingCubesRBF ()
{
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointNT> void
pcl::MarchingCubesRBF<PointNT>::voxelizeData ()
{
  // Initialize data structures
  unsigned int N = input_->size ();
  Eigen::MatrixXd M (2*N, 2*N),
                  d (2*N, 1);

  for (unsigned int row_i = 0; row_i < 2*N; ++row_i)
  {
    // boolean variable to determine whether we are in the off_surface domain for the rows
    bool row_off = (row_i >= N) ? 1 : 0;
    for (unsigned int col_i = 0; col_i < 2*N; ++col_i)
    {
      // boolean variable to determine whether we are in the off_surface domain for the columns
      bool col_off = (col_i >= N) ? 1 : 0;
      M (row_i, col_i) = kernel (input_->points[col_i%N].getVector3fMap () + input_->points[col_i%N].getNormalVector3fMap () * col_off * off_surface_epsilon_,
                                 input_->points[row_i%N].getVector3fMap () + input_->points[row_i%N].getNormalVector3fMap () * row_off * off_surface_epsilon_);
    }

    d (row_i, 0) = row_off * off_surface_epsilon_;
  }

  // Solve for the weights
  Eigen::MatrixXd w (2*N, 1);

  // Solve_linear_system (M, d, w);
  w = M.fullPivLu ().solve (d);

  std::vector<float> weights_;
  std::vector<Eigen::Vector3f> centers_;
  weights_.resize (2*N);
  centers_.resize (2*N);
  for (unsigned int i = 0; i < N; ++i)
  {
    centers_[i] = input_->points[i].getVector3fMap ();
    centers_[i + N] = input_->points[i].getVector3fMap () + input_->points[i].getNormalVector3fMap () * off_surface_epsilon_;
    weights_[i] = w (i, 0);
    weights_[i + N] = w (i + N, 0);
  }

  for (int x = 0; x < res_x_; ++x)
    for (int y = 0; y < res_y_; ++y)
      for (int z = 0; z < res_z_; ++z)
      {
        Eigen::Vector3f point;
        point[0] = min_p_[0] + (max_p_[0] - min_p_[0]) * x / res_x_;
        point[1] = min_p_[1] + (max_p_[1] - min_p_[1]) * y / res_y_;
        point[2] = min_p_[2] + (max_p_[2] - min_p_[2]) * z / res_z_;

        float f = 0.0f;
        std::vector<float>::const_iterator w_it (weights_.begin());
        for (std::vector<Eigen::Vector3f>::const_iterator c_it = centers_.begin ();
             c_it != centers_.end (); ++c_it, ++w_it)
          f += *w_it * kernel (*c_it, point);

        grid_[x * res_y_*res_z_ + y * res_z_ + z] = f;
      }
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointNT> float
pcl::MarchingCubesRBF<PointNT>::kernel (Eigen::Vector3f c, Eigen::Vector3f x)
{
  float r = (x - c).norm();
  return r*r*r;
}

#define PCL_INSTANTIATE_MarchingCubesRBF(T) template class PCL_EXPORTS pcl::MarchingCubesRBF<T>;

#endif    // PCL_SURFACE_IMPL_MARCHING_CUBES_HOPPE_H_

<commit_msg>marching cubes rbf - trying QR decomposition - might be more stable on 32 bit<commit_after>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2010, Willow Garage, Inc.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of Willow Garage, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef PCL_SURFACE_IMPL_MARCHING_CUBES_RBF_H_
#define PCL_SURFACE_IMPL_MARCHING_CUBES_RBF_H_

#include <pcl/surface/marching_cubes_rbf.h>
#include <pcl/common/common.h>
#include <pcl/common/vector_average.h>
#include <pcl/Vertices.h>
#include <pcl/kdtree/kdtree_flann.h>

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointNT>
pcl::MarchingCubesRBF<PointNT>::MarchingCubesRBF ()
  : MarchingCubes<PointNT> (),
    off_surface_epsilon_ (0.1f)
{
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointNT>
pcl::MarchingCubesRBF<PointNT>::~MarchingCubesRBF ()
{
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointNT> void
pcl::MarchingCubesRBF<PointNT>::voxelizeData ()
{
  // Initialize data structures
  unsigned int N = input_->size ();
  Eigen::MatrixXd M (2*N, 2*N),
                  d (2*N, 1);

  for (unsigned int row_i = 0; row_i < 2*N; ++row_i)
  {
    // boolean variable to determine whether we are in the off_surface domain for the rows
    bool row_off = (row_i >= N) ? 1 : 0;
    for (unsigned int col_i = 0; col_i < 2*N; ++col_i)
    {
      // boolean variable to determine whether we are in the off_surface domain for the columns
      bool col_off = (col_i >= N) ? 1 : 0;
      M (row_i, col_i) = kernel (input_->points[col_i%N].getVector3fMap () + input_->points[col_i%N].getNormalVector3fMap () * col_off * off_surface_epsilon_,
                                 input_->points[row_i%N].getVector3fMap () + input_->points[row_i%N].getNormalVector3fMap () * row_off * off_surface_epsilon_);
    }

    d (row_i, 0) = row_off * off_surface_epsilon_;
  }

  // Solve for the weights
  Eigen::MatrixXd w (2*N, 1);

  // Solve_linear_system (M, d, w);
  w = M.fullPivHouseholderQr ().solve (d);

  std::vector<float> weights_;
  std::vector<Eigen::Vector3f> centers_;
  weights_.resize (2*N);
  centers_.resize (2*N);
  for (unsigned int i = 0; i < N; ++i)
  {
    centers_[i] = input_->points[i].getVector3fMap ();
    centers_[i + N] = input_->points[i].getVector3fMap () + input_->points[i].getNormalVector3fMap () * off_surface_epsilon_;
    weights_[i] = w (i, 0);
    weights_[i + N] = w (i + N, 0);
  }

  for (int x = 0; x < res_x_; ++x)
    for (int y = 0; y < res_y_; ++y)
      for (int z = 0; z < res_z_; ++z)
      {
        Eigen::Vector3f point;
        point[0] = min_p_[0] + (max_p_[0] - min_p_[0]) * x / res_x_;
        point[1] = min_p_[1] + (max_p_[1] - min_p_[1]) * y / res_y_;
        point[2] = min_p_[2] + (max_p_[2] - min_p_[2]) * z / res_z_;

        float f = 0.0f;
        std::vector<float>::const_iterator w_it (weights_.begin());
        for (std::vector<Eigen::Vector3f>::const_iterator c_it = centers_.begin ();
             c_it != centers_.end (); ++c_it, ++w_it)
          f += *w_it * kernel (*c_it, point);

        grid_[x * res_y_*res_z_ + y * res_z_ + z] = f;
      }
}

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointNT> float
pcl::MarchingCubesRBF<PointNT>::kernel (Eigen::Vector3f c, Eigen::Vector3f x)
{
  float r = (x - c).norm();
  return r*r*r;
}

#define PCL_INSTANTIATE_MarchingCubesRBF(T) template class PCL_EXPORTS pcl::MarchingCubesRBF<T>;

#endif    // PCL_SURFACE_IMPL_MARCHING_CUBES_HOPPE_H_

<|endoftext|>


<commit_before>#include "command_group.h"
#include <unordered_map>

using namespace cl::sycl;

void command_group::enter() {
	detail::command::group_::last = this;
}
void command_group::exit() {
	detail::command::group_::last = nullptr;
}

// TODO: Reschedules commands to achieve better performance
void command_group::optimize() {
	DSELF();

	std::unordered_map<command_t*, bool> keep(commands.size());

	for(auto& command : commands) {
		keep[&command] = true;
	}

	decltype(commands) new_commands;
	new_commands.reserve(commands.size());

	for(auto& command : commands) {
		if(keep[&command]) {
			new_commands.push_back(std::move(command));
		}
	}

	commands = std::move(new_commands);
}

// Executes all commands in queue and removes them
void command_group::flush() {
	DSELF();

	optimize();

	using detail::command::type_t;

	for(auto& command : commands) {
		if(command.type == type_t::get_accessor) {
			auto& acc = command.data.buf_acc;
			auto d = debug();
			d << command.type << acc.data << acc.mode << acc.target;
		}
		else if(command.type == type_t::copy_data) {
			auto& copy = command.data.buf_copy;
			auto d = debug();
			d << command.type << copy.buf.data << copy.buf.mode << copy.buf.target << copy.mode;
		}
		else {
			debug() << "command:" << command.name;
		}
		command.function(q);
	}
	commands.clear();
}

command_group::~command_group() {
	// TODO: Move flush to end of caller command queue
	flush();
}

using namespace detail;

command_group* command::group_::last = nullptr;

bool command::group_::in_scope() {
	return last != nullptr;
}

void command::group_::check_scope(error::handler& handler) {
	if(!in_scope()) {
		handler.report(error::code::NOT_IN_COMMAND_GROUP_SCOPE);
	}
}
<commit_msg>Removing unnecessary read/write buffer commands.<commit_after>#include "command_group.h"
#include <unordered_map>
#include <unordered_set>

using namespace cl::sycl;

void command_group::enter() {
	detail::command::group_::last = this;
}
void command_group::exit() {
	detail::command::group_::last = nullptr;
}

// TODO: Reschedules commands to achieve better performance
void command_group::optimize() {
	DSELF();

	std::unordered_map<command_t*, bool> keep(commands.size());
	std::unordered_map<detail::buffer_base*, command_t*> last_read;
	std::unordered_set<detail::buffer_base*> was_written;

	using detail::command::type_t;

	for(auto& command : commands) {
		keep[&command] = true;

		if(command.type == type_t::get_accessor) {
			// User accesses the buffer

			auto ptr = command.data.buf_acc.data;

			{
				// Reset reads
				auto it = last_read.find(ptr);
				if(it != last_read.end()) {
					last_read.erase(it);
				}
			}

			{
				// Reset writes
				auto it = was_written.find(ptr);
				if(it != was_written.end()) {
					was_written.erase(it);
				}
			}
		}
		else if(command.type == type_t::copy_data) {
			auto ptr = command.data.buf_copy.buf.data;

			if(command.data.buf_copy.mode == access::read) {
				auto it = last_read.find(ptr);

				// Keep only the last read
				if(it != last_read.end()) {
					keep[it->second] = false;
				}

				last_read[ptr] = &command;
			}
			else if(command.data.buf_copy.mode == access::write) {
				auto it = was_written.find(ptr);

				// Keep only the first write
				if(it == was_written.end()) {
					was_written.insert(ptr);
				}
				else {
					keep[&command] = false;
				}
			}
		}
	}

	decltype(commands) new_commands;
	new_commands.reserve(commands.size());

	for(auto& command : commands) {
		if(keep[&command]) {
			new_commands.push_back(std::move(command));
		}
	}

	commands = std::move(new_commands);
}

// Executes all commands in queue and removes them
void command_group::flush() {
	DSELF();

	optimize();

	using detail::command::type_t;

	for(auto& command : commands) {
		if(command.type == type_t::get_accessor) {
			auto& acc = command.data.buf_acc;
			auto d = debug();
			d << command.type << acc.data << acc.mode << acc.target;
		}
		else if(command.type == type_t::copy_data) {
			auto& copy = command.data.buf_copy;
			auto d = debug();
			d << command.type << copy.buf.data << copy.buf.mode << copy.buf.target << copy.mode;
		}
		else {
			debug() << "command:" << command.name;
		}
		command.function(q);
	}
	commands.clear();
}

command_group::~command_group() {
	// TODO: Move flush to end of caller command queue
	flush();
}

using namespace detail;

command_group* command::group_::last = nullptr;

bool command::group_::in_scope() {
	return last != nullptr;
}

void command::group_::check_scope(error::handler& handler) {
	if(!in_scope()) {
		handler.report(error::code::NOT_IN_COMMAND_GROUP_SCOPE);
	}
}
<|endoftext|>