diff --git a/src/main.cpp b/src/main.cpp
index fbbdfd31e136c4318c9463382a245a6b714e1770..4c7c499650008c33f8118ad42e1ab84e98653224 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -8,6 +8,7 @@
 #include <Mesh.hpp>
 #include <BoundaryCondition.hpp>
 #include <AcousticSolver.hpp>
+#include <VoronoiSolver.hpp>
 
 #include <VTKWriter.hpp>
 
@@ -41,8 +42,19 @@ int main(int argc, char *argv[])
 
       std::shared_ptr<IMesh> p_mesh = gmsh_reader.mesh();
 
-      switch (p_mesh->meshDimension()) {
-        case 1: {
+      const bool use_voronoi
+          = [&] () {
+              char* voronoi_env = getenv("VORONOI");
+              return voronoi_env != nullptr;
+            } ();
+
+      if (use_voronoi) {
+        if (p_mesh->meshDimension() != 1) {
+          std::cerr << "higher dimension than 1 is not implemented for Voronoi\n";
+          std::abort();
+        }
+
+        {
           std::vector<std::string> sym_boundary_name_list = {"XMIN", "XMAX"};
           std::vector<std::shared_ptr<BoundaryConditionDescriptor>> bc_descriptor_list;
           for (const auto& sym_boundary_name : sym_boundary_name_list){
@@ -97,7 +109,7 @@ int main(int argc, char *argv[])
 
           unknowns.initializeSod();
 
-          AcousticSolver<MeshDataType> acoustic_solver(mesh_data, unknowns, bc_list);
+          VoronoiSolver<MeshDataType> voronoi_solver(mesh_data, unknowns, bc_list);
 
           using Rd = TinyVector<MeshType::dimension>;
 
@@ -120,13 +132,14 @@ int main(int argc, char *argv[])
           VTKWriter vtk_writer("mesh", 0.01);
 
           while((t<tmax) and (iteration<itermax)) {
+            std::cout << "time " << t << ' ';
             vtk_writer.write(mesh, t);
-            double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj);
+            double dt = 0.4*voronoi_solver.voronoi_dt(Vj, cj);
             if (t+dt>tmax) {
               dt=tmax-t;
             }
-            acoustic_solver.computeNextStep(t,dt, unknowns);
-
+            voronoi_solver.computeNextStep(t,dt, unknowns);
+            std::cout << "dt=" << dt << '\n';
             block_eos.updatePandCFromRhoE();
 
             t += dt;
@@ -135,9 +148,9 @@ int main(int argc, char *argv[])
           vtk_writer.write(mesh, t, true); // forces last output
 
           pout() << "* " << rang::style::underline << "Final time" << rang::style::reset
-                    << ":  " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n";
+                 << ":  " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n";
 
-          method_cost_map["AcousticSolverWithMesh"] = timer.seconds();
+          method_cost_map["VoronoiSolverWithMesh"] = timer.seconds();
 
           { // gnuplot output for density
             const CellValue<const Rd>& xj = mesh_data.xj();
@@ -148,202 +161,312 @@ int main(int argc, char *argv[])
             }
           }
 
-          break;
-        }
-        case 2: {
-          // test case boundary condition description
-          std::vector<std::string> sym_boundary_name_list = {"XMIN", "XMAX", "YMIN", "YMAX"};
-          std::vector<std::shared_ptr<BoundaryConditionDescriptor>> bc_descriptor_list;
-          for (const auto& sym_boundary_name : sym_boundary_name_list){
-            std::shared_ptr<BoundaryDescriptor> boudary_descriptor
-                = std::shared_ptr<BoundaryDescriptor>(new NamedBoundaryDescriptor(sym_boundary_name));
-            SymmetryBoundaryConditionDescriptor* sym_bc_descriptor
-                = new SymmetryBoundaryConditionDescriptor(boudary_descriptor);
-
-            bc_descriptor_list.push_back(std::shared_ptr<BoundaryConditionDescriptor>(sym_bc_descriptor));
-          }
-
-          using ConnectivityType = Connectivity2D;
-          using MeshType = Mesh<ConnectivityType>;
-          using MeshDataType = MeshData<MeshType>;
-          using UnknownsType = FiniteVolumesEulerUnknowns<MeshDataType>;
-
-          const MeshType& mesh = dynamic_cast<const MeshType&>(*gmsh_reader.mesh());
 
-          Timer timer;
-          timer.reset();
-          MeshDataType mesh_data(mesh);
+        }
+      } else {
+        switch (p_mesh->meshDimension()) {
+          case 1: {
+            std::vector<std::string> sym_boundary_name_list = {"XMIN", "XMAX"};
+            std::vector<std::shared_ptr<BoundaryConditionDescriptor>> bc_descriptor_list;
+            for (const auto& sym_boundary_name : sym_boundary_name_list){
+              std::shared_ptr<BoundaryDescriptor> boudary_descriptor
+                  = std::shared_ptr<BoundaryDescriptor>(new NamedBoundaryDescriptor(sym_boundary_name));
+              SymmetryBoundaryConditionDescriptor* sym_bc_descriptor
+                  = new SymmetryBoundaryConditionDescriptor(boudary_descriptor);
+
+              bc_descriptor_list.push_back(std::shared_ptr<BoundaryConditionDescriptor>(sym_bc_descriptor));
+            }
 
-          std::vector<BoundaryConditionHandler> bc_list;
-          {
-            for (const auto& bc_descriptor : bc_descriptor_list) {
-              switch (bc_descriptor->type()) {
-                case BoundaryConditionDescriptor::Type::symmetry: {
-                  const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor
-                      = dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor);
-                  for (size_t i_ref_face_list=0; i_ref_face_list<mesh.connectivity().numberOfRefFaceList();
-                       ++i_ref_face_list) {
-                    const RefFaceList& ref_face_list = mesh.connectivity().refFaceList(i_ref_face_list);
-                    const RefId& ref = ref_face_list.refId();
-                    if (ref == sym_bc_descriptor.boundaryDescriptor()) {
-                      SymmetryBoundaryCondition<MeshType::dimension>* sym_bc
-                          = new SymmetryBoundaryCondition<MeshType::dimension>(MeshFlatNodeBoundary<MeshType::dimension>(mesh, ref_face_list));
-                      std::shared_ptr<SymmetryBoundaryCondition<MeshType::dimension>> bc(sym_bc);
-                      bc_list.push_back(BoundaryConditionHandler(bc));
+            using ConnectivityType = Connectivity1D;
+            using MeshType = Mesh<ConnectivityType>;
+            using MeshDataType = MeshData<MeshType>;
+            using UnknownsType = FiniteVolumesEulerUnknowns<MeshDataType>;
+
+            const MeshType& mesh = dynamic_cast<const MeshType&>(*gmsh_reader.mesh());
+
+            Timer timer;
+            timer.reset();
+            MeshDataType mesh_data(mesh);
+
+            std::vector<BoundaryConditionHandler> bc_list;
+            {
+              for (const auto& bc_descriptor : bc_descriptor_list) {
+                switch (bc_descriptor->type()) {
+                  case BoundaryConditionDescriptor::Type::symmetry: {
+                    const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor
+                        = dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor);
+                    for (size_t i_ref_node_list=0; i_ref_node_list<mesh.connectivity().numberOfRefNodeList();
+                         ++i_ref_node_list) {
+                      const RefNodeList& ref_node_list = mesh.connectivity().refNodeList(i_ref_node_list);
+                      const RefId& ref = ref_node_list.refId();
+                      if (ref == sym_bc_descriptor.boundaryDescriptor()) {
+                        SymmetryBoundaryCondition<MeshType::dimension>* sym_bc
+                            = new SymmetryBoundaryCondition<MeshType::dimension>(MeshFlatNodeBoundary<MeshType::dimension>(mesh, ref_node_list));
+                        std::shared_ptr<SymmetryBoundaryCondition<MeshType::dimension>> bc(sym_bc);
+                        bc_list.push_back(BoundaryConditionHandler(bc));
+                      }
                     }
+                    break;
+                  }
+                  default: {
+                    perr() << "Unknown BCDescription\n";
+                    std::exit(1);
                   }
-                  break;
-                }
-                default: {
-                  perr() << "Unknown BCDescription\n";
-                  std::exit(1);
                 }
               }
             }
-          }
 
-          UnknownsType unknowns(mesh_data);
+            UnknownsType unknowns(mesh_data);
 
-          unknowns.initializeSod();
+            unknowns.initializeSod();
 
-          AcousticSolver<MeshDataType> acoustic_solver(mesh_data, unknowns, bc_list);
+            AcousticSolver<MeshDataType> acoustic_solver(mesh_data, unknowns, bc_list);
 
-          const CellValue<const double>& Vj = mesh_data.Vj();
+            using Rd = TinyVector<MeshType::dimension>;
 
-          const double tmax=0.2;
-          double t=0;
+            const CellValue<const double>& Vj = mesh_data.Vj();
 
-          int itermax=std::numeric_limits<int>::max();
-          int iteration=0;
+            const double tmax=0.2;
+            double t=0;
 
-          CellValue<double>& rhoj = unknowns.rhoj();
-          CellValue<double>& ej = unknowns.ej();
-          CellValue<double>& pj = unknowns.pj();
-          CellValue<double>& gammaj = unknowns.gammaj();
-          CellValue<double>& cj = unknowns.cj();
+            int itermax=std::numeric_limits<int>::max();
+            int iteration=0;
 
-          BlockPerfectGas block_eos(rhoj, ej, pj, gammaj, cj);
+            CellValue<double>& rhoj = unknowns.rhoj();
+            CellValue<double>& ej = unknowns.ej();
+            CellValue<double>& pj = unknowns.pj();
+            CellValue<double>& gammaj = unknowns.gammaj();
+            CellValue<double>& cj = unknowns.cj();
 
-          VTKWriter vtk_writer("mesh", 0.01);
+            BlockPerfectGas block_eos(rhoj, ej, pj, gammaj, cj);
 
-          while((t<tmax) and (iteration<itermax)) {
-            vtk_writer.write(mesh, t);
-            double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj);
-            if (t+dt>tmax) {
-              dt=tmax-t;
+            VTKWriter vtk_writer("mesh", 0.01);
+
+            while((t<tmax) and (iteration<itermax)) {
+              vtk_writer.write(mesh, t);
+              double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj);
+              if (t+dt>tmax) {
+                dt=tmax-t;
+              }
+              acoustic_solver.computeNextStep(t,dt, unknowns);
+
+              block_eos.updatePandCFromRhoE();
+
+              t += dt;
+              ++iteration;
             }
-            acoustic_solver.computeNextStep(t,dt, unknowns);
+            vtk_writer.write(mesh, t, true); // forces last output
 
-            block_eos.updatePandCFromRhoE();
+            pout() << "* " << rang::style::underline << "Final time" << rang::style::reset
+                   << ":  " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n";
 
-            t += dt;
-            ++iteration;
+            method_cost_map["AcousticSolverWithMesh"] = timer.seconds();
+
+            { // gnuplot output for density
+              const CellValue<const Rd>& xj = mesh_data.xj();
+              const CellValue<const double>& rhoj = unknowns.rhoj();
+              std::ofstream fout("rho");
+              for (CellId j=0; j<mesh.numberOfCells(); ++j) {
+                fout << xj[j][0] << ' ' << rhoj[j] << '\n';
+              }
+            }
+
+            break;
           }
-          vtk_writer.write(mesh, t, true); // forces last output
+          case 2: {
+            // test case boundary condition description
+            std::vector<std::string> sym_boundary_name_list = {"XMIN", "XMAX", "YMIN", "YMAX"};
+            std::vector<std::shared_ptr<BoundaryConditionDescriptor>> bc_descriptor_list;
+            for (const auto& sym_boundary_name : sym_boundary_name_list){
+              std::shared_ptr<BoundaryDescriptor> boudary_descriptor
+                  = std::shared_ptr<BoundaryDescriptor>(new NamedBoundaryDescriptor(sym_boundary_name));
+              SymmetryBoundaryConditionDescriptor* sym_bc_descriptor
+                  = new SymmetryBoundaryConditionDescriptor(boudary_descriptor);
+
+              bc_descriptor_list.push_back(std::shared_ptr<BoundaryConditionDescriptor>(sym_bc_descriptor));
+            }
 
-          pout() << "* " << rang::style::underline << "Final time" << rang::style::reset
-                    << ":  " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n";
+            using ConnectivityType = Connectivity2D;
+            using MeshType = Mesh<ConnectivityType>;
+            using MeshDataType = MeshData<MeshType>;
+            using UnknownsType = FiniteVolumesEulerUnknowns<MeshDataType>;
+
+            const MeshType& mesh = dynamic_cast<const MeshType&>(*gmsh_reader.mesh());
+
+            Timer timer;
+            timer.reset();
+            MeshDataType mesh_data(mesh);
+
+            std::vector<BoundaryConditionHandler> bc_list;
+            {
+              for (const auto& bc_descriptor : bc_descriptor_list) {
+                switch (bc_descriptor->type()) {
+                  case BoundaryConditionDescriptor::Type::symmetry: {
+                    const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor
+                        = dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor);
+                    for (size_t i_ref_face_list=0; i_ref_face_list<mesh.connectivity().numberOfRefFaceList();
+                         ++i_ref_face_list) {
+                      const RefFaceList& ref_face_list = mesh.connectivity().refFaceList(i_ref_face_list);
+                      const RefId& ref = ref_face_list.refId();
+                      if (ref == sym_bc_descriptor.boundaryDescriptor()) {
+                        SymmetryBoundaryCondition<MeshType::dimension>* sym_bc
+                            = new SymmetryBoundaryCondition<MeshType::dimension>(MeshFlatNodeBoundary<MeshType::dimension>(mesh, ref_face_list));
+                        std::shared_ptr<SymmetryBoundaryCondition<MeshType::dimension>> bc(sym_bc);
+                        bc_list.push_back(BoundaryConditionHandler(bc));
+                      }
+                    }
+                    break;
+                  }
+                  default: {
+                    perr() << "Unknown BCDescription\n";
+                    std::exit(1);
+                  }
+                }
+              }
+            }
 
-          method_cost_map["AcousticSolverWithMesh"] = timer.seconds();
-          break;
-        }
-        case 3: {
-          std::vector<std::string> sym_boundary_name_list = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"};
-          std::vector<std::shared_ptr<BoundaryConditionDescriptor>> bc_descriptor_list;
-          for (const auto& sym_boundary_name : sym_boundary_name_list){
-            std::shared_ptr<BoundaryDescriptor> boudary_descriptor
-                = std::shared_ptr<BoundaryDescriptor>(new NamedBoundaryDescriptor(sym_boundary_name));
-            SymmetryBoundaryConditionDescriptor* sym_bc_descriptor
-                = new SymmetryBoundaryConditionDescriptor(boudary_descriptor);
+            UnknownsType unknowns(mesh_data);
 
-            bc_descriptor_list.push_back(std::shared_ptr<BoundaryConditionDescriptor>(sym_bc_descriptor));
-          }
+            unknowns.initializeSod();
 
-          using ConnectivityType = Connectivity3D;
-          using MeshType = Mesh<ConnectivityType>;
-          using MeshDataType = MeshData<MeshType>;
-          using UnknownsType = FiniteVolumesEulerUnknowns<MeshDataType>;
+            AcousticSolver<MeshDataType> acoustic_solver(mesh_data, unknowns, bc_list);
 
-          const MeshType& mesh = dynamic_cast<const MeshType&>(*gmsh_reader.mesh());
+            const CellValue<const double>& Vj = mesh_data.Vj();
 
-          Timer timer;
-          timer.reset();
-          MeshDataType mesh_data(mesh);
+            const double tmax=0.2;
+            double t=0;
 
-          std::vector<BoundaryConditionHandler> bc_list;
-          {
-            for (const auto& bc_descriptor : bc_descriptor_list) {
-              switch (bc_descriptor->type()) {
-                case BoundaryConditionDescriptor::Type::symmetry: {
-                  const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor
-                      = dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor);
-                  for (size_t i_ref_face_list=0; i_ref_face_list<mesh.connectivity().numberOfRefFaceList();
-                       ++i_ref_face_list) {
-                    const RefFaceList& ref_face_list = mesh.connectivity().refFaceList(i_ref_face_list);
-                    const RefId& ref = ref_face_list.refId();
-                    if (ref == sym_bc_descriptor.boundaryDescriptor()) {
-                      SymmetryBoundaryCondition<MeshType::dimension>* sym_bc
-                          = new SymmetryBoundaryCondition<MeshType::dimension>(MeshFlatNodeBoundary<MeshType::dimension>(mesh, ref_face_list));
-                      std::shared_ptr<SymmetryBoundaryCondition<MeshType::dimension>> bc(sym_bc);
-                      bc_list.push_back(BoundaryConditionHandler(bc));
+            int itermax=std::numeric_limits<int>::max();
+            int iteration=0;
+
+            CellValue<double>& rhoj = unknowns.rhoj();
+            CellValue<double>& ej = unknowns.ej();
+            CellValue<double>& pj = unknowns.pj();
+            CellValue<double>& gammaj = unknowns.gammaj();
+            CellValue<double>& cj = unknowns.cj();
+
+            BlockPerfectGas block_eos(rhoj, ej, pj, gammaj, cj);
+
+            VTKWriter vtk_writer("mesh", 0.01);
+
+            while((t<tmax) and (iteration<itermax)) {
+              vtk_writer.write(mesh, t);
+              double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj);
+              if (t+dt>tmax) {
+                dt=tmax-t;
+              }
+              acoustic_solver.computeNextStep(t,dt, unknowns);
+
+              block_eos.updatePandCFromRhoE();
+
+              t += dt;
+              ++iteration;
+            }
+            vtk_writer.write(mesh, t, true); // forces last output
+
+            pout() << "* " << rang::style::underline << "Final time" << rang::style::reset
+                   << ":  " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n";
+
+            method_cost_map["AcousticSolverWithMesh"] = timer.seconds();
+            break;
+          }
+          case 3: {
+            std::vector<std::string> sym_boundary_name_list = {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"};
+            std::vector<std::shared_ptr<BoundaryConditionDescriptor>> bc_descriptor_list;
+            for (const auto& sym_boundary_name : sym_boundary_name_list){
+              std::shared_ptr<BoundaryDescriptor> boudary_descriptor
+                  = std::shared_ptr<BoundaryDescriptor>(new NamedBoundaryDescriptor(sym_boundary_name));
+              SymmetryBoundaryConditionDescriptor* sym_bc_descriptor
+                  = new SymmetryBoundaryConditionDescriptor(boudary_descriptor);
+
+              bc_descriptor_list.push_back(std::shared_ptr<BoundaryConditionDescriptor>(sym_bc_descriptor));
+            }
+
+            using ConnectivityType = Connectivity3D;
+            using MeshType = Mesh<ConnectivityType>;
+            using MeshDataType = MeshData<MeshType>;
+            using UnknownsType = FiniteVolumesEulerUnknowns<MeshDataType>;
+
+            const MeshType& mesh = dynamic_cast<const MeshType&>(*gmsh_reader.mesh());
+
+            Timer timer;
+            timer.reset();
+            MeshDataType mesh_data(mesh);
+
+            std::vector<BoundaryConditionHandler> bc_list;
+            {
+              for (const auto& bc_descriptor : bc_descriptor_list) {
+                switch (bc_descriptor->type()) {
+                  case BoundaryConditionDescriptor::Type::symmetry: {
+                    const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor
+                        = dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor);
+                    for (size_t i_ref_face_list=0; i_ref_face_list<mesh.connectivity().numberOfRefFaceList();
+                         ++i_ref_face_list) {
+                      const RefFaceList& ref_face_list = mesh.connectivity().refFaceList(i_ref_face_list);
+                      const RefId& ref = ref_face_list.refId();
+                      if (ref == sym_bc_descriptor.boundaryDescriptor()) {
+                        SymmetryBoundaryCondition<MeshType::dimension>* sym_bc
+                            = new SymmetryBoundaryCondition<MeshType::dimension>(MeshFlatNodeBoundary<MeshType::dimension>(mesh, ref_face_list));
+                        std::shared_ptr<SymmetryBoundaryCondition<MeshType::dimension>> bc(sym_bc);
+                        bc_list.push_back(BoundaryConditionHandler(bc));
+                      }
                     }
+                    break;
+                  }
+                  default: {
+                    perr() << "Unknown BCDescription\n";
+                    std::exit(1);
                   }
-                  break;
-                }
-                default: {
-                  perr() << "Unknown BCDescription\n";
-                  std::exit(1);
                 }
               }
             }
-          }
 
-          UnknownsType unknowns(mesh_data);
+            UnknownsType unknowns(mesh_data);
 
-          unknowns.initializeSod();
+            unknowns.initializeSod();
 
-          AcousticSolver<MeshDataType> acoustic_solver(mesh_data, unknowns, bc_list);
+            AcousticSolver<MeshDataType> acoustic_solver(mesh_data, unknowns, bc_list);
 
-          const CellValue<const double>& Vj = mesh_data.Vj();
+            const CellValue<const double>& Vj = mesh_data.Vj();
 
-          const double tmax=0.2;
-          double t=0;
+            const double tmax=0.2;
+            double t=0;
 
-          int itermax=std::numeric_limits<int>::max();
-          int iteration=0;
+            int itermax=std::numeric_limits<int>::max();
+            int iteration=0;
 
-          CellValue<double>& rhoj = unknowns.rhoj();
-          CellValue<double>& ej = unknowns.ej();
-          CellValue<double>& pj = unknowns.pj();
-          CellValue<double>& gammaj = unknowns.gammaj();
-          CellValue<double>& cj = unknowns.cj();
+            CellValue<double>& rhoj = unknowns.rhoj();
+            CellValue<double>& ej = unknowns.ej();
+            CellValue<double>& pj = unknowns.pj();
+            CellValue<double>& gammaj = unknowns.gammaj();
+            CellValue<double>& cj = unknowns.cj();
 
-          BlockPerfectGas block_eos(rhoj, ej, pj, gammaj, cj);
+            BlockPerfectGas block_eos(rhoj, ej, pj, gammaj, cj);
 
-          VTKWriter vtk_writer("mesh", 0.01);
+            VTKWriter vtk_writer("mesh", 0.01);
 
-          while((t<tmax) and (iteration<itermax)) {
-            vtk_writer.write(mesh, t);
-            double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj);
-            if (t+dt>tmax) {
-              dt=tmax-t;
-            }
-            acoustic_solver.computeNextStep(t,dt, unknowns);
-            block_eos.updatePandCFromRhoE();
+            while((t<tmax) and (iteration<itermax)) {
+              vtk_writer.write(mesh, t);
+              double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj);
+              if (t+dt>tmax) {
+                dt=tmax-t;
+              }
+              acoustic_solver.computeNextStep(t,dt, unknowns);
+              block_eos.updatePandCFromRhoE();
 
-            t += dt;
-            ++iteration;
-          }
-          vtk_writer.write(mesh, t, true); // forces last output
+              t += dt;
+              ++iteration;
+            }
+            vtk_writer.write(mesh, t, true); // forces last output
 
-          pout() << "* " << rang::style::underline << "Final time" << rang::style::reset
-                    << ":  " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n";
+            pout() << "* " << rang::style::underline << "Final time" << rang::style::reset
+                   << ":  " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n";
 
-          method_cost_map["AcousticSolverWithMesh"] = timer.seconds();
-          break;
+            method_cost_map["AcousticSolverWithMesh"] = timer.seconds();
+            break;
+          }
         }
       }
-
       pout() << "* "  << rang::fgB::red << "Could not be uglier!" << rang::fg::reset << " (" << __FILE__ << ':' << __LINE__ << ")\n";
 
     } else {
diff --git a/src/mesh/MeshData.hpp b/src/mesh/MeshData.hpp
index 6bd7e892576d329a82de8e10519f5b29fabb6e57..f9a2aeb575ac5265073d47ce2c4025da39db7d51 100644
--- a/src/mesh/MeshData.hpp
+++ b/src/mesh/MeshData.hpp
@@ -179,7 +179,7 @@ class MeshData
               const auto& face_nodes = face_to_node_matrix[l];
 
 #warning should this lambda be replaced by a precomputed correspondance?
-              std::function local_node_number_in_cell
+              auto local_node_number_in_cell
                   = [&](const NodeId& node_number) {
                       for (size_t i_node=0; i_node<cell_nodes.size(); ++i_node) {
                         if (node_number == cell_nodes[i_node]) {
diff --git a/src/scheme/VoronoiSolver.hpp b/src/scheme/VoronoiSolver.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..831ed235b70535e0ba279b4413ad2cea4d19f7ea
--- /dev/null
+++ b/src/scheme/VoronoiSolver.hpp
@@ -0,0 +1,392 @@
+#ifndef VORONOI_SOLVER_HPP
+#define VORONOI_SOLVER_HPP
+
+#include <rang.hpp>
+
+#include <ArrayUtils.hpp>
+
+#include <BlockPerfectGas.hpp>
+#include <PastisAssert.hpp>
+
+#include <TinyVector.hpp>
+#include <TinyMatrix.hpp>
+#include <Mesh.hpp>
+#include <MeshData.hpp>
+#include <FiniteVolumesEulerUnknowns.hpp>
+#include <BoundaryCondition.hpp>
+
+#include <SubItemValuePerItem.hpp>
+
+template<typename MeshData>
+class VoronoiSolver
+{
+  using MeshType = typename MeshData::MeshType;
+  using UnknownsType = FiniteVolumesEulerUnknowns<MeshData>;
+
+  MeshData& m_mesh_data;
+  const MeshType& m_mesh;
+  const typename MeshType::Connectivity& m_connectivity;
+  const std::vector<BoundaryConditionHandler>& m_boundary_condition_list;
+
+  constexpr static size_t dimension = MeshType::dimension;
+
+  using Rd = TinyVector<dimension>;
+  using Rdd = TinyMatrix<dimension>;
+
+ private:
+  PASTIS_INLINE
+  const CellValue<const double>
+  computeRhoCj(const CellValue<const double>& rhoj,
+               const CellValue<const double>& cj)
+  {
+    parallel_for(m_mesh.numberOfCells(), PASTIS_LAMBDA(const CellId& j) {
+        m_rhocj[j] = rhoj[j]*cj[j];
+      });
+    return m_rhocj;
+  }
+
+  PASTIS_INLINE
+  void computeAjr(const CellValue<const double>& rhocj,
+                  const NodeValuePerCell<const Rd>& Cjr,
+                  const NodeValuePerCell<const double>& ljr,
+                  const NodeValuePerCell<const Rd>& njr)
+  {
+    parallel_for(m_mesh.numberOfCells(), PASTIS_LAMBDA(const CellId& j) {
+        const size_t& nb_nodes =m_Ajr.numberOfSubValues(j);
+        const double& rho_c = rhocj[j];
+        for (size_t r=0; r<nb_nodes; ++r) {
+          m_Ajr(j,r) = tensorProduct(rho_c*Cjr(j,r), njr(j,r));
+        }
+      });
+  }
+
+  PASTIS_INLINE
+  const NodeValue<const Rdd>
+  computeAr(const NodeValuePerCell<const Rdd>& Ajr) {
+    const auto& node_to_cell_matrix
+        = m_connectivity.nodeToCellMatrix();
+    const auto& node_local_numbers_in_their_cells
+        = m_connectivity.nodeLocalNumbersInTheirCells();
+
+    parallel_for(m_mesh.numberOfNodes(), PASTIS_LAMBDA(const NodeId& r) {
+        Rdd sum = zero;
+        const auto& node_to_cell = node_to_cell_matrix[r];
+        const auto& node_local_number_in_its_cells
+            = node_local_numbers_in_their_cells.itemValues(r);
+
+        for (size_t j=0; j<node_to_cell.size(); ++j) {
+          const CellId J = node_to_cell[j];
+          const unsigned int R = node_local_number_in_its_cells[j];
+          sum += Ajr(J,R);
+        }
+        m_Ar[r] = sum;
+      });
+
+    return m_Ar;
+  }
+
+  PASTIS_INLINE
+  const NodeValue<const Rd>
+  computeBr(const NodeValuePerCell<Rdd>& Ajr,
+            const NodeValuePerCell<const Rd>& Cjr,
+            const CellValue<const Rd>& uj,
+            const CellValue<const double>& pj) {
+    const auto& node_to_cell_matrix
+        = m_connectivity.nodeToCellMatrix();
+    const auto& node_local_numbers_in_their_cells
+        = m_connectivity.nodeLocalNumbersInTheirCells();
+
+    parallel_for(m_mesh.numberOfNodes(), PASTIS_LAMBDA(const NodeId& r) {
+        Rd& br = m_br[r];
+        br = zero;
+        const auto& node_to_cell = node_to_cell_matrix[r];
+        const auto& node_local_number_in_its_cells
+            = node_local_numbers_in_their_cells.itemValues(r);
+        for (size_t j=0; j<node_to_cell.size(); ++j) {
+          const CellId J = node_to_cell[j];
+          const unsigned int R = node_local_number_in_its_cells[j];
+          br += Ajr(J,R)*uj[J] + pj[J]*Cjr(J,R);
+        }
+      });
+
+    return m_br;
+  }
+
+  void applyBoundaryConditions()
+  {
+    for (const auto& handler : m_boundary_condition_list) {
+      switch (handler.boundaryCondition().type()) {
+        case BoundaryCondition::normal_velocity: {
+          perr() << __FILE__ << ':' << __LINE__  << ": normal_velocity BC NIY\n";
+          std::exit(0);
+          break;
+        }
+        case BoundaryCondition::velocity: {
+          perr() << __FILE__ << ':' << __LINE__  << ": velocity BC NIY\n";
+          std::exit(0);
+          break;
+        }
+        case BoundaryCondition::pressure: {
+          // const PressureBoundaryCondition& pressure_bc
+          //   = dynamic_cast<const PressureBoundaryCondition&>(handler.boundaryCondition());
+          perr() << __FILE__ << ':' << __LINE__ << ": pressure BC NIY\n";
+          std::exit(0);
+          break;
+        }
+        case BoundaryCondition::symmetry: {
+          const SymmetryBoundaryCondition<dimension>& symmetry_bc
+              = dynamic_cast<const SymmetryBoundaryCondition<dimension>&>(handler.boundaryCondition());
+          const Rd& n = symmetry_bc.outgoingNormal();
+
+          const Rdd I = identity;
+          const Rdd nxn = tensorProduct(n,n);
+          const Rdd P = I-nxn;
+
+          const Array<const NodeId>& node_list
+              = symmetry_bc.nodeList();
+          parallel_for(symmetry_bc.numberOfNodes(), PASTIS_LAMBDA(const int& r_number) {
+              const NodeId r = node_list[r_number];
+
+              m_Ar[r] = P*m_Ar[r]*P + nxn;
+              m_br[r] = P*m_br[r];
+            });
+          break;
+        }
+      }
+    }
+  }
+
+  NodeValue<Rd>
+  computeUr(const NodeValue<const Rdd>& Ar,
+            const NodeValue<const Rd>& br)
+  {
+    inverse(Ar, m_inv_Ar);
+    const NodeValue<const Rdd> invAr = m_inv_Ar;
+    parallel_for(m_mesh.numberOfNodes(), PASTIS_LAMBDA(const NodeId& r) {
+        m_ur[r]=invAr[r]*br[r];
+      });
+
+    return m_ur;
+  }
+
+  void
+  computeFjr(const NodeValuePerCell<Rdd>& Ajr,
+             const NodeValue<const Rd>& ur,
+             const NodeValuePerCell<const Rd>& Cjr,
+             const CellValue<const Rd>& uj,
+             const CellValue<const double>& pj)
+  {
+    const auto& cell_to_node_matrix
+        = m_mesh.connectivity().cellToNodeMatrix();
+
+    parallel_for(m_mesh.numberOfCells(), PASTIS_LAMBDA(const CellId& j) {
+        const auto& cell_nodes = cell_to_node_matrix[j];
+
+        for (size_t r=0; r<cell_nodes.size(); ++r) {
+          m_Fjr(j,r) = Ajr(j,r)*(uj[j]-ur[cell_nodes[r]])+pj[j]*Cjr(j,r);
+        }
+      });
+  }
+
+  void inverse(const NodeValue<const Rdd>& A,
+               NodeValue<Rdd>& inv_A) const
+  {
+    parallel_for(m_mesh.numberOfNodes(), PASTIS_LAMBDA(const NodeId& r) {
+        inv_A[r] = ::inverse(A[r]);
+      });
+  }
+
+  PASTIS_INLINE
+  void computeExplicitFluxes(const NodeValue<const Rd>& xr,
+                             const CellValue<const Rd>& xj,
+                             const CellValue<const double>& rhoj,
+                             const CellValue<const Rd>& uj,
+                             const CellValue<const double>& pj,
+                             const CellValue<const double>& cj,
+                             const CellValue<const double>& Vj,
+                             const NodeValuePerCell<const Rd>& Cjr,
+                             const NodeValuePerCell<const double>& ljr,
+                             const NodeValuePerCell<const Rd>& njr) {
+    const CellValue<const double> rhocj = computeRhoCj(rhoj, cj);
+    computeAjr(rhocj, Cjr, ljr, njr);
+
+    NodeValuePerCell<const Rdd> Ajr = m_Ajr;
+    const NodeValue<const Rdd> Ar = computeAr(Ajr);
+    const NodeValue<const Rd> br = computeBr(m_Ajr, Cjr, uj, pj);
+
+    this->applyBoundaryConditions();
+
+    NodeValue<Rd>& ur = m_ur;
+    ur = computeUr(Ar, br);
+    computeFjr(m_Ajr, ur, Cjr, uj, pj);
+  }
+
+  NodeValue<Rd> m_br;
+  NodeValuePerCell<Rdd> m_Ajr;
+  NodeValue<Rdd> m_Ar;
+  NodeValue<Rdd> m_inv_Ar;
+  NodeValuePerCell<Rd> m_Fjr;
+  NodeValue<Rd> m_ur;
+  CellValue<double> m_rhocj;
+  CellValue<double> m_Vj_over_cj;
+
+  CellValue<Rd> m_xg;
+
+ public:
+  VoronoiSolver(MeshData& mesh_data,
+                 UnknownsType& unknowns,
+                 const std::vector<BoundaryConditionHandler>& bc_list)
+      : m_mesh_data(mesh_data),
+        m_mesh(mesh_data.mesh()),
+        m_connectivity(m_mesh.connectivity()),
+        m_boundary_condition_list(bc_list),
+        m_br(m_connectivity),
+        m_Ajr(m_connectivity),
+        m_Ar(m_connectivity),
+        m_inv_Ar(m_connectivity),
+        m_Fjr(m_connectivity),
+        m_ur(m_connectivity),
+        m_rhocj(m_connectivity),
+        m_Vj_over_cj(m_connectivity),
+        m_xg(m_connectivity)
+  {
+    ;
+  }
+
+  PASTIS_INLINE
+  double voronoi_dt(const CellValue<const double>& Vj,
+                     const CellValue<const double>& cj) const
+  {
+    const NodeValuePerCell<const double>& ljr = m_mesh_data.ljr();
+    const auto& cell_to_node_matrix
+        = m_mesh.connectivity().cellToNodeMatrix();
+
+    parallel_for(m_mesh.numberOfCells(), PASTIS_LAMBDA(const CellId& j){
+        const auto& cell_nodes = cell_to_node_matrix[j];
+
+        double S = 0;
+        for (size_t r=0; r<cell_nodes.size(); ++r) {
+          S += ljr(j,r);
+        }
+        m_Vj_over_cj[j] = 2*Vj[j]/(S*cj[j]);
+      });
+
+    return ReduceMin(m_Vj_over_cj);
+  }
+
+  void computeNextStep(const double& t, const double& dt,
+                       UnknownsType& unknowns)
+  {
+    CellValue<double>& rhoj = unknowns.rhoj();
+    CellValue<Rd>& uj = unknowns.uj();
+    CellValue<double>& Ej = unknowns.Ej();
+
+    CellValue<double>& ej = unknowns.ej();
+    CellValue<double>& pj = unknowns.pj();
+    CellValue<double>& cj = unknowns.cj();
+
+    const CellValue<const Rd>& xj = m_mesh_data.xj();
+
+    static bool init_xg=false;
+    if (not init_xg) {
+      parallel_for(m_mesh.numberOfCells(), PASTIS_LAMBDA(const CellId& j) {
+          m_xg[j] = xj[j];
+        });
+      init_xg = true;
+    }
+
+    const CellValue<const double>& Vj = m_mesh_data.Vj();
+    const NodeValuePerCell<const Rd>& Cjr = m_mesh_data.Cjr();
+    const NodeValuePerCell<const double>& ljr = m_mesh_data.ljr();
+    const NodeValuePerCell<const Rd>& njr = m_mesh_data.njr();
+    const NodeValue<const Rd>& xr = m_mesh.xr();
+
+    // computeExplicitFluxes(xr, xj, rhoj, uj, pj, cj, Vj, Cjr, ljr, njr);
+
+    CellValue<Rd> uj_star(m_mesh.connectivity());
+    parallel_for(m_mesh.numberOfCells(), PASTIS_LAMBDA(const CellId& j) {
+        if ((j != 0) and ( j != m_mesh.numberOfCells()-1)) {
+          CellId jp1 = j+1;
+          CellId jm1 = j-1;
+          double rhoc_jp1 = rhoj[jp1]*cj[jp1];
+          double rhoc_jm1 = rhoj[jm1]*cj[jm1];
+          uj_star[j]
+              = (1./ (rhoc_jp1+rhoc_jm1)) * (rhoc_jp1 * uj[jp1] + rhoc_jm1*uj[jm1])
+              + (pj[jm1] - pj[jp1]) / (rhoc_jp1+rhoc_jm1);
+        } else {
+          uj_star[j] = zero;
+        }
+      });
+
+    // for (CellId j=0; j<m_mesh.numberOfCells(); ++j) {
+    //   std::cout << "uj_star[" << j << "]= " << uj_star[j] << "\n";
+    // }
+
+
+    CellValue<double> pj_star(m_mesh.connectivity());
+    parallel_for(m_mesh.numberOfCells(), PASTIS_LAMBDA(const CellId& j) {
+        if ((j > 0) and ( j < m_mesh.numberOfCells()-1)) {
+          CellId jm1 = j-1;
+          double rhoc_jm1 = rhoj[jm1]*cj[jm1];
+          pj_star[j] = rhoc_jm1*(uj[jm1]-uj_star[j])[0] + pj[jm1];
+        } else {
+          pj_star[j] = pj[j];
+        }
+      });
+
+    const CellValue<const double> inv_mj = unknowns.invMj();
+    parallel_for(m_mesh.numberOfCells(), PASTIS_LAMBDA(const CellId& j) {
+        if ((j > 0) and ( j < m_mesh.numberOfCells()-1)) {
+          CellId jp1 = j+1;
+          CellId jm1 = j-1;
+          uj[j] -= (dt*inv_mj[j]) * 0.5*(pj_star[jp1]-pj_star[jm1]);
+          Ej[j] -= (dt*inv_mj[j]) * 0.5*(pj_star[jp1]*uj_star[jp1][0]-pj_star[jm1]*uj_star[jm1][0]);
+        }
+      });
+
+    parallel_for(m_mesh.numberOfCells(), PASTIS_LAMBDA(const CellId& j) {
+        m_xg[j] += dt*uj_star[j];
+      });
+
+    parallel_for(m_mesh.numberOfCells(), PASTIS_LAMBDA(const CellId& j) {
+        ej[j] = Ej[j] - 0.5 * (uj[j],uj[j]);
+      });
+
+    const auto& node_to_cell_matrix
+        = m_connectivity.nodeToCellMatrix();
+
+    NodeValue<Rd> mutable_xr = m_mesh.mutableXr();
+    parallel_for(m_mesh.numberOfNodes(), PASTIS_LAMBDA(const NodeId& r){
+        const auto& node_to_cell = node_to_cell_matrix[r];
+
+        if (node_to_cell.size() == 2) {
+          Rd new_xr = zero;
+          for (size_t j=0; j<node_to_cell.size(); ++j) {
+            const CellId J = node_to_cell[j];
+            new_xr += m_xg[J];
+          }
+          mutable_xr[r] = 0.5*new_xr;
+        }
+      });
+    m_mesh_data.updateAllData();
+
+    const CellValue<const double> mj = unknowns.mj();
+    parallel_for(m_mesh.numberOfCells(), PASTIS_LAMBDA(const CellId& j){
+        rhoj[j] = mj[j]/Vj[j];
+      });
+
+
+    // for (CellId j=0; j<m_mesh.numberOfCells(); ++j) {
+    //   std::cout << "rho[" << j << "]= " << rhoj[j] << "\t ";
+    //   std::cout << "u[" << j << "]= " << uj[j] << "\t ";
+    //   std::cout << "E[" << j << "]= " << Ej[j] << "\t ";
+    //   std::cout << "e[" << j << "]= " << ej[j] << "\n";
+    // }
+    // std::cout << std::flush;
+    // std::cout << "enter value to continue: ";
+    // int i;
+    // std::cin >> i;
+  }
+};
+
+#endif // VORONOI_SOLVER_HPP