From e3b0f644456fa30a57fc1fdc4c92a502a8469f9c Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 22 Aug 2023 10:22:28 -0400 Subject: [PATCH 001/134] AMIPS first draft --- src/wmtk/energy/AMIPS.cpp | 106 +++++++++++++++++++++++++++++++++++++ src/wmtk/energy/AMIPS.hpp | 2 + src/wmtk/energy/Energy.cpp | 9 ++++ src/wmtk/energy/Energy.hpp | 8 +-- 4 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 src/wmtk/energy/AMIPS.cpp create mode 100644 src/wmtk/energy/Energy.cpp diff --git a/src/wmtk/energy/AMIPS.cpp b/src/wmtk/energy/AMIPS.cpp new file mode 100644 index 0000000000..bb16fff54d --- /dev/null +++ b/src/wmtk/energy/AMIPS.cpp @@ -0,0 +1,106 @@ +#include "AMIPS.hpp" + +double AMIPS_2D::energy_eval(const Tuple& tuple) const override +{ + // get the uv coordinates of the triangle + ConstAccessor pos = m_mesh.create_accessor(m_position_handle); + + Eigen::Vector2d uv1 = pos.vector_attribute(tuple); + Eigen::Vector2d uv2 = pos.vector_attribute(switch_edge(switch_vertex(tuple))); + Eigen::Vector2d uv3 = pos.vector_attribute(switch_vertex(switch_edge(tuple))); + + // return the energy + return energy_eval(uv1, uv2, uv3); +} +DScalar AMIPS_2D::energy_eval_autodiff(const Tuple& tuple) const override +{ + // get the uv coordinates of the triangle + ConstAccessor pos = m_mesh.create_accessor(m_position_handle); + + Eigen::Vector2d uv1 = pos.vector_attribute(tuple); + Eigen::Vector2d uv2 = pos.vector_attribute(switch_edge(switch_vertex(tuple))); + Eigen::Vector2d uv3 = pos.vector_attribute(switch_vertex(switch_edge(tuple))); + + // return the energy + return energy_eval_autodiff(uv1, uv2, uv3); +} + +static double AMIPS_2D::energy_eval_autodiff( + const Eigen::Vector2d uv1, + const Eigen::Vector2d uv2, + const Eigen::Vector uv3) +{ + // (x0 - x1, y0 - y1, x0 - x2, y0 - y2).transpose + Eigen::Matrix Dm; + + Dm << uv2(0) - uv1(0), uv3(0) - uv1(0), uv2(1) - uv1(1), uv3(1) - uv1(1); + + Eigen::Matrix2d Ds, Dsinv; + Eigen::Vector2d target_A, target_B, target_C; + target_A << 0., 0.; + target_B << 1., 0.; + target_C << 1. / 2., sqrt(3) / 2.; + Ds << target_B.x() - target_A.x(), target_C.x() - target_A.x(), target_B.y() - target_A.y(), + target_C.y() - target_A.y(); + + auto Dsdet = Ds.determinant(); + if (std::abs(Dsdet) < std::numeric_limits::denorm_min()) { + return std::numeric_limits::infinity(); + } + Dsinv = Ds.inverse(); + + // define of transform matrix F = Dm@Ds.inv + Eigen::Matrix F; + F << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), + (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), + (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), + (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); + + auto Fdet = F.determinant(); + if (std::abs(Fdet) < std::numeric_limits::denorm_min()) { + return std::numeric_limits::infinity(); + } + return (F.transpose() * F).trace() / Fdet; +} + +static DScalar AMIPS_2D::energy_eval_autodiff( + const Eigen::Vector2d uv1, + const Eigen::Vector2d uv2, + const Eigen::Vector2d uv3) +{ + AMIPS::DScalar x0(0, input_triangle[state.idx * 2]), y0(1, input_triangle[state.idx * 2 + 1]); + + // (x0 - x1, y0 - y1, x0 - x2, y0 - y2).transpose + Eigen::Matrix Dm; + + Dm << input_triangle[(i * 2 + 2) % 6] - x0, input_triangle[(i * 2 + 4) % 6] - x0, + input_triangle[(i * 2 + 3) % 6] - y0, input_triangle[(i * 2 + 5) % 6] - y0; + + // define of transform matrix F = Ds@Dm.inv + Eigen::Matrix F; + + Eigen::Matrix2d Ds, Dsinv; + Ds << target_triangle[(i * 2 + 2) % 6] - target_triangle[(i * 2 + 0) % 6], + target_triangle[(i * 2 + 4) % 6] - target_triangle[(i * 2 + 0) % 6], + target_triangle[(i * 2 + 3) % 6] - target_triangle[(i * 2 + 1) % 6], + target_triangle[(i * 2 + 5) % 6] - target_triangle[(i * 2 + 1) % 6]; + + auto Dsdet = Ds.determinant(); + if (std::abs(Dsdet) < std::numeric_limits::denorm_min()) { + state.value = std::numeric_limits::infinity(); + return; + } + Dsinv = Ds.inverse(); + + F << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), + (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), + (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), + (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); + + auto Fdet = F.determinant(); + if (std::abs(Fdet.getValue()) < std::numeric_limits::denorm_min()) { + state.value = std::numeric_limits::infinity(); + return; + } + AMIPS::DScalar AMIPS_function = (F.transpose() * F).trace() / Fdet; +} \ No newline at end of file diff --git a/src/wmtk/energy/AMIPS.hpp b/src/wmtk/energy/AMIPS.hpp index 905405f418..217c6f1ee2 100644 --- a/src/wmtk/energy/AMIPS.hpp +++ b/src/wmtk/energy/AMIPS.hpp @@ -2,6 +2,8 @@ class AMIPS_2D : public wmtk::Energy { + using DScalar = DScalar2; + public: double energy_eval(const Tuple& tuple) const override{}; DScalar energy_eval_autodiff(const Tuple& tuple) const override{}; diff --git a/src/wmtk/energy/Energy.cpp b/src/wmtk/energy/Energy.cpp new file mode 100644 index 0000000000..b4580d4c6e --- /dev/null +++ b/src/wmtk/energy/Energy.cpp @@ -0,0 +1,9 @@ +#include "Energy.hpp" + +Energy::Energy(const Mesh& mesh) + : m_mesh(mesh) + , m_position_handle(m_mesh.get_attribute_handle("position", PrimitiveType::Vertex)){}; + +Energy::Energy(const Mesh& mesh, const MeshAttributeHandle& position_handle) + : m_mesh(mesh) + , m_position_handle(position_handle){}; diff --git a/src/wmtk/energy/Energy.hpp b/src/wmtk/energy/Energy.hpp index a0e3e07828..f08318f774 100644 --- a/src/wmtk/energy/Energy.hpp +++ b/src/wmtk/energy/Energy.hpp @@ -4,13 +4,13 @@ class Energy { private: - const Mesh& m; - const MeshAttributeHandle position_handle; + const Mesh& m_mesh; + const MeshAttributeHandle m_position_handle; public: - Energy(const Mesh& m, const MeshAttributeHandle& position_handle); - Enegry(const Mesh& m); + Energy(const Mesh& mesh, const MeshAttributeHandle& position_handle); + Enegry(const Mesh& mesh); public: virtual double energy_eval(const Tuple& tuple) const = 0; From b57e5a3316f1a56b0d562af057e71370925c0b5e Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Mon, 28 Aug 2023 10:30:10 -0400 Subject: [PATCH 002/134] adding cmake, changing to templated evaluation in child classes. not building due to autodiff not included --- src/wmtk/CMakeLists.txt | 1 + src/wmtk/energy/AMIPS.cpp | 56 ++++-------------------- src/wmtk/energy/AMIPS.hpp | 15 +++---- src/wmtk/energy/CMakeLists.txt | 9 ++++ src/wmtk/energy/DifferentiableEnergy.hpp | 2 +- src/wmtk/energy/Energy.cpp | 7 +-- src/wmtk/energy/Energy.hpp | 12 +++-- 7 files changed, 34 insertions(+), 68 deletions(-) create mode 100644 src/wmtk/energy/CMakeLists.txt diff --git a/src/wmtk/CMakeLists.txt b/src/wmtk/CMakeLists.txt index 9a2962ccc2..5d011a5368 100644 --- a/src/wmtk/CMakeLists.txt +++ b/src/wmtk/CMakeLists.txt @@ -30,3 +30,4 @@ add_subdirectory(utils) add_subdirectory(attribute) add_subdirectory(operations) add_subdirectory(autogen) +add_subdirectory(energy) \ No newline at end of file diff --git a/src/wmtk/energy/AMIPS.cpp b/src/wmtk/energy/AMIPS.cpp index bb16fff54d..2c10cfb30d 100644 --- a/src/wmtk/energy/AMIPS.cpp +++ b/src/wmtk/energy/AMIPS.cpp @@ -10,7 +10,7 @@ double AMIPS_2D::energy_eval(const Tuple& tuple) const override Eigen::Vector2d uv3 = pos.vector_attribute(switch_vertex(switch_edge(tuple))); // return the energy - return energy_eval(uv1, uv2, uv3); + return energy_eval(uv1, uv2, uv3); } DScalar AMIPS_2D::energy_eval_autodiff(const Tuple& tuple) const override { @@ -22,18 +22,20 @@ DScalar AMIPS_2D::energy_eval_autodiff(const Tuple& tuple) const override Eigen::Vector2d uv3 = pos.vector_attribute(switch_vertex(switch_edge(tuple))); // return the energy - return energy_eval_autodiff(uv1, uv2, uv3); + return energy_eval_autodiff(uv1, uv2, uv3); } -static double AMIPS_2D::energy_eval_autodiff( +template +static T AMIPS_2D::energy_eval_autodiff( const Eigen::Vector2d uv1, const Eigen::Vector2d uv2, const Eigen::Vector uv3) { // (x0 - x1, y0 - y1, x0 - x2, y0 - y2).transpose - Eigen::Matrix Dm; + Eigen::Matrix Dm; - Dm << uv2(0) - uv1(0), uv3(0) - uv1(0), uv2(1) - uv1(1), uv3(1) - uv1(1); + DScalar x0(0, uv1(0)), y1(1, uv1(1)); + Dm << DScalar(uv2(0)) - x0, DScalar(uv3(0)) - x0, DScalar(uv2(1)) - y0, DScalar(uv3(1)) - y0; Eigen::Matrix2d Ds, Dsinv; Eigen::Vector2d target_A, target_B, target_C; @@ -50,7 +52,7 @@ static double AMIPS_2D::energy_eval_autodiff( Dsinv = Ds.inverse(); // define of transform matrix F = Dm@Ds.inv - Eigen::Matrix F; + Eigen::Matrix F; F << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), @@ -62,45 +64,3 @@ static double AMIPS_2D::energy_eval_autodiff( } return (F.transpose() * F).trace() / Fdet; } - -static DScalar AMIPS_2D::energy_eval_autodiff( - const Eigen::Vector2d uv1, - const Eigen::Vector2d uv2, - const Eigen::Vector2d uv3) -{ - AMIPS::DScalar x0(0, input_triangle[state.idx * 2]), y0(1, input_triangle[state.idx * 2 + 1]); - - // (x0 - x1, y0 - y1, x0 - x2, y0 - y2).transpose - Eigen::Matrix Dm; - - Dm << input_triangle[(i * 2 + 2) % 6] - x0, input_triangle[(i * 2 + 4) % 6] - x0, - input_triangle[(i * 2 + 3) % 6] - y0, input_triangle[(i * 2 + 5) % 6] - y0; - - // define of transform matrix F = Ds@Dm.inv - Eigen::Matrix F; - - Eigen::Matrix2d Ds, Dsinv; - Ds << target_triangle[(i * 2 + 2) % 6] - target_triangle[(i * 2 + 0) % 6], - target_triangle[(i * 2 + 4) % 6] - target_triangle[(i * 2 + 0) % 6], - target_triangle[(i * 2 + 3) % 6] - target_triangle[(i * 2 + 1) % 6], - target_triangle[(i * 2 + 5) % 6] - target_triangle[(i * 2 + 1) % 6]; - - auto Dsdet = Ds.determinant(); - if (std::abs(Dsdet) < std::numeric_limits::denorm_min()) { - state.value = std::numeric_limits::infinity(); - return; - } - Dsinv = Ds.inverse(); - - F << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), - (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), - (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), - (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); - - auto Fdet = F.determinant(); - if (std::abs(Fdet.getValue()) < std::numeric_limits::denorm_min()) { - state.value = std::numeric_limits::infinity(); - return; - } - AMIPS::DScalar AMIPS_function = (F.transpose() * F).trace() / Fdet; -} \ No newline at end of file diff --git a/src/wmtk/energy/AMIPS.hpp b/src/wmtk/energy/AMIPS.hpp index 217c6f1ee2..1153f1088a 100644 --- a/src/wmtk/energy/AMIPS.hpp +++ b/src/wmtk/energy/AMIPS.hpp @@ -14,12 +14,10 @@ class AMIPS_2D : public wmtk::Energy * @param uv1 * @param uv2 * @param uv3 - * @return double energy value + * @return can be double or DScalar */ - static double - energy_eval(const Eigen::Vector2d uv1, const Eigen::Vector2d uv2, const Eigen::Vector2d uv3){}; - - static DScalar energy_eval_autodiff( + template + static T energy_eval_autodiff( const Eigen::Vector2d uv1, const Eigen::Vector2d uv2, const Eigen::Vector2d uv3){}; @@ -35,10 +33,7 @@ class AMIPS_3D : public wmtk::Energy double energy_eval(const Tuple& tuple) const override{}; DScalar energy_eval_autodiff(const Tuple& tuple) const override{}; - static double + template + static T energy_eval(const Eigen::Vector3d p1, const Eigen::Vector3d p2, const Eigen::Vector3d p3){}; - static DScalar energy_eval_autodiff( - const Eigen::Vector3d p1, - const Eigen::Vector3d p2, - const Eigen::Vector3d p3){}; }; \ No newline at end of file diff --git a/src/wmtk/energy/CMakeLists.txt b/src/wmtk/energy/CMakeLists.txt new file mode 100644 index 0000000000..9882e871ef --- /dev/null +++ b/src/wmtk/energy/CMakeLists.txt @@ -0,0 +1,9 @@ + +set(SRC_FILES + Energy.hpp + Energy.cpp + DifferentiableEnergy.hpp + AMIPS.hpp + AMIPS.cpp +) +target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/energy/DifferentiableEnergy.hpp b/src/wmtk/energy/DifferentiableEnergy.hpp index 0aa10b62be..0ccb499894 100644 --- a/src/wmtk/energy/DifferentiableEnergy.hpp +++ b/src/wmtk/energy/DifferentiableEnergy.hpp @@ -2,7 +2,7 @@ #include "Energy.hpp" class DifferentiableEnergy : public Energy { - using DScalar = DScalar2; + using DScalar = DScalar2; public: virtual DScalar energy_eval_autodiff(const Tuple& tuple) const = 0; diff --git a/src/wmtk/energy/Energy.cpp b/src/wmtk/energy/Energy.cpp index b4580d4c6e..fc8fd29f99 100644 --- a/src/wmtk/energy/Energy.cpp +++ b/src/wmtk/energy/Energy.cpp @@ -1,9 +1,6 @@ #include "Energy.hpp" - +namespace wmtk::energy { Energy::Energy(const Mesh& mesh) : m_mesh(mesh) , m_position_handle(m_mesh.get_attribute_handle("position", PrimitiveType::Vertex)){}; - -Energy::Energy(const Mesh& mesh, const MeshAttributeHandle& position_handle) - : m_mesh(mesh) - , m_position_handle(position_handle){}; +} \ No newline at end of file diff --git a/src/wmtk/energy/Energy.hpp b/src/wmtk/energy/Energy.hpp index f08318f774..8725281dcd 100644 --- a/src/wmtk/energy/Energy.hpp +++ b/src/wmtk/energy/Energy.hpp @@ -1,6 +1,8 @@ +#pragma once #include #include - +namespace wmtk { +namespace energy { class Energy { private: @@ -9,9 +11,11 @@ class Energy public: - Energy(const Mesh& mesh, const MeshAttributeHandle& position_handle); - Enegry(const Mesh& mesh); + Energy(const Mesh& mesh); + virtual ~Energy(); public: virtual double energy_eval(const Tuple& tuple) const = 0; -} \ No newline at end of file +}; +} // namespace energy +} // namespace wmtk \ No newline at end of file From c764b92ba56eb9a755aebe304d4028caace874fe Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Mon, 28 Aug 2023 10:41:57 -0400 Subject: [PATCH 003/134] added autodiff --- src/wmtk/energy/DifferentiableEnergy.hpp | 2 +- src/wmtk/energy/utils/autodiff.h | 1099 ++++++++++++++++++++++ 2 files changed, 1100 insertions(+), 1 deletion(-) create mode 100644 src/wmtk/energy/utils/autodiff.h diff --git a/src/wmtk/energy/DifferentiableEnergy.hpp b/src/wmtk/energy/DifferentiableEnergy.hpp index 0ccb499894..56b15fbf49 100644 --- a/src/wmtk/energy/DifferentiableEnergy.hpp +++ b/src/wmtk/energy/DifferentiableEnergy.hpp @@ -1,4 +1,4 @@ -#include +#include #include "Energy.hpp" class DifferentiableEnergy : public Energy { diff --git a/src/wmtk/energy/utils/autodiff.h b/src/wmtk/energy/utils/autodiff.h new file mode 100644 index 0000000000..a3645ef60a --- /dev/null +++ b/src/wmtk/energy/utils/autodiff.h @@ -0,0 +1,1099 @@ +// clang-format off + +/** + Automatic differentiation data type for C++, depends on the Eigen + linear algebra library. + + Copyright (c) 2012 by Wenzel Jakob. Based on code by Jon Kaldor + and Eitan Grinspun. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __AUTODIFF_H +#define __AUTODIFF_H + +#ifndef EIGEN_DONT_PARALLELIZE +#define EIGEN_DONT_PARALLELIZE +#endif + +#include +#include +#include +#include + +/** + * \brief Base class of all automatic differentiation types + * + * This class records the number of independent variables with respect + * to which derivatives are computed. + */ +struct DiffScalarBase +{ + // ====================================================================== + /// @{ \name Configuration + // ====================================================================== + + /** + * \brief Set the independent variable count used by the automatic + * differentiation layer + * + * This function must be called before doing any computations with + * \ref DScalar1 or \ref DScalar2. The value will be recorded in + * thread-local storage. + */ + static inline void setVariableCount(size_t value) + { + m_variableCount = value; + } + + /// Get the variable count used by the automatic differentiation layer + static inline size_t getVariableCount() + { + return m_variableCount; + } + + /// @} + // ====================================================================== + + // #ifdef WIN32 + // static __declspec(thread) size_t m_variableCount; + // #else + // static __thread size_t m_variableCount; + // #endif + static thread_local size_t m_variableCount; +}; + +// #ifdef WIN32 +// #define DECLARE_DIFFSCALAR_BASE() +// __declspec(thread) size_t DiffScalarBase::m_variableCount = 0 +// #else +// #define DECLARE_DIFFSCALAR_BASE() +// __thread size_t DiffScalarBase::m_variableCount = 0 +// #endif + +#define DECLARE_DIFFSCALAR_BASE() \ + thread_local size_t DiffScalarBase::m_variableCount = 0 + +/** + * \brief Automatic differentiation scalar with first-order derivatives + * + * This class provides an instrumented "scalar" value, which may be dependent on + * a number of independent variables. The implementation keeps tracks of + * first -order drivatives with respect to these variables using a set + * of overloaded operations and implementations of special functions (sin, + * tan, exp, ..). + * + * This is extremely useful for numerical zero-finding, particularly when + * analytic derivatives from programs like Maple or Mathematica suffer from + * excessively complicated expressions. + * + * The class relies on templates, which makes it possible to fix the + * number of independent variables at compile-time so that instances can + * be allocated on the stack. Otherwise, they will be placed on the heap. + * + * This is an extended C++ port of Jon Kaldor's implementation, which is + * based on a C version by Eitan Grinspun at Caltech) + * + * \sa DScalar2 + * \author Wenzel Jakob + */ +template > +struct DScalar1 : public DiffScalarBase +{ +public: + typedef _Scalar Scalar; + typedef _Gradient Gradient; + typedef Eigen::Matrix DVector2; + typedef Eigen::Matrix DVector3; + + // ====================================================================== + /// @{ \name Constructors and accessors + // ====================================================================== + + /// Create a new constant automatic differentiation scalar + explicit DScalar1(Scalar value_ = (Scalar)0) : value(value_) + { + size_t variableCount = getVariableCount(); + grad.resize(variableCount); + grad.setZero(); + } + + /// Construct a new scalar with the specified value and one first derivative set to 1 + DScalar1(size_t index, const Scalar &value_) + : value(value_) + { + size_t variableCount = getVariableCount(); + grad.resize(variableCount); + grad.setZero(); + grad(index) = 1; + } + + /// Construct a scalar associated with the given gradient + DScalar1(Scalar value_, const Gradient &grad_) + : value(value_), grad(grad_) {} + + /// Copy constructor + DScalar1(const DScalar1 &s) + : value(s.value), grad(s.grad) {} + + inline const Scalar &getValue() const { return value; } + inline const Gradient &getGradient() const { return grad; } + + // ====================================================================== + /// @{ \name Addition + // ====================================================================== + friend DScalar1 operator+(const DScalar1 &lhs, const DScalar1 &rhs) + { + return DScalar1(lhs.value + rhs.value, lhs.grad + rhs.grad); + } + + friend DScalar1 operator+(const DScalar1 &lhs, const Scalar &rhs) + { + return DScalar1(lhs.value + rhs, lhs.grad); + } + + friend DScalar1 operator+(const Scalar &lhs, const DScalar1 &rhs) + { + return DScalar1(rhs.value + lhs, rhs.grad); + } + + inline DScalar1 &operator+=(const DScalar1 &s) + { + value += s.value; + grad += s.grad; + return *this; + } + + inline DScalar1 &operator+=(const Scalar &v) + { + value += v; + return *this; + } + + /// @} + // ====================================================================== + + // ====================================================================== + /// @{ \name Subtraction + // ====================================================================== + + friend DScalar1 operator-(const DScalar1 &lhs, const DScalar1 &rhs) + { + return DScalar1(lhs.value - rhs.value, lhs.grad - rhs.grad); + } + + friend DScalar1 operator-(const DScalar1 &lhs, const Scalar &rhs) + { + return DScalar1(lhs.value - rhs, lhs.grad); + } + + friend DScalar1 operator-(const Scalar &lhs, const DScalar1 &rhs) + { + return DScalar1(lhs - rhs.value, -rhs.grad); + } + + friend DScalar1 operator-(const DScalar1 &s) + { + return DScalar1(-s.value, -s.grad); + } + + inline DScalar1 &operator-=(const DScalar1 &s) + { + value -= s.value; + grad -= s.grad; + return *this; + } + + inline DScalar1 &operator-=(const Scalar &v) + { + value -= v; + return *this; + } + /// @} + // ====================================================================== + + // ====================================================================== + /// @{ \name Division + // ====================================================================== + friend DScalar1 operator/(const DScalar1 &lhs, const Scalar &rhs) + { + if (rhs == 0) + throw std::runtime_error("DScalar1: Division by zero!"); + Scalar inv = 1.0f / rhs; + return DScalar1(lhs.value * inv, lhs.grad * inv); + } + + friend DScalar1 operator/(const Scalar &lhs, const DScalar1 &rhs) + { + return lhs * inverse(rhs); + } + + friend DScalar1 operator/(const DScalar1 &lhs, const DScalar1 &rhs) + { + return lhs * inverse(rhs); + } + + friend DScalar1 inverse(const DScalar1 &s) + { + Scalar valueSqr = s.value * s.value, + invValueSqr = (Scalar)1 / valueSqr; + + // vn = 1/v, Dvn = -1/(v^2) Dv + return DScalar1((Scalar)1 / s.value, s.grad * -invValueSqr); + } + + inline DScalar1 &operator/=(const Scalar &v) + { + value /= v; + grad /= v; + return *this; + } + + /// @} + // ====================================================================== + + // ====================================================================== + /// @{ \name Multiplication + // ====================================================================== + inline friend DScalar1 operator*(const DScalar1 &lhs, const Scalar &rhs) + { + return DScalar1(lhs.value * rhs, lhs.grad * rhs); + } + + inline friend DScalar1 operator*(const Scalar &lhs, const DScalar1 &rhs) + { + return DScalar1(rhs.value * lhs, rhs.grad * lhs); + } + + inline friend DScalar1 operator*(const DScalar1 &lhs, const DScalar1 &rhs) + { + // Product rule + return DScalar1(lhs.value * rhs.value, + rhs.grad * lhs.value + lhs.grad * rhs.value); + } + + inline DScalar1 &operator*=(const Scalar &v) + { + value *= v; + grad *= v; + return *this; + } + + /// @} + // ====================================================================== + + // ====================================================================== + /// @{ \name Miscellaneous functions + // ====================================================================== + + friend DScalar1 abs(const DScalar1 &s) + { + return s.value < 0? -s: s; + } + + friend DScalar1 sqrt(const DScalar1 &s) + { + Scalar sqrtVal = std::sqrt(s.value), + temp = (Scalar)1 / ((Scalar)2 * sqrtVal); + + // vn = sqrt(v) + // Dvn = 1/(2 sqrt(v)) Dv + return DScalar1(sqrtVal, s.grad * temp); + } + + friend DScalar1 pow(const DScalar1 &s, const Scalar &a) + { + Scalar powVal = std::pow(s.value, a), + temp = a * std::pow(s.value, a - 1); + // vn = v ^ a, Dvn = a*v^(a-1) * Dv + return DScalar1(powVal, s.grad * temp); + } + + friend DScalar1 exp(const DScalar1 &s) + { + Scalar expVal = std::exp(s.value); + + // vn = exp(v), Dvn = exp(v) * Dv + return DScalar1(expVal, s.grad * expVal); + } + + friend DScalar1 log(const DScalar1 &s) + { + Scalar logVal = std::log(s.value); + + // vn = log(v), Dvn = Dv / v + return DScalar1(logVal, s.grad / s.value); + } + + friend DScalar1 sin(const DScalar1 &s) + { + // vn = sin(v), Dvn = cos(v) * Dv + return DScalar1(std::sin(s.value), s.grad * std::cos(s.value)); + } + + friend DScalar1 cos(const DScalar1 &s) + { + // vn = cos(v), Dvn = -sin(v) * Dv + return DScalar1(std::cos(s.value), s.grad * -std::sin(s.value)); + } + + friend DScalar1 acos(const DScalar1 &s) + { + if (std::abs(s.value) >= 1) + throw std::runtime_error("acos: Expected a value in (-1, 1)"); + + Scalar temp = -std::sqrt((Scalar)1 - s.value * s.value); + + // vn = acos(v), Dvn = -1/sqrt(1-v^2) * Dv + return DScalar1(std::acos(s.value), + s.grad * ((Scalar)1 / temp)); + } + + friend DScalar1 asin(const DScalar1 &s) + { + if (std::abs(s.value) >= 1) + throw std::runtime_error("asin: Expected a value in (-1, 1)"); + + Scalar temp = std::sqrt((Scalar)1 - s.value * s.value); + + // vn = asin(v), Dvn = 1/sqrt(1-v^2) * Dv + return DScalar1(std::asin(s.value), + s.grad * ((Scalar)1 / temp)); + } + + friend DScalar1 atan2(const DScalar1 &y, const DScalar1 &x) + { + Scalar denom = x.value * x.value + y.value * y.value; + + // vn = atan2(y, x), Dvn = (x*Dy - y*Dx) / (x^2 + y^2) + return DScalar1(std::atan2(y.value, x.value), + y.grad * (x.value / denom) - x.grad * (y.value / denom)); + } + + /// @} + // ====================================================================== + + // ====================================================================== + /// @{ \name Comparison and assignment + // ====================================================================== + + inline void operator=(const DScalar1 &s) + { + value = s.value; + grad = s.grad; + } + inline void operator=(const Scalar &v) + { + value = v; + grad.setZero(); + } + inline bool operator<(const DScalar1 &s) const { return value < s.value; } + inline bool operator<=(const DScalar1 &s) const { return value <= s.value; } + inline bool operator>(const DScalar1 &s) const { return value > s.value; } + inline bool operator>=(const DScalar1 &s) const { return value >= s.value; } + inline bool operator<(const Scalar &s) const { return value < s; } + inline bool operator<=(const Scalar &s) const { return value <= s; } + inline bool operator>(const Scalar &s) const { return value > s; } + inline bool operator>=(const Scalar &s) const { return value >= s; } + inline bool operator==(const Scalar &s) const { return value == s; } + inline bool operator!=(const Scalar &s) const { return value != s; } + + /// @} + // ====================================================================== + + // ====================================================================== + /// @{ \name Vector helper functions + // ====================================================================== + + /// Initialize a constant two-dimensional vector + static inline DVector2 vector(const Eigen::Matrix &v) + { + return DVector2(DScalar1(v.x()), DScalar1(v.y())); + } + + /// Create a constant three-dimensional vector + static inline DVector3 vector(const Eigen::Matrix &v) + { + return DVector3(DScalar1(v.x()), DScalar1(v.y()), DScalar1(v.z())); + } + +#if defined(__MITSUBA_MITSUBA_H_) /* Mitsuba-specific */ + /// Initialize a constant two-dimensional vector + static inline DVector2 vector(const mitsuba::TVector2 &v) + { + return DVector2(DScalar1(v.x), DScalar1(v.y)); + } + + /// Initialize a constant two-dimensional vector + static inline DVector2 vector(const mitsuba::TPoint2 &p) + { + return DVector2(DScalar1(p.x), DScalar1(p.y)); + } + + /// Create a constant three-dimensional vector + static inline DVector3 vector(const mitsuba::TVector3 &v) + { + return DVector3(DScalar1(v.x), DScalar1(v.y), DScalar1(v.z)); + } + + /// Create a constant three-dimensional vector + static inline DVector3 vector(const mitsuba::TPoint3 &p) + { + return DVector3(DScalar1(p.x), DScalar1(p.y), DScalar1(p.z)); + } +#endif + + /// @} + // ====================================================================== +protected: + Scalar value; + Gradient grad; +}; + +template +std::ostream &operator<<(std::ostream &out, const DScalar1 &s) +{ + out << "[" << s.getValue() + << ", grad=" << s.getGradient().format(Eigen::IOFormat(4, 1, ", ", "; ", "", "", "[", "]")) + << "]"; + return out; +} + +/** + * \brief Automatic differentiation scalar with first- and second-order derivatives + * + * This class provides an instrumented "scalar" value, which may be dependent on + * a number of independent variables. The implementation keeps tracks of first + * and second-order drivatives with respect to these variables using a set + * of overloaded operations and implementations of special functions (sin, + * tan, exp, ..). + * + * This is extremely useful for numerical optimization, particularly when + * analytic derivatives from programs like Maple or Mathematica suffer from + * excessively complicated expressions. + * + * The class relies on templates, which makes it possible to fix the + * number of independent variables at compile-time so that instances can + * be allocated on the stack. Otherwise, they will be placed on the heap. + * + * This is an extended C++ port of Jon Kaldor's implementation, which is + * based on a C version by Eitan Grinspun at Caltech) + * + * \sa DScalar1 + * \author Wenzel Jakob + */ +template , + typename _Hessian = Eigen::Matrix<_Scalar, Eigen::Dynamic, Eigen::Dynamic>> +struct DScalar2 : public DiffScalarBase +{ +public: + typedef _Scalar Scalar; + typedef _Gradient Gradient; + typedef _Hessian Hessian; + typedef Eigen::Matrix DVector2; + typedef Eigen::Matrix DVector3; + + // ====================================================================== + /// @{ \name Constructors and accessors + // ====================================================================== + + /// Create a new constant automatic differentiation scalar + explicit DScalar2(Scalar value_ = (Scalar)0) : value(value_) + { + size_t variableCount = getVariableCount(); + + grad.resize(variableCount); + grad.setZero(); + hess.resize(variableCount, variableCount); + hess.setZero(); + } + + /// Construct a new scalar with the specified value and one first derivative set to 1 + DScalar2(size_t index, const Scalar &value_) + : value(value_) + { + size_t variableCount = getVariableCount(); + + grad.resize(variableCount); + grad.setZero(); + grad(index) = 1; + hess.resize(variableCount, variableCount); + hess.setZero(); + } + + /// Construct a scalar associated with the given gradient and Hessian + DScalar2(Scalar value_, const Gradient &grad_, const Hessian &hess_) + : value(value_), grad(grad_), hess(hess_) {} + + /// Copy constructor + DScalar2(const DScalar2 &s) + : value(s.value), grad(s.grad), hess(s.hess) {} + + inline const Scalar &getValue() const { return value; } + inline const Gradient &getGradient() const { return grad; } + inline const Hessian &getHessian() const { return hess; } + + // ====================================================================== + /// @{ \name Addition + // ====================================================================== + friend DScalar2 operator+(const DScalar2 &lhs, const DScalar2 &rhs) + { + return DScalar2(lhs.value + rhs.value, + lhs.grad + rhs.grad, lhs.hess + rhs.hess); + } + + friend DScalar2 operator+(const DScalar2 &lhs, const Scalar &rhs) + { + return DScalar2(lhs.value + rhs, lhs.grad, lhs.hess); + } + + friend DScalar2 operator+(const Scalar &lhs, const DScalar2 &rhs) + { + return DScalar2(rhs.value + lhs, rhs.grad, rhs.hess); + } + + inline DScalar2 &operator+=(const DScalar2 &s) + { + value += s.value; + grad += s.grad; + hess += s.hess; + return *this; + } + + inline DScalar2 &operator+=(const Scalar &v) + { + value += v; + return *this; + } + + /// @} + // ====================================================================== + + // ====================================================================== + /// @{ \name Subtraction + // ====================================================================== + + friend DScalar2 operator-(const DScalar2 &lhs, const DScalar2 &rhs) + { + return DScalar2(lhs.value - rhs.value, lhs.grad - rhs.grad, lhs.hess - rhs.hess); + } + + friend DScalar2 operator-(const DScalar2 &lhs, const Scalar &rhs) + { + return DScalar2(lhs.value - rhs, lhs.grad, lhs.hess); + } + + friend DScalar2 operator-(const Scalar &lhs, const DScalar2 &rhs) + { + return DScalar2(lhs - rhs.value, -rhs.grad, -rhs.hess); + } + + friend DScalar2 operator-(const DScalar2 &s) + { + return DScalar2(-s.value, -s.grad, -s.hess); + } + + inline DScalar2 &operator-=(const DScalar2 &s) + { + value -= s.value; + grad -= s.grad; + hess -= s.hess; + return *this; + } + + inline DScalar2 &operator-=(const Scalar &v) + { + value -= v; + return *this; + } + /// @} + // ====================================================================== + + // ====================================================================== + /// @{ \name Division + // ====================================================================== + friend DScalar2 operator/(const DScalar2 &lhs, const Scalar &rhs) + { + if (rhs == 0) + throw std::runtime_error("DScalar2: Division by zero!"); + Scalar inv = 1.0f / rhs; + return DScalar2(lhs.value * inv, lhs.grad * inv, lhs.hess * inv); + } + + friend DScalar2 operator/(const Scalar &lhs, const DScalar2 &rhs) + { + return lhs * inverse(rhs); + } + + friend DScalar2 operator/(const DScalar2 &lhs, const DScalar2 &rhs) + { + return lhs * inverse(rhs); + } + + friend DScalar2 inverse(const DScalar2 &s) + { + Scalar valueSqr = s.value * s.value, + valueCub = valueSqr * s.value, + invValueSqr = (Scalar)1 / valueSqr; + + // vn = 1/v + DScalar2 result((Scalar)1 / s.value); + + // Dvn = -1/(v^2) Dv + result.grad = s.grad * -invValueSqr; + + // D^2vn = -1/(v^2) D^2v + 2/(v^3) Dv Dv^T + result.hess = s.hess * -invValueSqr; + result.hess += s.grad * s.grad.transpose() + * ((Scalar)2 / valueCub); + + return result; + } + + inline DScalar2 &operator/=(const Scalar &v) + { + value /= v; + grad /= v; + hess /= v; + return *this; + } + /// @} + // ====================================================================== + + // ====================================================================== + /// @{ \name Multiplication + // ====================================================================== + friend DScalar2 operator*(const DScalar2 &lhs, const Scalar &rhs) + { + return DScalar2(lhs.value * rhs, lhs.grad * rhs, lhs.hess * rhs); + } + + friend DScalar2 operator*(const Scalar &lhs, const DScalar2 &rhs) + { + return DScalar2(rhs.value * lhs, rhs.grad * lhs, rhs.hess * lhs); + } + + friend DScalar2 operator*(const DScalar2 &lhs, const DScalar2 &rhs) + { + DScalar2 result(lhs.value * rhs.value); + + /// Product rule + result.grad = rhs.grad * lhs.value + lhs.grad * rhs.value; + + // (i,j) = g*F_xixj + g*G_xixj + F_xi*G_xj + F_xj*G_xi + result.hess = rhs.hess * lhs.value; + result.hess += lhs.hess * rhs.value; + result.hess += lhs.grad * rhs.grad.transpose(); + result.hess += rhs.grad * lhs.grad.transpose(); + + return result; + } + + inline DScalar2 &operator*=(const Scalar &v) + { + value *= v; + grad *= v; + hess *= v; + return *this; + } + + /// @} + // ====================================================================== + + // ====================================================================== + /// @{ \name Miscellaneous functions + // ====================================================================== + + friend DScalar2 abs(const DScalar2 &s) + { + return s.value < 0? -s: s; + } + + friend DScalar2 sqrt(const DScalar2 &s) + { + Scalar sqrtVal = std::sqrt(s.value), + temp = (Scalar)1 / ((Scalar)2 * sqrtVal); + + // vn = sqrt(v) + DScalar2 result(sqrtVal); + + // Dvn = 1/(2 sqrt(v)) Dv + result.grad = s.grad * temp; + + // D^2vn = 1/(2 sqrt(v)) D^2v - 1/(4 v*sqrt(v)) Dv Dv^T + result.hess = s.hess * temp; + result.hess += s.grad * s.grad.transpose() + * (-(Scalar)1 / ((Scalar)4 * s.value * sqrtVal)); + + return result; + } + + friend DScalar2 pow(const DScalar2 &s, const Scalar &a) + { + Scalar powVal = std::pow(s.value, a), + temp = a * std::pow(s.value, a - 1); + // vn = v ^ a + DScalar2 result(powVal); + + // Dvn = a*v^(a-1) * Dv + result.grad = s.grad * temp; + + // D^2vn = a*v^(a-1) D^2v - 1/(4 v*sqrt(v)) Dv Dv^T + result.hess = s.hess * temp; + result.hess += s.grad * s.grad.transpose() + * (a * (a - 1) * std::pow(s.value, a - 2)); + + return result; + } + + friend DScalar2 exp(const DScalar2 &s) + { + Scalar expVal = std::exp(s.value); + + // vn = exp(v) + DScalar2 result(expVal); + + // Dvn = exp(v) * Dv + result.grad = s.grad * expVal; + + // D^2vn = exp(v) * Dv*Dv^T + exp(v) * D^2v + result.hess = (s.grad * s.grad.transpose() + + s.hess) + * expVal; + + return result; + } + + friend DScalar2 log(const DScalar2 &s) + { + Scalar logVal = std::log(s.value); + + // vn = log(v) + DScalar2 result(logVal); + + // Dvn = Dv / v + result.grad = s.grad / s.value; + + // D^2vn = (v*D^2v - Dv*Dv^T)/(v^2) + result.hess = s.hess / s.value - (s.grad * s.grad.transpose() / (s.value * s.value)); + + return result; + } + + friend DScalar2 sin(const DScalar2 &s) + { + Scalar sinVal = std::sin(s.value), + cosVal = std::cos(s.value); + + // vn = sin(v) + DScalar2 result(sinVal); + + // Dvn = cos(v) * Dv + result.grad = s.grad * cosVal; + + // D^2vn = -sin(v) * Dv*Dv^T + cos(v) * Dv^2 + result.hess = s.hess * cosVal; + result.hess += s.grad * s.grad.transpose() * -sinVal; + + return result; + } + + friend DScalar2 cos(const DScalar2 &s) + { + Scalar sinVal = std::sin(s.value), + cosVal = std::cos(s.value); + // vn = cos(v) + DScalar2 result(cosVal); + + // Dvn = -sin(v) * Dv + result.grad = s.grad * -sinVal; + + // D^2vn = -cos(v) * Dv*Dv^T - sin(v) * Dv^2 + result.hess = s.hess * -sinVal; + result.hess += s.grad * s.grad.transpose() * -cosVal; + + return result; + } + + friend DScalar2 acos(const DScalar2 &s) + { + if (std::abs(s.value) >= 1) + throw std::runtime_error("acos: Expected a value in (-1, 1)"); + + Scalar temp = -std::sqrt((Scalar)1 - s.value * s.value); + + // vn = acos(v) + DScalar2 result(std::acos(s.value)); + + // Dvn = -1/sqrt(1-v^2) * Dv + result.grad = s.grad * ((Scalar)1 / temp); + + // D^2vn = -1/sqrt(1-v^2) * D^2v - v/[(1-v^2)^(3/2)] * Dv*Dv^T + result.hess = s.hess * ((Scalar)1 / temp); + result.hess += s.grad * s.grad.transpose() + * s.value / (temp * temp * temp); + + return result; + } + + friend DScalar2 asin(const DScalar2 &s) + { + if (std::abs(s.value) >= 1) + throw std::runtime_error("asin: Expected a value in (-1, 1)"); + + Scalar temp = std::sqrt((Scalar)1 - s.value * s.value); + + // vn = asin(v) + DScalar2 result(std::asin(s.value)); + + // Dvn = 1/sqrt(1-v^2) * Dv + result.grad = s.grad * ((Scalar)1 / temp); + + // D^2vn = 1/sqrt(1-v*v) * D^2v + v/[(1-v^2)^(3/2)] * Dv*Dv^T + result.hess = s.hess * ((Scalar)1 / temp); + result.hess += s.grad * s.grad.transpose() + * s.value / (temp * temp * temp); + + return result; + } + + friend DScalar2 atan2(const DScalar2 &y, const DScalar2 &x) + { + // vn = atan2(y, x) + DScalar2 result(std::atan2(y.value, x.value)); + + // Dvn = (x*Dy - y*Dx) / (x^2 + y^2) + Scalar denom = x.value * x.value + y.value * y.value, + denomSqr = denom * denom; + result.grad = y.grad * (x.value / denom) + - x.grad * (y.value / denom); + + // D^2vn = (Dy*Dx^T + xD^2y - Dx*Dy^T - yD^2x) / (x^2+y^2) + // - [(x*Dy - y*Dx) * (2*x*Dx + 2*y*Dy)^T] / (x^2+y^2)^2 + result.hess = (y.hess * x.value + + y.grad * x.grad.transpose() + - x.hess * y.value + - x.grad * y.grad.transpose()) + / denom; + + result.hess -= + (y.grad * (x.value / denomSqr) - x.grad * (y.value / denomSqr)) * (x.grad * ((Scalar)2 * x.value) + y.grad * ((Scalar)2 * y.value)).transpose(); + + return result; + } + + /// @} + // ====================================================================== + + // ====================================================================== + /// @{ \name Comparison and assignment + // ====================================================================== + + inline void operator=(const DScalar2 &s) + { + value = s.value; + grad = s.grad; + hess = s.hess; + } + inline void operator=(const Scalar &v) + { + value = v; + grad.setZero(); + hess.setZero(); + } + inline bool operator<(const DScalar2 &s) const { return value < s.value; } + inline bool operator<=(const DScalar2 &s) const { return value <= s.value; } + inline bool operator>(const DScalar2 &s) const { return value > s.value; } + inline bool operator>=(const DScalar2 &s) const { return value >= s.value; } + inline bool operator!=(const DScalar2 &s) const { return value != s.value; } + + inline bool operator<(const Scalar &s) const { return value < s; } + inline bool operator<=(const Scalar &s) const { return value <= s; } + inline bool operator>(const Scalar &s) const { return value > s; } + inline bool operator>=(const Scalar &s) const { return value >= s; } + inline bool operator==(const Scalar &s) const { return value == s; } + inline bool operator!=(const Scalar &s) const { return value != s; } + + /// @} + // ====================================================================== + + // ====================================================================== + /// @{ \name Vector helper functions + // ====================================================================== + +#if defined(__MITSUBA_MITSUBA_H_) /* Mitsuba-specific */ + /// Initialize a constant two-dimensional vector + static inline DVector2 vector(const mitsuba::TVector2 &v) + { + return DVector2(DScalar2(v.x), DScalar2(v.y)); + } + + /// Initialize a constant two-dimensional vector + static inline DVector2 vector(const mitsuba::TPoint2 &p) + { + return DVector2(DScalar2(p.x), DScalar2(p.y)); + } + + /// Create a constant three-dimensional vector + static inline DVector3 vector(const mitsuba::TVector3 &v) + { + return DVector3(DScalar2(v.x), DScalar2(v.y), DScalar2(v.z)); + } + + /// Create a constant three-dimensional vector + static inline DVector3 vector(const mitsuba::TPoint3 &p) + { + return DVector3(DScalar2(p.x), DScalar2(p.y), DScalar2(p.z)); + } +#endif + + /// Initialize a constant two-dimensional vector + static inline DVector2 vector(const Eigen::Matrix &v) + { + return DVector2(DScalar2(v.x()), DScalar2(v.y())); + } + + /// Create a constant three-dimensional vector + static inline DVector3 vector(const Eigen::Matrix &v) + { + return DVector3(DScalar2(v.x()), DScalar2(v.y()), DScalar2(v.z())); + } + + /// @} + // ====================================================================== +protected: + Scalar value; + Gradient grad; + Hessian hess; +}; + +template +std::ostream &operator<<(std::ostream &out, const DScalar2 &s) +{ + out << "[" << s.getValue() + << ", grad=" << s.getGradient().format(Eigen::IOFormat(4, 1, ", ", "; ", "", "", "[", "]")) + << ", hess=" << s.getHessian().format(Eigen::IOFormat(4, 0, ", ", "; ", "", "", "[", "]")) + << "]"; + return out; +} + +// clang-format on + +namespace std { + +template +class numeric_limits> +{ +public: + static const bool is_specialized = std::numeric_limits<_Scalar>::is_specialized; + static const bool is_signed = std::numeric_limits<_Scalar>::is_signed; + static const bool is_integer = std::numeric_limits<_Scalar>::is_integer; + static const bool is_exact = std::numeric_limits<_Scalar>::is_exact; + static const int radix = std::numeric_limits<_Scalar>::radix; + static const bool has_infinity = std::numeric_limits<_Scalar>::has_infinity; + static const bool has_quiet_NaN = std::numeric_limits<_Scalar>::has_quiet_NaN; + static const bool has_signaling_NaN = std::numeric_limits<_Scalar>::has_signaling_NaN; + static const bool is_iec559 = std::numeric_limits<_Scalar>::is_iec559; + static const bool is_bounded = std::numeric_limits<_Scalar>::is_bounded; + static const bool is_modulo = std::numeric_limits<_Scalar>::is_modulo; + static const bool traps = std::numeric_limits<_Scalar>::traps; + static const bool tinyness_before = std::numeric_limits<_Scalar>::tinyness_before; + static const std::float_denorm_style has_denorm = std::numeric_limits<_Scalar>::has_denorm; + static const bool has_denorm_loss = std::numeric_limits<_Scalar>::has_denorm_loss; + static const int min_exponent = std::numeric_limits<_Scalar>::min_exponent; + static const int max_exponent = std::numeric_limits<_Scalar>::max_exponent; + static const int min_exponent10 = std::numeric_limits<_Scalar>::min_exponent10; + static const int max_exponent10 = std::numeric_limits<_Scalar>::max_exponent10; + static const std::float_round_style round_style = std::numeric_limits<_Scalar>::round_style; + static const int digits = std::numeric_limits<_Scalar>::digits; + static const int digits10 = std::numeric_limits<_Scalar>::digits10; + static const int max_digits10 = std::numeric_limits<_Scalar>::max_digits10; + + static constexpr _Scalar min() { return std::numeric_limits<_Scalar>::min(); } + + static constexpr _Scalar max() { return std::numeric_limits<_Scalar>::max(); } + + static constexpr _Scalar lowest() { return std::numeric_limits<_Scalar>::lowest(); } + + static constexpr _Scalar epsilon() { return std::numeric_limits<_Scalar>::epsilon(); } + + static constexpr _Scalar round_error() { return std::numeric_limits<_Scalar>::round_error(); } + + static constexpr _Scalar infinity() { return std::numeric_limits<_Scalar>::infinity(); } + + static constexpr _Scalar quiet_NaN() { return std::numeric_limits<_Scalar>::quiet_NaN(); } + + static constexpr _Scalar signaling_NaN() + { + return std::numeric_limits<_Scalar>::signaling_NaN(); + } + + static constexpr _Scalar denorm_min() { return std::numeric_limits<_Scalar>::denorm_min(); } +}; + +template +class numeric_limits> +{ +public: + static const bool is_specialized = std::numeric_limits<_Scalar>::is_specialized; + static const bool is_signed = std::numeric_limits<_Scalar>::is_signed; + static const bool is_integer = std::numeric_limits<_Scalar>::is_integer; + static const bool is_exact = std::numeric_limits<_Scalar>::is_exact; + static const int radix = std::numeric_limits<_Scalar>::radix; + static const bool has_infinity = std::numeric_limits<_Scalar>::has_infinity; + static const bool has_quiet_NaN = std::numeric_limits<_Scalar>::has_quiet_NaN; + static const bool has_signaling_NaN = std::numeric_limits<_Scalar>::has_signaling_NaN; + static const bool is_iec559 = std::numeric_limits<_Scalar>::is_iec559; + static const bool is_bounded = std::numeric_limits<_Scalar>::is_bounded; + static const bool is_modulo = std::numeric_limits<_Scalar>::is_modulo; + static const bool traps = std::numeric_limits<_Scalar>::traps; + static const bool tinyness_before = std::numeric_limits<_Scalar>::tinyness_before; + static const std::float_denorm_style has_denorm = std::numeric_limits<_Scalar>::has_denorm; + static const bool has_denorm_loss = std::numeric_limits<_Scalar>::has_denorm_loss; + static const int min_exponent = std::numeric_limits<_Scalar>::min_exponent; + static const int max_exponent = std::numeric_limits<_Scalar>::max_exponent; + static const int min_exponent10 = std::numeric_limits<_Scalar>::min_exponent10; + static const int max_exponent10 = std::numeric_limits<_Scalar>::max_exponent10; + static const std::float_round_style round_style = std::numeric_limits<_Scalar>::round_style; + static const int digits = std::numeric_limits<_Scalar>::digits; + static const int digits10 = std::numeric_limits<_Scalar>::digits10; + static const int max_digits10 = std::numeric_limits<_Scalar>::max_digits10; + + static constexpr _Scalar min() { return std::numeric_limits<_Scalar>::min(); } + + static constexpr _Scalar max() { return std::numeric_limits<_Scalar>::max(); } + + static constexpr _Scalar lowest() { return std::numeric_limits<_Scalar>::lowest(); } + + static constexpr _Scalar epsilon() { return std::numeric_limits<_Scalar>::epsilon(); } + + static constexpr _Scalar round_error() { return std::numeric_limits<_Scalar>::round_error(); } + + static constexpr _Scalar infinity() { return std::numeric_limits<_Scalar>::infinity(); } + + static constexpr _Scalar quiet_NaN() { return std::numeric_limits<_Scalar>::quiet_NaN(); } + + static constexpr _Scalar signaling_NaN() + { + return std::numeric_limits<_Scalar>::signaling_NaN(); + } + + static constexpr _Scalar denorm_min() { return std::numeric_limits<_Scalar>::denorm_min(); } +}; + +} // namespace std + +#endif /* __AUTODIFF_H */ From 4c526ec693b56294c6dfb814a4fc0604936be0f5 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 29 Aug 2023 09:24:25 -0400 Subject: [PATCH 004/134] small error on boundary swap cost --- src/wmtk/operations/tri_mesh/EdgeSwap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wmtk/operations/tri_mesh/EdgeSwap.cpp b/src/wmtk/operations/tri_mesh/EdgeSwap.cpp index 3d8785c73e..791f7f8da1 100644 --- a/src/wmtk/operations/tri_mesh/EdgeSwap.cpp +++ b/src/wmtk/operations/tri_mesh/EdgeSwap.cpp @@ -33,10 +33,10 @@ bool EdgeSwap::before() const long val0 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), v0).size()); long val1 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), v1).size()); if (m_mesh.is_boundary_vertex(v0)) { - ++val0; + val0+=2; } if (m_mesh.is_boundary_vertex(v1)) { - ++val1; + val1+=2; } if (val0 < 4 || val1 < 4) { return false; From 12e3765f2246820c2f426f35dc43155a7d74aa4d Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 29 Aug 2023 09:24:53 -0400 Subject: [PATCH 005/134] valence energy for trimesh --- src/wmtk/energy/CMakeLists.txt | 2 ++ src/wmtk/energy/ValenceEnergy.cpp | 50 +++++++++++++++++++++++++++++++ src/wmtk/energy/ValenceEnergy.hpp | 14 +++++++++ 3 files changed, 66 insertions(+) create mode 100644 src/wmtk/energy/ValenceEnergy.cpp create mode 100644 src/wmtk/energy/ValenceEnergy.hpp diff --git a/src/wmtk/energy/CMakeLists.txt b/src/wmtk/energy/CMakeLists.txt index 9882e871ef..14111243e3 100644 --- a/src/wmtk/energy/CMakeLists.txt +++ b/src/wmtk/energy/CMakeLists.txt @@ -5,5 +5,7 @@ set(SRC_FILES DifferentiableEnergy.hpp AMIPS.hpp AMIPS.cpp + ValenceEnergy.hpp + ValenceEnergy.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/energy/ValenceEnergy.cpp b/src/wmtk/energy/ValenceEnergy.cpp new file mode 100644 index 0000000000..0983b79e98 --- /dev/null +++ b/src/wmtk/energy/ValenceEnergy.cpp @@ -0,0 +1,50 @@ +#include "ValenceEnergy.hpp" +#include + +using namespace wmtk; +using namespace wmtk::energy; + + +double ValenceEnergy::energy_eval(const Tuple& tuple) const +{ + // assume tuple is not a boundary edge + const Tuple current_v = tuple; + const Tuple other_v = m_mesh.switch_vertex(tuple); + long val0 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), current_v).size()); + long val1 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), other_v).size()); + if (m_mesh.is_boundary_vertex(current_v)) { + val0 += 2; + } + if (m_mesh.is_boundary_vertex(other_v)) { + val1 += 2; + } + if (val0 < 4 || val1 < 4) { + return false; + } + + // top_v + // / \ + // / \ + // current_v-----other_v + // \ / + // \ / + // bottom_v + const Tuple top_v = m_mesh.switch_vertex(m_mesh.switch_edge(tuple)); + const Tuple bottom_v = m_mesh.switch_vertex(m_mesh.switch_edge(m_mesh.switch_face(tuple))); + long val2 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), top_v).size()); + long val3 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), bottom_v).size()); + if (m_mesh.is_boundary_vertex(top_v)) { + val2 += 2; + } + if (m_mesh.is_boundary_vertex(bottom_v)) { + val3 += 2; + } + + // formula from: https://github.com/daniel-zint/hpmeshgen/blob/cdfb9163ed92523fcf41a127c8173097e935c0a3/src/HPMeshGen2/TriRemeshing.cpp#L315 + const long val_before = std::max(std::abs(val0 - 6), std::abs(val1 - 6)) + + std::max(std::abs(val2 - 6), std::abs(val3 - 6)); + const long val_after = std::max(std::abs(val0 - 7), std::abs(val1 - 7)) + + std::max(std::abs(val2 - 5), std::abs(val3 - 5)); + + return static_cast(val_before - val_after); +} \ No newline at end of file diff --git a/src/wmtk/energy/ValenceEnergy.hpp b/src/wmtk/energy/ValenceEnergy.hpp new file mode 100644 index 0000000000..4efa968e66 --- /dev/null +++ b/src/wmtk/energy/ValenceEnergy.hpp @@ -0,0 +1,14 @@ +#include "Energy.hpp" +namespace wmtk { +namespace energy { + +class ValenceEnergy : public Energy +{ +public: + double energy_eval(const Tuple& tuple) const override; + +protected: + TriMesh& mesh() const; +}; +} // namespace energy +} // namespace wmtk \ No newline at end of file From cf2c6dbeecd0290dc1edae83c474cb61d4c4e06b Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 29 Aug 2023 09:25:45 -0400 Subject: [PATCH 006/134] add deconstructor to energy. allow m_mesh casting between mesh to child classes --- src/wmtk/energy/Energy.cpp | 6 ++++-- src/wmtk/energy/Energy.hpp | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/wmtk/energy/Energy.cpp b/src/wmtk/energy/Energy.cpp index fc8fd29f99..0b298ab810 100644 --- a/src/wmtk/energy/Energy.cpp +++ b/src/wmtk/energy/Energy.cpp @@ -1,6 +1,8 @@ #include "Energy.hpp" namespace wmtk::energy { -Energy::Energy(const Mesh& mesh) +Energy::Energy(Mesh& mesh) : m_mesh(mesh) , m_position_handle(m_mesh.get_attribute_handle("position", PrimitiveType::Vertex)){}; -} \ No newline at end of file + +Energy::~Energy() = default; +} // namespace wmtk::energy diff --git a/src/wmtk/energy/Energy.hpp b/src/wmtk/energy/Energy.hpp index 8725281dcd..ec303f37e9 100644 --- a/src/wmtk/energy/Energy.hpp +++ b/src/wmtk/energy/Energy.hpp @@ -1,17 +1,18 @@ #pragma once #include +#include #include namespace wmtk { namespace energy { class Energy { -private: - const Mesh& m_mesh; +protected: + Mesh& m_mesh; const MeshAttributeHandle m_position_handle; public: - Energy(const Mesh& mesh); + Energy(Mesh& mesh); virtual ~Energy(); public: From c25a8035dcb37b098280425c03130280066c9d3e Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 29 Aug 2023 09:26:19 -0400 Subject: [PATCH 007/134] util function for casting between autodiff type and double --- src/wmtk/energy/utils/AutoDiffTypes.hpp | 31 +++++++++++++++++++++++++ src/wmtk/energy/utils/CMakeLists.txt | 6 +++++ 2 files changed, 37 insertions(+) create mode 100644 src/wmtk/energy/utils/AutoDiffTypes.hpp create mode 100644 src/wmtk/energy/utils/CMakeLists.txt diff --git a/src/wmtk/energy/utils/AutoDiffTypes.hpp b/src/wmtk/energy/utils/AutoDiffTypes.hpp new file mode 100644 index 0000000000..3dacfb8aaa --- /dev/null +++ b/src/wmtk/energy/utils/AutoDiffTypes.hpp @@ -0,0 +1,31 @@ +#include "autodiff.h" + + +namespace wmtk { +namespace energy { +template +class AutoDiffAllocator +{ +public: + T operator()(const int i, double v) const { return T(i, v); } +}; + +template <> +class AutoDiffAllocator +{ +public: + double operator()(const int i, double v) const { return v; } +}; + +template +void get_local_vector(const Eigen::MatrixXd& data, const int size, AutoDiffVect& local_vector) +{ + typedef typename AutoDiffVect::Scalar T; + const AutoDiffAllocator allocate_auto_diff_scalar; + local_vector.resize(size); + for (int i = 0; i < size; ++i) { + local_vector(i) = allocate_auto_diff_scalar(i, data(i)); + } +} +} // namespace energy +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/utils/CMakeLists.txt b/src/wmtk/energy/utils/CMakeLists.txt new file mode 100644 index 0000000000..e24cbcfa32 --- /dev/null +++ b/src/wmtk/energy/utils/CMakeLists.txt @@ -0,0 +1,6 @@ +set(SRC_FILES + AutoDiffTypes.hpp + AutoDiffTypes.cpp + autodiff.h +) +target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) \ No newline at end of file From fa2006c839925e3d3fecad09e9d7352b1702bff9 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 29 Aug 2023 09:26:54 -0400 Subject: [PATCH 008/134] AMIPS compiled --- src/wmtk/energy/AMIPS.cpp | 46 +++++++++++++----------- src/wmtk/energy/AMIPS.hpp | 31 ++++++++-------- src/wmtk/energy/DifferentiableEnergy.hpp | 14 ++++++-- 3 files changed, 53 insertions(+), 38 deletions(-) diff --git a/src/wmtk/energy/AMIPS.cpp b/src/wmtk/energy/AMIPS.cpp index 2c10cfb30d..e25943abf3 100644 --- a/src/wmtk/energy/AMIPS.cpp +++ b/src/wmtk/energy/AMIPS.cpp @@ -1,41 +1,45 @@ #include "AMIPS.hpp" - -double AMIPS_2D::energy_eval(const Tuple& tuple) const override +using namespace wmtk; +using namespace wmtk::energy; +double AMIPS_2D::energy_eval(const Tuple& tuple) const { // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_accessor(m_position_handle); + ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); Eigen::Vector2d uv1 = pos.vector_attribute(tuple); - Eigen::Vector2d uv2 = pos.vector_attribute(switch_edge(switch_vertex(tuple))); - Eigen::Vector2d uv3 = pos.vector_attribute(switch_vertex(switch_edge(tuple))); + Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); + Eigen::Vector2d uv3 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); // return the energy return energy_eval(uv1, uv2, uv3); } -DScalar AMIPS_2D::energy_eval_autodiff(const Tuple& tuple) const override +DScalar AMIPS_2D::energy_eval_autodiff(const Tuple& tuple) const { // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_accessor(m_position_handle); + ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); Eigen::Vector2d uv1 = pos.vector_attribute(tuple); - Eigen::Vector2d uv2 = pos.vector_attribute(switch_edge(switch_vertex(tuple))); - Eigen::Vector2d uv3 = pos.vector_attribute(switch_vertex(switch_edge(tuple))); + Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); + Eigen::Vector2d uv3 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); // return the energy - return energy_eval_autodiff(uv1, uv2, uv3); + return energy_eval(uv1, uv2, uv3); } - template -static T AMIPS_2D::energy_eval_autodiff( - const Eigen::Vector2d uv1, - const Eigen::Vector2d uv2, - const Eigen::Vector uv3) +auto AMIPS_2D::energy_eval( + const Eigen::Vector2d& uv1, + const Eigen::Vector2d& uv2, + const Eigen::Vector2d& uv3) -> T { // (x0 - x1, y0 - y1, x0 - x2, y0 - y2).transpose Eigen::Matrix Dm; - DScalar x0(0, uv1(0)), y1(1, uv1(1)); - Dm << DScalar(uv2(0)) - x0, DScalar(uv3(0)) - x0, DScalar(uv2(1)) - y0, DScalar(uv3(1)) - y0; + typedef Eigen::Matrix Vec2T; + Vec2T uv1_; + + get_local_vector(uv1, 2, uv1_); + + Dm << uv2(0) - uv1_(0), uv3(0) - uv1_(0), uv2(1) - uv1_(1), uv3(1) - uv1_(1); Eigen::Matrix2d Ds, Dsinv; Eigen::Vector2d target_A, target_B, target_C; @@ -46,8 +50,8 @@ static T AMIPS_2D::energy_eval_autodiff( target_C.y() - target_A.y(); auto Dsdet = Ds.determinant(); - if (std::abs(Dsdet) < std::numeric_limits::denorm_min()) { - return std::numeric_limits::infinity(); + if (abs(Dsdet) < std::numeric_limits::denorm_min()) { + return static_cast(std::numeric_limits::infinity()); } Dsinv = Ds.inverse(); @@ -59,8 +63,8 @@ static T AMIPS_2D::energy_eval_autodiff( (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); auto Fdet = F.determinant(); - if (std::abs(Fdet) < std::numeric_limits::denorm_min()) { - return std::numeric_limits::infinity(); + if (abs(Fdet) < std::numeric_limits::denorm_min()) { + return static_cast(std::numeric_limits::infinity()); } return (F.transpose() * F).trace() / Fdet; } diff --git a/src/wmtk/energy/AMIPS.hpp b/src/wmtk/energy/AMIPS.hpp index 1153f1088a..4a7f7562de 100644 --- a/src/wmtk/energy/AMIPS.hpp +++ b/src/wmtk/energy/AMIPS.hpp @@ -1,12 +1,13 @@ + #include "DifferentiableEnergy.hpp" +namespace wmtk { +namespace energy { -class AMIPS_2D : public wmtk::Energy +class AMIPS_2D : public DifferentiableEnergy { - using DScalar = DScalar2; - public: - double energy_eval(const Tuple& tuple) const override{}; - DScalar energy_eval_autodiff(const Tuple& tuple) const override{}; + double energy_eval(const Tuple& tuple) const override; + DScalar energy_eval_autodiff(const Tuple& tuple) const override; /** * @brief gradient defined wrt the first vertex @@ -17,23 +18,25 @@ class AMIPS_2D : public wmtk::Energy * @return can be double or DScalar */ template - static T energy_eval_autodiff( - const Eigen::Vector2d uv1, - const Eigen::Vector2d uv2, - const Eigen::Vector2d uv3){}; + static T + energy_eval(const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, const Eigen::Vector2d& uv3); }; /** * @brief TODO 3D AMIPS uses uv and displacement map to get the 3d cooridnates then evaluate * */ -class AMIPS_3D : public wmtk::Energy +class AMIPS_3D : public DifferentiableEnergy { + using DScalar = DScalar2; + public: - double energy_eval(const Tuple& tuple) const override{}; - DScalar energy_eval_autodiff(const Tuple& tuple) const override{}; + double energy_eval(const Tuple& tuple) const override; + DScalar energy_eval_autodiff(const Tuple& tuple) const override; template static T - energy_eval(const Eigen::Vector3d p1, const Eigen::Vector3d p2, const Eigen::Vector3d p3){}; -}; \ No newline at end of file + energy_eval(const Eigen::Vector3d& p1, const Eigen::Vector3d& p2, const Eigen::Vector3d& p3); +}; +} // namespace energy +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/DifferentiableEnergy.hpp b/src/wmtk/energy/DifferentiableEnergy.hpp index 56b15fbf49..89c11b3d89 100644 --- a/src/wmtk/energy/DifferentiableEnergy.hpp +++ b/src/wmtk/energy/DifferentiableEnergy.hpp @@ -1,9 +1,17 @@ #include +#include +#include #include "Energy.hpp" +namespace wmtk { +namespace energy { +using DScalar = DScalar2; +using Scalar = typename DScalar::Scalar; + + class DifferentiableEnergy : public Energy { - using DScalar = DScalar2; - public: virtual DScalar energy_eval_autodiff(const Tuple& tuple) const = 0; -} \ No newline at end of file +}; +} // namespace energy +} // namespace wmtk From 2073774437b37e81204db89fe6567e86e4cb1404 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 29 Aug 2023 22:28:14 -0400 Subject: [PATCH 009/134] add trimesh as a field for ValenceEnergy --- src/wmtk/energy/Energy.cpp | 2 +- src/wmtk/energy/Energy.hpp | 5 ++--- src/wmtk/energy/ValenceEnergy.cpp | 12 ++++++++---- src/wmtk/energy/ValenceEnergy.hpp | 2 ++ 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/wmtk/energy/Energy.cpp b/src/wmtk/energy/Energy.cpp index 0b298ab810..085733a25a 100644 --- a/src/wmtk/energy/Energy.cpp +++ b/src/wmtk/energy/Energy.cpp @@ -1,6 +1,6 @@ #include "Energy.hpp" namespace wmtk::energy { -Energy::Energy(Mesh& mesh) +Energy::Energy(const Mesh& mesh) : m_mesh(mesh) , m_position_handle(m_mesh.get_attribute_handle("position", PrimitiveType::Vertex)){}; diff --git a/src/wmtk/energy/Energy.hpp b/src/wmtk/energy/Energy.hpp index ec303f37e9..de9f82586a 100644 --- a/src/wmtk/energy/Energy.hpp +++ b/src/wmtk/energy/Energy.hpp @@ -1,18 +1,17 @@ #pragma once #include -#include #include namespace wmtk { namespace energy { class Energy { protected: - Mesh& m_mesh; + const Mesh& m_mesh; const MeshAttributeHandle m_position_handle; public: - Energy(Mesh& mesh); + Energy(const Mesh& mesh); virtual ~Energy(); public: diff --git a/src/wmtk/energy/ValenceEnergy.cpp b/src/wmtk/energy/ValenceEnergy.cpp index 0983b79e98..7b7ac02a12 100644 --- a/src/wmtk/energy/ValenceEnergy.cpp +++ b/src/wmtk/energy/ValenceEnergy.cpp @@ -1,9 +1,11 @@ #include "ValenceEnergy.hpp" #include -using namespace wmtk; -using namespace wmtk::energy; - +namespace wmtk { +namespace energy { +ValenceEnergy::ValenceEnergy(const TriMesh& mesh) + : Energy(mesh) +{} double ValenceEnergy::energy_eval(const Tuple& tuple) const { @@ -47,4 +49,6 @@ double ValenceEnergy::energy_eval(const Tuple& tuple) const std::max(std::abs(val2 - 5), std::abs(val3 - 5)); return static_cast(val_before - val_after); -} \ No newline at end of file +} +} // namespace energy +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/ValenceEnergy.hpp b/src/wmtk/energy/ValenceEnergy.hpp index 4efa968e66..f1f187b224 100644 --- a/src/wmtk/energy/ValenceEnergy.hpp +++ b/src/wmtk/energy/ValenceEnergy.hpp @@ -1,3 +1,4 @@ +#include #include "Energy.hpp" namespace wmtk { namespace energy { @@ -5,6 +6,7 @@ namespace energy { class ValenceEnergy : public Energy { public: + ValenceEnergy(const TriMesh& mesh); double energy_eval(const Tuple& tuple) const override; protected: From 334e57019e44256f96a5d467292596048a6c679b Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 29 Aug 2023 23:03:35 -0400 Subject: [PATCH 010/134] AMIPS init with trimesh --- src/wmtk/energy/AMIPS.cpp | 5 +++++ src/wmtk/energy/AMIPS.hpp | 2 ++ src/wmtk/energy/DifferentiableEnergy.hpp | 9 +++++++++ 3 files changed, 16 insertions(+) diff --git a/src/wmtk/energy/AMIPS.cpp b/src/wmtk/energy/AMIPS.cpp index e25943abf3..76a19c1573 100644 --- a/src/wmtk/energy/AMIPS.cpp +++ b/src/wmtk/energy/AMIPS.cpp @@ -1,6 +1,11 @@ #include "AMIPS.hpp" using namespace wmtk; using namespace wmtk::energy; + +AMIPS_2D::AMIPS_2D(const TriMesh& mesh) + : DifferentiableEnergy(mesh) +{} + double AMIPS_2D::energy_eval(const Tuple& tuple) const { // get the uv coordinates of the triangle diff --git a/src/wmtk/energy/AMIPS.hpp b/src/wmtk/energy/AMIPS.hpp index 4a7f7562de..bf9cd23e33 100644 --- a/src/wmtk/energy/AMIPS.hpp +++ b/src/wmtk/energy/AMIPS.hpp @@ -6,6 +6,8 @@ namespace energy { class AMIPS_2D : public DifferentiableEnergy { public: + AMIPS_2D(const TriMesh& mesh); + double energy_eval(const Tuple& tuple) const override; DScalar energy_eval_autodiff(const Tuple& tuple) const override; diff --git a/src/wmtk/energy/DifferentiableEnergy.hpp b/src/wmtk/energy/DifferentiableEnergy.hpp index 89c11b3d89..b43d832aaa 100644 --- a/src/wmtk/energy/DifferentiableEnergy.hpp +++ b/src/wmtk/energy/DifferentiableEnergy.hpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include "Energy.hpp" @@ -10,6 +12,13 @@ using Scalar = typename DScalar::Scalar; class DifferentiableEnergy : public Energy { +public: + DifferentiableEnergy(const TriMesh& mesh) + : Energy(mesh){}; + DifferentiableEnergy(const TetMesh& mesh) + : Energy(mesh){}; + virtual ~DifferentiableEnergy() = default; + public: virtual DScalar energy_eval_autodiff(const Tuple& tuple) const = 0; }; From 088a851d74b1e63c420cab29213b15d5b2fb32a5 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 31 Aug 2023 00:21:40 -0400 Subject: [PATCH 011/134] AccuracyError with constructor --- src/wmtk/energy/AreaAccuracy.cpp | 9 +++++++++ src/wmtk/energy/AreaAccuracy.hpp | 30 ++++++++++++++++++++++++++++++ src/wmtk/energy/CMakeLists.txt | 2 ++ 3 files changed, 41 insertions(+) create mode 100644 src/wmtk/energy/AreaAccuracy.cpp create mode 100644 src/wmtk/energy/AreaAccuracy.hpp diff --git a/src/wmtk/energy/AreaAccuracy.cpp b/src/wmtk/energy/AreaAccuracy.cpp new file mode 100644 index 0000000000..97d05e87ec --- /dev/null +++ b/src/wmtk/energy/AreaAccuracy.cpp @@ -0,0 +1,9 @@ +#include "AreaAccuracy.hpp" +using namespace wmtk; +using namespace wmtk::energy; + +AreaAccuracy::AreaAccuracy(const TriMesh& mesh1, const TriMesh& mesh2) + : DifferentiableEnergy(mesh1) + , m_position_mesh(mesh2) + , m_3d_position_handle(mesh2.get_attribute_handle("position", PrimitiveType::Vertex)) +{} diff --git a/src/wmtk/energy/AreaAccuracy.hpp b/src/wmtk/energy/AreaAccuracy.hpp new file mode 100644 index 0000000000..34c10deebe --- /dev/null +++ b/src/wmtk/energy/AreaAccuracy.hpp @@ -0,0 +1,30 @@ +#include "DifferentiableEnergy.hpp" +namespace wmtk { +namespace energy { + +class AreaAccuracy : public DifferentiableEnergy +{ +public: + AreaAccuracy(const TriMesh& mesh1, const TriMesh& mesh2); + + double energy_eval(const Tuple& tuple) const override; + DScalar energy_eval_autodiff(const Tuple& tuple) const override; + + /** + * @brief gradient defined wrt the first vertex + * + * @param uv1 + * @param uv2 + * @param uv3 + * @return can be double or DScalar + */ + template + static T + energy_eval(const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, const Eigen::Vector2d& uv3); + +protected: + const TriMesh& m_position_mesh; + const MeshAttributeHandle m_3d_position_handle; +}; +} // namespace energy +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/CMakeLists.txt b/src/wmtk/energy/CMakeLists.txt index 14111243e3..8a5b4a728f 100644 --- a/src/wmtk/energy/CMakeLists.txt +++ b/src/wmtk/energy/CMakeLists.txt @@ -7,5 +7,7 @@ set(SRC_FILES AMIPS.cpp ValenceEnergy.hpp ValenceEnergy.cpp + AreaAccuracy.hpp + AreaAccuracy.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) From 7d6b99dc2521dbe4583b9b046ceb2a5dcd4a645a Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 31 Aug 2023 00:27:35 -0400 Subject: [PATCH 012/134] change valence energy defition and naming --- src/wmtk/energy/CMakeLists.txt | 4 ++-- ...{ValenceEnergy.cpp => TriMeshValenceEnergy.cpp} | 14 +++++++------- ...{ValenceEnergy.hpp => TriMeshValenceEnergy.hpp} | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) rename src/wmtk/energy/{ValenceEnergy.cpp => TriMeshValenceEnergy.cpp} (78%) rename src/wmtk/energy/{ValenceEnergy.hpp => TriMeshValenceEnergy.hpp} (72%) diff --git a/src/wmtk/energy/CMakeLists.txt b/src/wmtk/energy/CMakeLists.txt index 8a5b4a728f..2278eaa272 100644 --- a/src/wmtk/energy/CMakeLists.txt +++ b/src/wmtk/energy/CMakeLists.txt @@ -5,8 +5,8 @@ set(SRC_FILES DifferentiableEnergy.hpp AMIPS.hpp AMIPS.cpp - ValenceEnergy.hpp - ValenceEnergy.cpp + TriMeshValenceEnergy.hpp + TriMeshValenceEnergy.cpp AreaAccuracy.hpp AreaAccuracy.cpp ) diff --git a/src/wmtk/energy/ValenceEnergy.cpp b/src/wmtk/energy/TriMeshValenceEnergy.cpp similarity index 78% rename from src/wmtk/energy/ValenceEnergy.cpp rename to src/wmtk/energy/TriMeshValenceEnergy.cpp index 7b7ac02a12..325dd493a0 100644 --- a/src/wmtk/energy/ValenceEnergy.cpp +++ b/src/wmtk/energy/TriMeshValenceEnergy.cpp @@ -1,13 +1,13 @@ -#include "ValenceEnergy.hpp" #include +#include "TriMeshValenceEnergy.hpp" namespace wmtk { namespace energy { -ValenceEnergy::ValenceEnergy(const TriMesh& mesh) +TriMeshValenceEnergy::TriMeshValenceEnergy(const TriMesh& mesh) : Energy(mesh) {} -double ValenceEnergy::energy_eval(const Tuple& tuple) const +double TriMeshValenceEnergy::energy_eval(const Tuple& tuple) const { // assume tuple is not a boundary edge const Tuple current_v = tuple; @@ -43,12 +43,12 @@ double ValenceEnergy::energy_eval(const Tuple& tuple) const } // formula from: https://github.com/daniel-zint/hpmeshgen/blob/cdfb9163ed92523fcf41a127c8173097e935c0a3/src/HPMeshGen2/TriRemeshing.cpp#L315 - const long val_before = std::max(std::abs(val0 - 6), std::abs(val1 - 6)) + + const long val_energy = std::max(std::abs(val0 - 6), std::abs(val1 - 6)) + std::max(std::abs(val2 - 6), std::abs(val3 - 6)); - const long val_after = std::max(std::abs(val0 - 7), std::abs(val1 - 7)) + - std::max(std::abs(val2 - 5), std::abs(val3 - 5)); + // const long val_after = std::max(std::abs(val0 - 7), std::abs(val1 - 7)) + + // std::max(std::abs(val2 - 5), std::abs(val3 - 5)); - return static_cast(val_before - val_after); + return static_cast(val_energy); } } // namespace energy } // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/ValenceEnergy.hpp b/src/wmtk/energy/TriMeshValenceEnergy.hpp similarity index 72% rename from src/wmtk/energy/ValenceEnergy.hpp rename to src/wmtk/energy/TriMeshValenceEnergy.hpp index f1f187b224..ccd116e2d2 100644 --- a/src/wmtk/energy/ValenceEnergy.hpp +++ b/src/wmtk/energy/TriMeshValenceEnergy.hpp @@ -3,10 +3,10 @@ namespace wmtk { namespace energy { -class ValenceEnergy : public Energy +class TriMeshValenceEnergy : public Energy { public: - ValenceEnergy(const TriMesh& mesh); + TriMeshValenceEnergy(const TriMesh& mesh); double energy_eval(const Tuple& tuple) const override; protected: From bb06bb5af7f3a59db4ac8dfcae4972b980b505bb Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 31 Aug 2023 06:17:24 -0400 Subject: [PATCH 013/134] test for valence energy --- src/wmtk/energy/Energy.cpp | 2 +- src/wmtk/energy/TriMeshValenceEnergy.cpp | 17 ++++---- tests/CMakeLists.txt | 4 ++ tests/energy/CMakeLists.txt | 11 ++++++ tests/energy/test_2d_valence_energy.cpp | 49 ++++++++++++++++++++++++ tests/tools/TriMesh_examples.cpp | 4 +- 6 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 tests/energy/CMakeLists.txt create mode 100644 tests/energy/test_2d_valence_energy.cpp diff --git a/src/wmtk/energy/Energy.cpp b/src/wmtk/energy/Energy.cpp index 085733a25a..339415dd3c 100644 --- a/src/wmtk/energy/Energy.cpp +++ b/src/wmtk/energy/Energy.cpp @@ -2,7 +2,7 @@ namespace wmtk::energy { Energy::Energy(const Mesh& mesh) : m_mesh(mesh) - , m_position_handle(m_mesh.get_attribute_handle("position", PrimitiveType::Vertex)){}; + , m_position_handle(mesh.get_attribute_handle("position", PrimitiveType::Vertex)){}; Energy::~Energy() = default; } // namespace wmtk::energy diff --git a/src/wmtk/energy/TriMeshValenceEnergy.cpp b/src/wmtk/energy/TriMeshValenceEnergy.cpp index 325dd493a0..975d5ddb7f 100644 --- a/src/wmtk/energy/TriMeshValenceEnergy.cpp +++ b/src/wmtk/energy/TriMeshValenceEnergy.cpp @@ -1,6 +1,7 @@ -#include #include "TriMeshValenceEnergy.hpp" - +#include +#include +#include namespace wmtk { namespace energy { TriMeshValenceEnergy::TriMeshValenceEnergy(const TriMesh& mesh) @@ -12,8 +13,8 @@ double TriMeshValenceEnergy::energy_eval(const Tuple& tuple) const // assume tuple is not a boundary edge const Tuple current_v = tuple; const Tuple other_v = m_mesh.switch_vertex(tuple); - long val0 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), current_v).size()); - long val1 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), other_v).size()); + long val0 = static_cast(SimplicialComplex::vertex_one_ring(m_mesh, current_v).size()); + long val1 = static_cast(SimplicialComplex::vertex_one_ring(m_mesh, other_v).size()); if (m_mesh.is_boundary_vertex(current_v)) { val0 += 2; } @@ -21,7 +22,7 @@ double TriMeshValenceEnergy::energy_eval(const Tuple& tuple) const val1 += 2; } if (val0 < 4 || val1 < 4) { - return false; + return -1; } // top_v @@ -33,15 +34,15 @@ double TriMeshValenceEnergy::energy_eval(const Tuple& tuple) const // bottom_v const Tuple top_v = m_mesh.switch_vertex(m_mesh.switch_edge(tuple)); const Tuple bottom_v = m_mesh.switch_vertex(m_mesh.switch_edge(m_mesh.switch_face(tuple))); - long val2 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), top_v).size()); - long val3 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), bottom_v).size()); + long val2 = static_cast(SimplicialComplex::vertex_one_ring(m_mesh, top_v).size()); + long val3 = static_cast(SimplicialComplex::vertex_one_ring(m_mesh, bottom_v).size()); + if (m_mesh.is_boundary_vertex(top_v)) { val2 += 2; } if (m_mesh.is_boundary_vertex(bottom_v)) { val3 += 2; } - // formula from: https://github.com/daniel-zint/hpmeshgen/blob/cdfb9163ed92523fcf41a127c8173097e935c0a3/src/HPMeshGen2/TriRemeshing.cpp#L315 const long val_energy = std::max(std::abs(val0 - 6), std::abs(val1 - 6)) + std::max(std::abs(val2 - 6), std::abs(val3 - 6)); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a7e985f040..21a38062de 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -44,6 +44,10 @@ target_link_libraries(wmtk_tests PUBLIC add_subdirectory(components) source_group("components" REGULAR_EXPRESSION "components\/.*\.(cpp|h|hpp)?$") + +add_subdirectory(energy) +source_group("energy" REGULAR_EXPRESSION "energy\/.*\.(cpp|h|hpp)?$") + wmtk_copy_dll(wmtk_tests) # Register unit tests diff --git a/tests/energy/CMakeLists.txt b/tests/energy/CMakeLists.txt new file mode 100644 index 0000000000..ec44b8420b --- /dev/null +++ b/tests/energy/CMakeLists.txt @@ -0,0 +1,11 @@ +# Sources +set(TEST_SOURCES + test_2d_valence_energy.cpp + +) +target_sources(wmtk_tests PRIVATE ${TEST_SOURCES}) + +target_link_libraries(wmtk_tests PUBLIC + wildmeshing_toolkit +) + diff --git a/tests/energy/test_2d_valence_energy.cpp b/tests/energy/test_2d_valence_energy.cpp new file mode 100644 index 0000000000..1dfa993d7e --- /dev/null +++ b/tests/energy/test_2d_valence_energy.cpp @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include "../tools/DEBUG_TriMesh.hpp" +#include "../tools/TriMesh_examples.hpp" +using namespace wmtk; +using namespace wmtk::energy; +using namespace wmtk::tests; +TEST_CASE("energy_valence") +{ + // 0---1---2 + // /0\1/2\3/4\ . + // 3---4---5---6 + // \5/6\7/ . + // 7---8 + const DEBUG_TriMesh example_mesh = hex_plus_two_with_position(); + auto e1 = example_mesh.edge_tuple_between_v1_v2(3, 4, 0); + auto e2 = example_mesh.edge_tuple_between_v1_v2(4, 0, 1); + auto e3 = example_mesh.edge_tuple_between_v1_v2(4, 5, 2); + auto e4 = example_mesh.edge_tuple_between_v1_v2(5, 1, 3); + + + const TriMesh tri_mesh = static_cast(example_mesh); + const auto edges = tri_mesh.get_all(PrimitiveType::Edge); + + TriMeshValenceEnergy valence_energy(tri_mesh); + + + REQUIRE(valence_energy.energy_eval(e1) == 2); + REQUIRE(valence_energy.energy_eval(e2) == 2); + REQUIRE(valence_energy.energy_eval(e3) == 2); + REQUIRE(valence_energy.energy_eval(e4) == 2); +} + +TEST_CASE("amips2d") + +{ + SECTION("equilateral triangle") + { + TriMesh m; + RowVectors3l tris; + tris.resize(1, 3); + tris.row(0) << 0, 1, 2; + m.initialize(tris); + // AMIPS_2D amips2d(m); + } +} \ No newline at end of file diff --git a/tests/tools/TriMesh_examples.cpp b/tests/tools/TriMesh_examples.cpp index 4840a3d1a5..87690736ad 100644 --- a/tests/tools/TriMesh_examples.cpp +++ b/tests/tools/TriMesh_examples.cpp @@ -109,9 +109,9 @@ TriMesh interior_edge() TriMesh hex_plus_two() { // 0---1---2 - // / \ / \ / \ . + // /0\1/2\3/4\ . // 3---4---5---6 - // \ / \ / . + // \5/6\7/ . // 7---8 TriMesh m; RowVectors3l tris; From 5dc090453097391a30acb88b3b739a9cc37a87ea Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 31 Aug 2023 10:39:05 -0400 Subject: [PATCH 014/134] update trimesh example generating for new examples --- tests/tools/TriMesh_examples.cpp | 12 ++++++++++++ tests/tools/TriMesh_examples.hpp | 5 ++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/tools/TriMesh_examples.cpp b/tests/tools/TriMesh_examples.cpp index 87690736ad..2ce6858c02 100644 --- a/tests/tools/TriMesh_examples.cpp +++ b/tests/tools/TriMesh_examples.cpp @@ -13,6 +13,18 @@ TriMesh single_triangle() return m; } +TriMesh single_equilateral_triangle() +{ + TriMesh m = single_triangle(); + Eigen::MatrixXd V; + V.resize(3, 3); + V.row(0) << 0., 0., 0; + V.row(1) << 0., 1, 0; + V.row(2) << 0.5, sqrt(3)/2., 0; + mesh_utils::set_matrix_attribute(V, "position", PrimitiveType::Vertex, m); + return m; +} + TriMesh quad() { TriMesh m; diff --git a/tests/tools/TriMesh_examples.hpp b/tests/tools/TriMesh_examples.hpp index 4bda9b06fa..93478888d6 100644 --- a/tests/tools/TriMesh_examples.hpp +++ b/tests/tools/TriMesh_examples.hpp @@ -14,12 +14,15 @@ namespace wmtk::tests { // TriMesh single_triangle(); +// a single triangle with position +// (0,0,0), (0,1,0), (0.5, sqrt(3)/2, 0)ß +TriMesh single_equilateral_triangle(); // 3--1--- 0 // | / \ . // 2 f1 /2 1 // | 0/ f0 \ . -// | / \ . +// | / \ // 1 ----0---- 2 // TriMesh one_ear(); // an alias for quad From 216b6c5a19d235c0b068725f2c920dcaf1e62ecf Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 31 Aug 2023 10:40:20 -0400 Subject: [PATCH 015/134] tests changed name to test_2d_energy. valence energy test is done --- tests/energy/CMakeLists.txt | 2 +- ...2d_valence_energy.cpp => test_2d_energy.cpp} | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) rename tests/energy/{test_2d_valence_energy.cpp => test_2d_energy.cpp} (75%) diff --git a/tests/energy/CMakeLists.txt b/tests/energy/CMakeLists.txt index ec44b8420b..f05c9ed59b 100644 --- a/tests/energy/CMakeLists.txt +++ b/tests/energy/CMakeLists.txt @@ -1,6 +1,6 @@ # Sources set(TEST_SOURCES - test_2d_valence_energy.cpp + test_2d_energy.cpp ) target_sources(wmtk_tests PRIVATE ${TEST_SOURCES}) diff --git a/tests/energy/test_2d_valence_energy.cpp b/tests/energy/test_2d_energy.cpp similarity index 75% rename from tests/energy/test_2d_valence_energy.cpp rename to tests/energy/test_2d_energy.cpp index 1dfa993d7e..6fa92eb7cc 100644 --- a/tests/energy/test_2d_valence_energy.cpp +++ b/tests/energy/test_2d_energy.cpp @@ -1,3 +1,4 @@ +#pragma once #include #include #include @@ -23,7 +24,6 @@ TEST_CASE("energy_valence") const TriMesh tri_mesh = static_cast(example_mesh); - const auto edges = tri_mesh.get_all(PrimitiveType::Edge); TriMeshValenceEnergy valence_energy(tri_mesh); @@ -35,15 +35,16 @@ TEST_CASE("energy_valence") } TEST_CASE("amips2d") - { SECTION("equilateral triangle") { - TriMesh m; - RowVectors3l tris; - tris.resize(1, 3); - tris.row(0) << 0, 1, 2; - m.initialize(tris); - // AMIPS_2D amips2d(m); + const DEBUG_TriMesh example_mesh = single_equilateral_triangle(); + auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); + const TriMesh tri_mesh = static_cast(example_mesh); + + AMIPS_2D amips2d(tri_mesh); + + // auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); + // REQUIRE(amips2d.energy_eval(e1) == 2.0); } } \ No newline at end of file From a95ab0eda72c67998c6c28642f0b9ca42c9eb789 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 31 Aug 2023 10:40:51 -0400 Subject: [PATCH 016/134] differentiable energy update --- src/wmtk/energy/AMIPS.hpp | 6 +++--- src/wmtk/energy/CMakeLists.txt | 1 + src/wmtk/energy/DifferentiableEnergy.hpp | 7 +++---- src/wmtk/energy/utils/AutoDiffTypes.hpp | 4 +++- src/wmtk/energy/utils/CMakeLists.txt | 1 - 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/wmtk/energy/AMIPS.hpp b/src/wmtk/energy/AMIPS.hpp index bf9cd23e33..9e45898db2 100644 --- a/src/wmtk/energy/AMIPS.hpp +++ b/src/wmtk/energy/AMIPS.hpp @@ -28,11 +28,11 @@ class AMIPS_2D : public DifferentiableEnergy * @brief TODO 3D AMIPS uses uv and displacement map to get the 3d cooridnates then evaluate * */ -class AMIPS_3D : public DifferentiableEnergy +class AMIPS_3DEmbedded : public DifferentiableEnergy { - using DScalar = DScalar2; - public: + AMIPS_3DEmbedded(const TriMesh& mesh); + double energy_eval(const Tuple& tuple) const override; DScalar energy_eval_autodiff(const Tuple& tuple) const override; diff --git a/src/wmtk/energy/CMakeLists.txt b/src/wmtk/energy/CMakeLists.txt index 2278eaa272..60a4f8fef1 100644 --- a/src/wmtk/energy/CMakeLists.txt +++ b/src/wmtk/energy/CMakeLists.txt @@ -11,3 +11,4 @@ set(SRC_FILES AreaAccuracy.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) +add_subdirectory(utils) \ No newline at end of file diff --git a/src/wmtk/energy/DifferentiableEnergy.hpp b/src/wmtk/energy/DifferentiableEnergy.hpp index b43d832aaa..2c481df2c7 100644 --- a/src/wmtk/energy/DifferentiableEnergy.hpp +++ b/src/wmtk/energy/DifferentiableEnergy.hpp @@ -6,17 +6,16 @@ #include "Energy.hpp" namespace wmtk { namespace energy { + using DScalar = DScalar2; using Scalar = typename DScalar::Scalar; - class DifferentiableEnergy : public Energy { public: - DifferentiableEnergy(const TriMesh& mesh) - : Energy(mesh){}; - DifferentiableEnergy(const TetMesh& mesh) + DifferentiableEnergy(const Mesh& mesh) : Energy(mesh){}; + virtual ~DifferentiableEnergy() = default; public: diff --git a/src/wmtk/energy/utils/AutoDiffTypes.hpp b/src/wmtk/energy/utils/AutoDiffTypes.hpp index 3dacfb8aaa..54198f46fa 100644 --- a/src/wmtk/energy/utils/AutoDiffTypes.hpp +++ b/src/wmtk/energy/utils/AutoDiffTypes.hpp @@ -1,8 +1,9 @@ #include "autodiff.h" - namespace wmtk { namespace energy { +using DScalar = DScalar2; +using Scalar = typename DScalar::Scalar; template class AutoDiffAllocator { @@ -21,6 +22,7 @@ template void get_local_vector(const Eigen::MatrixXd& data, const int size, AutoDiffVect& local_vector) { typedef typename AutoDiffVect::Scalar T; + DiffScalarBase::setVariableCount(size); const AutoDiffAllocator allocate_auto_diff_scalar; local_vector.resize(size); for (int i = 0; i < size; ++i) { diff --git a/src/wmtk/energy/utils/CMakeLists.txt b/src/wmtk/energy/utils/CMakeLists.txt index e24cbcfa32..f626088009 100644 --- a/src/wmtk/energy/utils/CMakeLists.txt +++ b/src/wmtk/energy/utils/CMakeLists.txt @@ -1,6 +1,5 @@ set(SRC_FILES AutoDiffTypes.hpp - AutoDiffTypes.cpp autodiff.h ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) \ No newline at end of file From 0e60bb5c72b4b2b8f5bd102c4b0426eafcdf03aa Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Thu, 31 Aug 2023 15:44:22 -0400 Subject: [PATCH 017/134] making autodiff link --- src/wmtk/energy/utils/CMakeLists.txt | 3 ++- src/wmtk/energy/utils/autodiff.cpp | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 src/wmtk/energy/utils/autodiff.cpp diff --git a/src/wmtk/energy/utils/CMakeLists.txt b/src/wmtk/energy/utils/CMakeLists.txt index f626088009..da178df07e 100644 --- a/src/wmtk/energy/utils/CMakeLists.txt +++ b/src/wmtk/energy/utils/CMakeLists.txt @@ -1,5 +1,6 @@ set(SRC_FILES AutoDiffTypes.hpp autodiff.h + autodiff.cpp ) -target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) \ No newline at end of file +target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/energy/utils/autodiff.cpp b/src/wmtk/energy/utils/autodiff.cpp new file mode 100644 index 0000000000..f9e233ee0a --- /dev/null +++ b/src/wmtk/energy/utils/autodiff.cpp @@ -0,0 +1,5 @@ +#include "autodiff.h" + + + +DECLARE_DIFFSCALAR_BASE(); From 6d56afff93ecc4fa6c33b5f5edfe5a8ddb0504fe Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 31 Aug 2023 21:44:49 -0400 Subject: [PATCH 018/134] amips test for equilateral traingle done --- src/wmtk/energy/AMIPS.cpp | 10 ++++++---- tests/energy/test_2d_energy.cpp | 3 +-- tests/tools/TriMesh_examples.cpp | 4 ++-- tests/tools/TriMesh_examples.hpp | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/wmtk/energy/AMIPS.cpp b/src/wmtk/energy/AMIPS.cpp index 76a19c1573..743483edfb 100644 --- a/src/wmtk/energy/AMIPS.cpp +++ b/src/wmtk/energy/AMIPS.cpp @@ -1,4 +1,5 @@ #include "AMIPS.hpp" +#include using namespace wmtk; using namespace wmtk::energy; @@ -10,10 +11,11 @@ double AMIPS_2D::energy_eval(const Tuple& tuple) const { // get the uv coordinates of the triangle ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); - - Eigen::Vector2d uv1 = pos.vector_attribute(tuple); - Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); - Eigen::Vector2d uv3 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); + Eigen::Vector2d uv1 = pos.vector_attribute(tuple).head<2>(); + Eigen::Vector2d uv2 = + pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))).head<2>(); + Eigen::Vector2d uv3 = + pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))).head<2>(); // return the energy return energy_eval(uv1, uv2, uv3); diff --git a/tests/energy/test_2d_energy.cpp b/tests/energy/test_2d_energy.cpp index 6fa92eb7cc..b47565f022 100644 --- a/tests/energy/test_2d_energy.cpp +++ b/tests/energy/test_2d_energy.cpp @@ -44,7 +44,6 @@ TEST_CASE("amips2d") AMIPS_2D amips2d(tri_mesh); - // auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); - // REQUIRE(amips2d.energy_eval(e1) == 2.0); + REQUIRE(amips2d.energy_eval(e1) == 2.0); } } \ No newline at end of file diff --git a/tests/tools/TriMesh_examples.cpp b/tests/tools/TriMesh_examples.cpp index 2ce6858c02..7c53a76716 100644 --- a/tests/tools/TriMesh_examples.cpp +++ b/tests/tools/TriMesh_examples.cpp @@ -19,8 +19,8 @@ TriMesh single_equilateral_triangle() Eigen::MatrixXd V; V.resize(3, 3); V.row(0) << 0., 0., 0; - V.row(1) << 0., 1, 0; - V.row(2) << 0.5, sqrt(3)/2., 0; + V.row(1) << 1., 0, 0; + V.row(2) << 0.5, sqrt(3) / 2., 0; mesh_utils::set_matrix_attribute(V, "position", PrimitiveType::Vertex, m); return m; } diff --git a/tests/tools/TriMesh_examples.hpp b/tests/tools/TriMesh_examples.hpp index 93478888d6..7d6ce9e1ac 100644 --- a/tests/tools/TriMesh_examples.hpp +++ b/tests/tools/TriMesh_examples.hpp @@ -15,7 +15,7 @@ namespace wmtk::tests { TriMesh single_triangle(); // a single triangle with position -// (0,0,0), (0,1,0), (0.5, sqrt(3)/2, 0)ß +// (0,0,0), (1,0,0), (0.5, sqrt(3)/2, 0)ß TriMesh single_equilateral_triangle(); // 3--1--- 0 From 5357e72168670153125f92add95794d962f3a618 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Fri, 1 Sep 2023 05:15:37 -0400 Subject: [PATCH 019/134] dof to position filler --- src/wmtk/energy/utils/CMakeLists.txt | 1 + src/wmtk/energy/utils/DofsToPosition.hpp | 41 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/wmtk/energy/utils/DofsToPosition.hpp diff --git a/src/wmtk/energy/utils/CMakeLists.txt b/src/wmtk/energy/utils/CMakeLists.txt index da178df07e..39256f5889 100644 --- a/src/wmtk/energy/utils/CMakeLists.txt +++ b/src/wmtk/energy/utils/CMakeLists.txt @@ -2,5 +2,6 @@ set(SRC_FILES AutoDiffTypes.hpp autodiff.h autodiff.cpp + DofsToPosition.hpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/energy/utils/DofsToPosition.hpp b/src/wmtk/energy/utils/DofsToPosition.hpp new file mode 100644 index 0000000000..6306a21e69 --- /dev/null +++ b/src/wmtk/energy/utils/DofsToPosition.hpp @@ -0,0 +1,41 @@ +#include +#include +#include "autodiff.h" +// #include + +namespace wmtk { +namespace energy { +class DofsToPosition +{ // size = 2: uv position, size =1, t of boundary curve + using DofVectorX = Eigen::Matrix; + +public: + DofsToPosition(); + ~DofsToPosition(); + + template + Eigen::Matrix operator()(const DofVectorX& dof) const + { + Eigen::Matrix pos; + int size = dof.rows(); + typedef Eigen::Matrix Vec2T; + Vec2T dofT; + get_local_vector(dof, size, dofT); + if (size == 2) { + // TODO retrive position using displacement map + // for now just return itself + pos << dofT(0), dofT(1), static_cast(0.0); + } else + //(dofx.size() == 1) + { + // curve parameterization + // TODO can also be implemented as a overload operator()? + + pos << dofT(0), static_cast(0.0), static_cast(0.0); + } + return pos; + } +} + +} // namespace energy +} // namespace wmtk From 484434f91b1b17d80f84ec539f623b8301333355 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Fri, 1 Sep 2023 09:50:33 -0400 Subject: [PATCH 020/134] AMIPS 3d with dummy filler for get position --- src/wmtk/energy/AMIPS.cpp | 97 +++++++++++++++++-- src/wmtk/energy/AMIPS.hpp | 61 ++++++++++-- .../{AutoDiffTypes.hpp => AutoDiffUtils.hpp} | 9 +- src/wmtk/energy/utils/DofsToPosition.hpp | 15 +-- 4 files changed, 156 insertions(+), 26 deletions(-) rename src/wmtk/energy/utils/{AutoDiffTypes.hpp => AutoDiffUtils.hpp} (75%) diff --git a/src/wmtk/energy/AMIPS.cpp b/src/wmtk/energy/AMIPS.cpp index 743483edfb..89e261d4cd 100644 --- a/src/wmtk/energy/AMIPS.cpp +++ b/src/wmtk/energy/AMIPS.cpp @@ -3,7 +3,7 @@ using namespace wmtk; using namespace wmtk::energy; -AMIPS_2D::AMIPS_2D(const TriMesh& mesh) +AMIPS::AMIPS(const TriMesh& mesh) : DifferentiableEnergy(mesh) {} @@ -18,7 +18,7 @@ double AMIPS_2D::energy_eval(const Tuple& tuple) const pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))).head<2>(); // return the energy - return energy_eval(uv1, uv2, uv3); + return energy_eval(uv1, uv2, uv3, m_target_triangle); } DScalar AMIPS_2D::energy_eval_autodiff(const Tuple& tuple) const { @@ -30,13 +30,14 @@ DScalar AMIPS_2D::energy_eval_autodiff(const Tuple& tuple) const Eigen::Vector2d uv3 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); // return the energy - return energy_eval(uv1, uv2, uv3); + return energy_eval(uv1, uv2, uv3, m_target_triangle); } template auto AMIPS_2D::energy_eval( const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, - const Eigen::Vector2d& uv3) -> T + const Eigen::Vector2d& uv3, + const std::array& m_target_triangle) -> T { // (x0 - x1, y0 - y1, x0 - x2, y0 - y2).transpose Eigen::Matrix Dm; @@ -50,9 +51,9 @@ auto AMIPS_2D::energy_eval( Eigen::Matrix2d Ds, Dsinv; Eigen::Vector2d target_A, target_B, target_C; - target_A << 0., 0.; - target_B << 1., 0.; - target_C << 1. / 2., sqrt(3) / 2.; + target_A << m_target_triangle[0], m_target_triangle[1]; + target_B << m_target_triangle[2], m_target_triangle[3]; + target_C << m_target_triangle[4], m_target_triangle[5]; Ds << target_B.x() - target_A.x(), target_C.x() - target_A.x(), target_B.y() - target_A.y(), target_C.y() - target_A.y(); @@ -75,3 +76,85 @@ auto AMIPS_2D::energy_eval( } return (F.transpose() * F).trace() / Fdet; } + + +template +auto AMIPS_3DEmbedded::energy_eval( + const Eigen::Vector2d& uv1, + const Eigen::Vector2d& uv2, + const Eigen::Vector2d& uv3, + const std::array& m_target_triangle) -> T +{ + DofsToPosition dof2pos; + Eigen::Matrix pos1 = dof2pos.dof_to_pos(uv1); + Eigen::Matrix pos2 = dof2pos.dof_to_pos(uv2); + Eigen::Matrix pos3 = dof2pos.dof_to_pos(uv3); + auto V2_V1 = pos2 - pos1; + auto V3_V1 = pos3 - pos1; + + // tangent bases + // e1 = (V2 - V1).normalize() + // e1 = V2_V1.stableNormalized(); + assert(V2_V1.norm() > 0); // check norm is not 0 + auto e1 = V2_V1 / V2_V1.norm(); + auto n = V2_V1.cross(V3_V1); + + // if (n.lpNorm().getValue() < std::numeric_limits::denorm_min()) { + // wmtk::logger().critical("n.lpNorm {}", n.lpNorm().getValue()); + // std::cout << "V1 " << std::endl; + // std::cout << std::hexfloat << x1 << " " << y1 << v1(2) << std::endl; + // std::cout << "V2 " << std::endl; + // std::cout << std::hexfloat << v2(0) << " " << v2(1) << v2(2) << std::endl; + // std::cout << "V3 " << std::endl; + // std::cout << std::hexfloat << v3(0) << " " << v3(1) << v3(2) << std::endl; + // assert(false); + // } + // n = n.stableNormalized(); + assert(n.norm() > 0); // check norm is not 0 + n = n / n.norm(); + auto e2 = n.cross(e1); + // Eigen::Matrix e2_stableNormalized = e2.stableNormalized(); + assert(e2.norm() > 0); // check norm is not 0 + e2 = e2 / e2.norm(); + + + // project V1, V2, V3 to tangent plane to VT1, VT2, VT3 + + Eigen::Matrix VT1, VT2, VT3; + VT1 << static_cast(0.), static_cast(0.); // the origin + VT2 << V2_V1.dot(e1), V2_V1.dot(e2); + VT3 << V3_V1.dot(e1), V3_V1.dot(e2); + + // now construct Dm as before in tangent plane + // (x2 - x1, y2 - y1, x3 - x1, y2 - y1).transpose + Eigen::Matrix Dm; + Dm << VT2.x() - VT1.x(), VT3.x() - VT1.x(), VT2.y() - VT1.y(), VT3.y() - VT1.y(); + assert(Dm.determinant().getValue() > 0); + + Eigen::Matrix2d Ds, Dsinv; + Eigen::Vector2d target_A, target_B, target_C; + target_A << m_target_triangle[0], m_target_triangle[1]; + target_B << m_target_triangle[2], m_target_triangle[3]; + target_C << m_target_triangle[4], m_target_triangle[5]; + Ds << target_B.x() - target_A.x(), target_C.x() - target_A.x(), target_B.y() - target_A.y(), + target_C.y() - target_A.y(); + + auto Dsdet = Ds.determinant(); + if (abs(Dsdet) < std::numeric_limits::denorm_min()) { + return static_cast(std::numeric_limits::infinity()); + } + Dsinv = Ds.inverse(); + + // define of transform matrix F = Dm@Ds.inv + Eigen::Matrix F; + F << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), + (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), + (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), + (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); + + auto Fdet = F.determinant(); + if (abs(Fdet) < std::numeric_limits::denorm_min()) { + return static_cast(std::numeric_limits::infinity()); + } + return (F.transpose() * F).trace() / Fdet; +} \ No newline at end of file diff --git a/src/wmtk/energy/AMIPS.hpp b/src/wmtk/energy/AMIPS.hpp index 9e45898db2..782673a854 100644 --- a/src/wmtk/energy/AMIPS.hpp +++ b/src/wmtk/energy/AMIPS.hpp @@ -1,13 +1,45 @@ - +#pragma once +#include #include "DifferentiableEnergy.hpp" namespace wmtk { namespace energy { +class AMIPS : public DifferentiableEnergy +{ +public: + AMIPS(const TriMesh& mesh); + +public: + std::array m_target_triangle = {0., 0., 1., 0., 1. / 2., sqrt(3) / 2.}; + double m_target_edge_length = 1.; -class AMIPS_2D : public DifferentiableEnergy + void set_target_triangle(const std::array& target_triangle) + { + m_target_triangle = target_triangle; + } + void set_target_edge_length(const double target_edge_length) + { + m_target_edge_length = target_edge_length; + } + void set_target_triangle_with_scaling(const double target_edge_length) + { + m_target_edge_length = target_edge_length; + m_target_triangle[0] *= m_target_edge_length; + m_target_triangle[1] *= m_target_edge_length; + m_target_triangle[2] *= m_target_edge_length; + m_target_triangle[3] *= m_target_edge_length; + m_target_triangle[4] *= m_target_edge_length; + m_target_triangle[5] *= m_target_edge_length; + } +}; + +class AMIPS_2D : public AMIPS { public: - AMIPS_2D(const TriMesh& mesh); + AMIPS_2D(const TriMesh& mesh) + : AMIPS(mesh) + {} +public: double energy_eval(const Tuple& tuple) const override; DScalar energy_eval_autodiff(const Tuple& tuple) const override; @@ -20,25 +52,34 @@ class AMIPS_2D : public DifferentiableEnergy * @return can be double or DScalar */ template - static T - energy_eval(const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, const Eigen::Vector2d& uv3); -}; + static T energy_eval( + const Eigen::Vector2d& uv1, + const Eigen::Vector2d& uv2, + const Eigen::Vector2d& uv3, + const std::array& m_target_triangle); +}; // namespace energy /** * @brief TODO 3D AMIPS uses uv and displacement map to get the 3d cooridnates then evaluate * */ -class AMIPS_3DEmbedded : public DifferentiableEnergy +class AMIPS_3DEmbedded : public AMIPS { public: - AMIPS_3DEmbedded(const TriMesh& mesh); + AMIPS_3DEmbedded(const TriMesh& mesh) + : AMIPS(mesh) + {} +public: double energy_eval(const Tuple& tuple) const override; DScalar energy_eval_autodiff(const Tuple& tuple) const override; template - static T - energy_eval(const Eigen::Vector3d& p1, const Eigen::Vector3d& p2, const Eigen::Vector3d& p3); + static T energy_eval( + const Eigen::Vector2d& uv1, + const Eigen::Vector2d& uv2, + const Eigen::Vector2d& uv3, + const std::array& m_target_triangle); }; } // namespace energy } // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/utils/AutoDiffTypes.hpp b/src/wmtk/energy/utils/AutoDiffUtils.hpp similarity index 75% rename from src/wmtk/energy/utils/AutoDiffTypes.hpp rename to src/wmtk/energy/utils/AutoDiffUtils.hpp index 54198f46fa..1183e295d8 100644 --- a/src/wmtk/energy/utils/AutoDiffTypes.hpp +++ b/src/wmtk/energy/utils/AutoDiffUtils.hpp @@ -19,15 +19,18 @@ class AutoDiffAllocator }; template -void get_local_vector(const Eigen::MatrixXd& data, const int size, AutoDiffVect& local_vector) +void get_local_vector(const Eigen::MatrixXd& data, const int size, AutoDiffVect& T_vector) { typedef typename AutoDiffVect::Scalar T; DiffScalarBase::setVariableCount(size); const AutoDiffAllocator allocate_auto_diff_scalar; - local_vector.resize(size); + T_vector.resize(size); for (int i = 0; i < size; ++i) { - local_vector(i) = allocate_auto_diff_scalar(i, data(i)); + T_vector(i) = allocate_auto_diff_scalar(i, data(i)); } } +template +void get_double_vector(const AutoDiffVect& T_vector, const int size, Eigen::MatrixXd& double_t) +{} } // namespace energy } // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/utils/DofsToPosition.hpp b/src/wmtk/energy/utils/DofsToPosition.hpp index 6306a21e69..ef4cf18707 100644 --- a/src/wmtk/energy/utils/DofsToPosition.hpp +++ b/src/wmtk/energy/utils/DofsToPosition.hpp @@ -1,5 +1,6 @@ +#pragma once #include -#include +#include #include "autodiff.h" // #include @@ -14,20 +15,22 @@ class DofsToPosition ~DofsToPosition(); template - Eigen::Matrix operator()(const DofVectorX& dof) const + Eigen::Matrix dof_to_pos(const Eigen::Matrix& dof) const { Eigen::Matrix pos; int size = dof.rows(); - typedef Eigen::Matrix Vec2T; - Vec2T dofT; - get_local_vector(dof, size, dofT); + if (size == 2) { + Eigen::Matrix dofT; + get_local_vector>(dof, size, dofT); // TODO retrive position using displacement map // for now just return itself pos << dofT(0), dofT(1), static_cast(0.0); } else //(dofx.size() == 1) { + Eigen::Matrix dofT; + get_local_vector>(dof, size, dofT); // curve parameterization // TODO can also be implemented as a overload operator()? @@ -35,7 +38,7 @@ class DofsToPosition } return pos; } -} +}; } // namespace energy } // namespace wmtk From b6d18cad77ac158420c7baaf606aab4a12dff476 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Fri, 1 Sep 2023 09:50:51 -0400 Subject: [PATCH 021/134] test for amips 3d using double type --- tests/energy/test_2d_energy.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/energy/test_2d_energy.cpp b/tests/energy/test_2d_energy.cpp index b47565f022..f07c3da0e8 100644 --- a/tests/energy/test_2d_energy.cpp +++ b/tests/energy/test_2d_energy.cpp @@ -44,6 +44,20 @@ TEST_CASE("amips2d") AMIPS_2D amips2d(tri_mesh); + REQUIRE(amips2d.energy_eval(e1) == 2.0); + } +} + +TEST_CASE("amips3d") +{ + SECTION("equilateral triangle") + { + const DEBUG_TriMesh example_mesh = single_equilateral_triangle(); + auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); + const TriMesh tri_mesh = static_cast(example_mesh); + + AMIPS_2D amips2d(tri_mesh); + REQUIRE(amips2d.energy_eval(e1) == 2.0); } } \ No newline at end of file From e355cf43d14bd3a286c2abceb2a42e9e4dc7a86e Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 5 Sep 2023 05:21:07 -0400 Subject: [PATCH 022/134] port in image and smapling. compiled --- src/wmtk/CMakeLists.txt | 10 +- src/wmtk/energy/AMIPS.cpp | 72 ++++- src/wmtk/energy/AMIPS.hpp | 19 +- src/wmtk/energy/AreaAccuracy.cpp | 4 + src/wmtk/energy/AreaAccuracy.hpp | 2 + src/wmtk/energy/DifferentiableEnergy.hpp | 3 +- src/wmtk/energy/utils/AutoDiffUtils.hpp | 28 +- src/wmtk/energy/utils/CMakeLists.txt | 2 +- src/wmtk/energy/utils/DofsToPosition.hpp | 21 +- src/wmtk/image/CMakeLists.txt | 12 + src/wmtk/image/Image.cpp | 292 ++++++++++++++++++++ src/wmtk/image/Image.hpp | 109 ++++++++ src/wmtk/image/Sampling.hpp | 78 ++++++ src/wmtk/image/bicubic_interpolation.cpp | 123 +++++++++ src/wmtk/image/bicubic_interpolation.hpp | 83 ++++++ src/wmtk/image/load_image_exr.cpp | 336 +++++++++++++++++++++++ src/wmtk/image/load_image_exr.h | 22 ++ src/wmtk/image/save_image_exr.cpp | 147 ++++++++++ src/wmtk/image/save_image_exr.h | 28 ++ 19 files changed, 1359 insertions(+), 32 deletions(-) create mode 100644 src/wmtk/image/CMakeLists.txt create mode 100644 src/wmtk/image/Image.cpp create mode 100644 src/wmtk/image/Image.hpp create mode 100644 src/wmtk/image/Sampling.hpp create mode 100644 src/wmtk/image/bicubic_interpolation.cpp create mode 100644 src/wmtk/image/bicubic_interpolation.hpp create mode 100644 src/wmtk/image/load_image_exr.cpp create mode 100644 src/wmtk/image/load_image_exr.h create mode 100644 src/wmtk/image/save_image_exr.cpp create mode 100644 src/wmtk/image/save_image_exr.h diff --git a/src/wmtk/CMakeLists.txt b/src/wmtk/CMakeLists.txt index 5d011a5368..34a9315ffd 100644 --- a/src/wmtk/CMakeLists.txt +++ b/src/wmtk/CMakeLists.txt @@ -30,4 +30,12 @@ add_subdirectory(utils) add_subdirectory(attribute) add_subdirectory(operations) add_subdirectory(autogen) -add_subdirectory(energy) \ No newline at end of file +add_subdirectory(energy) +add_subdirectory(image) + +include(stb) +include(tinyexr) +target_link_libraries(wildmeshing_toolkit PUBLIC + miniz # MTAO: I had a build issue with windows not finding miniz at linktime - adding here to make sure it's there? + tinyexr + stb::image) \ No newline at end of file diff --git a/src/wmtk/energy/AMIPS.cpp b/src/wmtk/energy/AMIPS.cpp index 89e261d4cd..1d129637a5 100644 --- a/src/wmtk/energy/AMIPS.cpp +++ b/src/wmtk/energy/AMIPS.cpp @@ -6,6 +6,15 @@ using namespace wmtk::energy; AMIPS::AMIPS(const TriMesh& mesh) : DifferentiableEnergy(mesh) {} +AMIPS_2D::AMIPS_2D(const TriMesh& mesh) + : AMIPS(mesh) +{} + +AMIPS_3DEmbedded::AMIPS_3DEmbedded(const TriMesh& mesh, const image::Image& image) + : AMIPS(mesh) + , m_image(image) + , m_dofs_to_pos(image) +{} double AMIPS_2D::energy_eval(const Tuple& tuple) const { @@ -77,42 +86,73 @@ auto AMIPS_2D::energy_eval( return (F.transpose() * F).trace() / Fdet; } +double AMIPS_3DEmbedded::energy_eval(const Tuple& tuple) const +{ + // get the uv coordinates of the triangle + ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); + Eigen::Vector2d uv1 = pos.vector_attribute(tuple).head<2>(); + Eigen::Vector2d uv2 = + pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))).head<2>(); + Eigen::Vector2d uv3 = + pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))).head<2>(); + + // return the energy + return energy_eval(uv1, uv2, uv3, m_target_triangle, m_dofs_to_pos); +} +DScalar AMIPS_3DEmbedded::energy_eval_autodiff(const Tuple& tuple) const +{ + // get the uv coordinates of the triangle + ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); + + Eigen::Vector2d uv1 = pos.vector_attribute(tuple); + Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); + Eigen::Vector2d uv3 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); + + // return the energy + return energy_eval(uv1, uv2, uv3, m_target_triangle, m_dofs_to_pos); +} template auto AMIPS_3DEmbedded::energy_eval( const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, const Eigen::Vector2d& uv3, - const std::array& m_target_triangle) -> T + const std::array& m_target_triangle, + const DofsToPosition& m_dofs_to_pos) -> T { - DofsToPosition dof2pos; - Eigen::Matrix pos1 = dof2pos.dof_to_pos(uv1); - Eigen::Matrix pos2 = dof2pos.dof_to_pos(uv2); - Eigen::Matrix pos3 = dof2pos.dof_to_pos(uv3); - auto V2_V1 = pos2 - pos1; - auto V3_V1 = pos3 - pos1; + Eigen::Matrix pos1 = m_dofs_to_pos.dof_to_pos(uv1); + Eigen::Matrix pos2 = m_dofs_to_pos.dof_to_pos(uv2); + Eigen::Matrix pos3 = m_dofs_to_pos.dof_to_pos(uv3); + Eigen::Matrix V2_V1; + V2_V1 << pos2(0) - pos1(0), pos2(1) - pos1(1), pos2(2) - pos1(2); + Eigen::Matrix V3_V1; + V3_V1 << pos3(0) - pos1(0), pos3(1) - pos1(1), pos3(2) - pos1(2); // tangent bases // e1 = (V2 - V1).normalize() // e1 = V2_V1.stableNormalized(); assert(V2_V1.norm() > 0); // check norm is not 0 - auto e1 = V2_V1 / V2_V1.norm(); - auto n = V2_V1.cross(V3_V1); - - // if (n.lpNorm().getValue() < std::numeric_limits::denorm_min()) { - // wmtk::logger().critical("n.lpNorm {}", n.lpNorm().getValue()); + Eigen::Matrix e1 = V2_V1 / V2_V1.norm(); + Eigen::Matrix n = V2_V1.cross(V3_V1); + + // #ifdef DEBUG + // Eigen::MatrixXd double_n; + // get_double_vecto(n, 3, double_n); + // if (double_n.lpNorm() < std::numeric_limits::denorm_min()) { + // wmtk::logger().critical("n.lpNorm {}", double_n.lpNorm()); // std::cout << "V1 " << std::endl; - // std::cout << std::hexfloat << x1 << " " << y1 << v1(2) << std::endl; + // std::cout << std::hexfloat << get_value(pos1(0))<< " " << y1 << v1(2) << std::endl; // std::cout << "V2 " << std::endl; // std::cout << std::hexfloat << v2(0) << " " << v2(1) << v2(2) << std::endl; // std::cout << "V3 " << std::endl; // std::cout << std::hexfloat << v3(0) << " " << v3(1) << v3(2) << std::endl; // assert(false); // } + // #endif // n = n.stableNormalized(); assert(n.norm() > 0); // check norm is not 0 n = n / n.norm(); - auto e2 = n.cross(e1); + Eigen::Matrix e2 = n.cross(e1); // Eigen::Matrix e2_stableNormalized = e2.stableNormalized(); assert(e2.norm() > 0); // check norm is not 0 e2 = e2 / e2.norm(); @@ -129,7 +169,9 @@ auto AMIPS_3DEmbedded::energy_eval( // (x2 - x1, y2 - y1, x3 - x1, y2 - y1).transpose Eigen::Matrix Dm; Dm << VT2.x() - VT1.x(), VT3.x() - VT1.x(), VT2.y() - VT1.y(), VT3.y() - VT1.y(); - assert(Dm.determinant().getValue() > 0); + + T Dmdet = Dm.determinant(); + assert(wmtk::energy::get_value(Dmdet) > 0); Eigen::Matrix2d Ds, Dsinv; Eigen::Vector2d target_A, target_B, target_C; diff --git a/src/wmtk/energy/AMIPS.hpp b/src/wmtk/energy/AMIPS.hpp index 782673a854..269543001a 100644 --- a/src/wmtk/energy/AMIPS.hpp +++ b/src/wmtk/energy/AMIPS.hpp @@ -1,6 +1,8 @@ #pragma once -#include +#include #include "DifferentiableEnergy.hpp" +#include "utils/AutoDiffUtils.hpp" +#include "utils/DofsToPosition.hpp" namespace wmtk { namespace energy { class AMIPS : public DifferentiableEnergy @@ -35,9 +37,7 @@ class AMIPS : public DifferentiableEnergy class AMIPS_2D : public AMIPS { public: - AMIPS_2D(const TriMesh& mesh) - : AMIPS(mesh) - {} + AMIPS_2D(const TriMesh& mesh); public: double energy_eval(const Tuple& tuple) const override; @@ -66,9 +66,7 @@ class AMIPS_2D : public AMIPS class AMIPS_3DEmbedded : public AMIPS { public: - AMIPS_3DEmbedded(const TriMesh& mesh) - : AMIPS(mesh) - {} + AMIPS_3DEmbedded(const TriMesh& mesh, const image::Image& image); public: double energy_eval(const Tuple& tuple) const override; @@ -79,7 +77,12 @@ class AMIPS_3DEmbedded : public AMIPS const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, const Eigen::Vector2d& uv3, - const std::array& m_target_triangle); + const std::array& m_target_triangle, + const DofsToPosition& m_dofs_to_pos); + +protected: + image::Image m_image; + DofsToPosition m_dofs_to_pos; }; } // namespace energy } // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/AreaAccuracy.cpp b/src/wmtk/energy/AreaAccuracy.cpp index 97d05e87ec..e9505ff88e 100644 --- a/src/wmtk/energy/AreaAccuracy.cpp +++ b/src/wmtk/energy/AreaAccuracy.cpp @@ -7,3 +7,7 @@ AreaAccuracy::AreaAccuracy(const TriMesh& mesh1, const TriMesh& mesh2) , m_position_mesh(mesh2) , m_3d_position_handle(mesh2.get_attribute_handle("position", PrimitiveType::Vertex)) {} + +template +T energy_eval(const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, const Eigen::Vector2d& uv3) +{} diff --git a/src/wmtk/energy/AreaAccuracy.hpp b/src/wmtk/energy/AreaAccuracy.hpp index 34c10deebe..52400c20e2 100644 --- a/src/wmtk/energy/AreaAccuracy.hpp +++ b/src/wmtk/energy/AreaAccuracy.hpp @@ -1,3 +1,5 @@ +#include +#include #include "DifferentiableEnergy.hpp" namespace wmtk { namespace energy { diff --git a/src/wmtk/energy/DifferentiableEnergy.hpp b/src/wmtk/energy/DifferentiableEnergy.hpp index 2c481df2c7..b56424fade 100644 --- a/src/wmtk/energy/DifferentiableEnergy.hpp +++ b/src/wmtk/energy/DifferentiableEnergy.hpp @@ -1,8 +1,9 @@ +#pragma once #include #include #include #include -#include +// #include #include "Energy.hpp" namespace wmtk { namespace energy { diff --git a/src/wmtk/energy/utils/AutoDiffUtils.hpp b/src/wmtk/energy/utils/AutoDiffUtils.hpp index 1183e295d8..eb471a01d0 100644 --- a/src/wmtk/energy/utils/AutoDiffUtils.hpp +++ b/src/wmtk/energy/utils/AutoDiffUtils.hpp @@ -1,3 +1,4 @@ +#pragma once #include "autodiff.h" namespace wmtk { @@ -18,6 +19,26 @@ class AutoDiffAllocator double operator()(const int i, double v) const { return v; } }; +inline double get_value(float x) +{ + return static_cast(x); +} +inline double get_value(double x) +{ + return x; +} +inline double get_value( + DScalar2, Eigen::Matrix> x) +{ + return x.getValue(); +} + +inline double get_value( + DScalar2, Eigen::Matrix> x) +{ + return x.getValue(); +} + template void get_local_vector(const Eigen::MatrixXd& data, const int size, AutoDiffVect& T_vector) { @@ -31,6 +52,11 @@ void get_local_vector(const Eigen::MatrixXd& data, const int size, AutoDiffVect& } template void get_double_vector(const AutoDiffVect& T_vector, const int size, Eigen::MatrixXd& double_t) -{} +{ + double_t.resize(size, 1); + for (int i = 0; i < size; ++i) { + double_t(i) = T_vector(i).getValue(); + } +} } // namespace energy } // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/utils/CMakeLists.txt b/src/wmtk/energy/utils/CMakeLists.txt index 39256f5889..9c70628599 100644 --- a/src/wmtk/energy/utils/CMakeLists.txt +++ b/src/wmtk/energy/utils/CMakeLists.txt @@ -1,5 +1,5 @@ set(SRC_FILES - AutoDiffTypes.hpp + AutoDiffUtils.hpp autodiff.h autodiff.cpp DofsToPosition.hpp diff --git a/src/wmtk/energy/utils/DofsToPosition.hpp b/src/wmtk/energy/utils/DofsToPosition.hpp index ef4cf18707..1b879ccbc4 100644 --- a/src/wmtk/energy/utils/DofsToPosition.hpp +++ b/src/wmtk/energy/utils/DofsToPosition.hpp @@ -1,8 +1,10 @@ #pragma once #include -#include +#include +#include +#include +#include "AutoDiffUtils.hpp" #include "autodiff.h" -// #include namespace wmtk { namespace energy { @@ -10,9 +12,17 @@ class DofsToPosition { // size = 2: uv position, size =1, t of boundary curve using DofVectorX = Eigen::Matrix; +protected: + wmtk::image::SamplingBicubic m_sampling; + public: - DofsToPosition(); - ~DofsToPosition(); + DofsToPosition() = default; + ~DofsToPosition() = default; + DofsToPosition& operator=(const DofsToPosition&) = default; // copy assignment operator + DofsToPosition& operator=(DofsToPosition&&) = default; // move assignment operator + DofsToPosition(const image::Image& image) + : m_sampling(image) + {} template Eigen::Matrix dof_to_pos(const Eigen::Matrix& dof) const @@ -25,7 +35,8 @@ class DofsToPosition get_local_vector>(dof, size, dofT); // TODO retrive position using displacement map // for now just return itself - pos << dofT(0), dofT(1), static_cast(0.0); + pos << dofT(0), dofT(1), m_sampling.sample_T(dofT(0), dofT(1)); + } else //(dofx.size() == 1) { diff --git a/src/wmtk/image/CMakeLists.txt b/src/wmtk/image/CMakeLists.txt new file mode 100644 index 0000000000..39824b44f6 --- /dev/null +++ b/src/wmtk/image/CMakeLists.txt @@ -0,0 +1,12 @@ +set(SRC_FILES + bicubic_interpolation.hpp + bicubic_interpolation.cpp + Image.cpp + Image.hpp + load_image_exr.cpp + load_image_exr.h + Sampling.hpp + save_image_exr.cpp + save_image_exr.h +) +target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) \ No newline at end of file diff --git a/src/wmtk/image/Image.cpp b/src/wmtk/image/Image.cpp new file mode 100644 index 0000000000..b0ca07fd3b --- /dev/null +++ b/src/wmtk/image/Image.cpp @@ -0,0 +1,292 @@ +#include +#include +#include +#include +#include +#include + +using namespace wmtk; + +using namespace image; +float modulo(double x, double n) +{ + float y = fmod(x, n); + if (y < 0) { + y += n; + } + return y; +} + +unsigned char double_to_unsignedchar(const double d) +{ + return round(std::max(std::min(1., d), 0.) * 255); +} + +int Image::get_coordinate(const int x, const WrappingMode mode) const +{ + auto size = std::max(width(), height()); + assert(-size < x && x < 2 * size); + switch (mode) { + case WrappingMode::REPEAT: return (x + size) % size; + + case WrappingMode::MIRROR_REPEAT: + if (x < 0) + return -(x % size); + else if (x < size) + return x; + else + return size - 1 - (x - size) % size; + + case WrappingMode::CLAMP_TO_EDGE: return std::clamp(x, 0, size - 1); + default: return (x + size) % size; + } +} + +std::pair Image::get_pixel_index(const double& u, const double& v) const +{ + int w = width(); + int h = height(); + auto size = std::max(w, h); + // x, y are between 0 and 1 + auto x = u * size; + auto y = v * size; + const auto sx = static_cast(std::floor(x - 0.5)); + const auto sy = static_cast(std::floor(y - 0.5)); + + return {sx, sy}; +} + +// set an image to have same value as the analytical function and save it to the file given +bool Image::set( + const std::function& f, + WrappingMode mode_x, + WrappingMode mode_y) +{ + int h = height(); + int w = width(); + + m_image.resize(h, w); + + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + double u, v; + u = (static_cast(j) + 0.5) / static_cast(w); + v = (static_cast(i) + 0.5) / static_cast(h); + m_image(i, j) = f(u, v); + } + } + set_wrapping_mode(mode_x, mode_y); + return true; +} + +// save to hdr or exr +bool Image::save(const std::filesystem::path& path) const +{ + wmtk::logger().trace("[save_image_hdr] start \"{}\"", path.string()); + int w = width(); + int h = height(); + std::vector buffer; + buffer.resize(w * h); + + for (auto i = 0; i < h; i++) { + for (auto j = 0; j < w; j++) { + buffer[i * w + j] = m_image(i, j); + } + } + if (path.extension() == ".hdr") { + auto res = stbi_write_hdr(path.string().c_str(), w, h, 1, buffer.data()); + assert(res); + } else if (path.extension() == ".exr") { + auto res = save_image_exr_red_channel(w, h, buffer, path); + } else { + wmtk::logger().trace("[save_image_hdr] format doesn't support \"{}\"", path.string()); + return false; + } + + wmtk::logger().trace("[save_image] done \"{}\"", path.string()); + + return true; +} + +// load from hdr or exr +void Image::load( + const std::filesystem::path& path, + const WrappingMode mode_x, + const WrappingMode mode_y) +{ + int w, h, channels; + channels = 1; + std::vector buffer; + if (path.extension() == ".exr") { + std::tie(w, h, buffer) = load_image_exr_red_channel(path); + assert(!buffer.empty()); + } else if (path.extension() == ".hdr") { + auto res = stbi_loadf(path.string().c_str(), &w, &h, &channels, 1); + buffer.assign(res, res + w * h); + } else { + wmtk::logger().trace("[load_image] format doesn't support \"{}\"", path.string()); + return; + } + + m_image.resize(w, h); + + for (int i = 0, k = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + m_image(i, j) = buffer[k++]; + } + } + m_image.colwise().reverseInPlace(); + set_wrapping_mode(mode_x, mode_y); +} + +// down sample a image to size/2 by size/2 +// used for mipmap construction +Image Image::down_sample() const +{ + auto h = height(); + auto w = width(); + Image low_res_image(h / 2, w / 2); + for (int r = 0; r < h / 2; r++) { + for (int c = 0; c < w / 2; c++) { + low_res_image.set( + r, + c, + (m_image(r * 2, c * 2) + m_image(r * 2, c * 2 + 1) + m_image(r * 2 + 1, c * 2) + + m_image(r * 2 + 1, c * 2 + 1)) / + 4.); + } + } + return low_res_image; +} + +std::array combine_position_normal_texture( + double normalization_scale, + const Eigen::Matrix& offset, + const std::filesystem::path& position_path, + const std::filesystem::path& normal_path, + const std::filesystem::path& height_path, + float min_height, + float max_height) +{ + assert(std::filesystem::exists(position_path)); + auto [w_p, h_p, index_red_p, index_green_p, index_blue_p, buffer_r_p, buffer_g_p, buffer_b_p] = + load_image_exr_split_3channels(position_path); + + auto buffer_size = buffer_r_p.size(); + std::vector buffer_r_d(buffer_size); + std::vector buffer_g_d(buffer_size); + std::vector buffer_b_d(buffer_size); + + if (std::filesystem::exists(normal_path) && std::filesystem::exists(height_path)) { + // Load normal + heightmap and compute displaced positions. + auto + [w_n, + h_n, + index_red_n, + index_green_n, + index_blue_n, + buffer_r_n, + buffer_g_n, + buffer_b_n] = load_image_exr_split_3channels(normal_path); + auto + [w_h, + h_h, + index_red_h, + index_green_h, + index_blue_h, + buffer_r_h, + buffer_g_h, + buffer_b_h] = load_image_exr_split_3channels(height_path); + assert(buffer_r_p.size() == buffer_r_n.size()); + assert(buffer_r_p.size() == buffer_r_h.size()); + assert(buffer_r_p.size() == buffer_g_p.size()); + assert(buffer_r_p.size() == buffer_b_p.size()); + auto scale = [&](float h) { return min_height * (1.f - h) + max_height * h; }; + // displaced = positions * normalization_scale + heights * (2.0 * normals - 1.0) - offset + for (int i = 0; i < buffer_size; i++) { + buffer_r_d[i] = buffer_r_p[i] * normalization_scale + + scale(buffer_r_h[i]) * (2.0 * buffer_r_n[i] - 1.0) - offset[0]; + buffer_g_d[i] = buffer_g_p[i] * normalization_scale + + scale(buffer_g_h[i]) * (2.0 * buffer_g_n[i] - 1.0) - offset[1]; + buffer_b_d[i] = buffer_b_p[i] * normalization_scale + + scale(buffer_b_h[i]) * (2.0 * buffer_b_n[i] - 1.0) - offset[2]; + } + } else { + // Missing heightmap info: we use the position map as our displaced coordinates. + wmtk::logger().info("No heightmap provided: using positions as displaced coordinates."); + // displaced = positions * normalization_scale - offset + for (int i = 0; i < buffer_size; i++) { + buffer_r_d[i] = buffer_r_p[i] * normalization_scale - offset[0]; + buffer_g_d[i] = buffer_g_p[i] * normalization_scale - offset[1]; + buffer_b_d[i] = buffer_b_p[i] * normalization_scale - offset[2]; + } + } + + return { + buffer_to_image(buffer_r_d, w_p, h_p), + buffer_to_image(buffer_g_d, w_p, h_p), + buffer_to_image(buffer_b_d, w_p, h_p), + }; +} + +void split_and_save_3channels(const std::filesystem::path& path) +{ + int w, h, channels, index_red, index_blue, index_green; + channels = 1; + std::vector buffer_r, buffer_g, buffer_b; + if (path.extension() == ".exr") { + std::tie(w, h, index_red, index_green, index_blue, buffer_r, buffer_g, buffer_b) = + load_image_exr_split_3channels(path); + assert(!buffer_r.empty()); + assert(!buffer_g.empty()); + assert(!buffer_b.empty()); + } else { + spdlog::trace("[split_image] format doesn't support \"{}\"", path.string()); + return; + } + const std::filesystem::path directory = path.parent_path(); + const std::string file = path.stem().string(); + const std::filesystem::path path_r = directory / (file + "_r.exr"); + const std::filesystem::path path_g = directory / (file + "_g.exr"); + const std::filesystem::path path_b = directory / (file + "_b.exr"); + // just saves single channel data to red channel + auto res = save_image_exr_red_channel(w, h, buffer_r, path_r); + assert(res); + res = save_image_exr_red_channel(w, h, buffer_g, path_g); + assert(res); + res = save_image_exr_red_channel(w, h, buffer_b, path_b); + assert(res); +} + +Image buffer_to_image(const std::vector& buffer, int w, int h) +{ + Image image(w, h); + for (int i = 0, k = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + image.set(h - i - 1, j, buffer[k++]); + } + } + return image; +} + +std::array load_rgb_image(const std::filesystem::path& path) +{ + int w, h, channels, index_red, index_blue, index_green; + channels = 1; + std::vector buffer_r, buffer_g, buffer_b; + if (path.extension() == ".exr") { + std::tie(w, h, index_red, index_green, index_blue, buffer_r, buffer_g, buffer_b) = + load_image_exr_split_3channels(path); + assert(!buffer_r.empty()); + assert(!buffer_g.empty()); + assert(!buffer_b.empty()); + } else { + wmtk::logger().error("[load_rgb_image] format doesn't support \"{}\"", path.string()); + exit(-1); + } + return { + wmtk::image::buffer_to_image(buffer_r, w, h), + wmtk::image::buffer_to_image(buffer_g, w, h), + wmtk::image::buffer_to_image(buffer_b, w, h), + }; +} diff --git a/src/wmtk/image/Image.hpp b/src/wmtk/image/Image.hpp new file mode 100644 index 0000000000..022277ec5a --- /dev/null +++ b/src/wmtk/image/Image.hpp @@ -0,0 +1,109 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bicubic_interpolation.hpp" +#include "load_image_exr.h" +#include "save_image_exr.h" + +namespace wmtk { +namespace image { +class Image +{ + using DScalar = DScalar2; + using ImageMatrixf = + Eigen::Matrix; + +protected: + ImageMatrixf m_image; // saving scanline images + WrappingMode m_mode_x = WrappingMode::CLAMP_TO_EDGE; + WrappingMode m_mode_y = WrappingMode::CLAMP_TO_EDGE; + +public: + Image() = default; + Image(int height_, int width_) { m_image.resize(height_, width_); }; + + ImageMatrixf& ref_raw_image() { return m_image; } + const ImageMatrixf& get_raw_image() const { return m_image; } + +public: + // point coordinates between [0, 1] + int width() const { return static_cast(m_image.cols()); }; + int height() const { return static_cast(m_image.rows()); }; + + template + std::decay_t get(const T& u, const T& v) const; + float get_pixel(const int i, const int j) const { return m_image(i, j); }; + std::pair get_pixel_index(const double& u, const double& v) const; + int get_coordinate(const int x, const WrappingMode mode) const; + WrappingMode get_wrapping_mode_x() const { return m_mode_x; }; + WrappingMode get_wrapping_mode_y() const { return m_mode_y; }; + bool set( + const std::function& f, + const WrappingMode mode_x = WrappingMode::CLAMP_TO_EDGE, + const WrappingMode mode_y = WrappingMode::CLAMP_TO_EDGE); + bool set(const int r, const int c, const float v) + { + m_image(r, c) = v; + return true; + }; + bool save(const std::filesystem::path& path) const; + void load(const std::filesystem::path& path, WrappingMode mode_x, WrappingMode mode_y); + + void set_wrapping_mode(WrappingMode mode_x, WrappingMode mode_y) + { + m_mode_x = mode_x; + m_mode_y = mode_y; + }; + Image down_sample() const; +}; + +/// @brief +/// @param p coordinates between (0,1) +/// @return / +template +std::decay_t Image::get(const T& u, const T& v) const +{ + int w = width(); + int h = height(); + auto size = std::max(w, h); + // x, y are between 0 and 1 + auto x = u * static_cast>(size); + auto y = v * static_cast>(size); + // use bicubic interpolation + + BicubicVector sample_vector = extract_samples( + static_cast(w), + static_cast(h), + m_image.data(), + get_value(x), + get_value(y), + m_mode_x, + m_mode_y); + BicubicVector bicubic_coeff = get_bicubic_matrix() * sample_vector; + return eval_bicubic_coeffs(bicubic_coeff, x, y); +}; + +void split_and_save_3channels(const std::filesystem::path& path); + +Image buffer_to_image(const std::vector& buffer, int w, int h); + +std::array load_rgb_image(const std::filesystem::path& path); + +std::array combine_position_normal_texture( + double normalization_scale, + const Eigen::Matrix& offset, + const std::filesystem::path& position_path, + const std::filesystem::path& normal_path, + const std::filesystem::path& texture_path, + float min_height = 0.f, + float max_height = 1.f); +} // namespace image +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/image/Sampling.hpp b/src/wmtk/image/Sampling.hpp new file mode 100644 index 0000000000..0365454d6d --- /dev/null +++ b/src/wmtk/image/Sampling.hpp @@ -0,0 +1,78 @@ +#pragma once +#include +#include "Image.hpp" +#include "bicubic_interpolation.hpp" + +namespace wmtk { +namespace image { +enum class SAMPLING_MODE { BICUBIC, SPLINE }; +class Sampling +{ +public: + using DScalar = DScalar2; + virtual ~Sampling(){}; + +public: + virtual double sample(const double u, const double v) const = 0; + virtual DScalar sample(const DScalar& u, const DScalar& v) const = 0; +}; + +template +class SamplingImage : public Sampling +{ +protected: + const Image& m_image; + +public: + SamplingImage(const Image& img) + : m_image(img) + { + assert(m_image.width() == m_image.height()); + assert(m_image.width() != 0); + } + +public: + double sample(const double u, const double v) const override + { + return static_cast(this)->sample(u, v); + } + DScalar sample(const DScalar& u, const DScalar& v) const override + { + return static_cast(this)->sample(u, v); + } +}; + +class SamplingBicubic : public SamplingImage +{ +public: + using Super = SamplingImage; + using Super::Super; + template + T sample_T(T u, T v) const + { + auto w = m_image.width(); + auto h = m_image.height(); + // x, y are between 0 and 1 + T x = u * static_cast>(w); + T y = v * static_cast>(h); + + // use bicubic interpolation + BicubicVector sample_vector = extract_samples( + static_cast(w), + static_cast(h), + m_image.get_raw_image().data(), + wmtk::image::get_value(x), + wmtk::image::get_value(y), + m_image.get_wrapping_mode_x(), + m_image.get_wrapping_mode_y()); + BicubicVector bicubic_coeff = get_bicubic_matrix() * sample_vector; + return eval_bicubic_coeffs(bicubic_coeff, x, y); + } + double sample(const double u, const double v) const override { return sample_T(u, v); } + DScalar sample(const DScalar& u, const DScalar& v) const override + { + return sample_T(u, v); + } +}; +} // namespace image +} // namespace wmtk diff --git a/src/wmtk/image/bicubic_interpolation.cpp b/src/wmtk/image/bicubic_interpolation.cpp new file mode 100644 index 0000000000..1dd94584f4 --- /dev/null +++ b/src/wmtk/image/bicubic_interpolation.cpp @@ -0,0 +1,123 @@ +#include "bicubic_interpolation.hpp" + +#include +#include +using namespace wmtk; +using namespace wmtk::image; +wmtk::image::BicubicVector wmtk::image::extract_samples( + const size_t width, + const size_t height, + const float* buffer, + const double sx_, + const double sy_, + const WrappingMode mode_x, + const WrappingMode mode_y) +{ + BicubicVector samples; + + const auto get_coordinate = [](const int x, const int size, const WrappingMode mode) -> int { + switch (mode) { + case WrappingMode::REPEAT: return (x + size) % size; + + case WrappingMode::MIRROR_REPEAT: + if (x < 0) + return -(x % size); + else if (x < size) + return x; + else + return size - 1 - (x - size) % size; + case WrappingMode::CLAMP_TO_EDGE: return std::clamp(x, 0, size - 1); + default: return (x + size) % size; + } + }; + const auto get_buffer_value = [&](int xx, int yy) -> float { + xx = get_coordinate(xx, static_cast(width), mode_x); + yy = get_coordinate(yy, static_cast(height), mode_y); + const int index = (yy % height) * width + (xx % width); + return buffer[index]; + }; + + const auto sx = static_cast(std::floor(sx_ - 0.5)); + const auto sy = static_cast(std::floor(sy_ - 0.5)); + + samples(0) = get_buffer_value(sx - 1, sy - 1); + samples(1) = get_buffer_value(sx, sy - 1); + samples(2) = get_buffer_value(sx + 1, sy - 1); + samples(3) = get_buffer_value(sx + 2, sy - 1); + + samples(4) = get_buffer_value(sx - 1, sy); + samples(5) = get_buffer_value(sx, sy); + samples(6) = get_buffer_value(sx + 1, sy); + samples(7) = get_buffer_value(sx + 2, sy); + + samples(8) = get_buffer_value(sx - 1, sy + 1); + samples(9) = get_buffer_value(sx, sy + 1); + samples(10) = get_buffer_value(sx + 1, sy + 1); + samples(11) = get_buffer_value(sx + 2, sy + 1); + + samples(12) = get_buffer_value(sx - 1, sy + 2); + samples(13) = get_buffer_value(sx, sy + 2); + samples(14) = get_buffer_value(sx + 1, sy + 2); + samples(15) = get_buffer_value(sx + 2, sy + 2); + + return samples; +} + +wmtk::image::BicubicMatrix wmtk::image::make_samples_to_bicubic_coeffs_operator() +{ + BicubicMatrix ope; + Eigen::Index row = 0; + for (float yy = -1; yy < 3; yy++) + for (float xx = -1; xx < 3; xx++) { + ope(row, 0) = 1; + ope(row, 1) = xx; + ope(row, 2) = xx * xx; + ope(row, 3) = xx * xx * xx; + + ope(row, 4) = yy; + ope(row, 5) = xx * yy; + ope(row, 6) = xx * xx * yy; + ope(row, 7) = xx * xx * xx * yy; + + ope(row, 8) = yy * yy; + ope(row, 9) = xx * yy * yy; + ope(row, 10) = xx * xx * yy * yy; + ope(row, 11) = xx * xx * xx * yy * yy; + + ope(row, 12) = yy * yy * yy; + ope(row, 13) = xx * yy * yy * yy; + ope(row, 14) = xx * xx * yy * yy * yy; + ope(row, 15) = xx * xx * xx * yy * yy * yy; + + row++; + } + + { + std::stringstream ss; + ss << ope << std::endl; + spdlog::debug("ope det {}\n{}", ope.determinant(), ss.str()); + } + + // invert operator + BicubicMatrix ope_inv = ope.inverse(); + + // prune "zeros" + ope_inv = ope_inv.unaryExpr([](const float& xx) { return fabs(xx) < 1e-5f ? 0 : xx; }); + + { + std::stringstream ss; + ss << ope_inv << std::endl; + spdlog::debug("ope_inv det {}\n{}", ope_inv.determinant(), ss.str()); + } + + // double check inverse property + assert((ope * ope_inv - BicubicMatrix::Identity()).array().abs().maxCoeff() < 1e-5); + + return ope_inv; +} + +const wmtk::image::BicubicMatrix& wmtk::image::get_bicubic_matrix() +{ + static BicubicMatrix mat = wmtk::image::make_samples_to_bicubic_coeffs_operator(); + return mat; +} diff --git a/src/wmtk/image/bicubic_interpolation.hpp b/src/wmtk/image/bicubic_interpolation.hpp new file mode 100644 index 0000000000..18016eca96 --- /dev/null +++ b/src/wmtk/image/bicubic_interpolation.hpp @@ -0,0 +1,83 @@ +#pragma once + +#include +#include +#include +#include +enum class WrappingMode { REPEAT, MIRROR_REPEAT, CLAMP_TO_EDGE }; +namespace wmtk { +namespace image { +inline double get_value(float x) +{ + return static_cast(x); +} +inline double get_value(double x) +{ + return x; +} +inline double get_value( + DScalar2, Eigen::Matrix> x) +{ + return x.getValue(); +} + +inline double get_value( + DScalar2, Eigen::Matrix> x) +{ + return x.getValue(); +} + +template +using BicubicVector = Eigen::Matrix; +using BicubicMatrix = Eigen::Matrix; + +BicubicVector extract_samples( + const size_t width, + const size_t height, + const float* buffer, + const double xx, + const double yy, + const WrappingMode mode_x, + const WrappingMode mode_y); + +BicubicMatrix make_samples_to_bicubic_coeffs_operator(); + +const BicubicMatrix& get_bicubic_matrix(); + +template +std::decay_t eval_bicubic_coeffs(const BicubicVector& coeffs, const T& sx, const T& sy) +{ + using ImageScalar = std::decay_t; + + const auto xx = sx - (floor(get_value(sx) - 0.5f) + 0.5f); + const auto yy = sy - (floor(get_value(sy) - 0.5f) + 0.5f); + assert(0 <= get_value(xx) && get_value(xx) < 1); + assert(0 <= get_value(yy) && get_value(yy) < 1); + + BicubicVector vv; + + vv(0) = 1; + vv(1) = xx; + vv(2) = xx * xx; + vv(3) = xx * xx * xx; + + vv(4) = yy; + vv(5) = xx * yy; + vv(6) = xx * xx * yy; + vv(7) = xx * xx * xx * yy; + + vv(8) = yy * yy; + vv(9) = xx * yy * yy; + vv(10) = xx * xx * yy * yy; + vv(11) = xx * xx * xx * yy * yy; + + vv(12) = yy * yy * yy; + vv(13) = xx * yy * yy * yy; + vv(14) = xx * xx * yy * yy * yy; + vv(15) = xx * xx * xx * yy * yy * yy; + + return coeffs.cast().dot(vv); +} +} // namespace image + +} // namespace wmtk diff --git a/src/wmtk/image/load_image_exr.cpp b/src/wmtk/image/load_image_exr.cpp new file mode 100644 index 0000000000..53ebada184 --- /dev/null +++ b/src/wmtk/image/load_image_exr.cpp @@ -0,0 +1,336 @@ +#include "load_image_exr.h" + +#include +#define TINYEXR_USE_MINIZ 0 +#define TINYEXR_USE_STB_ZLIB 1 +#define TINYEXR_IMPLEMENTATION +#include +#include +#include +using namespace wmtk; +using namespace wmtk::image; +auto load_image_exr_red_channel(const std::filesystem::path& path) + -> std::tuple> +{ + using namespace wmtk; + wmtk::logger().debug("[load_image_exr_red_channel] start \"{}\"", path.string()); + assert(std::filesystem::exists(path)); + const std::string filename_ = path.string(); + const char* filename = filename_.c_str(); + + const auto exr_version = [&filename, &path]() -> EXRVersion { // parse version + EXRVersion exr_version_; + + const auto ret = ParseEXRVersionFromFile(&exr_version_, filename); + if (ret != TINYEXR_SUCCESS) { + wmtk::logger().error("failed LoadImageEXR \"{}\" \"version error\"", path.string()); + throw std::runtime_error("LoadImageEXRError"); + } + + if (exr_version_.multipart || exr_version_.non_image) { + wmtk::logger().error( + "failed LoadImageEXR \"{}\" \"multipart or non image\"", + path.string()); + throw std::runtime_error("LoadImageEXRError"); + } + + return exr_version_; + }(); + + auto exr_header_data = + [&filename, &path, &exr_version]() -> std::tuple { // parse header + EXRHeader exr_header_; + InitEXRHeader(&exr_header_); + + [[maybe_unused]] const char* err = nullptr; + const auto ret = ParseEXRHeaderFromFile(&exr_header_, &exr_version, filename, &err); + if (ret != TINYEXR_SUCCESS) { + wmtk::logger().error("failed LoadImageEXR \"{}\" \"header error\"", path.string()); + FreeEXRHeader(&exr_header_); + throw std::runtime_error("LoadImageEXRError"); + } + + // sanity check, only support all channels are the same type + for (int i = 0; i < exr_header_.num_channels; i++) { + if (exr_header_.pixel_types[i] != exr_header_.pixel_types[0] || + exr_header_.requested_pixel_types[i] != exr_header_.pixel_types[i]) { + wmtk::logger().error( + "failed LoadImageEXR \"{}\" \"inconsistent pixel_types\"", + path.string()); + FreeEXRHeader(&exr_header_); + throw std::runtime_error("LoadImageEXRError"); + } + } + + // read HALF channel as FLOAT. + for (int i = 0; i < exr_header_.num_channels; i++) { + if (exr_header_.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) { + exr_header_.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; + } + } + + // only FLOAT are supported + if (exr_header_.requested_pixel_types[0] != TINYEXR_PIXELTYPE_FLOAT) { + wmtk::logger().error( + "failed LoadImageEXR \"{}\" \"only float exr are supported\"", + path.string()); + FreeEXRHeader(&exr_header_); + throw std::runtime_error("LoadImageEXRError"); + } + + // only non tiled image are supported + if (exr_header_.tiled) { + wmtk::logger().error( + "failed LoadImageEXR \"{}\" \"only non tiled exr are supported\"", + path.string()); + FreeEXRHeader(&exr_header_); + throw std::runtime_error("LoadImageEXRError"); + } + + + int index_red_ = -1; + for (int i = 0; i < exr_header_.num_channels; i++) { + if (strcmp(exr_header_.channels[i].name, "R") == 0) index_red_ = i; + } + if (index_red_ < 0) { + wmtk::logger().warn("Could not find R channel. Looking for Y channel instead."); + for (int i = 0; i < exr_header_.num_channels; i++) { + if (strcmp(exr_header_.channels[i].name, "Y") == 0) index_red_ = i; + } + } + + if (index_red_ < 0) { + std::vector channels; + for (int i = 0; i < exr_header_.num_channels; i++) { + channels.push_back(exr_header_.channels[i].name); + } + wmtk::logger().error( + "failed LoadImageEXR \"{}\" can't find all expected channels: [{}]", + path.string(), + fmt::join(channels, ",")); + FreeEXRHeader(&exr_header_); + throw std::runtime_error("LoadImageEXRError"); + } + + return {exr_header_, index_red_}; + }(); + auto& exr_header = std::get<0>(exr_header_data); + const auto& index_data = std::get<1>(exr_header_data); + + auto exr_image = [&filename, &path, &exr_header]() -> EXRImage { + EXRImage exr_image_; + InitEXRImage(&exr_image_); + + [[maybe_unused]] const char* err = nullptr; + const auto ret = LoadEXRImageFromFile(&exr_image_, &exr_header, filename, &err); + if (ret != TINYEXR_SUCCESS) { + wmtk::logger().error( + "failed LoadImageEXR \"{}\" \"failed to load image data\"", + path.string()); + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image_); + throw std::runtime_error("LoadImageEXRError"); + } + + return exr_image_; + }(); + + wmtk::logger().debug( + "[load_image_exr_red_channel] num_channels {} tiled {}", + exr_header.num_channels, + exr_header.tiled); + wmtk::logger().debug("[load_image_exr_red_channel] index_data {}", index_data); + assert(index_data >= 0); + assert(!exr_header.tiled); + + std::vector data_r; + data_r.reserve(static_cast(exr_image.width) * static_cast(exr_image.height)); + + const auto images = reinterpret_cast(exr_image.images); + for (int i = 0; i < exr_image.width * exr_image.height; i++) + data_r.emplace_back(images[index_data][i]); + + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image); + + wmtk::logger().debug("[load_image_exr_red_channel] done \"{}\"", path.string()); + + return { + static_cast(exr_image.width), + static_cast(exr_image.height), + std::move(data_r), + }; +} + +auto load_image_exr_split_3channels(const std::filesystem::path& path) -> std:: + tuple, std::vector, std::vector> +{ + using namespace wmtk; + wmtk::logger().debug("[load_image_exr_red_channel] start \"{}\"", path.string()); + assert(std::filesystem::exists(path)); + const std::string filename_ = path.string(); + const char* filename = filename_.c_str(); + + const auto exr_version = [&filename, &path]() -> EXRVersion { // parse version + EXRVersion exr_version_; + + const auto ret = ParseEXRVersionFromFile(&exr_version_, filename); + if (ret != TINYEXR_SUCCESS) { + wmtk::logger().error("failed LoadImageEXR \"{}\" \"version error\"", path.string()); + throw std::runtime_error("LoadImageEXRError"); + } + + if (exr_version_.multipart || exr_version_.non_image) { + wmtk::logger().error( + "failed LoadImageEXR \"{}\" \"multipart or non image\"", + path.string()); + throw std::runtime_error("LoadImageEXRError"); + } + + return exr_version_; + }(); + + auto exr_header_data = + [&filename, &path, &exr_version]() -> std::tuple { // parse header + EXRHeader exr_header_; + InitEXRHeader(&exr_header_); + + [[maybe_unused]] const char* err = nullptr; + const auto ret = ParseEXRHeaderFromFile(&exr_header_, &exr_version, filename, &err); + if (ret != TINYEXR_SUCCESS) { + wmtk::logger().error("failed LoadImageEXR \"{}\" \"header error\"", path.string()); + FreeEXRHeader(&exr_header_); + throw std::runtime_error("LoadImageEXRError"); + } + + // sanity check, only support all channels are the same type + for (int i = 0; i < exr_header_.num_channels; i++) { + if (exr_header_.pixel_types[i] != exr_header_.pixel_types[0] || + exr_header_.requested_pixel_types[i] != exr_header_.pixel_types[i]) { + wmtk::logger().error( + "failed LoadImageEXR \"{}\" \"inconsistent pixel_types\"", + path.string()); + FreeEXRHeader(&exr_header_); + throw std::runtime_error("LoadImageEXRError"); + } + } + + // read HALF channel as FLOAT. + for (int i = 0; i < exr_header_.num_channels; i++) { + if (exr_header_.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) { + exr_header_.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; + } + } + + // only FLOAT are supported + if (exr_header_.requested_pixel_types[0] != TINYEXR_PIXELTYPE_FLOAT) { + wmtk::logger().error( + "failed LoadImageEXR \"{}\" \"only float exr are supported\"", + path.string()); + FreeEXRHeader(&exr_header_); + throw std::runtime_error("LoadImageEXRError"); + } + + // only non tiled image are supported + if (exr_header_.tiled) { + wmtk::logger().error( + "failed LoadImageEXR \"{}\" \"only non tiled exr are supported\"", + path.string()); + FreeEXRHeader(&exr_header_); + throw std::runtime_error("LoadImageEXRError"); + } + + + int index_red_ = -1; + int index_green_ = -1; + int index_blue_ = -1; + if (exr_header_.num_channels == 1) { + wmtk::logger().warn("Treat grayscale image as RGB: {}", path.string()); + index_red_ = 0; + index_green_ = 0; + index_blue_ = 0; + } else { + for (int i = 0; i < exr_header_.num_channels; i++) { + if (strcmp(exr_header_.channels[i].name, "R") == 0) index_red_ = i; + if (strcmp(exr_header_.channels[i].name, "G") == 0) index_green_ = i; + if (strcmp(exr_header_.channels[i].name, "B") == 0) index_blue_ = i; + } + } + + if (index_red_ < 0) { + std::vector channels; + for (int i = 0; i < exr_header_.num_channels; i++) { + channels.push_back(exr_header_.channels[i].name); + } + wmtk::logger().error( + "failed LoadImageEXR \"{}\" can't find all 3 expected channels: [{}]", + path.string(), + fmt::join(channels, ",")); + FreeEXRHeader(&exr_header_); + throw std::runtime_error("LoadImageEXRError"); + } + + return {exr_header_, index_red_, index_green_, index_blue_}; + }(); + auto& exr_header = std::get<0>(exr_header_data); + const auto& index_red = std::get<1>(exr_header_data); + const auto& index_green = std::get<2>(exr_header_data); + const auto& index_blue = std::get<3>(exr_header_data); + + auto exr_image = [&filename, &path, &exr_header]() -> EXRImage { + EXRImage exr_image_; + InitEXRImage(&exr_image_); + + [[maybe_unused]] const char* err = nullptr; + const auto ret = LoadEXRImageFromFile(&exr_image_, &exr_header, filename, &err); + if (ret != TINYEXR_SUCCESS) { + wmtk::logger().error( + "failed LoadImageEXR \"{}\" \"failed to load image data\"", + path.string()); + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image_); + throw std::runtime_error("LoadImageEXRError"); + } + + return exr_image_; + }(); + + wmtk::logger().debug( + "[load_image_exr_3channels] num_channels {} tiled {}", + exr_header.num_channels, + exr_header.tiled); + wmtk::logger().debug("[load_image_exr_3channels] index_red {}", index_red); + wmtk::logger().debug("[load_image_exr_3channels] index_green {}", index_green); + wmtk::logger().debug("[load_image_exr_3channels] index_blue {}", index_blue); + assert(index_red >= 0); + assert(index_green >= 0); + assert(index_blue >= 0); + assert(!exr_header.tiled); + + std::vector data_r; + std::vector data_g; + std::vector data_b; + data_r.reserve(static_cast(exr_image.width) * static_cast(exr_image.height)); + data_g.reserve(static_cast(exr_image.width) * static_cast(exr_image.height)); + data_b.reserve(static_cast(exr_image.width) * static_cast(exr_image.height)); + + const auto images = reinterpret_cast(exr_image.images); + for (int i = 0; i < exr_image.width * exr_image.height; i++) { + data_r.emplace_back(images[index_red][i]); + data_g.emplace_back(images[index_green][i]); + data_b.emplace_back(images[index_blue][i]); + } + FreeEXRHeader(&exr_header); + FreeEXRImage(&exr_image); + + wmtk::logger().debug("[load_image_exr_3channels] done \"{}\"", path.string()); + + return {static_cast(exr_image.width), + static_cast(exr_image.height), + std::move(index_red), + std::move(index_green), + std::move(index_blue), + std::move(data_r), + std::move(data_g), + std::move(data_b)}; +} diff --git a/src/wmtk/image/load_image_exr.h b/src/wmtk/image/load_image_exr.h new file mode 100644 index 0000000000..71b8f3f6e2 --- /dev/null +++ b/src/wmtk/image/load_image_exr.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include +#include +namespace wmtk { +namespace image { + +auto load_image_exr_red_channel(const std::filesystem::path& path) + -> std::tuple>; + +auto load_image_exr_split_3channels(const std::filesystem::path& path) -> std::tuple< + size_t, + size_t, + int, + int, + int, + std::vector, + std::vector, + std::vector>; +} // namespace image +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/image/save_image_exr.cpp b/src/wmtk/image/save_image_exr.cpp new file mode 100644 index 0000000000..61fa4ab77b --- /dev/null +++ b/src/wmtk/image/save_image_exr.cpp @@ -0,0 +1,147 @@ +#include "save_image_exr.h" +#include +#include +#include +using namespace wmtk; +using namespace wmtk::image; +bool save_image_exr_red_channel( + size_t width, + size_t height, + const std::vector& data, + const std::filesystem::path& path) +{ + EXRHeader header; + InitEXRHeader(&header); + + EXRImage image; + InitEXRImage(&image); + + image.num_channels = 3; + + std::vector images[3]; + images[0].resize(width * height); + images[1].resize(width * height); + images[2].resize(width * height); + + // Split RGBRGBRGB... into R, G and B layer + for (int i = 0; i < width * height; i++) { + images[0][i] = data[i]; + images[1][i] = -1.; + images[2][i] = -1.; + } + + float* image_ptr[3]; + image_ptr[0] = &(images[2].at(0)); // B + image_ptr[1] = &(images[1].at(0)); // G + image_ptr[2] = &(images[0].at(0)); // R + + image.images = (unsigned char**)image_ptr; + image.width = width; + image.height = height; + + header.num_channels = 3; + header.channels = (EXRChannelInfo*)malloc(sizeof(EXRChannelInfo) * header.num_channels); + // Must be (A)BGR order, since most of EXR viewers expect this channel order. + strncpy(header.channels[0].name, "B", 255); + header.channels[0].name[strlen("B")] = '\0'; + strncpy(header.channels[1].name, "G", 255); + header.channels[1].name[strlen("G")] = '\0'; + strncpy(header.channels[2].name, "R", 255); + header.channels[2].name[strlen("R")] = '\0'; + + header.pixel_types = (int*)malloc(sizeof(int) * header.num_channels); + header.requested_pixel_types = (int*)malloc(sizeof(int) * header.num_channels); + for (int i = 0; i < header.num_channels; i++) { + header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image + header.requested_pixel_types[i] = + TINYEXR_PIXELTYPE_HALF; // pixel type of output image to be stored in .EXR + } + + const char* err = NULL; // or nullptr in C++11 or later. + int ret = SaveEXRImageToFile(&image, &header, path.string().data(), &err); + if (ret != TINYEXR_SUCCESS) { + fprintf(stderr, "Save EXR err: %s\n", err); + FreeEXRErrorMessage(err); // free's buffer for an error message + return ret; + } + wmtk::logger().debug("Saved exr file. {} ", path); + + free(header.channels); + free(header.pixel_types); + free(header.requested_pixel_types); + return 0; +} + +bool save_image_exr_3channels( + size_t width, + size_t height, + int r, + int g, + int b, + const std::vector& data_r, + const std::vector& data_g, + const std::vector& data_b, + const std::filesystem::path& path) +{ + EXRHeader header; + InitEXRHeader(&header); + + EXRImage image; + InitEXRImage(&image); + + image.num_channels = 3; + + std::vector images[3]; + images[0].resize(width * height); + images[1].resize(width * height); + images[2].resize(width * height); + + // Split RGBRGBRGB... into R, G and B layer + for (int i = 0; i < width * height; i++) { + images[r][i] = data_r[i]; + images[g][i] = data_g[i]; + images[b][i] = data_b[i]; + } + wmtk::logger() + .info("[save r {} {}, g {} {} b {} {}]", r, images[r][0], g, images[g][0], b, images[b][0]); + float* image_ptr[3]; + image_ptr[0] = &(images[2].at(0)); // B + image_ptr[1] = &(images[1].at(0)); // G + image_ptr[2] = &(images[0].at(0)); // R + + image.images = (unsigned char**)image_ptr; + image.width = width; + image.height = height; + + header.num_channels = 3; + header.channels = (EXRChannelInfo*)malloc(sizeof(EXRChannelInfo) * header.num_channels); + // Must be (A)BGR order, since most of EXR viewers expect this channel order. + strncpy(header.channels[0].name, "B", 255); + header.channels[0].name[strlen("B")] = '\0'; + strncpy(header.channels[1].name, "G", 255); + header.channels[1].name[strlen("G")] = '\0'; + strncpy(header.channels[2].name, "R", 255); + header.channels[2].name[strlen("R")] = '\0'; + + header.pixel_types = (int*)malloc(sizeof(int) * header.num_channels); + header.requested_pixel_types = (int*)malloc(sizeof(int) * header.num_channels); + for (int i = 0; i < header.num_channels; i++) { + header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image + header.requested_pixel_types[i] = + TINYEXR_PIXELTYPE_HALF; // pixel type of output image to be stored in .EXR + } + + const char* err = NULL; // or nullptr in C++11 or later. + int ret = SaveEXRImageToFile(&image, &header, path.string().data(), &err); + if (ret != TINYEXR_SUCCESS) { + fprintf(stderr, "Save EXR err: %s\n", err); + FreeEXRErrorMessage(err); // free's buffer for an error message + return ret; + } + printf("Saved exr file 3 channels. [ %s ] \n", path.c_str()); + + free(header.channels); + free(header.pixel_types); + free(header.requested_pixel_types); + return 0; +} diff --git a/src/wmtk/image/save_image_exr.h b/src/wmtk/image/save_image_exr.h new file mode 100644 index 0000000000..cfde4a476a --- /dev/null +++ b/src/wmtk/image/save_image_exr.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include +#include +#include +#define TINYEXR_USE_MINIZ 0 +#define TINYEXR_USE_STB_ZLIB 1 +// #define TINYEXR_IMPLEMENTATION +namespace wmtk { +namespace image { +bool save_image_exr_red_channel( + size_t weigth, + size_t height, + const std::vector& data, + const std::filesystem::path& path); +bool save_image_exr_3channels( + size_t width, + size_t height, + int r, + int g, + int b, + const std::vector& data_r, + const std::vector& data_g, + const std::vector& data_b, + const std::filesystem::path& path); +} // namespace image +} // namespace wmtk \ No newline at end of file From af372b781df9ac2555ff795a69edbb0ad091d291 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 6 Sep 2023 09:00:35 -0400 Subject: [PATCH 023/134] new dofstoposition and sampling with analytic function and templated constructor finally compiled --- src/wmtk/energy/utils/DofsToPosition.hpp | 40 +++++++++++----- src/wmtk/image/Sampling.hpp | 59 +++++++++++++++++++++++- 2 files changed, 86 insertions(+), 13 deletions(-) diff --git a/src/wmtk/energy/utils/DofsToPosition.hpp b/src/wmtk/energy/utils/DofsToPosition.hpp index 1b879ccbc4..5c4d2d3197 100644 --- a/src/wmtk/energy/utils/DofsToPosition.hpp +++ b/src/wmtk/energy/utils/DofsToPosition.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -8,44 +9,59 @@ namespace wmtk { namespace energy { +template class DofsToPosition { // size = 2: uv position, size =1, t of boundary curve using DofVectorX = Eigen::Matrix; protected: - wmtk::image::SamplingBicubic m_sampling; + std::unique_ptr m_sampling; public: DofsToPosition() = default; ~DofsToPosition() = default; DofsToPosition& operator=(const DofsToPosition&) = default; // copy assignment operator DofsToPosition& operator=(DofsToPosition&&) = default; // move assignment operator + + DofsToPosition(std::function f) + { + m_sampling = std::make_unique>(f); + } + + /** + * @brief Construct a new Dofs To Position object using a displacement map (requires a sampler) + * + * @param image + */ DofsToPosition(const image::Image& image) - : m_sampling(image) - {} + { + m_sampling = std::make_unique>(image); + } + - template - Eigen::Matrix dof_to_pos(const Eigen::Matrix& dof) const + Eigen::Matrix dof_to_pos( + const Eigen::Matrix& dof) const { - Eigen::Matrix pos; + Eigen::Matrix pos; int size = dof.rows(); if (size == 2) { - Eigen::Matrix dofT; - get_local_vector>(dof, size, dofT); + Eigen::Matrix dofT; + get_local_vector>(dof, size, dofT); // TODO retrive position using displacement map // for now just return itself - pos << dofT(0), dofT(1), m_sampling.sample_T(dofT(0), dofT(1)); + pos << static_cast(dofT(0)), static_cast(dofT(1)), + m_sampling->sample(dofT(0), dofT(1)); } else //(dofx.size() == 1) { - Eigen::Matrix dofT; - get_local_vector>(dof, size, dofT); + Eigen::Matrix dofT; + get_local_vector>(dof, size, dofT); // curve parameterization // TODO can also be implemented as a overload operator()? - pos << dofT(0), static_cast(0.0), static_cast(0.0); + pos << dofT(0), static_cast(0.0), static_cast(0.0); } return pos; } diff --git a/src/wmtk/image/Sampling.hpp b/src/wmtk/image/Sampling.hpp index 0365454d6d..a5583fd179 100644 --- a/src/wmtk/image/Sampling.hpp +++ b/src/wmtk/image/Sampling.hpp @@ -9,7 +9,7 @@ enum class SAMPLING_MODE { BICUBIC, SPLINE }; class Sampling { public: - using DScalar = DScalar2; + using DScalar = DScalar2, Eigen::Matrix>; virtual ~Sampling(){}; public: @@ -17,6 +17,63 @@ class Sampling virtual DScalar sample(const DScalar& u, const DScalar& v) const = 0; }; +template +class SamplingAnalyticFunction : public Sampling +{ +protected: + std::function m_f; +}; + +// Specialization for SamplingAnalyticFunction +template <> +class SamplingAnalyticFunction : public Sampling +{ +protected: + std::function m_f; + +public: + SamplingAnalyticFunction(std::function f) + : m_f(f) + {} + + double sample(const double u, const double v) const override { return m_f(u, v); } + + DScalar sample(const DScalar& u, const DScalar& v) const override + { + // Handle the case where SamplingType is double + // return a default DScalar + return DScalar{m_f(u.getValue(), v.getValue()), + Eigen::Vector2d::Zero(), + Eigen::Matrix2d::Zero()}; + } +}; + +// Specialization for SamplingAnalyticFunction +template <> +class SamplingAnalyticFunction : public Sampling +{ +protected: + std::function m_f; + +public: + SamplingAnalyticFunction( + std::function f) + : m_f(f) + {} + + double sample(const double u, const double v) const override + { + // Handle the case where SamplingType is DScalar + // return a default double + return m_f(DScalar(u), DScalar(v)).getValue(); + } + + Sampling::DScalar sample(const Sampling::DScalar& u, const Sampling::DScalar& v) const override + { + return m_f(u, v); + } +}; + template class SamplingImage : public Sampling { From 058825fdfc5bcf591f65bd800e4a91f681631e90 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 6 Sep 2023 09:01:39 -0400 Subject: [PATCH 024/134] using the new sampling method for Amips3D. Rewrite testing. Not compiling --- src/wmtk/energy/AMIPS.cpp | 139 +++------------------------ src/wmtk/energy/AMIPS.hpp | 161 +++++++++++++++++++++++++------- tests/energy/test_2d_energy.cpp | 8 +- 3 files changed, 143 insertions(+), 165 deletions(-) diff --git a/src/wmtk/energy/AMIPS.cpp b/src/wmtk/energy/AMIPS.cpp index 1d129637a5..50aa3ea7c1 100644 --- a/src/wmtk/energy/AMIPS.cpp +++ b/src/wmtk/energy/AMIPS.cpp @@ -10,11 +10,11 @@ AMIPS_2D::AMIPS_2D(const TriMesh& mesh) : AMIPS(mesh) {} -AMIPS_3DEmbedded::AMIPS_3DEmbedded(const TriMesh& mesh, const image::Image& image) - : AMIPS(mesh) - , m_image(image) - , m_dofs_to_pos(image) -{} +Eigen::Matrix AMIPS::get_target_triangle(double scaling) const +{ + const static std::array m_target_triangle = {0., 0., 1., 0., 1. / 2., sqrt(3) / 2.}; + return scaling * Eigen::Matrix::ConstMapType(m_target_triangle.data()); +} double AMIPS_2D::energy_eval(const Tuple& tuple) const { @@ -27,7 +27,7 @@ double AMIPS_2D::energy_eval(const Tuple& tuple) const pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))).head<2>(); // return the energy - return energy_eval(uv1, uv2, uv3, m_target_triangle); + return energy_eval(uv1, uv2, uv3); } DScalar AMIPS_2D::energy_eval_autodiff(const Tuple& tuple) const { @@ -39,14 +39,13 @@ DScalar AMIPS_2D::energy_eval_autodiff(const Tuple& tuple) const Eigen::Vector2d uv3 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); // return the energy - return energy_eval(uv1, uv2, uv3, m_target_triangle); + return energy_eval(uv1, uv2, uv3); } template auto AMIPS_2D::energy_eval( const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, - const Eigen::Vector2d& uv3, - const std::array& m_target_triangle) -> T + const Eigen::Vector2d& uv3) const -> T { // (x0 - x1, y0 - y1, x0 - x2, y0 - y2).transpose Eigen::Matrix Dm; @@ -59,10 +58,11 @@ auto AMIPS_2D::energy_eval( Dm << uv2(0) - uv1_(0), uv3(0) - uv1_(0), uv2(1) - uv1_(1), uv3(1) - uv1_(1); Eigen::Matrix2d Ds, Dsinv; + Eigen::Matrix target_triangle = get_target_triangle(1.0); Eigen::Vector2d target_A, target_B, target_C; - target_A << m_target_triangle[0], m_target_triangle[1]; - target_B << m_target_triangle[2], m_target_triangle[3]; - target_C << m_target_triangle[4], m_target_triangle[5]; + target_A = target_triangle.row(0); + target_B = target_triangle.row(1); + target_C = target_triangle.row(2); Ds << target_B.x() - target_A.x(), target_C.x() - target_A.x(), target_B.y() - target_A.y(), target_C.y() - target_A.y(); @@ -85,118 +85,3 @@ auto AMIPS_2D::energy_eval( } return (F.transpose() * F).trace() / Fdet; } - -double AMIPS_3DEmbedded::energy_eval(const Tuple& tuple) const -{ - // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); - Eigen::Vector2d uv1 = pos.vector_attribute(tuple).head<2>(); - Eigen::Vector2d uv2 = - pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))).head<2>(); - Eigen::Vector2d uv3 = - pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))).head<2>(); - - // return the energy - return energy_eval(uv1, uv2, uv3, m_target_triangle, m_dofs_to_pos); -} -DScalar AMIPS_3DEmbedded::energy_eval_autodiff(const Tuple& tuple) const -{ - // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); - - Eigen::Vector2d uv1 = pos.vector_attribute(tuple); - Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); - Eigen::Vector2d uv3 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); - - // return the energy - return energy_eval(uv1, uv2, uv3, m_target_triangle, m_dofs_to_pos); -} - -template -auto AMIPS_3DEmbedded::energy_eval( - const Eigen::Vector2d& uv1, - const Eigen::Vector2d& uv2, - const Eigen::Vector2d& uv3, - const std::array& m_target_triangle, - const DofsToPosition& m_dofs_to_pos) -> T -{ - Eigen::Matrix pos1 = m_dofs_to_pos.dof_to_pos(uv1); - Eigen::Matrix pos2 = m_dofs_to_pos.dof_to_pos(uv2); - Eigen::Matrix pos3 = m_dofs_to_pos.dof_to_pos(uv3); - Eigen::Matrix V2_V1; - V2_V1 << pos2(0) - pos1(0), pos2(1) - pos1(1), pos2(2) - pos1(2); - Eigen::Matrix V3_V1; - V3_V1 << pos3(0) - pos1(0), pos3(1) - pos1(1), pos3(2) - pos1(2); - - // tangent bases - // e1 = (V2 - V1).normalize() - // e1 = V2_V1.stableNormalized(); - assert(V2_V1.norm() > 0); // check norm is not 0 - Eigen::Matrix e1 = V2_V1 / V2_V1.norm(); - Eigen::Matrix n = V2_V1.cross(V3_V1); - - // #ifdef DEBUG - // Eigen::MatrixXd double_n; - // get_double_vecto(n, 3, double_n); - // if (double_n.lpNorm() < std::numeric_limits::denorm_min()) { - // wmtk::logger().critical("n.lpNorm {}", double_n.lpNorm()); - // std::cout << "V1 " << std::endl; - // std::cout << std::hexfloat << get_value(pos1(0))<< " " << y1 << v1(2) << std::endl; - // std::cout << "V2 " << std::endl; - // std::cout << std::hexfloat << v2(0) << " " << v2(1) << v2(2) << std::endl; - // std::cout << "V3 " << std::endl; - // std::cout << std::hexfloat << v3(0) << " " << v3(1) << v3(2) << std::endl; - // assert(false); - // } - // #endif - // n = n.stableNormalized(); - assert(n.norm() > 0); // check norm is not 0 - n = n / n.norm(); - Eigen::Matrix e2 = n.cross(e1); - // Eigen::Matrix e2_stableNormalized = e2.stableNormalized(); - assert(e2.norm() > 0); // check norm is not 0 - e2 = e2 / e2.norm(); - - - // project V1, V2, V3 to tangent plane to VT1, VT2, VT3 - - Eigen::Matrix VT1, VT2, VT3; - VT1 << static_cast(0.), static_cast(0.); // the origin - VT2 << V2_V1.dot(e1), V2_V1.dot(e2); - VT3 << V3_V1.dot(e1), V3_V1.dot(e2); - - // now construct Dm as before in tangent plane - // (x2 - x1, y2 - y1, x3 - x1, y2 - y1).transpose - Eigen::Matrix Dm; - Dm << VT2.x() - VT1.x(), VT3.x() - VT1.x(), VT2.y() - VT1.y(), VT3.y() - VT1.y(); - - T Dmdet = Dm.determinant(); - assert(wmtk::energy::get_value(Dmdet) > 0); - - Eigen::Matrix2d Ds, Dsinv; - Eigen::Vector2d target_A, target_B, target_C; - target_A << m_target_triangle[0], m_target_triangle[1]; - target_B << m_target_triangle[2], m_target_triangle[3]; - target_C << m_target_triangle[4], m_target_triangle[5]; - Ds << target_B.x() - target_A.x(), target_C.x() - target_A.x(), target_B.y() - target_A.y(), - target_C.y() - target_A.y(); - - auto Dsdet = Ds.determinant(); - if (abs(Dsdet) < std::numeric_limits::denorm_min()) { - return static_cast(std::numeric_limits::infinity()); - } - Dsinv = Ds.inverse(); - - // define of transform matrix F = Dm@Ds.inv - Eigen::Matrix F; - F << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), - (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), - (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), - (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); - - auto Fdet = F.determinant(); - if (abs(Fdet) < std::numeric_limits::denorm_min()) { - return static_cast(std::numeric_limits::infinity()); - } - return (F.transpose() * F).trace() / Fdet; -} \ No newline at end of file diff --git a/src/wmtk/energy/AMIPS.hpp b/src/wmtk/energy/AMIPS.hpp index 269543001a..b33a4bcd1a 100644 --- a/src/wmtk/energy/AMIPS.hpp +++ b/src/wmtk/energy/AMIPS.hpp @@ -9,29 +9,7 @@ class AMIPS : public DifferentiableEnergy { public: AMIPS(const TriMesh& mesh); - -public: - std::array m_target_triangle = {0., 0., 1., 0., 1. / 2., sqrt(3) / 2.}; - double m_target_edge_length = 1.; - - void set_target_triangle(const std::array& target_triangle) - { - m_target_triangle = target_triangle; - } - void set_target_edge_length(const double target_edge_length) - { - m_target_edge_length = target_edge_length; - } - void set_target_triangle_with_scaling(const double target_edge_length) - { - m_target_edge_length = target_edge_length; - m_target_triangle[0] *= m_target_edge_length; - m_target_triangle[1] *= m_target_edge_length; - m_target_triangle[2] *= m_target_edge_length; - m_target_triangle[3] *= m_target_edge_length; - m_target_triangle[4] *= m_target_edge_length; - m_target_triangle[5] *= m_target_edge_length; - } + Eigen::Matrix get_target_triangle(double scaling) const; }; class AMIPS_2D : public AMIPS @@ -52,37 +30,150 @@ class AMIPS_2D : public AMIPS * @return can be double or DScalar */ template - static T energy_eval( + T energy_eval( const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, - const Eigen::Vector2d& uv3, - const std::array& m_target_triangle); + const Eigen::Vector2d& uv3) const; }; // namespace energy /** * @brief TODO 3D AMIPS uses uv and displacement map to get the 3d cooridnates then evaluate * */ +template class AMIPS_3DEmbedded : public AMIPS { public: - AMIPS_3DEmbedded(const TriMesh& mesh, const image::Image& image); + AMIPS_3DEmbedded(const TriMesh& mesh, const image::Image& image) + : AMIPS(mesh) + , m_dofs_to_pos(image) + {} + AMIPS_3DEmbedded( + const TriMesh& mesh, + const std::function& f) + : AMIPS(mesh) + , m_dofs_to_pos(f) + {} + public: - double energy_eval(const Tuple& tuple) const override; - DScalar energy_eval_autodiff(const Tuple& tuple) const override; + double energy_eval(const Tuple& tuple) const override + { + // get the uv coordinates of the triangle + ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); + Eigen::Vector2d uv1 = pos.vector_attribute(tuple).head<2>(); + Eigen::Vector2d uv2 = + pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))).head<2>(); + Eigen::Vector2d uv3 = + pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))).head<2>(); + + // return the energy + return energy_eval(uv1, uv2, uv3); + } + DScalar energy_eval_autodiff(const Tuple& tuple) const override + { + // get the uv coordinates of the triangle + ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); + + Eigen::Vector2d uv1 = pos.vector_attribute(tuple); + Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); + Eigen::Vector2d uv3 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); + + // return the energy + return energy_eval(uv1, uv2, uv3); + } + template - static T energy_eval( + T energy_eval( const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, - const Eigen::Vector2d& uv3, - const std::array& m_target_triangle, - const DofsToPosition& m_dofs_to_pos); + const Eigen::Vector2d& uv3) const + { + Eigen::Matrix pos1 = m_dofs_to_pos.dof_to_pos(uv1); + Eigen::Matrix pos2 = m_dofs_to_pos.dof_to_pos(uv2); + Eigen::Matrix pos3 = m_dofs_to_pos.dof_to_pos(uv3); + Eigen::Matrix V2_V1; + V2_V1 << pos2(0) - pos1(0), pos2(1) - pos1(1), pos2(2) - pos1(2); + Eigen::Matrix V3_V1; + V3_V1 << pos3(0) - pos1(0), pos3(1) - pos1(1), pos3(2) - pos1(2); + + // tangent bases + // e1 = (V2 - V1).normalize() + // e1 = V2_V1.stableNormalized(); + assert(V2_V1.norm() > 0); // check norm is not 0 + Eigen::Matrix e1 = V2_V1 / V2_V1.norm(); + Eigen::Matrix n = V2_V1.cross(V3_V1); + + // #ifdef DEBUG + // Eigen::MatrixXd double_n; + // get_double_vecto(n, 3, double_n); + // if (double_n.lpNorm() < std::numeric_limits::denorm_min()) { + // wmtk::logger().critical("n.lpNorm {}", double_n.lpNorm()); + // std::cout << "V1 " << std::endl; + // std::cout << std::hexfloat << get_value(pos1(0))<< " " << y1 << v1(2) << std::endl; + // std::cout << "V2 " << std::endl; + // std::cout << std::hexfloat << v2(0) << " " << v2(1) << v2(2) << std::endl; + // std::cout << "V3 " << std::endl; + // std::cout << std::hexfloat << v3(0) << " " << v3(1) << v3(2) << std::endl; + // assert(false); + // } + // #endif + // n = n.stableNormalized(); + assert(n.norm() > 0); // check norm is not 0 + n = n / n.norm(); + Eigen::Matrix e2 = n.cross(e1); + // Eigen::Matrix e2_stableNormalized = e2.stableNormalized(); + assert(e2.norm() > 0); // check norm is not 0 + e2 = e2 / e2.norm(); + + + // project V1, V2, V3 to tangent plane to VT1, VT2, VT3 + + Eigen::Matrix VT1, VT2, VT3; + VT1 << static_cast(0.), static_cast(0.); // the origin + VT2 << V2_V1.dot(e1), V2_V1.dot(e2); + VT3 << V3_V1.dot(e1), V3_V1.dot(e2); + + // now construct Dm as before in tangent plane + // (x2 - x1, y2 - y1, x3 - x1, y2 - y1).transpose + Eigen::Matrix Dm; + Dm << VT2.x() - VT1.x(), VT3.x() - VT1.x(), VT2.y() - VT1.y(), VT3.y() - VT1.y(); + + T Dmdet = Dm.determinant(); + assert(wmtk::energy::get_value(Dmdet) > 0); + + Eigen::Matrix2d Ds, Dsinv; + Eigen::Matrix target_triangle = get_target_triangle(1.0); + Eigen::Vector2d target_A, target_B, target_C; + target_A = target_triangle.row(0); + target_B = target_triangle.row(1); + target_C = target_triangle.row(2); + Ds << target_B.x() - target_A.x(), target_C.x() - target_A.x(), target_B.y() - target_A.y(), + target_C.y() - target_A.y(); + + auto Dsdet = Ds.determinant(); + if (abs(Dsdet) < std::numeric_limits::denorm_min()) { + return static_cast(std::numeric_limits::infinity()); + } + Dsinv = Ds.inverse(); + + // define of transform matrix F = Dm@Ds.inv + Eigen::Matrix F; + F << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), + (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), + (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), + (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); + + auto Fdet = F.determinant(); + if (abs(Fdet) < std::numeric_limits::denorm_min()) { + return static_cast(std::numeric_limits::infinity()); + } + return (F.transpose() * F).trace() / Fdet; + } protected: - image::Image m_image; - DofsToPosition m_dofs_to_pos; + DofsToPosition m_dofs_to_pos; }; } // namespace energy } // namespace wmtk \ No newline at end of file diff --git a/tests/energy/test_2d_energy.cpp b/tests/energy/test_2d_energy.cpp index f07c3da0e8..3807418437 100644 --- a/tests/energy/test_2d_energy.cpp +++ b/tests/energy/test_2d_energy.cpp @@ -1,4 +1,4 @@ -#pragma once + #include #include #include @@ -55,9 +55,11 @@ TEST_CASE("amips3d") const DEBUG_TriMesh example_mesh = single_equilateral_triangle(); auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); const TriMesh tri_mesh = static_cast(example_mesh); + std::function f = []([[maybe_unused]] double x, + [[maybe_unused]] double y) { return 0.; }; - AMIPS_2D amips2d(tri_mesh); + AMIPS_3DEmbedded amips3d(tri_mesh, f); - REQUIRE(amips2d.energy_eval(e1) == 2.0); + REQUIRE(amips3d.energy_eval(e1) == 2.0); } } \ No newline at end of file From 3ac19723747b54608d567cf8fc209c1d9cbf82aa Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 6 Sep 2023 09:48:17 -0400 Subject: [PATCH 025/134] change samplingImage to samplingBicubic --- src/wmtk/energy/utils/DofsToPosition.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wmtk/energy/utils/DofsToPosition.hpp b/src/wmtk/energy/utils/DofsToPosition.hpp index 5c4d2d3197..99ab107a0b 100644 --- a/src/wmtk/energy/utils/DofsToPosition.hpp +++ b/src/wmtk/energy/utils/DofsToPosition.hpp @@ -35,7 +35,7 @@ class DofsToPosition */ DofsToPosition(const image::Image& image) { - m_sampling = std::make_unique>(image); + m_sampling = std::make_unique(image); } From 1ab67d5dee46f8f0d368252b922a0a58a2582046 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 6 Sep 2023 10:31:27 -0400 Subject: [PATCH 026/134] fix some compiation problme still not compiling --- src/wmtk/energy/AMIPS.cpp | 2 +- src/wmtk/energy/utils/DofsToPosition.hpp | 7 +++---- tests/energy/test_2d_energy.cpp | 5 +++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/wmtk/energy/AMIPS.cpp b/src/wmtk/energy/AMIPS.cpp index 50aa3ea7c1..596ae7c724 100644 --- a/src/wmtk/energy/AMIPS.cpp +++ b/src/wmtk/energy/AMIPS.cpp @@ -53,7 +53,7 @@ auto AMIPS_2D::energy_eval( typedef Eigen::Matrix Vec2T; Vec2T uv1_; - get_local_vector(uv1, 2, uv1_); + get_T_vector(uv1, 2, uv1_); Dm << uv2(0) - uv1_(0), uv3(0) - uv1_(0), uv2(1) - uv1_(1), uv3(1) - uv1_(1); diff --git a/src/wmtk/energy/utils/DofsToPosition.hpp b/src/wmtk/energy/utils/DofsToPosition.hpp index 99ab107a0b..8b62e06f0a 100644 --- a/src/wmtk/energy/utils/DofsToPosition.hpp +++ b/src/wmtk/energy/utils/DofsToPosition.hpp @@ -47,17 +47,16 @@ class DofsToPosition if (size == 2) { Eigen::Matrix dofT; - get_local_vector>(dof, size, dofT); + get_T_vector>(dof, size, dofT); // TODO retrive position using displacement map // for now just return itself - pos << static_cast(dofT(0)), static_cast(dofT(1)), - m_sampling->sample(dofT(0), dofT(1)); + pos << dofT(0), dofT(1), m_sampling->sample(dofT(0), dofT(1)); } else //(dofx.size() == 1) { Eigen::Matrix dofT; - get_local_vector>(dof, size, dofT); + get_T_vector>(dof, size, dofT); // curve parameterization // TODO can also be implemented as a overload operator()? diff --git a/tests/energy/test_2d_energy.cpp b/tests/energy/test_2d_energy.cpp index 3807418437..2611ddeb7b 100644 --- a/tests/energy/test_2d_energy.cpp +++ b/tests/energy/test_2d_energy.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "../tools/DEBUG_TriMesh.hpp" #include "../tools/TriMesh_examples.hpp" using namespace wmtk; @@ -52,6 +53,10 @@ TEST_CASE("amips3d") { SECTION("equilateral triangle") { + DofsToPosition dofs2pos([](double x, double y) { return 0.; }); + Eigen::Vector2d dofs = Eigen::Vector2d::Zero(2); + + Eigen::Vector3d pos = dofs2pos.dof_to_pos(dofs); const DEBUG_TriMesh example_mesh = single_equilateral_triangle(); auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); const TriMesh tri_mesh = static_cast(example_mesh); From 679716512ed43eaf1cb5a97883f00f40ad3c0c25 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 7 Sep 2023 05:32:53 -0400 Subject: [PATCH 027/134] changing sampling and dof to pos to not expose autodiff type --- src/wmtk/energy/utils/DofsToPosition.hpp | 23 +++----- src/wmtk/image/Sampling.hpp | 67 ++++++++++-------------- 2 files changed, 33 insertions(+), 57 deletions(-) diff --git a/src/wmtk/energy/utils/DofsToPosition.hpp b/src/wmtk/energy/utils/DofsToPosition.hpp index 8b62e06f0a..fd21f018c6 100644 --- a/src/wmtk/energy/utils/DofsToPosition.hpp +++ b/src/wmtk/energy/utils/DofsToPosition.hpp @@ -9,7 +9,6 @@ namespace wmtk { namespace energy { -template class DofsToPosition { // size = 2: uv position, size =1, t of boundary curve using DofVectorX = Eigen::Matrix; @@ -23,11 +22,6 @@ class DofsToPosition DofsToPosition& operator=(const DofsToPosition&) = default; // copy assignment operator DofsToPosition& operator=(DofsToPosition&&) = default; // move assignment operator - DofsToPosition(std::function f) - { - m_sampling = std::make_unique>(f); - } - /** * @brief Construct a new Dofs To Position object using a displacement map (requires a sampler) * @@ -38,16 +32,13 @@ class DofsToPosition m_sampling = std::make_unique(image); } - - Eigen::Matrix dof_to_pos( - const Eigen::Matrix& dof) const + template + Eigen::Matrix dof_to_pos(const Eigen::Matrix& dofT) const { - Eigen::Matrix pos; - int size = dof.rows(); + Eigen::Matrix pos; + int size = dofT.rows(); if (size == 2) { - Eigen::Matrix dofT; - get_T_vector>(dof, size, dofT); // TODO retrive position using displacement map // for now just return itself pos << dofT(0), dofT(1), m_sampling->sample(dofT(0), dofT(1)); @@ -55,12 +46,10 @@ class DofsToPosition } else //(dofx.size() == 1) { - Eigen::Matrix dofT; - get_T_vector>(dof, size, dofT); // curve parameterization - // TODO can also be implemented as a overload operator()? + // TODO covert to uv first and sample using the uv - pos << dofT(0), static_cast(0.0), static_cast(0.0); + pos << dofT(0), static_cast(0.0), static_cast(0.0); } return pos; } diff --git a/src/wmtk/image/Sampling.hpp b/src/wmtk/image/Sampling.hpp index a5583fd179..77179399e7 100644 --- a/src/wmtk/image/Sampling.hpp +++ b/src/wmtk/image/Sampling.hpp @@ -17,60 +17,47 @@ class Sampling virtual DScalar sample(const DScalar& u, const DScalar& v) const = 0; }; -template + class SamplingAnalyticFunction : public Sampling { protected: - std::function m_f; -}; + enum FunctionType { Linear, Quadratic }; -// Specialization for SamplingAnalyticFunction -template <> -class SamplingAnalyticFunction : public Sampling -{ -protected: - std::function m_f; + FunctionType m_type = FunctionType::Linear; + double A = 0.0; + double B = 0.0; + double C = 0.0; -public: - SamplingAnalyticFunction(std::function f) - : m_f(f) - {} - - double sample(const double u, const double v) const override { return m_f(u, v); } - - DScalar sample(const DScalar& u, const DScalar& v) const override + template + auto _evaluate(const S& u, const S& v) const { - // Handle the case where SamplingType is double - // return a default DScalar - return DScalar{m_f(u.getValue(), v.getValue()), - Eigen::Vector2d::Zero(), - Eigen::Matrix2d::Zero()}; + if (m_type == Linear) { + return _evaluate_linear(u, v); + } } -}; -// Specialization for SamplingAnalyticFunction -template <> -class SamplingAnalyticFunction : public Sampling -{ -protected: - std::function m_f; + template + auto _evaluate_linear(const S& u, const S& v) const + { + return A * u + B * v + C; + } public: - SamplingAnalyticFunction( - std::function f) - : m_f(f) - {} + // make a contructor + SamplingAnalyticFunction() {} - double sample(const double u, const double v) const override + + void set_coefficients(const FunctionType type, double a, const double b, const double c) { - // Handle the case where SamplingType is DScalar - // return a default double - return m_f(DScalar(u), DScalar(v)).getValue(); + m_type = type; + A = a; + B = b; + C = c; } - - Sampling::DScalar sample(const Sampling::DScalar& u, const Sampling::DScalar& v) const override + double sample(const double u, const double v) const override { return _evaluate(u, v); } + DScalar sample(const DScalar& u, const DScalar& v) const override { - return m_f(u, v); + return _evaluate(u, v); } }; From c5f148d7ba1646bc2138b83fb9f41657c5e3fd7d Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 7 Sep 2023 05:33:22 -0400 Subject: [PATCH 028/134] using the new dofs to pos and smapling method for amips 3d --- src/wmtk/energy/AMIPS.cpp | 128 ++++++++++++++++++++++++++++++++++++++ src/wmtk/energy/AMIPS.hpp | 125 +++---------------------------------- 2 files changed, 136 insertions(+), 117 deletions(-) diff --git a/src/wmtk/energy/AMIPS.cpp b/src/wmtk/energy/AMIPS.cpp index 596ae7c724..54281af715 100644 --- a/src/wmtk/energy/AMIPS.cpp +++ b/src/wmtk/energy/AMIPS.cpp @@ -85,3 +85,131 @@ auto AMIPS_2D::energy_eval( } return (F.transpose() * F).trace() / Fdet; } + +double AMIPS_3DEmbedded::energy_eval(const Tuple& tuple) const +{ + // get the uv coordinates of the triangle + ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); + Eigen::Vector2d uv1 = pos.vector_attribute(tuple).head<2>(); + // TODO curve mesh uv -> t conversion happens here + // check if it's on the curve mesh or not + // if curve mesh, convert to t + // else + int size = 2; + Eigen::Matrix dofT; + get_T_vector>(uv1, size, dofT); + + Eigen::Vector2d uv2 = + pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))).head<2>(); + Eigen::Vector2d uv3 = + pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))).head<2>(); + + // return the energy + return energy_eval(dofT, uv2, uv3); +} + +DScalar AMIPS_3DEmbedded::energy_eval_autodiff(const Tuple& tuple) const +{ + // get the uv coordinates of the triangle + ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); + + // TODO curve mesh uv -> t conversion happens here + Eigen::Vector2d uv1 = pos.vector_attribute(tuple); + + int size = 2; + Eigen::Matrix dofT; + get_T_vector>(uv1, size, dofT); + + Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); + Eigen::Vector2d uv3 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); + + // return the energy + return energy_eval(dofT, uv2, uv3); +} +template +auto AMIPS_3DEmbedded::energy_eval( + const Eigen::Matrix& dofT, + const Eigen::Vector2d& uv2, + const Eigen::Vector2d& uv3) const -> T +{ + Eigen::Matrix pos1 = m_dofs_to_pos.dof_to_pos(dofT); + Eigen::Matrix pos2 = m_dofs_to_pos.dof_to_pos(uv2); + Eigen::Matrix pos3 = m_dofs_to_pos.dof_to_pos(uv3); + Eigen::Matrix V2_V1; + V2_V1 << pos2(0) - pos1(0), pos2(1) - pos1(1), pos2(2) - pos1(2); + Eigen::Matrix V3_V1; + V3_V1 << pos3(0) - pos1(0), pos3(1) - pos1(1), pos3(2) - pos1(2); + + // tangent bases + // e1 = (V2 - V1).normalize() + // e1 = V2_V1.stableNormalized(); + assert(V2_V1.norm() > 0); // check norm is not 0 + Eigen::Matrix e1 = V2_V1 / V2_V1.norm(); + Eigen::Matrix n = V2_V1.cross(V3_V1); + + // #ifdef DEBUG + // Eigen::MatrixXd double_n; + // get_double_vecto(n, 3, double_n); + // if (double_n.lpNorm() < std::numeric_limits::denorm_min()) { + // wmtk::logger().critical("n.lpNorm {}", double_n.lpNorm()); + // std::cout << "V1 " << std::endl; + // std::cout << std::hexfloat << get_value(pos1(0))<< " " << y1 << v1(2) << std::endl; + // std::cout << "V2 " << std::endl; + // std::cout << std::hexfloat << v2(0) << " " << v2(1) << v2(2) << std::endl; + // std::cout << "V3 " << std::endl; + // std::cout << std::hexfloat << v3(0) << " " << v3(1) << v3(2) << std::endl; + // assert(false); + // } + // #endif + // n = n.stableNormalized(); + assert(n.norm() > 0); // check norm is not 0 + n = n / n.norm(); + Eigen::Matrix e2 = n.cross(e1); + // Eigen::Matrix e2_stableNormalized = e2.stableNormalized(); + assert(e2.norm() > 0); // check norm is not 0 + e2 = e2 / e2.norm(); + + + // project V1, V2, V3 to tangent plane to VT1, VT2, VT3 + + Eigen::Matrix VT1, VT2, VT3; + VT1 << static_cast(0.), static_cast(0.); // the origin + VT2 << V2_V1.dot(e1), V2_V1.dot(e2); + VT3 << V3_V1.dot(e1), V3_V1.dot(e2); + + // now construct Dm as before in tangent plane + // (x2 - x1, y2 - y1, x3 - x1, y2 - y1).transpose + Eigen::Matrix Dm; + Dm << VT2.x() - VT1.x(), VT3.x() - VT1.x(), VT2.y() - VT1.y(), VT3.y() - VT1.y(); + + T Dmdet = Dm.determinant(); + assert(wmtk::energy::get_value(Dmdet) > 0); + + Eigen::Matrix2d Ds, Dsinv; + Eigen::Matrix target_triangle = get_target_triangle(1.0); + Eigen::Vector2d target_A, target_B, target_C; + target_A = target_triangle.row(0); + target_B = target_triangle.row(1); + target_C = target_triangle.row(2); + Ds << target_B.x() - target_A.x(), target_C.x() - target_A.x(), target_B.y() - target_A.y(), + target_C.y() - target_A.y(); + + auto Dsdet = Ds.determinant(); + if (abs(Dsdet) < std::numeric_limits::denorm_min()) { + return static_cast(std::numeric_limits::infinity()); + } + Dsinv = Ds.inverse(); + + // define of transform matrix F = Dm@Ds.inv + Eigen::Matrix F; + F << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), + (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), + (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), + (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); + + auto Fdet = F.determinant(); + if (abs(Fdet) < std::numeric_limits::denorm_min()) { + return static_cast(std::numeric_limits::infinity()); + } + return (F.transpose() * F).trace() / Fdet; +} diff --git a/src/wmtk/energy/AMIPS.hpp b/src/wmtk/energy/AMIPS.hpp index b33a4bcd1a..606ad2a47c 100644 --- a/src/wmtk/energy/AMIPS.hpp +++ b/src/wmtk/energy/AMIPS.hpp @@ -37,10 +37,9 @@ class AMIPS_2D : public AMIPS }; // namespace energy /** - * @brief TODO 3D AMIPS uses uv and displacement map to get the 3d cooridnates then evaluate + * @brief 3D AMIPS uses uv and displacement map to get the 3d cooridnates then evaluate * */ -template class AMIPS_3DEmbedded : public AMIPS { public: @@ -48,132 +47,24 @@ class AMIPS_3DEmbedded : public AMIPS : AMIPS(mesh) , m_dofs_to_pos(image) {} - AMIPS_3DEmbedded( - const TriMesh& mesh, - const std::function& f) + AMIPS_3DEmbedded(const TriMesh& mesh) : AMIPS(mesh) - , m_dofs_to_pos(f) + , m_dofs_to_pos() {} public: - double energy_eval(const Tuple& tuple) const override - { - // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); - Eigen::Vector2d uv1 = pos.vector_attribute(tuple).head<2>(); - Eigen::Vector2d uv2 = - pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))).head<2>(); - Eigen::Vector2d uv3 = - pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))).head<2>(); - - // return the energy - return energy_eval(uv1, uv2, uv3); - } - DScalar energy_eval_autodiff(const Tuple& tuple) const override - { - // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); - - Eigen::Vector2d uv1 = pos.vector_attribute(tuple); - Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); - Eigen::Vector2d uv3 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); - - // return the energy - return energy_eval(uv1, uv2, uv3); - } - + double energy_eval(const Tuple& tuple) const override; + DScalar energy_eval_autodiff(const Tuple& tuple) const override; template T energy_eval( - const Eigen::Vector2d& uv1, + const Eigen::Matrix& dofT, const Eigen::Vector2d& uv2, - const Eigen::Vector2d& uv3) const - { - Eigen::Matrix pos1 = m_dofs_to_pos.dof_to_pos(uv1); - Eigen::Matrix pos2 = m_dofs_to_pos.dof_to_pos(uv2); - Eigen::Matrix pos3 = m_dofs_to_pos.dof_to_pos(uv3); - Eigen::Matrix V2_V1; - V2_V1 << pos2(0) - pos1(0), pos2(1) - pos1(1), pos2(2) - pos1(2); - Eigen::Matrix V3_V1; - V3_V1 << pos3(0) - pos1(0), pos3(1) - pos1(1), pos3(2) - pos1(2); - - // tangent bases - // e1 = (V2 - V1).normalize() - // e1 = V2_V1.stableNormalized(); - assert(V2_V1.norm() > 0); // check norm is not 0 - Eigen::Matrix e1 = V2_V1 / V2_V1.norm(); - Eigen::Matrix n = V2_V1.cross(V3_V1); - - // #ifdef DEBUG - // Eigen::MatrixXd double_n; - // get_double_vecto(n, 3, double_n); - // if (double_n.lpNorm() < std::numeric_limits::denorm_min()) { - // wmtk::logger().critical("n.lpNorm {}", double_n.lpNorm()); - // std::cout << "V1 " << std::endl; - // std::cout << std::hexfloat << get_value(pos1(0))<< " " << y1 << v1(2) << std::endl; - // std::cout << "V2 " << std::endl; - // std::cout << std::hexfloat << v2(0) << " " << v2(1) << v2(2) << std::endl; - // std::cout << "V3 " << std::endl; - // std::cout << std::hexfloat << v3(0) << " " << v3(1) << v3(2) << std::endl; - // assert(false); - // } - // #endif - // n = n.stableNormalized(); - assert(n.norm() > 0); // check norm is not 0 - n = n / n.norm(); - Eigen::Matrix e2 = n.cross(e1); - // Eigen::Matrix e2_stableNormalized = e2.stableNormalized(); - assert(e2.norm() > 0); // check norm is not 0 - e2 = e2 / e2.norm(); - - - // project V1, V2, V3 to tangent plane to VT1, VT2, VT3 - - Eigen::Matrix VT1, VT2, VT3; - VT1 << static_cast(0.), static_cast(0.); // the origin - VT2 << V2_V1.dot(e1), V2_V1.dot(e2); - VT3 << V3_V1.dot(e1), V3_V1.dot(e2); - - // now construct Dm as before in tangent plane - // (x2 - x1, y2 - y1, x3 - x1, y2 - y1).transpose - Eigen::Matrix Dm; - Dm << VT2.x() - VT1.x(), VT3.x() - VT1.x(), VT2.y() - VT1.y(), VT3.y() - VT1.y(); - - T Dmdet = Dm.determinant(); - assert(wmtk::energy::get_value(Dmdet) > 0); - - Eigen::Matrix2d Ds, Dsinv; - Eigen::Matrix target_triangle = get_target_triangle(1.0); - Eigen::Vector2d target_A, target_B, target_C; - target_A = target_triangle.row(0); - target_B = target_triangle.row(1); - target_C = target_triangle.row(2); - Ds << target_B.x() - target_A.x(), target_C.x() - target_A.x(), target_B.y() - target_A.y(), - target_C.y() - target_A.y(); - - auto Dsdet = Ds.determinant(); - if (abs(Dsdet) < std::numeric_limits::denorm_min()) { - return static_cast(std::numeric_limits::infinity()); - } - Dsinv = Ds.inverse(); - - // define of transform matrix F = Dm@Ds.inv - Eigen::Matrix F; - F << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), - (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), - (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), - (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); - - auto Fdet = F.determinant(); - if (abs(Fdet) < std::numeric_limits::denorm_min()) { - return static_cast(std::numeric_limits::infinity()); - } - return (F.transpose() * F).trace() / Fdet; - } + const Eigen::Vector2d& uv3) const; protected: - DofsToPosition m_dofs_to_pos; + DofsToPosition m_dofs_to_pos; }; } // namespace energy } // namespace wmtk \ No newline at end of file From 41e877da11cfec3d64fa37c946994ea46d3e0819 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 7 Sep 2023 05:33:56 -0400 Subject: [PATCH 029/134] small type changes. Compiles --- src/wmtk/energy/AreaAccuracy.cpp | 2 +- src/wmtk/energy/DifferentiableEnergy.hpp | 2 +- src/wmtk/energy/utils/AutoDiffUtils.hpp | 4 ++-- src/wmtk/image/Image.hpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wmtk/energy/AreaAccuracy.cpp b/src/wmtk/energy/AreaAccuracy.cpp index e9505ff88e..cb2a06c5c4 100644 --- a/src/wmtk/energy/AreaAccuracy.cpp +++ b/src/wmtk/energy/AreaAccuracy.cpp @@ -8,6 +8,6 @@ AreaAccuracy::AreaAccuracy(const TriMesh& mesh1, const TriMesh& mesh2) , m_3d_position_handle(mesh2.get_attribute_handle("position", PrimitiveType::Vertex)) {} -template +template T energy_eval(const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, const Eigen::Vector2d& uv3) {} diff --git a/src/wmtk/energy/DifferentiableEnergy.hpp b/src/wmtk/energy/DifferentiableEnergy.hpp index b56424fade..38debba6ca 100644 --- a/src/wmtk/energy/DifferentiableEnergy.hpp +++ b/src/wmtk/energy/DifferentiableEnergy.hpp @@ -8,7 +8,7 @@ namespace wmtk { namespace energy { -using DScalar = DScalar2; +using DScalar = DScalar2, Eigen::Matrix>; using Scalar = typename DScalar::Scalar; class DifferentiableEnergy : public Energy diff --git a/src/wmtk/energy/utils/AutoDiffUtils.hpp b/src/wmtk/energy/utils/AutoDiffUtils.hpp index eb471a01d0..aa9fc2196b 100644 --- a/src/wmtk/energy/utils/AutoDiffUtils.hpp +++ b/src/wmtk/energy/utils/AutoDiffUtils.hpp @@ -3,7 +3,7 @@ namespace wmtk { namespace energy { -using DScalar = DScalar2; +using DScalar = DScalar2, Eigen::Matrix>; using Scalar = typename DScalar::Scalar; template class AutoDiffAllocator @@ -40,7 +40,7 @@ inline double get_value( } template -void get_local_vector(const Eigen::MatrixXd& data, const int size, AutoDiffVect& T_vector) +void get_T_vector(const Eigen::MatrixXd& data, const int size, AutoDiffVect& T_vector) { typedef typename AutoDiffVect::Scalar T; DiffScalarBase::setVariableCount(size); diff --git a/src/wmtk/image/Image.hpp b/src/wmtk/image/Image.hpp index 022277ec5a..1fce7ae1a5 100644 --- a/src/wmtk/image/Image.hpp +++ b/src/wmtk/image/Image.hpp @@ -17,7 +17,7 @@ namespace wmtk { namespace image { class Image { - using DScalar = DScalar2; + using DScalar = DScalar2, Eigen::Matrix>; using ImageMatrixf = Eigen::Matrix; From b61bbb4e16a8a143df017a5af41a1831697d07fa Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 7 Sep 2023 08:14:53 -0400 Subject: [PATCH 030/134] constructor for new SamplingAnalyticFunction --- src/wmtk/energy/utils/DofsToPosition.hpp | 10 ++++++++++ src/wmtk/image/Sampling.hpp | 17 +++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/wmtk/energy/utils/DofsToPosition.hpp b/src/wmtk/energy/utils/DofsToPosition.hpp index fd21f018c6..ce7cc36a77 100644 --- a/src/wmtk/energy/utils/DofsToPosition.hpp +++ b/src/wmtk/energy/utils/DofsToPosition.hpp @@ -32,6 +32,16 @@ class DofsToPosition m_sampling = std::make_unique(image); } + DofsToPosition( + const wmtk::image::SamplingAnalyticFunction::FunctionType type, + const double a, + const double b, + const double c) + { + m_sampling = std::make_unique(type, a, b, c); + } + + template Eigen::Matrix dof_to_pos(const Eigen::Matrix& dofT) const { diff --git a/src/wmtk/image/Sampling.hpp b/src/wmtk/image/Sampling.hpp index 77179399e7..12cd4def76 100644 --- a/src/wmtk/image/Sampling.hpp +++ b/src/wmtk/image/Sampling.hpp @@ -20,9 +20,10 @@ class Sampling class SamplingAnalyticFunction : public Sampling { -protected: +public: enum FunctionType { Linear, Quadratic }; +protected: FunctionType m_type = FunctionType::Linear; double A = 0.0; double B = 0.0; @@ -44,12 +45,20 @@ class SamplingAnalyticFunction : public Sampling public: // make a contructor - SamplingAnalyticFunction() {} + SamplingAnalyticFunction( + const FunctionType type, + const double a, + const double b, + const double c) + : m_type(type) + , A(a) + , B(b) + , C(c) + {} - void set_coefficients(const FunctionType type, double a, const double b, const double c) + void set_coefficients(double a, const double b, const double c) { - m_type = type; A = a; B = b; C = c; From 8955b25ab60977dbf55e20efdf126827388b8a9a Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 7 Sep 2023 08:37:36 -0400 Subject: [PATCH 031/134] change AMIPS 3d constructor . Chnage ConstMapType data ordering --- src/wmtk/energy/AMIPS.cpp | 3 +-- src/wmtk/energy/AMIPS.hpp | 9 +++++++-- tests/energy/test_2d_energy.cpp | 14 +++++++------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/wmtk/energy/AMIPS.cpp b/src/wmtk/energy/AMIPS.cpp index 54281af715..0282abe074 100644 --- a/src/wmtk/energy/AMIPS.cpp +++ b/src/wmtk/energy/AMIPS.cpp @@ -12,7 +12,7 @@ AMIPS_2D::AMIPS_2D(const TriMesh& mesh) Eigen::Matrix AMIPS::get_target_triangle(double scaling) const { - const static std::array m_target_triangle = {0., 0., 1., 0., 1. / 2., sqrt(3) / 2.}; + const static std::array m_target_triangle = {0., 1., 1. / 2., 0., 0., sqrt(3) / 2.}; return scaling * Eigen::Matrix::ConstMapType(m_target_triangle.data()); } @@ -169,7 +169,6 @@ auto AMIPS_3DEmbedded::energy_eval( assert(e2.norm() > 0); // check norm is not 0 e2 = e2 / e2.norm(); - // project V1, V2, V3 to tangent plane to VT1, VT2, VT3 Eigen::Matrix VT1, VT2, VT3; diff --git a/src/wmtk/energy/AMIPS.hpp b/src/wmtk/energy/AMIPS.hpp index 606ad2a47c..7c2c42bff9 100644 --- a/src/wmtk/energy/AMIPS.hpp +++ b/src/wmtk/energy/AMIPS.hpp @@ -47,9 +47,14 @@ class AMIPS_3DEmbedded : public AMIPS : AMIPS(mesh) , m_dofs_to_pos(image) {} - AMIPS_3DEmbedded(const TriMesh& mesh) + AMIPS_3DEmbedded( + const TriMesh& mesh, + const wmtk::image::SamplingAnalyticFunction::FunctionType type, + const double a, + const double b, + const double c) : AMIPS(mesh) - , m_dofs_to_pos() + , m_dofs_to_pos(type, a, b, c) {} diff --git a/tests/energy/test_2d_energy.cpp b/tests/energy/test_2d_energy.cpp index 2611ddeb7b..651d10ddac 100644 --- a/tests/energy/test_2d_energy.cpp +++ b/tests/energy/test_2d_energy.cpp @@ -53,17 +53,17 @@ TEST_CASE("amips3d") { SECTION("equilateral triangle") { - DofsToPosition dofs2pos([](double x, double y) { return 0.; }); - Eigen::Vector2d dofs = Eigen::Vector2d::Zero(2); - - Eigen::Vector3d pos = dofs2pos.dof_to_pos(dofs); const DEBUG_TriMesh example_mesh = single_equilateral_triangle(); auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); const TriMesh tri_mesh = static_cast(example_mesh); - std::function f = []([[maybe_unused]] double x, - [[maybe_unused]] double y) { return 0.; }; - AMIPS_3DEmbedded amips3d(tri_mesh, f); + + AMIPS_3DEmbedded amips3d( + tri_mesh, + wmtk::image::SamplingAnalyticFunction::FunctionType::Linear, + 0.0, + 0.0, + 1.0); REQUIRE(amips3d.energy_eval(e1) == 2.0); } From c2ed59d1f075a312c5f4e3a8ec78fb1077192771 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Thu, 7 Sep 2023 15:20:30 -0400 Subject: [PATCH 032/134] initial stab at selfhosted runners --- .github/workflows/continuous.yml | 28 +++++++++++++++------------- .github/workflows/doxygen.yml | 2 +- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/.github/workflows/continuous.yml b/.github/workflows/continuous.yml index 68c3b6a011..a5056648c3 100644 --- a/.github/workflows/continuous.yml +++ b/.github/workflows/continuous.yml @@ -4,10 +4,9 @@ on: push: branches: - main - - hackathon/dev + - hackathon/dev pull_request: branches: - - main - hackathon/dev env: @@ -21,15 +20,16 @@ jobs: Linux: name: ${{ matrix.name }} (${{ matrix.config }}) - runs-on: ${{ matrix.os }} + runs-on: [self-hosted, Linux] + #runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ubuntu-latest] + # os: [ubuntu-latest] config: [Debug, Release] - include: - - os: ubuntu-latest - name: Linux + # include: + # - os: ubuntu-latest + # name: Linux steps: - name: Checkout repository uses: actions/checkout@v1 @@ -79,15 +79,16 @@ jobs: MacOS: name: ${{ matrix.name }}-${{ matrix.arch }} (${{ matrix.config }}) runs-on: ${{ matrix.os }} + runs-on: [self-hosted, MacOS] strategy: fail-fast: false matrix: - os: [macos-latest] - arch: [x86_64] # arm64 no arm as GMP is binary + #os: [macos-latest] + #arch: [x86_64] # arm64 no arm as GMP is binary config: [Debug, Release] - include: - - os: macos-latest - name: macOS + #include: + # - os: macos-latest + # name: macOS steps: - name: Checkout repository @@ -135,7 +136,8 @@ jobs: Windows: name: Windows (${{ matrix.config }}) - runs-on: windows-2022 + runs-on: [self-hosted, Windows] + #runs-on: windows-2022 env: CC: cl.exe CXX: cl.exe diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 7ef2f643f9..12fe4ce9f8 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -7,7 +7,7 @@ on: jobs: deploy: - runs-on: ubuntu-latest + runs-on: [self-hosted, Linux] steps: - uses: actions/checkout@v3 - name: Config From 4cdb69a412a80a5a088d61e55fd929fb46b7bf25 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Fri, 8 Sep 2023 12:52:14 -0400 Subject: [PATCH 033/134] second stab at updating actions for self-runner --- .github/workflows/continuous.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/continuous.yml b/.github/workflows/continuous.yml index a5056648c3..66e9034d2c 100644 --- a/.github/workflows/continuous.yml +++ b/.github/workflows/continuous.yml @@ -20,16 +20,16 @@ jobs: Linux: name: ${{ matrix.name }} (${{ matrix.config }}) - runs-on: [self-hosted, Linux] + runs-on: [self-hosted,Linux] #runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - # os: [ubuntu-latest] + os: [ubuntu-latest] config: [Debug, Release] - # include: - # - os: ubuntu-latest - # name: Linux + include: + - os: ubuntu-latest + name: Linux steps: - name: Checkout repository uses: actions/checkout@v1 @@ -78,17 +78,17 @@ jobs: MacOS: name: ${{ matrix.name }}-${{ matrix.arch }} (${{ matrix.config }}) - runs-on: ${{ matrix.os }} + #runs-on: ${{ matrix.os }} runs-on: [self-hosted, MacOS] strategy: fail-fast: false matrix: - #os: [macos-latest] - #arch: [x86_64] # arm64 no arm as GMP is binary + os: [macos-latest] + arch: [ARM64] # arm64 no arm as GMP is binary config: [Debug, Release] - #include: - # - os: macos-latest - # name: macOS + include: + - os: macos-latest + name: macOS steps: - name: Checkout repository @@ -153,7 +153,7 @@ jobs: fetch-depth: 10 - uses: seanmiddleditch/gha-setup-ninja@master - - name: Stetup Conda + - name: Setup Conda uses: s-weigand/setup-conda@v1 with: conda-channels: anaconda, conda-forge From 33131c58b5ea699fc459010136236ade0528ea5f Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Fri, 8 Sep 2023 12:54:12 -0400 Subject: [PATCH 034/134] trying minor indentation fix on actions workflow --- .github/workflows/continuous.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous.yml b/.github/workflows/continuous.yml index 66e9034d2c..ee65756a3f 100644 --- a/.github/workflows/continuous.yml +++ b/.github/workflows/continuous.yml @@ -29,7 +29,7 @@ jobs: config: [Debug, Release] include: - os: ubuntu-latest - name: Linux + name: Linux steps: - name: Checkout repository uses: actions/checkout@v1 From 830f5f0e4d72d28c70691071365c44c96223a833 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Fri, 8 Sep 2023 13:43:51 -0400 Subject: [PATCH 035/134] Update continuous.yml for linux --- .github/workflows/continuous.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/continuous.yml b/.github/workflows/continuous.yml index ee65756a3f..aa7ea387b1 100644 --- a/.github/workflows/continuous.yml +++ b/.github/workflows/continuous.yml @@ -38,11 +38,6 @@ jobs: - name: Dependencies run: | - sudo apt-get update - sudo apt-get -o Acquire::Retries=3 install \ - libblas-dev \ - xorg-dev \ - ccache echo 'CACHE_PATH=~/.ccache' >> "$GITHUB_ENV" - name: Cache Build @@ -55,7 +50,7 @@ jobs: - name: Prepare ccache run: | - ccache --max-size=1.0G + ccache --max-size=20.0G ccache -V && ccache --show-stats && ccache --zero-stats - name: Configure @@ -67,7 +62,7 @@ jobs: -DCMAKE_BUILD_TYPE=${{ matrix.config }} - name: Build - run: cd build; make -j2; ccache --show-stats + run: cd build; make -j8; ccache --show-stats - name: Tests run: cd build; ctest --verbose --output-on-failure From 03b420fa18be74ca3e020df5dc46a1903d7d8f75 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Fri, 8 Sep 2023 13:47:06 -0400 Subject: [PATCH 036/134] Update continuous.yml for mac --- .github/workflows/continuous.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/continuous.yml b/.github/workflows/continuous.yml index aa7ea387b1..cec066f841 100644 --- a/.github/workflows/continuous.yml +++ b/.github/workflows/continuous.yml @@ -93,7 +93,6 @@ jobs: - name: Dependencies run: | - brew install ccache echo 'CACHE_PATH=~/Library/Caches/ccache' >> "$GITHUB_ENV" - name: Cache Build @@ -106,7 +105,7 @@ jobs: - name: Prepare ccache run: | - ccache --max-size=1.0G + ccache --max-size=20.0G ccache -V && ccache --show-stats && ccache --zero-stats - name: Configure @@ -119,10 +118,10 @@ jobs: -DCMAKE_BUILD_TYPE=${{ matrix.config }} - name: Build - run: cd build; make -j2; ccache --show-stats + run: cd build; make -j8; ccache --show-stats - name: Tests - if: matrix.arch == 'x86_64' # no ARM vm on actions yet + #if: matrix.arch == 'x86_64' # no ARM vm on actions yet run: cd build; ctest --verbose --output-on-failure #################### From 061c5b023fd5ac47655a8fb4bf7562b32da48931 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Fri, 8 Sep 2023 13:55:41 -0400 Subject: [PATCH 037/134] Update continuous.yml to use arm64 instead of ARM64 --- .github/workflows/continuous.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous.yml b/.github/workflows/continuous.yml index cec066f841..cf2e45893d 100644 --- a/.github/workflows/continuous.yml +++ b/.github/workflows/continuous.yml @@ -79,7 +79,7 @@ jobs: fail-fast: false matrix: os: [macos-latest] - arch: [ARM64] # arm64 no arm as GMP is binary + arch: [arm64] # arm64 no arm as GMP is binary config: [Debug, Release] include: - os: macos-latest From fb161cf0a0cae7a0e817ba81a6e3655bf059a27d Mon Sep 17 00:00:00 2001 From: Daniele Panozzo Date: Fri, 8 Sep 2023 15:04:18 -0400 Subject: [PATCH 038/134] Update continuous.yml -- windows fix --- .github/workflows/continuous.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous.yml b/.github/workflows/continuous.yml index cf2e45893d..faa7efd164 100644 --- a/.github/workflows/continuous.yml +++ b/.github/workflows/continuous.yml @@ -146,12 +146,13 @@ jobs: with: fetch-depth: 10 - uses: seanmiddleditch/gha-setup-ninja@master - + - name: Setup Conda - uses: s-weigand/setup-conda@v1 + uses: s-weigand/setup-conda@v1.2.1 with: conda-channels: anaconda, conda-forge - python-version: 3.6 + python-version: 3.9 + update-conda: true - name: Install Dependencies shell: powershell From a1ad942da25ad719182e2e62011fae2359704d1f Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Fri, 8 Sep 2023 17:29:23 -0400 Subject: [PATCH 039/134] updating doxygen build to not install --- .github/workflows/doxygen.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 12fe4ce9f8..167727ac95 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -12,7 +12,6 @@ jobs: - uses: actions/checkout@v3 - name: Config run: | - sudo apt-get install doxygen -y mkdir -p build cd build cmake -DWMTK_BUILD_DOCS=ON .. From 5d98ca89b0a6c6806b2fff1ff7c1d6b38c5f477b Mon Sep 17 00:00:00 2001 From: Daniele Panozzo Date: Fri, 8 Sep 2023 18:12:56 -0400 Subject: [PATCH 040/134] Update continuous.yml -- windows fix without cache --- .github/workflows/continuous.yml | 43 ++------------------------------ 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/.github/workflows/continuous.yml b/.github/workflows/continuous.yml index faa7efd164..04607ab889 100644 --- a/.github/workflows/continuous.yml +++ b/.github/workflows/continuous.yml @@ -145,59 +145,20 @@ jobs: uses: actions/checkout@v1 with: fetch-depth: 10 - - uses: seanmiddleditch/gha-setup-ninja@master - - name: Setup Conda - uses: s-weigand/setup-conda@v1.2.1 - with: - conda-channels: anaconda, conda-forge - python-version: 3.9 - update-conda: true - - - name: Install Dependencies - shell: powershell - run: | - conda install -c conda-forge mpir -y - - - name: Set env - run: | - echo "BOOST_ROOT=$env:BOOST_ROOT_1_72_0" >> ${env:GITHUB_ENV} - echo "appdata=$env:LOCALAPPDATA" >> ${env:GITHUB_ENV} - - - name: Cache build - id: cache-build - uses: actions/cache@v2 - with: - path: ${{ env.appdata }}\Mozilla\sccache - key: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.tbb }}-cache-${{ github.sha }} - restore-keys: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.tbb }}-cache - - - name: Prepare sccache - run: | - iwr -useb 'https://raw.githubusercontent.com/scoopinstaller/install/master/install.ps1' -outfile 'install.ps1' - .\install.ps1 -RunAsAdmin - scoop install sccache --global - # Scoop modifies the PATH so we make it available for the next steps of the job - echo "${env:PATH}" >> ${env:GITHUB_PATH} - - name: Configure and build shell: cmd run: | - call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=x64 + call "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat" -arch=x64 cmake --version cmake -G Ninja ^ - -DCMAKE_CXX_COMPILER_LAUNCHER=sccache ^ -DCMAKE_BUILD_TYPE=${{ matrix.config }} ^ -B build ^ -S . cd build - ninja -j1 + ninja -j8 - name: Tests run: | cd build ctest --verbose --output-on-failure - - - name: Setup tmate session - if: ${{ failure() }} - uses: mxschmitt/action-tmate@v3 From 88eb4863038b39dccb98e5e36d3d26b95100ba53 Mon Sep 17 00:00:00 2001 From: Daniele Panozzo Date: Fri, 8 Sep 2023 18:29:43 -0400 Subject: [PATCH 041/134] Update continuous.yml -- fix windows tests --- .github/workflows/continuous.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/continuous.yml b/.github/workflows/continuous.yml index 04607ab889..29c246ca32 100644 --- a/.github/workflows/continuous.yml +++ b/.github/workflows/continuous.yml @@ -159,6 +159,8 @@ jobs: ninja -j8 - name: Tests + shell: cmd run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat" -arch=x64 cd build ctest --verbose --output-on-failure From 3dae6db4b98943e9dbd2ee432927ce6d1ed55893 Mon Sep 17 00:00:00 2001 From: Daniel Zint Date: Sun, 10 Sep 2023 11:31:38 -0400 Subject: [PATCH 042/134] fix IDE groups --- CMakeLists.txt | 4 +++- components/CMakeLists.txt | 2 +- src/CMakeLists.txt | 4 ---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 52eb8bc3c5..60ecbaf8cd 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,7 +87,9 @@ add_library(wildmeshing_toolkit) add_library(wmtk::toolkit ALIAS wildmeshing_toolkit) add_subdirectory(src) - +# Group source files for IDEs +file(GLOB_RECURSE WMTK_FILES_FOR_SOURCE_GROUP "src/wmtk/*.cpp" "src/wmtk/*.h" "src/wmtk/*.hpp") +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/src/wmtk" PREFIX "wmtk" FILES ${WMTK_FILES_FOR_SOURCE_GROUP}) # Compile definitions diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 4c6a614906..ff133bf698 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -11,5 +11,5 @@ target_include_directories(wildmeshing_components PUBLIC ${CMAKE_CURRENT_SOURCE_ add_subdirectory(wmtk_components) # Group source files for IDEs -file(GLOB_RECURSE COMPONENTS_FILES_FOR_SOURCE_GROUP "cpp" "*/h" "*/hpp") +file(GLOB_RECURSE COMPONENTS_FILES_FOR_SOURCE_GROUP "*.cpp" "*.h" "*.hpp") source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/wmtk_components" PREFIX "components" FILES ${COMPONENTS_FILES_FOR_SOURCE_GROUP}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 49a3451748..5e0efb9d3c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,3 @@ # Include headers target_include_directories(wildmeshing_toolkit PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(wmtk) - -# Group source files for IDEs -file(GLOB_RECURSE WMTK_FILES_FOR_SOURCE_GROUP "*.cpp" "*.h" "*.hpp") -source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/wmtk" PREFIX "wmtk" FILES ${WMTK_FILES_FOR_SOURCE_GROUP}) From 66ad4707a51c0bf68e2bc4b9efb9dd49f00b869a Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sun, 10 Sep 2023 00:13:52 -0400 Subject: [PATCH 043/134] inheritance based accessors works --- .../isotropic_remeshing.cpp | 4 +- src/wmtk/Accessor.cpp | 13 +++ src/wmtk/Accessor.hpp | 51 ++++++---- src/wmtk/CMakeLists.txt | 1 - src/wmtk/Mesh.cpp | 30 +++--- src/wmtk/Mesh.hpp | 33 ++++--- src/wmtk/PointMesh.cpp | 2 +- src/wmtk/TetMesh.cpp | 34 +++---- src/wmtk/TriMesh.cpp | 46 ++++----- src/wmtk/TriMesh.hpp | 10 +- src/wmtk/TriMeshOperationExecutor.cpp | 74 +++++++-------- src/wmtk/TriMeshOperationExecutor.hpp | 1 - src/wmtk/attribute/AccessorBase.cpp | 42 ++++++--- src/wmtk/attribute/AccessorBase.hpp | 21 +++-- src/wmtk/attribute/Attribute.cpp | 4 +- src/wmtk/attribute/Attribute.hpp | 6 +- src/wmtk/attribute/AttributeCache.cpp | 2 +- src/wmtk/attribute/AttributeCache.hpp | 2 +- src/wmtk/attribute/AttributeHandle.hpp | 15 ++- src/wmtk/attribute/AttributeManager.cpp | 4 +- src/wmtk/attribute/AttributeManager.hpp | 8 +- src/wmtk/attribute/AttributeScope.cpp | 4 +- src/wmtk/attribute/AttributeScope.hpp | 10 +- src/wmtk/attribute/AttributeScopeHandle.cpp | 4 +- src/wmtk/attribute/AttributeScopeHandle.hpp | 4 +- src/wmtk/attribute/AttributeScopeStack.cpp | 6 +- src/wmtk/attribute/AttributeScopeStack.hpp | 6 +- src/wmtk/attribute/CMakeLists.txt | 17 +++- src/wmtk/attribute/CachingAccessor.cpp | 94 +++++++++++++++++++ src/wmtk/attribute/CachingAccessor.hpp | 85 +++++++++++++++++ src/wmtk/attribute/ConstAccessor.cpp | 35 +++++++ src/wmtk/attribute/ConstAccessor.hpp | 71 ++++++++++++++ src/wmtk/attribute/MeshAttributes.cpp | 4 +- src/wmtk/attribute/MeshAttributes.hpp | 6 +- src/wmtk/attribute/MutableAccessor.cpp | 18 ++++ src/wmtk/attribute/MutableAccessor.hpp | 42 +++++++++ .../PerThreadAttributeScopeStacks.cpp | 4 +- .../PerThreadAttributeScopeStacks.hpp | 4 +- src/wmtk/attribute/TupleAccessor.cpp | 44 +++++++++ src/wmtk/attribute/TupleAccessor.hpp | 51 ++++++++++ .../invariants/MaxEdgeLengthInvariant.cpp | 15 ++- .../invariants/MinEdgeLengthInvariant.cpp | 4 +- src/wmtk/io/MeshReader.cpp | 4 +- src/wmtk/utils/mesh_utils.cpp | 9 +- src/wmtk/utils/mesh_utils.hpp | 11 ++- tests/test_2d_operations.cpp | 44 ++++----- tests/test_accessor.cpp | 14 ++- tests/tools/DEBUG_PointMesh.hpp | 8 +- tests/tools/DEBUG_TriMesh.cpp | 4 +- tests/tools/DEBUG_TriMesh.hpp | 10 +- 50 files changed, 784 insertions(+), 251 deletions(-) create mode 100644 src/wmtk/attribute/CachingAccessor.cpp create mode 100644 src/wmtk/attribute/CachingAccessor.hpp create mode 100644 src/wmtk/attribute/ConstAccessor.cpp create mode 100644 src/wmtk/attribute/ConstAccessor.hpp create mode 100644 src/wmtk/attribute/MutableAccessor.cpp create mode 100644 src/wmtk/attribute/MutableAccessor.hpp create mode 100644 src/wmtk/attribute/TupleAccessor.cpp create mode 100644 src/wmtk/attribute/TupleAccessor.hpp diff --git a/components/wmtk_components/isotropic_remeshing/isotropic_remeshing.cpp b/components/wmtk_components/isotropic_remeshing/isotropic_remeshing.cpp index 87765c80ae..48fd9ac87f 100644 --- a/components/wmtk_components/isotropic_remeshing/isotropic_remeshing.cpp +++ b/components/wmtk_components/isotropic_remeshing/isotropic_remeshing.cpp @@ -22,7 +22,7 @@ double relative_to_absolute_length(const TriMesh& mesh, const double length_rel) p_max.setConstant(std::numeric_limits::max()); for (const Tuple& v : mesh.get_all(PrimitiveType::Vertex)) { - const Eigen::Vector3d p = pos.vector_attribute(v); + const Eigen::Vector3d p = pos.const_vector_attribute(v); p_max[0] = std::max(p_max[0], p[0]); p_max[1] = std::max(p_max[1], p[1]); p_max[2] = std::max(p_max[2], p[2]); @@ -74,4 +74,4 @@ void isotropic_remeshing( } } } // namespace components -} // namespace wmtk \ No newline at end of file +} // namespace wmtk diff --git a/src/wmtk/Accessor.cpp b/src/wmtk/Accessor.cpp index f59cd89a87..efa1c027f9 100644 --- a/src/wmtk/Accessor.cpp +++ b/src/wmtk/Accessor.cpp @@ -200,6 +200,19 @@ std::optional Accessor::stack_depth() const } } + +template +auto MutableAccessor::vector_attribute(const long index) -> MapResultT +{ + return cacheable_vector_attribute(index); +} + + +template +auto MutableAccessor::scalar_attribute(const long index) -> TT +{ + return cacheable_scalar_attribute(index); +} template class Accessor; template class Accessor; template class Accessor; diff --git a/src/wmtk/Accessor.hpp b/src/wmtk/Accessor.hpp index d4988bc75f..3046c57864 100644 --- a/src/wmtk/Accessor.hpp +++ b/src/wmtk/Accessor.hpp @@ -1,28 +1,38 @@ #pragma once -#include -#include -#include -#include "Tuple.hpp" -#include "attribute/AccessorBase.hpp" -#include "attribute/AttributeAccessMode.hpp" -#include "attribute/AttributeHandle.hpp" +// #include +// #include +// #include +// #include "Tuple.hpp" +//#include "attribute/AccessorBase.hpp" +//#include "attribute/AttributeAccessMode.hpp" +//#include "attribute/AttributeHandle.hpp" +#include "attribute/ConstAccessor.hpp" +#include "attribute/MutableAccessor.hpp" #include namespace wmtk { -class Mesh; -class TriMesh; -class TetMesh; -class PointMesh; -template -class AttributeScopeStack; +// class Mesh; +// class TriMesh; +// class TetMesh; +// class PointMesh; +// namespace attribute { +// template +// class AttributeScopeStack; +// +// template +// class AttributeCache; +//} // namespace attribute + +template +using Accessor = + std::conditional_t, attribute::MutableAccessor>; -template -class AttributeCache; +/* template -class Accessor : public AccessorBase +class Accessor : public attribute::AccessorBase { public: friend class Mesh; @@ -31,8 +41,8 @@ class Accessor : public AccessorBase friend class TetMesh; using Scalar = T; - friend class AttributeCache; - using BaseType = AccessorBase; + friend class attribute::AttributeCache; + using BaseType = attribute::AccessorBase; using MeshType = std::conditional_t; // const correct Mesh object using MapResult = typename BaseType::MapResult; // Eigen::Map> @@ -70,8 +80,8 @@ class Accessor : public AccessorBase // returns the size of the underlying attribute - using BaseType::reserved_size; // const() -> long using BaseType::dimension; // const() -> long + using BaseType::reserved_size; // const() -> long using BaseType::attribute; // access to Attribute object being used here using BaseType::set_attribute; // (const vector&) -> void @@ -102,7 +112,7 @@ class Accessor : public AccessorBase MeshType& m_mesh; AttributeAccessMode m_mode; - AttributeScopeStack* m_cache_stack = nullptr; + attribute::AttributeScopeStack* m_cache_stack = nullptr; }; // template @@ -111,6 +121,7 @@ class Accessor : public AccessorBase // Accessor(const Mesh& mesh, const MeshAttributeHandle&) -> Accessor; +*/ template using ConstAccessor = Accessor; } // namespace wmtk diff --git a/src/wmtk/CMakeLists.txt b/src/wmtk/CMakeLists.txt index a0c80695cd..4e35e76e26 100644 --- a/src/wmtk/CMakeLists.txt +++ b/src/wmtk/CMakeLists.txt @@ -1,6 +1,5 @@ set(SRC_FILES - Accessor.cpp Accessor.hpp Mesh.cpp Mesh.hpp diff --git a/src/wmtk/Mesh.cpp b/src/wmtk/Mesh.cpp index 83b5085b88..43fe819bc4 100644 --- a/src/wmtk/Mesh.cpp +++ b/src/wmtk/Mesh.cpp @@ -28,11 +28,12 @@ Mesh::~Mesh() = default; std::vector Mesh::get_all(PrimitiveType type) const { ConstAccessor flag_accessor = get_flag_accessor(type); + const attribute::CachingAccessor& flag_accessor_indices = flag_accessor.index_access(); std::vector ret; long cap = capacity(type); ret.reserve(cap); for (size_t index = 0; index < cap; ++index) { - if ((flag_accessor.scalar_attribute(index) & 1)) { + if ((flag_accessor_indices.const_scalar_attribute(index) & 1)) { ret.emplace_back(tuple_from_id(type, index)); } } @@ -81,9 +82,10 @@ std::vector Mesh::request_simplex_indices(PrimitiveType type, long count) m_attribute_manager.m_capacities[simplex_dim] = new_capacity; + attribute::CachingAccessor& flag_accessor_indices = flag_accessor.index_access(); for (const long simplex_index : ret) { - flag_accessor.scalar_attribute(simplex_index) |= 0x1; + flag_accessor_indices.scalar_attribute(simplex_index) |= 0x1; } return ret; @@ -164,7 +166,11 @@ Accessor Mesh::get_cell_hash_accessor() void Mesh::update_cell_hash(const Tuple& cell, Accessor& hash_accessor) { const long cid = cell.m_global_cid; - ++hash_accessor.scalar_attribute(cid); + update_cell_hash(cid, hash_accessor); +} +void Mesh::update_cell_hash(const long cid, Accessor& hash_accessor) +{ + ++hash_accessor.index_access().scalar_attribute(cid); } void Mesh::update_cell_hashes(const std::vector& cells, Accessor& hash_accessor) @@ -173,6 +179,12 @@ void Mesh::update_cell_hashes(const std::vector& cells, Accessor& h update_cell_hash(t, hash_accessor); } } +void Mesh::update_cell_hashes(const std::vector& cells, Accessor& hash_accessor) +{ + for (const long t : cells) { + update_cell_hash(t, hash_accessor); + } +} void Mesh::update_cell_hashes_slow(const std::vector& cells) { @@ -180,17 +192,11 @@ void Mesh::update_cell_hashes_slow(const std::vector& cells) update_cell_hashes(cells, hash_accessor); } -Tuple Mesh::resurrect_tuple(const Tuple& tuple, const Accessor& hash_accessor) const -{ - Tuple t = tuple; - t.m_hash = hash_accessor.scalar_attribute(tuple.m_global_cid); - return t; -} Tuple Mesh::resurrect_tuple(const Tuple& tuple, const ConstAccessor& hash_accessor) const { Tuple t = tuple; - t.m_hash = hash_accessor.scalar_attribute(tuple.m_global_cid); + t.m_hash = get_cell_hash(tuple.m_global_cid, hash_accessor); return t; } @@ -202,7 +208,7 @@ Tuple Mesh::resurrect_tuple_slow(const Tuple& tuple) long Mesh::get_cell_hash(long cell_index, const ConstAccessor& hash_accessor) const { - return hash_accessor.scalar_attribute(cell_index); + return hash_accessor.index_access().const_scalar_attribute(cell_index); } long Mesh::get_cell_hash_slow(long cell_index) const @@ -241,7 +247,7 @@ std::vector> Mesh::simplices_to_gids( return gids; } -AttributeScopeHandle Mesh::create_scope() +attribute::AttributeScopeHandle Mesh::create_scope() { return m_attribute_manager.create_scope(*this); } diff --git a/src/wmtk/Mesh.hpp b/src/wmtk/Mesh.hpp index 9bb409fecf..03b59a6fb5 100644 --- a/src/wmtk/Mesh.hpp +++ b/src/wmtk/Mesh.hpp @@ -15,7 +15,12 @@ namespace wmtk { // thread management tool that we will PImpl +namespace attribute { class AttributeScopeManager; +template +class TupleAccessor; + +} namespace operations { class Operation; } @@ -25,7 +30,9 @@ class Mesh : public std::enable_shared_from_this public: template - friend class AccessorBase; + friend class attribute::AccessorBase; + template + friend class attribute::TupleAccessor; friend class ParaviewWriter; friend class MeshReader; friend class MultiMeshManager; @@ -98,7 +105,7 @@ class Mesh : public std::enable_shared_from_this // creates a scope as long as the AttributeScopeHandle exists - [[nodiscard]] AttributeScopeHandle create_scope(); + [[nodiscard]] attribute::AttributeScopeHandle create_scope(); ConstAccessor get_flag_accessor(PrimitiveType type) const; @@ -138,19 +145,23 @@ class Mesh : public std::enable_shared_from_this /** * @brief same as `update_cell_hashes` but slow because it creates a new accessor */ - void update_cell_hashes_slow(const std::vector& cells); - /** - * @brief DEPRECATED return the same tuple but with updated hash + * @brief update hash in given cell * - * This function should only be used in operations to create a valid return tuple in a known - * position. + * @param cell tuple in which the hash should be updated + * @param hash_accessor hash accessor + */ + void update_cell_hash(const long cell_index, Accessor& hash_accessor); + + /** + * @brief update hashes in given cells * - * @param tuple tuple with potentially outdated hash + * @param cells vector of tuples in which the hash should be updated * @param hash_accessor hash accessor - * @return tuple with updated hash */ - Tuple resurrect_tuple(const Tuple& tuple, const Accessor& hash_accessor) const; + void update_cell_hashes(const std::vector& cell_indices, Accessor& hash_accessor); + + void update_cell_hashes_slow(const std::vector& cells); /** * @brief return the same tuple but with updated hash @@ -286,7 +297,7 @@ class Mesh : public std::enable_shared_from_this //[[nodiscard]] AccessorScopeHandle push_accesor_scope(); private: // members - AttributeManager m_attribute_manager; + attribute::AttributeManager m_attribute_manager; // PImpl'd manager of per-thread update stacks // Every time a new access scope is requested the manager creates another level of indirection diff --git a/src/wmtk/PointMesh.cpp b/src/wmtk/PointMesh.cpp index 3eae7d0966..fcd54d748f 100644 --- a/src/wmtk/PointMesh.cpp +++ b/src/wmtk/PointMesh.cpp @@ -44,7 +44,7 @@ void PointMesh::initialize(long count) reserve_attributes_to_fit(); Accessor v_flag_accessor = get_flag_accessor(PrimitiveType::Vertex); for (long i = 0; i < capacity(PrimitiveType::Vertex); ++i) { - v_flag_accessor.scalar_attribute(i) |= 0x1; + v_flag_accessor.index_access().scalar_attribute(i) |= 0x1; } } diff --git a/src/wmtk/TetMesh.cpp b/src/wmtk/TetMesh.cpp index bfcc529bb4..edd07b2367 100644 --- a/src/wmtk/TetMesh.cpp +++ b/src/wmtk/TetMesh.cpp @@ -58,26 +58,26 @@ void TetMesh::initialize( // iterate over the matrices and fill attributes for (long i = 0; i < capacity(PrimitiveType::Tetrahedron); ++i) { - tv_accessor.vector_attribute(i) = TV.row(i).transpose(); - te_accessor.vector_attribute(i) = TE.row(i).transpose(); - tf_accessor.vector_attribute(i) = TF.row(i).transpose(); - tt_accessor.vector_attribute(i) = TT.row(i).transpose(); - t_flag_accessor.scalar_attribute(i) |= 0x1; + tv_accessor.index_access().vector_attribute(i) = TV.row(i).transpose(); + te_accessor.index_access().vector_attribute(i) = TE.row(i).transpose(); + tf_accessor.index_access().vector_attribute(i) = TF.row(i).transpose(); + tt_accessor.index_access().vector_attribute(i) = TT.row(i).transpose(); + t_flag_accessor.index_access().scalar_attribute(i) |= 0x1; } // m_vt for (long i = 0; i < capacity(PrimitiveType::Vertex); ++i) { - vt_accessor.scalar_attribute(i) = VT(i); - v_flag_accessor.scalar_attribute(i) |= 0x1; + vt_accessor.index_access().scalar_attribute(i) = VT(i); + v_flag_accessor.index_access().scalar_attribute(i) |= 0x1; } // m_et for (long i = 0; i < capacity(PrimitiveType::Edge); ++i) { - et_accessor.scalar_attribute(i) = ET(i); - e_flag_accessor.scalar_attribute(i) |= 0x1; + et_accessor.index_access().scalar_attribute(i) = ET(i); + e_flag_accessor.index_access().scalar_attribute(i) |= 0x1; } // m_ft for (long i = 0; i < capacity(PrimitiveType::Face); ++i) { - ft_accessor.scalar_attribute(i) = FT(i); - f_flag_accessor.scalar_attribute(i) |= 0x1; + ft_accessor.index_access().scalar_attribute(i) = FT(i); + f_flag_accessor.index_access().scalar_attribute(i) |= 0x1; } } @@ -98,9 +98,9 @@ long TetMesh::_debug_id(const Tuple& tuple, PrimitiveType type) const Tuple TetMesh::vertex_tuple_from_id(long id) const { ConstAccessor vt_accessor = create_accessor(m_vt_handle); - auto t = vt_accessor.scalar_attribute(id); + long t = vt_accessor.index_access().scalar_attribute(id); ConstAccessor tv_accessor = create_accessor(m_tv_handle); - auto tv = tv_accessor.vector_attribute(t); + auto tv = tv_accessor.index_access().vector_attribute(t); long lvid = -1, leid = -1, lfid = -1; for (long i = 0; i < 4; ++i) { @@ -128,9 +128,9 @@ Tuple TetMesh::vertex_tuple_from_id(long id) const Tuple TetMesh::edge_tuple_from_id(long id) const { ConstAccessor et_accessor = create_accessor(m_et_handle); - auto t = et_accessor.scalar_attribute(id); + long t = et_accessor.index_access().scalar_attribute(id); ConstAccessor te_accessor = create_accessor(m_te_handle); - auto te = te_accessor.vector_attribute(t); + auto te = te_accessor.index_access().vector_attribute(t); long lvid = -1, leid = -1, lfid = -1; @@ -159,9 +159,9 @@ Tuple TetMesh::edge_tuple_from_id(long id) const Tuple TetMesh::face_tuple_from_id(long id) const { ConstAccessor ft_accessor = create_accessor(m_ft_handle); - auto t = ft_accessor.scalar_attribute(id); + long t = ft_accessor.index_access().scalar_attribute(id); ConstAccessor tf_accessor = create_accessor(m_tf_handle); - auto tf = tf_accessor.vector_attribute(t); + auto tf = tf_accessor.index_access().vector_attribute(t); long lvid = -1, leid = -1, lfid = -1; diff --git a/src/wmtk/TriMesh.cpp b/src/wmtk/TriMesh.cpp index c1241a2155..4ad5330a7d 100644 --- a/src/wmtk/TriMesh.cpp +++ b/src/wmtk/TriMesh.cpp @@ -119,10 +119,10 @@ Tuple TriMesh::switch_tuple(const Tuple& tuple, PrimitiveType type) const long lvid_new = -1, leid_new = -1; ConstAccessor fv_accessor = create_const_accessor(m_fv_handle); - auto fv = fv_accessor.vector_attribute(gcid_new); + auto fv = fv_accessor.index_access().vector_attribute(gcid_new); ConstAccessor fe_accessor = create_const_accessor(m_fe_handle); - auto fe = fe_accessor.vector_attribute(gcid_new); + auto fe = fe_accessor.index_access().vector_attribute(gcid_new); for (long i = 0; i < 3; ++i) { if (fe(i) == geid) { @@ -189,21 +189,21 @@ void TriMesh::initialize( // iterate over the matrices and fill attributes for (long i = 0; i < capacity(PrimitiveType::Face); ++i) { - fv_accessor.vector_attribute(i) = FV.row(i).transpose(); - fe_accessor.vector_attribute(i) = FE.row(i).transpose(); - ff_accessor.vector_attribute(i) = FF.row(i).transpose(); + fv_accessor.index_access().vector_attribute(i) = FV.row(i).transpose(); + fe_accessor.index_access().vector_attribute(i) = FE.row(i).transpose(); + ff_accessor.index_access().vector_attribute(i) = FF.row(i).transpose(); - f_flag_accessor.scalar_attribute(i) |= 0x1; + f_flag_accessor.index_access().scalar_attribute(i) |= 0x1; } // m_vf for (long i = 0; i < capacity(PrimitiveType::Vertex); ++i) { - vf_accessor.scalar_attribute(i) = VF(i); - v_flag_accessor.scalar_attribute(i) |= 0x1; + vf_accessor.index_access().scalar_attribute(i) = VF(i); + v_flag_accessor.index_access().scalar_attribute(i) |= 0x1; } // m_ef for (long i = 0; i < capacity(PrimitiveType::Edge); ++i) { - ef_accessor.scalar_attribute(i) = EF(i); - e_flag_accessor.scalar_attribute(i) |= 0x1; + ef_accessor.index_access().scalar_attribute(i) = EF(i); + e_flag_accessor.index_access().scalar_attribute(i) |= 0x1; } } @@ -243,9 +243,9 @@ Tuple TriMesh::tuple_from_id(const PrimitiveType type, const long gid) const Tuple TriMesh::vertex_tuple_from_id(long id) const { ConstAccessor vf_accessor = create_const_accessor(m_vf_handle); - auto f = vf_accessor.scalar_attribute(id); + auto f = vf_accessor.index_access().scalar_attribute(id); ConstAccessor fv_accessor = create_const_accessor(m_fv_handle); - auto fv = fv_accessor.vector_attribute(f); + auto fv = fv_accessor.index_access().vector_attribute(f); for (long i = 0; i < 3; ++i) { if (fv(i) == id) { assert(autogen::auto_2d_table_complete_vertex[i][0] == i); @@ -267,9 +267,9 @@ Tuple TriMesh::vertex_tuple_from_id(long id) const Tuple TriMesh::edge_tuple_from_id(long id) const { ConstAccessor ef_accessor = create_const_accessor(m_ef_handle); - auto f = ef_accessor.scalar_attribute(id); + auto f = ef_accessor.index_access().scalar_attribute(id); ConstAccessor fe_accessor = create_const_accessor(m_fe_handle); - auto fe = fe_accessor.vector_attribute(f); + auto fe = fe_accessor.index_access().vector_attribute(f); for (long i = 0; i < 3; ++i) { if (fe(i) == id) { assert(autogen::auto_2d_table_complete_edge[i][1] == i); @@ -331,13 +331,13 @@ bool TriMesh::is_connectivity_valid() const // EF and FE for (long i = 0; i < capacity(PrimitiveType::Edge); ++i) { - if (e_flag_accessor.scalar_attribute(i) == 0) { + if (e_flag_accessor.index_access().scalar_attribute(i) == 0) { wmtk::logger().debug("Edge {} is deleted", i); continue; } int cnt = 0; for (long j = 0; j < 3; ++j) { - if (fe_accessor.vector_attribute(ef_accessor.scalar_attribute(i))[j] == i) { + if (fe_accessor.index_access().vector_attribute(ef_accessor.index_access().scalar_attribute(i))[j] == i) { cnt++; } } @@ -349,13 +349,13 @@ bool TriMesh::is_connectivity_valid() const // VF and FV for (long i = 0; i < capacity(PrimitiveType::Vertex); ++i) { - if (v_flag_accessor.scalar_attribute(i) == 0) { + if (v_flag_accessor.index_access().scalar_attribute(i) == 0) { wmtk::logger().debug("Vertex {} is deleted", i); continue; } int cnt = 0; for (long j = 0; j < 3; ++j) { - if (fv_accessor.vector_attribute(vf_accessor.scalar_attribute(i))[j] == i) { + if (fv_accessor.index_access().vector_attribute(vf_accessor.index_access().scalar_attribute(i))[j] == i) { cnt++; } } @@ -367,14 +367,14 @@ bool TriMesh::is_connectivity_valid() const // FE and EF for (long i = 0; i < capacity(PrimitiveType::Face); ++i) { - if (f_flag_accessor.scalar_attribute(i) == 0) { + if (f_flag_accessor.index_access().scalar_attribute(i) == 0) { wmtk::logger().debug("Face {} is deleted", i); continue; } for (long j = 0; j < 3; ++j) { - long nb = ff_accessor.vector_attribute(i)[j]; + long nb = ff_accessor.index_access().vector_attribute(i)[j]; if (nb == -1) { - if (ef_accessor.scalar_attribute(fe_accessor.vector_attribute(i)[j]) != i) { + if (ef_accessor.index_access().const_scalar_attribute(fe_accessor.index_access().const_vector_attribute(i)[j]) != i) { // std::cout << "FF and FE not compatible" << std::endl; return false; } @@ -384,7 +384,7 @@ bool TriMesh::is_connectivity_valid() const int cnt = 0; int id_in_nb; for (long k = 0; k < 3; ++k) { - if (ff_accessor.vector_attribute(nb)[k] == i) { + if (ff_accessor.index_access().const_vector_attribute(nb)[k] == i) { cnt++; id_in_nb = k; } @@ -395,7 +395,7 @@ bool TriMesh::is_connectivity_valid() const return false; } - if (fe_accessor.vector_attribute(i)[j] != fe_accessor.vector_attribute(nb)[id_in_nb]) { + if (fe_accessor.index_access().const_vector_attribute(i)[j] != fe_accessor.index_access().const_vector_attribute(nb)[id_in_nb]) { // std::cout << "FF and FE not compatible" << std::endl; return false; } diff --git a/src/wmtk/TriMesh.hpp b/src/wmtk/TriMesh.hpp index 642806452c..f245520978 100644 --- a/src/wmtk/TriMesh.hpp +++ b/src/wmtk/TriMesh.hpp @@ -86,12 +86,12 @@ class TriMesh : public Mesh Tuple tuple_from_id(const PrimitiveType type, const long gid) const override; protected: - MeshAttributeHandle m_vf_handle; - MeshAttributeHandle m_ef_handle; + attribute::MeshAttributeHandle m_vf_handle; + attribute::MeshAttributeHandle m_ef_handle; - MeshAttributeHandle m_fv_handle; - MeshAttributeHandle m_fe_handle; - MeshAttributeHandle m_ff_handle; + attribute::MeshAttributeHandle m_fv_handle; + attribute::MeshAttributeHandle m_fe_handle; + attribute::MeshAttributeHandle m_ff_handle; Tuple vertex_tuple_from_id(long id) const; Tuple edge_tuple_from_id(long id) const; diff --git a/src/wmtk/TriMeshOperationExecutor.cpp b/src/wmtk/TriMeshOperationExecutor.cpp index 3a3bb7ac50..9478ad14d8 100644 --- a/src/wmtk/TriMeshOperationExecutor.cpp +++ b/src/wmtk/TriMeshOperationExecutor.cpp @@ -94,16 +94,14 @@ void TriMesh::TriMeshOperationExecutor::delete_simplices() { for (size_t d = 0; d < simplex_ids_to_delete.size(); ++d) { for (const long id : simplex_ids_to_delete[d]) { - flag_accessors[d].scalar_attribute(id) = 0; + flag_accessors[d].index_access().scalar_attribute(id) = 0; } } } void TriMesh::TriMeshOperationExecutor::update_cell_hash() { - for (const long& cell_id : cell_ids_to_update_hash) { - ++hash_accessor.scalar_attribute(cell_id); - } + m_mesh.update_cell_hashes(cell_ids_to_update_hash, hash_accessor); } const std::array, 3> @@ -163,8 +161,8 @@ void TriMesh::TriMeshOperationExecutor::update_ids_in_ear( // ----- if (ear_fid < 0) return; - auto ear_ff = ff_accessor.vector_attribute(ear_fid); - auto ear_fe = fe_accessor.vector_attribute(ear_fid); + auto ear_ff = ff_accessor.index_access().vector_attribute(ear_fid); + auto ear_fe = fe_accessor.index_access().vector_attribute(ear_fid); for (int i = 0; i < 3; ++i) { if (ear_ff[i] == old_fid) { ear_ff[i] = new_fid; @@ -173,7 +171,7 @@ void TriMesh::TriMeshOperationExecutor::update_ids_in_ear( } } - ef_accessor.scalar_attribute(new_eid) = ear_fid; + ef_accessor.index_access().scalar_attribute(new_eid) = ear_fid; } void TriMesh::TriMeshOperationExecutor::connect_ears() @@ -207,12 +205,12 @@ void TriMesh::TriMeshOperationExecutor::connect_ears() assert(ef0 != ef1); // change face for v2 - long& v2_face = vf_accessor.scalar_attribute(v2); + long& v2_face = vf_accessor.index_access().scalar_attribute(v2); // use ef0 if it exists v2_face = (ef0 < 0) ? ef1 : ef0; - ef_accessor.scalar_attribute(ee1) = v2_face; - vf_accessor.scalar_attribute(v1) = v2_face; + ef_accessor.index_access().scalar_attribute(ee1) = v2_face; + vf_accessor.index_access().scalar_attribute(v1) = v2_face; // change FF and FE for ears update_ids_in_ear(ef0, ef1, f_old, ee1); @@ -230,8 +228,8 @@ void TriMesh::TriMeshOperationExecutor::connect_faces_across_spine() const long f_old_bottom = m_incident_face_datas[1].fid; const long f0_bottom = m_incident_face_datas[1].split_f0; const long f1_bottom = m_incident_face_datas[1].split_f1; - auto ff_old_top = ff_accessor.vector_attribute(f_old_top); - auto ff_old_bottom = ff_accessor.vector_attribute(f_old_bottom); + auto ff_old_top = ff_accessor.index_access().vector_attribute(f_old_top); + auto ff_old_bottom = ff_accessor.index_access().vector_attribute(f_old_bottom); assert(m_mesh.capacity(PrimitiveType::Face) > f0_top); assert(m_mesh.capacity(PrimitiveType::Face) > f1_top); assert(m_mesh.capacity(PrimitiveType::Face) > f0_bottom); @@ -251,10 +249,10 @@ void TriMesh::TriMeshOperationExecutor::connect_faces_across_spine() assert(local_eid_top > -1); assert(local_eid_bottom > -1); // TODO write test for assumming top and bottom new fids are in right correspondence - ff_accessor.vector_attribute(f0_top)[local_eid_top] = f0_bottom; - ff_accessor.vector_attribute(f0_bottom)[local_eid_bottom] = f0_top; - ff_accessor.vector_attribute(f1_top)[local_eid_top] = f1_bottom; - ff_accessor.vector_attribute(f1_bottom)[local_eid_bottom] = f1_top; + ff_accessor.index_access().vector_attribute(f0_top)[local_eid_top] = f0_bottom; + ff_accessor.index_access().vector_attribute(f0_bottom)[local_eid_bottom] = f0_top; + ff_accessor.index_access().vector_attribute(f1_top)[local_eid_top] = f1_bottom; + ff_accessor.index_access().vector_attribute(f1_bottom)[local_eid_bottom] = f1_top; } void TriMesh::TriMeshOperationExecutor::replace_incident_face( @@ -302,12 +300,12 @@ void TriMesh::TriMeshOperationExecutor::replace_incident_face( { update_ids_in_ear(ef0, f0, f_old, ee0); - auto fv = fv_accessor.vector_attribute(f0); - auto fe = fe_accessor.vector_attribute(f0); - auto ff = ff_accessor.vector_attribute(f0); - fv = fv_accessor.vector_attribute(f_old); - fe = fe_accessor.vector_attribute(f_old); - ff = ff_accessor.vector_attribute(f_old); + auto fv = fv_accessor.index_access().vector_attribute(f0); + auto fe = fe_accessor.index_access().vector_attribute(f0); + auto ff = ff_accessor.index_access().vector_attribute(f0); + fv = fv_accessor.index_access().vector_attribute(f_old); + fe = fe_accessor.index_access().vector_attribute(f_old); + ff = ff_accessor.index_access().vector_attribute(f_old); // correct old connectivity for (size_t i = 0; i < 3; ++i) { if (fe[i] == ee1) { @@ -328,12 +326,12 @@ void TriMesh::TriMeshOperationExecutor::replace_incident_face( { update_ids_in_ear(ef1, f1, f_old, ee1); - auto fv = fv_accessor.vector_attribute(f1); - auto fe = fe_accessor.vector_attribute(f1); - auto ff = ff_accessor.vector_attribute(f1); - fv = fv_accessor.vector_attribute(f_old); - fe = fe_accessor.vector_attribute(f_old); - ff = ff_accessor.vector_attribute(f_old); + auto fv = fv_accessor.index_access().vector_attribute(f1); + auto fe = fe_accessor.index_access().vector_attribute(f1); + auto ff = ff_accessor.index_access().vector_attribute(f1); + fv = fv_accessor.index_access().vector_attribute(f_old); + fe = fe_accessor.index_access().vector_attribute(f_old); + ff = ff_accessor.index_access().vector_attribute(f_old); // correct old connectivity for (size_t i = 0; i < 3; ++i) { if (fe[i] == ee0) { @@ -352,16 +350,16 @@ void TriMesh::TriMeshOperationExecutor::replace_incident_face( } // assign each edge one face - ef_accessor.scalar_attribute(ee0) = f0; - ef_accessor.scalar_attribute(ee1) = f1; - ef_accessor.scalar_attribute(oe) = f0; - ef_accessor.scalar_attribute(se0) = f0; - ef_accessor.scalar_attribute(se1) = f1; + ef_accessor.index_access().scalar_attribute(ee0) = f0; + ef_accessor.index_access().scalar_attribute(ee1) = f1; + ef_accessor.index_access().scalar_attribute(oe) = f0; + ef_accessor.index_access().scalar_attribute(se0) = f0; + ef_accessor.index_access().scalar_attribute(se1) = f1; // assign each vertex one face - vf_accessor.scalar_attribute(v0) = f0; - vf_accessor.scalar_attribute(v1) = f1; - vf_accessor.scalar_attribute(v2) = f0; - vf_accessor.scalar_attribute(v_new) = f0; + vf_accessor.index_access().scalar_attribute(v0) = f0; + vf_accessor.index_access().scalar_attribute(v1) = f1; + vf_accessor.index_access().scalar_attribute(v2) = f0; + vf_accessor.index_access().scalar_attribute(v_new) = f0; // face neighbors on the other side of the spine are updated separately @@ -600,7 +598,7 @@ Tuple TriMesh::TriMeshOperationExecutor::collapse_edge_single_mesh() // replace v0 by v1 in incident faces for (const Simplex& f : v0_star.get_faces()) { const long fid = m_mesh.id(f); - auto fv = fv_accessor.vector_attribute(fid); + auto fv = fv_accessor.index_access().vector_attribute(fid); for (long i = 0; i < 3; ++i) { if (fv[i] == v0) { fv[i] = v1; diff --git a/src/wmtk/TriMeshOperationExecutor.hpp b/src/wmtk/TriMeshOperationExecutor.hpp index a471cb77b9..da0b2fe376 100644 --- a/src/wmtk/TriMeshOperationExecutor.hpp +++ b/src/wmtk/TriMeshOperationExecutor.hpp @@ -116,7 +116,6 @@ class TriMesh::TriMeshOperationExecutor TriMesh& m_mesh; Tuple m_operating_tuple; - long hash_at_cell(const long cell) { return hash_accessor.scalar_attribute(cell); } private: std::vector> prepare_operating_tuples_for_child_meshes() const; diff --git a/src/wmtk/attribute/AccessorBase.cpp b/src/wmtk/attribute/AccessorBase.cpp index a161aa1a65..4c68932d9a 100644 --- a/src/wmtk/attribute/AccessorBase.cpp +++ b/src/wmtk/attribute/AccessorBase.cpp @@ -5,18 +5,36 @@ #include "MeshAttributes.hpp" #include "wmtk/Mesh.hpp" -namespace wmtk { +namespace wmtk::attribute { template AccessorBase::AccessorBase(Mesh& m, const MeshAttributeHandle& handle) - : AccessorBase(m.m_attribute_manager, handle) + : m_mesh(m) + , m_handle(handle) {} +template +Mesh& AccessorBase::mesh() +{ + return m_mesh; +} +template +const Mesh& AccessorBase::mesh() const +{ + return m_mesh; +} template -AccessorBase::AccessorBase(AttributeManager& am, const MeshAttributeHandle& handle) - : m_attribute_manager(am) - , m_handle(handle) -{} +const AttributeManager& AccessorBase::attribute_manager() const +{ + return mesh().m_attribute_manager; +} + +template +AttributeManager& AccessorBase::attribute_manager() +{ + return mesh().m_attribute_manager; +} + template AccessorBase::~AccessorBase() = default; @@ -37,12 +55,12 @@ long AccessorBase::dimension() const template auto AccessorBase::attributes() -> MeshAttributes& { - return m_attribute_manager.get(m_handle); + return attribute_manager().get(m_handle); } template auto AccessorBase::attributes() const -> const MeshAttributes& { - return m_attribute_manager.get(m_handle); + return attribute_manager().get(m_handle); } template auto AccessorBase::attribute() -> Attribute& @@ -65,12 +83,6 @@ PrimitiveType AccessorBase::primitive_type() const { return handle().m_primitive_type; } -template -long AccessorBase::index(const Mesh& mesh, const Tuple& t) const -{ - assert(mesh.is_valid_slow(t)); - return mesh.id(t, m_handle.m_primitive_type); -} template auto AccessorBase::const_vector_attribute(const long index) const -> ConstMapResult @@ -111,4 +123,4 @@ template class AccessorBase; template class AccessorBase; template class AccessorBase; template class AccessorBase; -} // namespace wmtk +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/AccessorBase.hpp b/src/wmtk/attribute/AccessorBase.hpp index 363108a180..eb95a2f756 100644 --- a/src/wmtk/attribute/AccessorBase.hpp +++ b/src/wmtk/attribute/AccessorBase.hpp @@ -9,16 +9,16 @@ #include -namespace wmtk { +namespace wmtk::attribute { template class MeshAttributes; template class AccessorCache; - // The basic implementation of an accessor using indices. -// This should never be +// This should never be externally used except within the main accessor +// interface, in unit tests, or in topological updates template class AccessorBase { @@ -55,20 +55,21 @@ class AccessorBase ~AccessorBase(); AccessorBase(Mesh& m, const MeshAttributeHandle& handle); - AccessorBase(AttributeManager& m, const MeshAttributeHandle& handle); const MeshAttributeHandle& handle() const; PrimitiveType primitive_type() const; - // convenience function for use within Accessor - // Mainly here to avoid having to friend an Accessor to Mesh as well - long index(const Mesh& m, const Tuple& t) const; + Mesh& mesh(); + const Mesh& mesh() const; + protected: - // Mesh& m_mesh; - AttributeManager& m_attribute_manager; + Mesh& m_mesh; MeshAttributeHandle m_handle; + + const AttributeManager& attribute_manager() const; + AttributeManager& attribute_manager(); }; -} // namespace wmtk +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/Attribute.cpp b/src/wmtk/attribute/Attribute.cpp index a6a3c502bf..af137fdf19 100644 --- a/src/wmtk/attribute/Attribute.cpp +++ b/src/wmtk/attribute/Attribute.cpp @@ -3,7 +3,7 @@ #include #include -namespace wmtk { +namespace wmtk::attribute { template @@ -162,4 +162,4 @@ template class Attribute; template class Attribute; template class Attribute; template class Attribute; -} // namespace wmtk +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/Attribute.hpp b/src/wmtk/attribute/Attribute.hpp index 2c8f02c6a0..8f9d925fc8 100644 --- a/src/wmtk/attribute/Attribute.hpp +++ b/src/wmtk/attribute/Attribute.hpp @@ -6,6 +6,8 @@ namespace wmtk { class MeshWriter; + +namespace attribute { template class AccessorBase; @@ -45,7 +47,8 @@ class Attribute T& scalar_attribute(const long index); void set(std::vector val); - // The total number of elements in a vector. This is greater than the number of active values in the attribute, and the set of active values is handled by a higher level abstraction + // The total number of elements in a vector. This is greater than the number of active values in + // the attribute, and the set of active values is handled by a higher level abstraction long reserved_size() const; // The number of data for each element in the vector long dimension() const; @@ -65,4 +68,5 @@ class Attribute std::unique_ptr> m_scope_stacks; long m_dimension = -1; }; +} // namespace attribute } // namespace wmtk diff --git a/src/wmtk/attribute/AttributeCache.cpp b/src/wmtk/attribute/AttributeCache.cpp index 53a16b8c98..6096c63a16 100644 --- a/src/wmtk/attribute/AttributeCache.cpp +++ b/src/wmtk/attribute/AttributeCache.cpp @@ -1,7 +1,7 @@ #include "AttributeCache.hpp" #include -namespace wmtk { +namespace wmtk::attribute { template diff --git a/src/wmtk/attribute/AttributeCache.hpp b/src/wmtk/attribute/AttributeCache.hpp index 5a3bf1eec6..4eb0b216ab 100644 --- a/src/wmtk/attribute/AttributeCache.hpp +++ b/src/wmtk/attribute/AttributeCache.hpp @@ -6,7 +6,7 @@ #include "AttributeCacheData.hpp" -namespace wmtk { +namespace wmtk::attribute { template class AttributeCache { diff --git a/src/wmtk/attribute/AttributeHandle.hpp b/src/wmtk/attribute/AttributeHandle.hpp index 40d8aeaa53..0c91ca501a 100644 --- a/src/wmtk/attribute/AttributeHandle.hpp +++ b/src/wmtk/attribute/AttributeHandle.hpp @@ -2,14 +2,16 @@ #include #include "wmtk/Primitive.hpp" namespace wmtk { + class Mesh; +namespace attribute { template class MeshAttributes; -template -class Accessor; template class AccessorBase; template -class MeshAttribteHandle; +class TupleAccessor; +template +class MeshAttributeHandle; struct AttributeManager; class AttributeHandle @@ -40,9 +42,10 @@ template class MeshAttributeHandle { private: - friend class Mesh; + friend class wmtk::Mesh; friend class MeshAttributes; friend class AccessorBase; + friend class TupleAccessor; friend struct AttributeManager; AttributeHandle m_base_handle; PrimitiveType m_primitive_type; @@ -69,4 +72,8 @@ class MeshAttributeHandle m_primitive_type == o.m_primitive_type; } }; +} // namespace attribute +using AttributeHandle = attribute::AttributeHandle; +template +using MeshAttributeHandle = attribute::MeshAttributeHandle; } // namespace wmtk diff --git a/src/wmtk/attribute/AttributeManager.cpp b/src/wmtk/attribute/AttributeManager.cpp index e4b01894de..17b02418a6 100644 --- a/src/wmtk/attribute/AttributeManager.cpp +++ b/src/wmtk/attribute/AttributeManager.cpp @@ -2,7 +2,7 @@ #include #include #include "PerThreadAttributeScopeStacks.hpp" -namespace wmtk { +namespace wmtk::attribute { AttributeManager::AttributeManager(long size) : m_char_attributes(size) , m_long_attributes(size) @@ -120,4 +120,4 @@ void AttributeManager::clear_current_scope() ma.clear_current_scope(); } } -} // namespace wmtk +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/AttributeManager.hpp b/src/wmtk/attribute/AttributeManager.hpp index 3f06bd032c..c4b658dbd1 100644 --- a/src/wmtk/attribute/AttributeManager.hpp +++ b/src/wmtk/attribute/AttributeManager.hpp @@ -7,10 +7,11 @@ namespace wmtk { class Mesh; -template -class MeshAttributes; class MeshWriter; +namespace attribute { +template +class MeshAttributes; struct AttributeManager { AttributeManager(long size); @@ -80,7 +81,7 @@ const MeshAttributes& AttributeManager::get(PrimitiveType ptype) const return m_double_attributes[index]; } if constexpr (std::is_same_v) { - return m_rational_attributes; + return m_rational_attributes[index]; } } @@ -128,4 +129,5 @@ MeshAttributeHandle AttributeManager::register_attribute( r.m_primitive_type = ptype; return r; } +} // namespace attribute } // namespace wmtk diff --git a/src/wmtk/attribute/AttributeScope.cpp b/src/wmtk/attribute/AttributeScope.cpp index f8a2d62d9e..f22f237db2 100644 --- a/src/wmtk/attribute/AttributeScope.cpp +++ b/src/wmtk/attribute/AttributeScope.cpp @@ -1,6 +1,6 @@ #include "AttributeScope.hpp" #include -namespace wmtk { +namespace wmtk::attribute { template AttributeScope::AttributeScope() = default; @@ -154,4 +154,4 @@ template class AttributeScope; template class AttributeScope; template class AttributeScope; template class AttributeScope; -} // namespace wmtk +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/AttributeScope.hpp b/src/wmtk/attribute/AttributeScope.hpp index a71a7d94e8..00ca87eea0 100644 --- a/src/wmtk/attribute/AttributeScope.hpp +++ b/src/wmtk/attribute/AttributeScope.hpp @@ -1,23 +1,24 @@ #pragma once #include +#include "Attribute.hpp" #include "AttributeAccessMode.hpp" #include "AttributeCache.hpp" namespace wmtk { -template -class Accessor; +namespace attribute { template class AttributeScopeStack; +template +class CachingAccessor; template class AttributeScope : public AttributeCache { public: - template - friend class Accessor; + friend class CachingAccessor; friend class AttributeScopeStack; AttributeScope(); ~AttributeScope(); @@ -71,4 +72,5 @@ class AttributeScope : public AttributeCache long m_checkpoint_index = -1; }; +} // namespace attribute } // namespace wmtk diff --git a/src/wmtk/attribute/AttributeScopeHandle.cpp b/src/wmtk/attribute/AttributeScopeHandle.cpp index 98a72ee0f9..b0cd706ddd 100644 --- a/src/wmtk/attribute/AttributeScopeHandle.cpp +++ b/src/wmtk/attribute/AttributeScopeHandle.cpp @@ -1,7 +1,7 @@ #include "AttributeScopeHandle.hpp" #include "AttributeManager.hpp" #include "AttributeScope.hpp" -namespace wmtk { +namespace wmtk::attribute { AttributeScopeHandle::AttributeScopeHandle(AttributeManager& manager) : m_manager(manager) { @@ -18,4 +18,4 @@ AttributeScopeHandle::~AttributeScopeHandle() { m_manager.pop_scope(!m_failed); } -} // namespace wmtk +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/AttributeScopeHandle.hpp b/src/wmtk/attribute/AttributeScopeHandle.hpp index 81dd406d79..8fb7898d7e 100644 --- a/src/wmtk/attribute/AttributeScopeHandle.hpp +++ b/src/wmtk/attribute/AttributeScopeHandle.hpp @@ -1,6 +1,6 @@ #pragma once -namespace wmtk { +namespace wmtk::attribute { struct AttributeManager; class AttributeScopeHandle { @@ -15,4 +15,4 @@ class AttributeScopeHandle AttributeManager& m_manager; bool m_failed = false; }; -} // namespace wmtk +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/AttributeScopeStack.cpp b/src/wmtk/attribute/AttributeScopeStack.cpp index d30d346490..076ffc3070 100644 --- a/src/wmtk/attribute/AttributeScopeStack.cpp +++ b/src/wmtk/attribute/AttributeScopeStack.cpp @@ -3,7 +3,7 @@ #include "Attribute.hpp" #include "AttributeScope.hpp" -namespace wmtk { +namespace wmtk::attribute { template AttributeScopeStack::AttributeScopeStack() = default; @@ -85,7 +85,7 @@ long AttributeScopeStack::add_checkpoint() return r; } template -AttributeScope const * AttributeScopeStack::get_checkpoint(long index) const +AttributeScope const* AttributeScopeStack::get_checkpoint(long index) const { if (m_checkpoints.empty()) { return nullptr; @@ -97,4 +97,4 @@ template class AttributeScopeStack; template class AttributeScopeStack; template class AttributeScopeStack; template class AttributeScopeStack; -} // namespace wmtk +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/AttributeScopeStack.hpp b/src/wmtk/attribute/AttributeScopeStack.hpp index 0982f82438..8678b5473b 100644 --- a/src/wmtk/attribute/AttributeScopeStack.hpp +++ b/src/wmtk/attribute/AttributeScopeStack.hpp @@ -1,10 +1,11 @@ #pragma once #include -#include "AttributeHandle.hpp" #include +#include "AttributeHandle.hpp" namespace wmtk { class Mesh; +namespace attribute { template class Attribute; template @@ -36,9 +37,10 @@ struct AttributeScopeStack protected: std::unique_ptr> m_leaf; - std::vectorconst *> m_checkpoints; + std::vector const*> m_checkpoints; // Mesh& m_mesh; // AttributeManager& m_attribute_manager; // MeshAttributeHandle m_handle; }; +} // namespace attribute } // namespace wmtk diff --git a/src/wmtk/attribute/CMakeLists.txt b/src/wmtk/attribute/CMakeLists.txt index c0edde0c66..e862f21891 100644 --- a/src/wmtk/attribute/CMakeLists.txt +++ b/src/wmtk/attribute/CMakeLists.txt @@ -5,8 +5,6 @@ set(SRC_FILES AttributeCache.cpp AttributeCache.hpp AttributeHandle.hpp - AccessorBase.cpp - AccessorBase.hpp AttributeScope.hpp AttributeScope.cpp MeshAttributes.cpp @@ -21,5 +19,20 @@ set(SRC_FILES PerThreadAttributeScopeStacks.cpp AttributeScopeHandle.hpp AttributeScopeHandle.cpp + + AccessorBase.cpp + AccessorBase.hpp + + CachingAccessor.cpp + CachingAccessor.hpp + + TupleAccessor.cpp + TupleAccessor.hpp + + ConstAccessor.cpp + ConstAccessor.hpp + + MutableAccessor.cpp + MutableAccessor.hpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/attribute/CachingAccessor.cpp b/src/wmtk/attribute/CachingAccessor.cpp new file mode 100644 index 0000000000..9915f3174f --- /dev/null +++ b/src/wmtk/attribute/CachingAccessor.cpp @@ -0,0 +1,94 @@ + +#include "CachingAccessor.hpp" +#include +#include "AttributeScope.hpp" +#include "AttributeScopeStack.hpp" + +namespace wmtk::attribute { + +template +CachingAccessor::CachingAccessor( + Mesh& mesh, + const MeshAttributeHandle& handle, + AttributeAccessMode mode) + : BaseType(mesh, handle) + , m_mode(mode) +{ + m_cache_stack = attribute().get_local_scope_stack_ptr(); +} + +template +CachingAccessor::~CachingAccessor() = default; +template +bool CachingAccessor::has_stack() const +{ + return m_cache_stack && !m_cache_stack->empty(); +} + +template +std::optional CachingAccessor::stack_depth() const +{ + if (m_cache_stack != nullptr) { + return m_cache_stack->depth(); + } else { + return {}; + } +} + +template +auto CachingAccessor::vector_attribute(const long index) -> MapResult +{ + if (has_stack()) { + return m_cache_stack->current_scope_ptr()->vector_attribute(*this, m_mode, index); + } else { + return BaseType::vector_attribute(index); + } +} + + +template +auto CachingAccessor::scalar_attribute(const long index) -> T& +{ + if (has_stack()) { + return m_cache_stack->current_scope_ptr()->scalar_attribute(*this, m_mode, index); + } else { + return BaseType::scalar_attribute(index); + } +} + +template +auto CachingAccessor::const_vector_attribute(const long index) const -> ConstMapResult +{ + if (has_stack()) { + return m_cache_stack->current_scope_ptr()->const_vector_attribute(*this, m_mode, index); + } else { + return BaseType::const_vector_attribute(index); + } +} + + +template +auto CachingAccessor::const_scalar_attribute(const long index) const -> T +{ + if (has_stack()) { + return m_cache_stack->current_scope_ptr()->const_scalar_attribute(*this, m_mode, index); + } else { + return BaseType::const_scalar_attribute(index); + } +} + +template +auto CachingAccessor::vector_attribute(const long index) const -> ConstMapResult +{ + return const_vector_attribute(index); +} +template +T CachingAccessor::scalar_attribute(const long index) const +{ + return const_scalar_attribute(index); +} +template class CachingAccessor; +template class CachingAccessor; +template class CachingAccessor; +template class CachingAccessor; +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/CachingAccessor.hpp b/src/wmtk/attribute/CachingAccessor.hpp new file mode 100644 index 0000000000..aba8908b38 --- /dev/null +++ b/src/wmtk/attribute/CachingAccessor.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include "AccessorBase.hpp" +#include "AttributeAccessMode.hpp" + +namespace wmtk { +class Mesh; +class TetMesh; +class TriMesh; +} // namespace wmtk +namespace wmtk::attribute { + +template +class AttributeCache; + +template +class CachingAccessor : public AccessorBase +{ +public: + friend class wmtk::Mesh; + friend class wmtk::TetMesh; + friend class wmtk::TriMesh; + using Scalar = T; + + friend class AttributeCache; + using BaseType = AccessorBase; + + using ConstMapResult = typename BaseType::ConstMapResult; // Eigen::Map> + using MapResult = typename BaseType::MapResult; // Eigen::Map> + + + CachingAccessor( + Mesh& m, + const MeshAttributeHandle& handle, + AttributeAccessMode access_mode = AttributeAccessMode::Immediate); + + ~CachingAccessor(); + CachingAccessor(const CachingAccessor&) = delete; + CachingAccessor& operator=(const CachingAccessor&) = delete; + + AttributeAccessMode access_mode() const; + + + // returns the size of the underlying attribute + + //using BaseType::dimension; // const() -> long + //using BaseType::reserved_size; // const() -> long + + //using BaseType::attribute; // access to Attribute object being used here + //using BaseType::set_attribute; // (const vector&) -> void + // shows the depth of scope stacks if they exist, mostly for debug + std::optional stack_depth() const; + + bool has_stack() const; + + ConstMapResult const_vector_attribute(const long index) const; + + T const_scalar_attribute(const long index) const; + + MapResult vector_attribute(const long index); + + T& scalar_attribute(const long index); + + // deprecated because we should be more explicit in const/nonconst on internal interfaces + ConstMapResult vector_attribute(const long index) const; + //[[deprecated]] ConstMapResult vector_attribute(const long index) const; + // deprecated because we should be more explicit in const/nonconst on internal interfaces + T scalar_attribute(const long index) const; + //[[deprecated]] T scalar_attribute(const long index) const; + + using BaseType::attribute; + using BaseType::mesh; + +protected: + + BaseType& base_type() { return *this; } + const BaseType& base_type() const { return *this; } + +private: + AttributeAccessMode m_mode; + + AttributeScopeStack* m_cache_stack = nullptr; +}; +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/ConstAccessor.cpp b/src/wmtk/attribute/ConstAccessor.cpp new file mode 100644 index 0000000000..2ea2ad904f --- /dev/null +++ b/src/wmtk/attribute/ConstAccessor.cpp @@ -0,0 +1,35 @@ +#include "ConstAccessor.hpp" +#include +#include "AttributeManager.hpp" +#include "AttributeScope.hpp" +#include "AttributeScopeStack.hpp" +#include "MeshAttributes.hpp" + +namespace wmtk::attribute { + +template +ConstAccessor::ConstAccessor( + const Mesh& mesh, + const MeshAttributeHandle& handle, + AttributeAccessMode mode) + : TupleBaseType(const_cast(mesh), handle, mode) +{} +//=================================================== +// These following methods just forward to to const names +template +auto ConstAccessor::vector_attribute(const Tuple& t) const -> ConstMapResult +{ + return const_vector_attribute(t); +} +template +T ConstAccessor::scalar_attribute(const Tuple& t) const +{ + return const_scalar_attribute(t); +} +//=================================================== + + +template class ConstAccessor; +template class ConstAccessor; +template class ConstAccessor; +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/ConstAccessor.hpp b/src/wmtk/attribute/ConstAccessor.hpp new file mode 100644 index 0000000000..3b8d7bc2ea --- /dev/null +++ b/src/wmtk/attribute/ConstAccessor.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include "TupleAccessor.hpp" + +namespace wmtk { +class Mesh; +class TriMesh; +class TetMesh; +class TriMeshOperationExecutor; +} // namespace wmtk +namespace wmtk::attribute { + + +template +class ConstAccessor : protected TupleAccessor +{ +public: + friend class wmtk::Mesh; + friend class wmtk::TetMesh; + friend class wmtk::TriMesh; + friend class wmtk::PointMesh; + friend class wmtk::TriMeshOperationExecutor; + using Scalar = T; + + friend class AttributeCache; + + using BaseType = AccessorBase; + using TupleBaseType = TupleAccessor; + using CachingBaseType = CachingAccessor; + + using ConstMapResult = typename BaseType::ConstMapResult; // Eigen::Map> + + ConstAccessor( + const Mesh& m, + const MeshAttributeHandle& handle, + AttributeAccessMode access_mode = AttributeAccessMode::Immediate); + + + using TupleBaseType::const_scalar_attribute; + using TupleBaseType::const_vector_attribute; + + + ConstMapResult vector_attribute(const Tuple& t) const; + T scalar_attribute(const Tuple& t) const; + + // returns the size of the underlying attribute + + using BaseType::dimension; // const() -> long + using BaseType::reserved_size; // const() -> long + + using BaseType::attribute; // access to Attribute object being used here + // shows the depth of scope stacks if they exist, mostly for debug + + using CachingBaseType::stack_depth; + using CachingBaseType::has_stack; + +protected: + using TupleBaseType::caching_base_type; + using TupleBaseType::base_type; + using TupleBaseType::scalar_attribute; + using TupleBaseType::vector_attribute; + + + TupleBaseType& tuple_base_type() { return *this; } + const TupleBaseType& tuple_base_type() const { return *this; } + + + const CachingBaseType& index_access() const { return caching_base_type(); } +}; + +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/MeshAttributes.cpp b/src/wmtk/attribute/MeshAttributes.cpp index 8140529a5c..75776f91bc 100644 --- a/src/wmtk/attribute/MeshAttributes.cpp +++ b/src/wmtk/attribute/MeshAttributes.cpp @@ -7,7 +7,7 @@ #include #include -namespace wmtk { +namespace wmtk::attribute { template MeshAttributes::MeshAttributes() @@ -135,4 +135,4 @@ template class MeshAttributes; template class MeshAttributes; template class MeshAttributes; -} // namespace wmtk +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/MeshAttributes.hpp b/src/wmtk/attribute/MeshAttributes.hpp index 411cb26b58..67a7297ddc 100644 --- a/src/wmtk/attribute/MeshAttributes.hpp +++ b/src/wmtk/attribute/MeshAttributes.hpp @@ -14,6 +14,7 @@ namespace wmtk { class MeshWriter; class Mesh; +namespace attribute { template class AccessorBase; @@ -21,7 +22,7 @@ template class MeshAttributes { friend class AccessorBase; - friend class Mesh; + friend class wmtk::Mesh; typedef Eigen::Map> MapResult; typedef Eigen::Map> ConstMapResult; @@ -68,6 +69,5 @@ class MeshAttributes std::vector> m_attributes; }; - - +} // namespace attribute } // namespace wmtk diff --git a/src/wmtk/attribute/MutableAccessor.cpp b/src/wmtk/attribute/MutableAccessor.cpp new file mode 100644 index 0000000000..b8ae1e822f --- /dev/null +++ b/src/wmtk/attribute/MutableAccessor.cpp @@ -0,0 +1,18 @@ +#include "MutableAccessor.hpp" + + +namespace wmtk::attribute { + + +// template +// MutableAccessor::MutableAccessor( +// Mesh& mesh, +// const MeshAttributeHandle& handle, +// AttributeAccessMode mode) +// : ConstAccessorType(mesh, handle, mode) +//{} + +template class MutableAccessor; +template class MutableAccessor; +template class MutableAccessor; +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/MutableAccessor.hpp b/src/wmtk/attribute/MutableAccessor.hpp new file mode 100644 index 0000000000..57b54373e9 --- /dev/null +++ b/src/wmtk/attribute/MutableAccessor.hpp @@ -0,0 +1,42 @@ +#pragma once +#include "ConstAccessor.hpp" + + +namespace wmtk { +class Mesh; +class TriMesh; +class TetMesh; +} // namespace wmtk +namespace wmtk::attribute { +template +class MutableAccessor : public ConstAccessor +{ +public: + friend class wmtk::Mesh; + friend class wmtk::TetMesh; + friend class wmtk::TriMesh; + friend class wmtk::PointMesh; + friend class wmtk::TriMeshOperationExecutor; + using CachingBaseType = CachingAccessor; + using ConstAccessorType = ConstAccessor; + + using ConstAccessorType::ConstAccessorType; + + using ConstAccessorType::const_scalar_attribute; + using ConstAccessorType::const_vector_attribute; + using ConstAccessorType::dimension; + using ConstAccessorType::reserved_size; + using ConstAccessorType::scalar_attribute; + using ConstAccessorType::vector_attribute; + + + using CachingBaseType::stack_depth; + using CachingBaseType::has_stack; +protected: + using ConstAccessorType::base_type; + using ConstAccessorType::caching_base_type; + using ConstAccessorType::index_access; + using ConstAccessorType::tuple_base_type; + CachingBaseType& index_access() { return caching_base_type(); } +}; +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/PerThreadAttributeScopeStacks.cpp b/src/wmtk/attribute/PerThreadAttributeScopeStacks.cpp index 480f1613bd..1480637c1f 100644 --- a/src/wmtk/attribute/PerThreadAttributeScopeStacks.cpp +++ b/src/wmtk/attribute/PerThreadAttributeScopeStacks.cpp @@ -2,10 +2,10 @@ #include -namespace wmtk { +namespace wmtk::attribute { template class PerThreadAttributeScopeStacks; template class PerThreadAttributeScopeStacks; template class PerThreadAttributeScopeStacks; template class PerThreadAttributeScopeStacks; -} // namespace wmtk +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/PerThreadAttributeScopeStacks.hpp b/src/wmtk/attribute/PerThreadAttributeScopeStacks.hpp index b05f459e40..5f61f69d90 100644 --- a/src/wmtk/attribute/PerThreadAttributeScopeStacks.hpp +++ b/src/wmtk/attribute/PerThreadAttributeScopeStacks.hpp @@ -3,7 +3,7 @@ #include "AttributeScopeStack.hpp" -namespace wmtk { +namespace wmtk::attribute { template struct PerThreadAttributeScopeStacks @@ -12,4 +12,4 @@ struct PerThreadAttributeScopeStacks const AttributeScopeStack& local() const { return m_stacks.local(); } mutable tbb::enumerable_thread_specific> m_stacks; }; -} // namespace wmtk +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/TupleAccessor.cpp b/src/wmtk/attribute/TupleAccessor.cpp new file mode 100644 index 0000000000..0e54dcbb40 --- /dev/null +++ b/src/wmtk/attribute/TupleAccessor.cpp @@ -0,0 +1,44 @@ +#include "TupleAccessor.hpp" +#include +#include + +namespace wmtk::attribute { + +template +auto TupleAccessor::const_vector_attribute(const Tuple& t) const -> ConstMapResult +{ + const long idx = index(t); + return CachingBaseType::const_vector_attribute(idx); +} + +template +auto TupleAccessor::vector_attribute(const Tuple& t) -> MapResult +{ + const long idx = index(t); + return CachingBaseType::vector_attribute(idx); +} + +template +auto TupleAccessor::scalar_attribute(const Tuple& t) -> T& +{ + const long idx = index(t); + return CachingBaseType::scalar_attribute(idx); +} + +template +T TupleAccessor::const_scalar_attribute(const Tuple& t) const +{ + const long idx = index(t); + return CachingBaseType::const_scalar_attribute(idx); +} +template +long TupleAccessor::index(const Tuple& t) const +{ + assert(mesh().is_valid_slow(t)); + return mesh().id(t, BaseType::handle().m_primitive_type); +} +template class TupleAccessor; +template class TupleAccessor; +template class TupleAccessor; +template class TupleAccessor; +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/TupleAccessor.hpp b/src/wmtk/attribute/TupleAccessor.hpp new file mode 100644 index 0000000000..476d0ade97 --- /dev/null +++ b/src/wmtk/attribute/TupleAccessor.hpp @@ -0,0 +1,51 @@ +#pragma once +#include "CachingAccessor.hpp" + +namespace wmtk { +class Mesh; +class TetMesh; +class TriMesh; +} // namespace wmtk +namespace wmtk::attribute { +template +class TupleAccessor : protected CachingAccessor +{ +public: + friend class wmtk::Mesh; + friend class wmtk::TetMesh; + friend class wmtk::TriMesh; + using Scalar = T; + + friend class AttributeCache; + using BaseType = AccessorBase; + using CachingBaseType = CachingAccessor; + + using ConstMapResult = typename BaseType::ConstMapResult; // Eigen::Map> + using MapResult = typename BaseType::MapResult; // Eigen::Map> + + using CachingBaseType::CachingBaseType; + + TupleAccessor(const TupleAccessor&) = delete; + TupleAccessor& operator=(const TupleAccessor&) = delete; + + + T const_scalar_attribute(const Tuple& t) const; + T& scalar_attribute(const Tuple& t); + + ConstMapResult const_vector_attribute(const Tuple& t) const; + MapResult vector_attribute(const Tuple& t); + + long index(const Tuple& t) const; + using BaseType::dimension; // const() -> long + using BaseType::reserved_size; // const() -> long + + using BaseType::attribute; // access to Attribute object being used here + using CachingBaseType::mesh; + using CachingBaseType::stack_depth; + using CachingBaseType::has_stack; +protected: + using CachingBaseType::base_type; + CachingBaseType& caching_base_type() { return *this; } + const CachingBaseType& caching_base_type() const { return *this; } +}; +} // namespace wmtk::attribute diff --git a/src/wmtk/invariants/MaxEdgeLengthInvariant.cpp b/src/wmtk/invariants/MaxEdgeLengthInvariant.cpp index ec03cddcc0..7eb6f6d32e 100644 --- a/src/wmtk/invariants/MaxEdgeLengthInvariant.cpp +++ b/src/wmtk/invariants/MaxEdgeLengthInvariant.cpp @@ -2,13 +2,20 @@ #include namespace wmtk { - MaxEdgeLengthInvariant::MaxEdgeLengthInvariant(const Mesh& m, const MeshAttributeHandle& coordinate, double threshold_squared): MeshInvariant(m), m_coordinate_handle(coordinate), m_threshold_squared(threshold_squared) {} +MaxEdgeLengthInvariant::MaxEdgeLengthInvariant( + const Mesh& m, + const MeshAttributeHandle& coordinate, + double threshold_squared) + : MeshInvariant(m) + , m_coordinate_handle(coordinate) + , m_threshold_squared(threshold_squared) +{} bool MaxEdgeLengthInvariant::before(const Tuple& t) const { - ConstAccessor accessor = mesh().create_accessor(m_coordinate_handle); + ConstAccessor accessor = mesh().create_accessor(m_coordinate_handle); - auto p0 = accessor.vector_attribute(t); - auto p1 = accessor.vector_attribute(mesh().switch_vertex(t)); + auto p0 = accessor.const_vector_attribute(t); + auto p1 = accessor.const_vector_attribute(mesh().switch_vertex(t)); const double l_squared = (p1 - p0).squaredNorm(); return l_squared < m_threshold_squared; } diff --git a/src/wmtk/invariants/MinEdgeLengthInvariant.cpp b/src/wmtk/invariants/MinEdgeLengthInvariant.cpp index 28d3564a58..31d49668d0 100644 --- a/src/wmtk/invariants/MinEdgeLengthInvariant.cpp +++ b/src/wmtk/invariants/MinEdgeLengthInvariant.cpp @@ -15,8 +15,8 @@ bool MinEdgeLengthInvariant::before(const Tuple& t) const { ConstAccessor accessor = mesh().create_accessor(m_coordinate_handle); - auto p0 = accessor.vector_attribute(t); - auto p1 = accessor.vector_attribute(mesh().switch_vertex(t)); + auto p0 = accessor.const_vector_attribute(t); + auto p1 = accessor.const_vector_attribute(mesh().switch_vertex(t)); const double l_squared = (p1 - p0).squaredNorm(); return l_squared > m_threshold_squared; } diff --git a/src/wmtk/io/MeshReader.cpp b/src/wmtk/io/MeshReader.cpp index d0b6c94c4b..ec28c1b83c 100644 --- a/src/wmtk/io/MeshReader.cpp +++ b/src/wmtk/io/MeshReader.cpp @@ -76,9 +76,9 @@ void MeshReader::set_attribute( Mesh& mesh) { auto handle = mesh.register_attribute(name, pt, stride, true); - auto accessor = mesh.create_accessor(handle); + auto accessor = attribute::AccessorBase(mesh, handle); - static_cast&>(accessor).set_attribute(v); + accessor.set_attribute(v); } diff --git a/src/wmtk/utils/mesh_utils.cpp b/src/wmtk/utils/mesh_utils.cpp index c011255959..5bd5d1d0ff 100644 --- a/src/wmtk/utils/mesh_utils.cpp +++ b/src/wmtk/utils/mesh_utils.cpp @@ -1,8 +1,9 @@ #include "mesh_utils.hpp" +#include namespace wmtk::mesh_utils { Eigen::Vector3d -compute_face_normal_area_weighted(const Mesh& m, const Accessor& pos, const Tuple& f) +compute_face_normal_area_weighted(const TriMesh& m, const Accessor& pos, const Tuple& f) { const Tuple v0 = f; const Tuple v1 = m.switch_vertex(f); @@ -14,12 +15,12 @@ compute_face_normal_area_weighted(const Mesh& m, const Accessor& pos, co return ((p0 - p2).cross(p1 - p2)); } -Eigen::Vector3d compute_face_normal(const Mesh& m, const Accessor& pos, const Tuple& f) +Eigen::Vector3d compute_face_normal(const TriMesh& m, const Accessor& pos, const Tuple& f) { return compute_face_normal_area_weighted(m, pos, f).normalized(); } -Eigen::Vector3d compute_vertex_normal(const Mesh& m, const Accessor& pos, const Tuple& v) +Eigen::Vector3d compute_vertex_normal(const TriMesh& m, const Accessor& pos, const Tuple& v) { const SimplicialComplex closed_star = SimplicialComplex::closed_star(m, Simplex::vertex(v)); @@ -35,4 +36,4 @@ Eigen::Vector3d compute_vertex_normal(const Mesh& m, const Accessor& pos return n; } -} // namespace wmtk::mesh_utils \ No newline at end of file +} // namespace wmtk::mesh_utils diff --git a/src/wmtk/utils/mesh_utils.hpp b/src/wmtk/utils/mesh_utils.hpp index ac98011956..5bb0687f37 100644 --- a/src/wmtk/utils/mesh_utils.hpp +++ b/src/wmtk/utils/mesh_utils.hpp @@ -13,8 +13,8 @@ inline MeshAttributeHandle set_matrix_attribute( const PrimitiveType& type, Mesh& mesh) { - MeshAttributeHandle handle = - mesh.register_attribute(name, type, data.cols()); + attribute::MeshAttributeHandle handle = + mesh.template register_attribute(name, type, data.cols()); auto accessor = mesh.create_accessor(handle); const auto tuples = mesh.get_all(type); @@ -30,16 +30,17 @@ inline MeshAttributeHandle set_matrix_attribute( * @brief compute area vector of face */ Eigen::Vector3d -compute_face_normal_area_weighted(const Mesh& m, const Accessor& pos, const Tuple& f); +compute_face_normal_area_weighted(const TriMesh& m, const Accessor& pos, const Tuple& f); /** * @brief compute the normalized face normal based on the vertex positions */ -Eigen::Vector3d compute_face_normal(const Mesh& m, const Accessor& pos, const Tuple& f); +Eigen::Vector3d compute_face_normal(const TriMesh& m, const Accessor& pos, const Tuple& f); /** * @brief compute the normalized vertex normal from the incident area weighted face normals */ -Eigen::Vector3d compute_vertex_normal(const Mesh& m, const Accessor& pos, const Tuple& v); +Eigen::Vector3d +compute_vertex_normal(const TriMesh& m, const Accessor& pos, const Tuple& v); } // namespace wmtk::mesh_utils diff --git a/tests/test_2d_operations.cpp b/tests/test_2d_operations.cpp index 4ed6d37562..aac19ce8ca 100644 --- a/tests/test_2d_operations.cpp +++ b/tests/test_2d_operations.cpp @@ -433,11 +433,11 @@ TEST_CASE("hash_update", "[operations][2D]") auto executor = m.get_tmoe(edge, hash_accessor); auto& ha = executor.hash_accessor; - CHECK(executor.hash_at_cell(0) == 0); + CHECK(m.get_cell_hash_slow(0) == 0); executor.update_cell_hash(); - CHECK(executor.hash_at_cell(0) == 1); + CHECK(m.get_cell_hash_slow(0) == 1); } SECTION("edge_region") { @@ -450,29 +450,29 @@ TEST_CASE("hash_update", "[operations][2D]") auto executor = m.get_tmoe(edge, hash_accessor); auto& ha = executor.hash_accessor; - CHECK(executor.hash_at_cell(0) == 0); - CHECK(executor.hash_at_cell(1) == 0); - CHECK(executor.hash_at_cell(2) == 0); - CHECK(executor.hash_at_cell(3) == 0); - CHECK(executor.hash_at_cell(4) == 0); - CHECK(executor.hash_at_cell(5) == 0); - CHECK(executor.hash_at_cell(6) == 0); - CHECK(executor.hash_at_cell(7) == 0); - CHECK(executor.hash_at_cell(8) == 0); - CHECK(executor.hash_at_cell(9) == 0); + CHECK(m.get_cell_hash_slow(0) == 0); + CHECK(m.get_cell_hash_slow(1) == 0); + CHECK(m.get_cell_hash_slow(2) == 0); + CHECK(m.get_cell_hash_slow(3) == 0); + CHECK(m.get_cell_hash_slow(4) == 0); + CHECK(m.get_cell_hash_slow(5) == 0); + CHECK(m.get_cell_hash_slow(6) == 0); + CHECK(m.get_cell_hash_slow(7) == 0); + CHECK(m.get_cell_hash_slow(8) == 0); + CHECK(m.get_cell_hash_slow(9) == 0); executor.update_cell_hash(); - CHECK(executor.hash_at_cell(0) == 1); - CHECK(executor.hash_at_cell(1) == 1); - CHECK(executor.hash_at_cell(2) == 1); - CHECK(executor.hash_at_cell(3) == 0); - CHECK(executor.hash_at_cell(4) == 0); - CHECK(executor.hash_at_cell(5) == 1); - CHECK(executor.hash_at_cell(6) == 1); - CHECK(executor.hash_at_cell(7) == 1); - CHECK(executor.hash_at_cell(8) == 0); - CHECK(executor.hash_at_cell(9) == 0); + CHECK(m.get_cell_hash_slow(0) == 1); + CHECK(m.get_cell_hash_slow(1) == 1); + CHECK(m.get_cell_hash_slow(2) == 1); + CHECK(m.get_cell_hash_slow(3) == 0); + CHECK(m.get_cell_hash_slow(4) == 0); + CHECK(m.get_cell_hash_slow(5) == 1); + CHECK(m.get_cell_hash_slow(6) == 1); + CHECK(m.get_cell_hash_slow(7) == 1); + CHECK(m.get_cell_hash_slow(8) == 0); + CHECK(m.get_cell_hash_slow(9) == 0); } } diff --git a/tests/test_accessor.cpp b/tests/test_accessor.cpp index 06a7740f75..57e2c467f4 100644 --- a/tests/test_accessor.cpp +++ b/tests/test_accessor.cpp @@ -13,7 +13,7 @@ void populate(DEBUG_PointMesh& m, VectorAcc& va, bool for_zeros = false) { auto vertices = m.get_all(wmtk::PrimitiveType::Vertex); size_t dimension = va.dimension(); - Eigen::Matrix x; + Eigen::Matrix x; for (const wmtk::Tuple& tup : vertices) { long id = m.id(tup); auto v = va.vector_attribute(tup); @@ -29,7 +29,7 @@ void check(DEBUG_PointMesh& m, VectorAcc& va, bool for_zeros = false) { auto vertices = m.get_all(wmtk::PrimitiveType::Vertex); size_t dimension = va.dimension(); - Eigen::Matrix x; + Eigen::Matrix x; bool is_scalar = va.dimension() == 1; x.resize(va.dimension()); for (const wmtk::Tuple& tup : vertices) { @@ -65,6 +65,10 @@ TEST_CASE("test_accessor_basic") auto long_acc = m.create_accessor(long_handle); auto double_acc = m.create_accessor(double_handle); + auto char_bacc = m.create_base_accessor(char_handle); + auto long_bacc = m.create_base_accessor(long_handle); + auto double_bacc = m.create_base_accessor(double_handle); + auto vertices = m.get_all(wmtk::PrimitiveType::Vertex); // check characteristics are all right @@ -90,17 +94,17 @@ TEST_CASE("test_accessor_basic") { std::vector d(size); std::iota(d.begin(), d.end(), char(0)); - static_cast&>(char_acc).set_attribute(d); + char_bacc.set_attribute(d); } { std::vector d(size); std::iota(d.begin(), d.end(), long(0)); - static_cast&>(long_acc).set_attribute(d); + long_bacc.set_attribute(d); } { std::vector d(3 * size); std::iota(d.begin(), d.end(), double(0)); - static_cast&>(double_acc).set_attribute(d); + double_bacc.set_attribute(d); } for (const wmtk::Tuple& tup : vertices) { long id = m.id(tup); diff --git a/tests/tools/DEBUG_PointMesh.hpp b/tests/tools/DEBUG_PointMesh.hpp index a7328a4202..db25356f1f 100644 --- a/tests/tools/DEBUG_PointMesh.hpp +++ b/tests/tools/DEBUG_PointMesh.hpp @@ -11,15 +11,15 @@ class DEBUG_PointMesh : public wmtk::PointMesh return PointMesh::id(tup, wmtk::PrimitiveType::Vertex); } template - AccessorBase create_base_accessor(const MeshAttributeHandle& handle) + attribute::AccessorBase create_base_accessor(const MeshAttributeHandle& handle) { - return AccessorBase(*this, handle); + return attribute::AccessorBase(*this, handle); } template - AccessorBase create_const_base_accessor(const MeshAttributeHandle& handle) const + attribute::AccessorBase create_const_base_accessor(const MeshAttributeHandle& handle) const { - return AccessorBase(const_cast(*this), handle); + return attribute::AccessorBase(const_cast(*this), handle); } }; } // namespace wmtk::tests diff --git a/tests/tools/DEBUG_TriMesh.cpp b/tests/tools/DEBUG_TriMesh.cpp index 498c1924c3..01ab146118 100644 --- a/tests/tools/DEBUG_TriMesh.cpp +++ b/tests/tools/DEBUG_TriMesh.cpp @@ -53,8 +53,8 @@ auto DEBUG_TriMesh::edge_tuple_between_v1_v2(const long v1, const long v2, const ConstAccessor fv = create_accessor(m_fv_handle); auto fv_base = create_base_accessor(m_fv_handle); Tuple face = face_tuple_from_id(fid); - auto fv0 = fv.vector_attribute(face); - REQUIRE(fv0 == fv_base.vector_attribute(fid)); + auto fv0 = fv.const_vector_attribute(face); + REQUIRE(fv0 == fv_base.const_vector_attribute(fid)); long local_vid1 = -1, local_vid2 = -1; for (long i = 0; i < fv0.size(); ++i) { if (fv0[i] == v1) { diff --git a/tests/tools/DEBUG_TriMesh.hpp b/tests/tools/DEBUG_TriMesh.hpp index eaf015d797..533472b8ac 100644 --- a/tests/tools/DEBUG_TriMesh.hpp +++ b/tests/tools/DEBUG_TriMesh.hpp @@ -28,18 +28,18 @@ class DEBUG_TriMesh : public TriMesh Tuple tuple_from_face_id(const long fid) const; template - AccessorBase create_base_accessor(const MeshAttributeHandle& handle) + attribute::AccessorBase create_base_accessor(const MeshAttributeHandle& handle) { - return AccessorBase(*this, handle); + return attribute::AccessorBase(*this, handle); } template - AccessorBase create_const_base_accessor(const MeshAttributeHandle& handle) const + attribute::AccessorBase create_const_base_accessor(const MeshAttributeHandle& handle) const { - return AccessorBase(const_cast(*this), handle); + return attribute::AccessorBase(const_cast(*this), handle); } template - AccessorBase create_base_accessor(const MeshAttributeHandle& handle) const + attribute::AccessorBase create_base_accessor(const MeshAttributeHandle& handle) const { return create_const_base_accessor(handle); } From f51d0e9de4e9faa5db0a2b55a6ff55b2f91321a3 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sun, 10 Sep 2023 13:15:22 -0400 Subject: [PATCH 044/134] fixing compilation errors from merge --- src/wmtk/MultiMeshManager.cpp | 4 ++-- src/wmtk/TriMeshOperationExecutor.cpp | 4 ++-- tests/tools/DEBUG_TriMesh.cpp | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/wmtk/MultiMeshManager.cpp b/src/wmtk/MultiMeshManager.cpp index de0d542e24..34f194ab44 100644 --- a/src/wmtk/MultiMeshManager.cpp +++ b/src/wmtk/MultiMeshManager.cpp @@ -47,7 +47,7 @@ namespace wmtk std::tuple MultiMeshManager::read_tuple_map_attribute(MeshAttributeHandle map_handle, const Mesh& source_mesh, const Tuple& source_tuple) { auto map_accessor = source_mesh.create_accessor(map_handle); - auto map = map_accessor.vector_attribute(source_tuple); + auto map = map_accessor.const_vector_attribute(source_tuple); return std::make_tuple(Tuple(map(0), map(1), map(2), map(3), map(4)), Tuple(map(5), map(6), map(7), map(8), map(9))); } @@ -271,4 +271,4 @@ namespace wmtk } return true; } -} \ No newline at end of file +} diff --git a/src/wmtk/TriMeshOperationExecutor.cpp b/src/wmtk/TriMeshOperationExecutor.cpp index 9478ad14d8..97a717949e 100644 --- a/src/wmtk/TriMeshOperationExecutor.cpp +++ b/src/wmtk/TriMeshOperationExecutor.cpp @@ -519,7 +519,7 @@ void TriMesh::TriMeshOperationExecutor::update_hash_in_map(TriMesh& child_mesh) { auto [t_parent_old, t_child_old] = MultiMeshManager::read_tuple_map_attribute(m_mesh.multi_mesh_manager.map_to_child_handles[child_id], m_mesh, m_mesh.tuple_from_id(m_mesh.top_simplex_type(), parent_cell_id)); - long parent_cell_hash = hash_at_cell(parent_cell_id); + long parent_cell_hash = m_mesh.get_cell_hash_slow(parent_cell_id); Tuple t_parent_new = t_parent_old.with_updated_hash(parent_cell_hash); if (t_child_old.is_null()) @@ -528,7 +528,7 @@ void TriMesh::TriMeshOperationExecutor::update_hash_in_map(TriMesh& child_mesh) } else { - long child_cell_hash = child_hash_accessor.scalar_attribute(t_child_old.m_global_cid); + long child_cell_hash = child_hash_accessor.index_access().const_scalar_attribute(t_child_old.m_global_cid); Tuple t_child_new = t_child_old.with_updated_hash(child_cell_hash); MultiMeshManager::write_tuple_map_attribute(m_mesh.multi_mesh_manager.map_to_child_handles[child_id], m_mesh, t_parent_new, t_child_new); diff --git a/tests/tools/DEBUG_TriMesh.cpp b/tests/tools/DEBUG_TriMesh.cpp index 01ab146118..f32bb07153 100644 --- a/tests/tools/DEBUG_TriMesh.cpp +++ b/tests/tools/DEBUG_TriMesh.cpp @@ -29,8 +29,8 @@ void DEBUG_TriMesh::print_vf() const auto f_flag_accessor = get_flag_accessor(PrimitiveType::Face); for (long id = 0; id < capacity(PrimitiveType::Face); ++id) { - auto fv = fv_accessor.vector_attribute(id); - if (f_flag_accessor.scalar_attribute(tuple_from_id(PrimitiveType::Face, id)) == 0) + auto fv = fv_accessor.const_vector_attribute(id); + if (f_flag_accessor.const_scalar_attribute(tuple_from_id(PrimitiveType::Face, id)) == 0) { std::cout << "face " << id << " is deleted" << std::endl; } @@ -74,7 +74,7 @@ auto DEBUG_TriMesh::edge_tuple_from_vids(const long v1, const long v2) const -> for (long fid = 0; fid < capacity(PrimitiveType::Face); ++fid) { Tuple face = face_tuple_from_id(fid); - auto fv0 = fv.vector_attribute(face); + auto fv0 = fv.const_vector_attribute(face); long local_vid1 = -1, local_vid2 = -1; for (long i = 0; i < fv0.size(); ++i) { if (fv0[i] == v1) { @@ -99,7 +99,7 @@ auto DEBUG_TriMesh::face_tuple_from_vids(const long v1, const long v2, const lon for (long fid = 0; fid < capacity(PrimitiveType::Face); ++fid) { Tuple face = face_tuple_from_id(fid); - auto fv0 = fv.vector_attribute(face); + auto fv0 = fv.const_vector_attribute(face); bool find_v1 = false, find_v2 = false, find_v3 = false; for (long i = 0; i < fv0.size(); ++i) { if (fv0[i] == v1) { From e0eb32b26ce41250df4843ef63e25d305e1a78a2 Mon Sep 17 00:00:00 2001 From: zlyfunction Date: Mon, 11 Sep 2023 17:57:39 -0400 Subject: [PATCH 045/134] terrible bug fix --- src/wmtk/SimplicialComplex.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wmtk/SimplicialComplex.cpp b/src/wmtk/SimplicialComplex.cpp index 4adc12c4ea..05af132032 100644 --- a/src/wmtk/SimplicialComplex.cpp +++ b/src/wmtk/SimplicialComplex.cpp @@ -212,7 +212,7 @@ SimplicialComplex SimplicialComplex::top_coface_simplex(const Mesh& m, const Sim while (!q.empty()) { Tuple cur_t = q.front(); q.pop(); - if (sc.add_simplex(Simplex(PF, cur_t))) { + if (sc.add_simplex(Simplex(PT, cur_t))) { const Tuple t1 = cur_t; const Tuple t2 = sw(cur_t, PF); const Tuple t3 = sw(sw(cur_t, PE), PF); From 12d3cadffa9204dec76b97ab902c616e6f5f75cd Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Mon, 11 Sep 2023 20:45:41 -0400 Subject: [PATCH 046/134] adding ability for mesh to return an attribute\'s dimension --- src/wmtk/Mesh.cpp | 1 - src/wmtk/Mesh.hpp | 7 +++++++ src/wmtk/attribute/AttributeManager.hpp | 8 ++++++++ src/wmtk/attribute/MeshAttributes.cpp | 4 ++++ src/wmtk/attribute/MeshAttributes.hpp | 3 +++ tests/test_accessor.cpp | 3 +++ 6 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/wmtk/Mesh.cpp b/src/wmtk/Mesh.cpp index 43fe819bc4..060afbcde6 100644 --- a/src/wmtk/Mesh.cpp +++ b/src/wmtk/Mesh.cpp @@ -252,7 +252,6 @@ attribute::AttributeScopeHandle Mesh::create_scope() return m_attribute_manager.create_scope(*this); } - template MeshAttributeHandle Mesh::register_attribute(const std::string&, PrimitiveType, long, bool); template MeshAttributeHandle diff --git a/src/wmtk/Mesh.hpp b/src/wmtk/Mesh.hpp index 03b59a6fb5..30efd433f9 100644 --- a/src/wmtk/Mesh.hpp +++ b/src/wmtk/Mesh.hpp @@ -103,6 +103,9 @@ class Mesh : public std::enable_shared_from_this template ConstAccessor create_accessor(const MeshAttributeHandle& handle) const; + template + long get_attribute_dimension(const MeshAttributeHandle& handle) const; + // creates a scope as long as the AttributeScopeHandle exists [[nodiscard]] attribute::AttributeScopeHandle create_scope(); @@ -348,6 +351,10 @@ MeshAttributeHandle Mesh::get_attribute_handle( r.m_primitive_type = ptype; return r; } +template +long Mesh::get_attribute_dimension(const MeshAttributeHandle& handle) const { + return m_attribute_manager.get_attribute_dimension(handle); +} inline Tuple Mesh::switch_vertex(const Tuple& tuple) const { diff --git a/src/wmtk/attribute/AttributeManager.hpp b/src/wmtk/attribute/AttributeManager.hpp index c4b658dbd1..9b056cffcc 100644 --- a/src/wmtk/attribute/AttributeManager.hpp +++ b/src/wmtk/attribute/AttributeManager.hpp @@ -65,6 +65,9 @@ struct AttributeManager void push_scope(); void pop_scope(bool apply_updates = true); void clear_current_scope(); + + template + long get_attribute_dimension(const MeshAttributeHandle& handle) const; }; template @@ -129,5 +132,10 @@ MeshAttributeHandle AttributeManager::register_attribute( r.m_primitive_type = ptype; return r; } +template +long AttributeManager::get_attribute_dimension(const MeshAttributeHandle& handle) const +{ + return get(handle).dimension(handle.m_base_handle); +} } // namespace attribute } // namespace wmtk diff --git a/src/wmtk/attribute/MeshAttributes.cpp b/src/wmtk/attribute/MeshAttributes.cpp index 75776f91bc..cc9d72b94f 100644 --- a/src/wmtk/attribute/MeshAttributes.cpp +++ b/src/wmtk/attribute/MeshAttributes.cpp @@ -128,6 +128,10 @@ void MeshAttributes::reserve(const long size) attr.reserve(size); } } +template + long MeshAttributes::dimension(const AttributeHandle& handle) const { + return attribute(handle).dimension(); + } template class MeshAttributes; diff --git a/src/wmtk/attribute/MeshAttributes.hpp b/src/wmtk/attribute/MeshAttributes.hpp index 67a7297ddc..c573829682 100644 --- a/src/wmtk/attribute/MeshAttributes.hpp +++ b/src/wmtk/attribute/MeshAttributes.hpp @@ -48,6 +48,9 @@ class MeshAttributes void pop_scope(bool apply_updates = true); void clear_current_scope(); + + long dimension(const AttributeHandle& handle) const; + protected: AttributeHandle attribute_handle(const std::string& name) const; diff --git a/tests/test_accessor.cpp b/tests/test_accessor.cpp index 57e2c467f4..af632dd548 100644 --- a/tests/test_accessor.cpp +++ b/tests/test_accessor.cpp @@ -60,6 +60,9 @@ TEST_CASE("test_accessor_basic") auto long_handle = m.register_attribute("long", wmtk::PrimitiveType::Vertex, 1); auto double_handle = m.register_attribute("double", wmtk::PrimitiveType::Vertex, 3); + REQUIRE(m.get_attribute_dimension(char_handle) == 1); + REQUIRE(m.get_attribute_dimension(long_handle) == 1); + REQUIRE(m.get_attribute_dimension(double_handle) == 3); auto char_acc = m.create_accessor(char_handle); auto long_acc = m.create_accessor(long_handle); From 13a1981014c60b4cad7dc79d2ed61566ad7dc16a Mon Sep 17 00:00:00 2001 From: Daniel Zint Date: Mon, 11 Sep 2023 19:15:52 -0400 Subject: [PATCH 047/134] throw an error if perl is not installed --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 60ecbaf8cd..9061a57045 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,9 @@ option (BUILD_SHARED_LIBS "Build Shared Libraries" OFF) # we globally want to di list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/recipes/") +# check if Perl is installed (required by HDF5 but not set as required there) +find_package (Perl REQUIRED) + include(sanitizers) include(wmtk_colors) include(wmtk_warnings) From 7ed7075612d1cb875e5210969d07b0d87ea5c6a1 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Mon, 28 Aug 2023 10:30:10 -0400 Subject: [PATCH 048/134] adding cmake, changing to templated evaluation in child classes. not building due to autodiff not included --- src/wmtk/energy/AMIPS.hpp | 2 +- src/wmtk/energy/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wmtk/energy/AMIPS.hpp b/src/wmtk/energy/AMIPS.hpp index 7c2c42bff9..247a75674d 100644 --- a/src/wmtk/energy/AMIPS.hpp +++ b/src/wmtk/energy/AMIPS.hpp @@ -72,4 +72,4 @@ class AMIPS_3DEmbedded : public AMIPS DofsToPosition m_dofs_to_pos; }; } // namespace energy -} // namespace wmtk \ No newline at end of file +} // namespace wmtk diff --git a/src/wmtk/energy/CMakeLists.txt b/src/wmtk/energy/CMakeLists.txt index 60a4f8fef1..9b00c8bee9 100644 --- a/src/wmtk/energy/CMakeLists.txt +++ b/src/wmtk/energy/CMakeLists.txt @@ -11,4 +11,4 @@ set(SRC_FILES AreaAccuracy.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) -add_subdirectory(utils) \ No newline at end of file +add_subdirectory(utils) From 2dee27e210a775e959648c6a4f993b19c5d3d4de Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 5 Sep 2023 05:21:07 -0400 Subject: [PATCH 049/134] port in image and smapling. compiled --- src/wmtk/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/wmtk/CMakeLists.txt b/src/wmtk/CMakeLists.txt index 4e35e76e26..2c4a8b2189 100644 --- a/src/wmtk/CMakeLists.txt +++ b/src/wmtk/CMakeLists.txt @@ -45,3 +45,12 @@ add_subdirectory(image) add_subdirectory(invariants) +add_subdirectory(energy) +add_subdirectory(image) + +include(stb) +include(tinyexr) +target_link_libraries(wildmeshing_toolkit PUBLIC + miniz # MTAO: I had a build issue with windows not finding miniz at linktime - adding here to make sure it's there? + tinyexr + stb::image) From 1afce58b7cbd52986cb326e3825dd2d8683d583c Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Mon, 11 Sep 2023 23:13:30 -0400 Subject: [PATCH 050/134] energy refactor to function. Adding AutodiffFunction and use attri handle in constructor --- src/wmtk/CMakeLists.txt | 3 +- src/wmtk/energy/AreaAccuracy.cpp | 13 -- src/wmtk/energy/CMakeLists.txt | 14 -- src/wmtk/energy/DifferentiableEnergy.hpp | 26 --- src/wmtk/energy/Energy.cpp | 8 - src/wmtk/energy/Energy.hpp | 21 --- src/wmtk/energy/TriMeshValenceEnergy.hpp | 16 -- src/wmtk/energy/utils/AutoDiffUtils.hpp | 62 ------- src/wmtk/energy/utils/CMakeLists.txt | 7 - src/wmtk/energy/utils/DofsToPosition.hpp | 69 -------- src/wmtk/{energy => function}/AMIPS.cpp | 160 +++++++++--------- src/wmtk/{energy => function}/AMIPS.hpp | 50 +++--- src/wmtk/function/AreaAccuracy.cpp | 13 ++ .../{energy => function}/AreaAccuracy.hpp | 17 +- src/wmtk/function/AutodiffFunction.cpp | 22 +++ src/wmtk/function/AutodiffFunction.hpp | 25 +++ src/wmtk/function/CMakeLists.txt | 17 ++ src/wmtk/function/DifferentiableFunction.cpp | 9 + src/wmtk/function/DifferentiableFunction.hpp | 26 +++ src/wmtk/function/Function.cpp | 9 + src/wmtk/function/Function.hpp | 21 +++ .../TriMeshValenceFunction.cpp} | 12 +- src/wmtk/function/TriMeshValenceFunction.hpp | 16 ++ .../{energy => function}/utils/autodiff.cpp | 0 .../{energy => function}/utils/autodiff.h | 0 25 files changed, 274 insertions(+), 362 deletions(-) delete mode 100644 src/wmtk/energy/AreaAccuracy.cpp delete mode 100644 src/wmtk/energy/CMakeLists.txt delete mode 100644 src/wmtk/energy/DifferentiableEnergy.hpp delete mode 100644 src/wmtk/energy/Energy.cpp delete mode 100644 src/wmtk/energy/Energy.hpp delete mode 100644 src/wmtk/energy/TriMeshValenceEnergy.hpp delete mode 100644 src/wmtk/energy/utils/AutoDiffUtils.hpp delete mode 100644 src/wmtk/energy/utils/CMakeLists.txt delete mode 100644 src/wmtk/energy/utils/DofsToPosition.hpp rename src/wmtk/{energy => function}/AMIPS.cpp (54%) rename src/wmtk/{energy => function}/AMIPS.hpp (51%) create mode 100644 src/wmtk/function/AreaAccuracy.cpp rename src/wmtk/{energy => function}/AreaAccuracy.hpp (56%) create mode 100644 src/wmtk/function/AutodiffFunction.cpp create mode 100644 src/wmtk/function/AutodiffFunction.hpp create mode 100644 src/wmtk/function/CMakeLists.txt create mode 100644 src/wmtk/function/DifferentiableFunction.cpp create mode 100644 src/wmtk/function/DifferentiableFunction.hpp create mode 100644 src/wmtk/function/Function.cpp create mode 100644 src/wmtk/function/Function.hpp rename src/wmtk/{energy/TriMeshValenceEnergy.cpp => function/TriMeshValenceFunction.cpp} (88%) create mode 100644 src/wmtk/function/TriMeshValenceFunction.hpp rename src/wmtk/{energy => function}/utils/autodiff.cpp (100%) rename src/wmtk/{energy => function}/utils/autodiff.h (100%) diff --git a/src/wmtk/CMakeLists.txt b/src/wmtk/CMakeLists.txt index 2c4a8b2189..ca9121470a 100644 --- a/src/wmtk/CMakeLists.txt +++ b/src/wmtk/CMakeLists.txt @@ -44,8 +44,7 @@ add_subdirectory(energy) add_subdirectory(image) add_subdirectory(invariants) - -add_subdirectory(energy) +add_subdirectory(function) add_subdirectory(image) include(stb) diff --git a/src/wmtk/energy/AreaAccuracy.cpp b/src/wmtk/energy/AreaAccuracy.cpp deleted file mode 100644 index cb2a06c5c4..0000000000 --- a/src/wmtk/energy/AreaAccuracy.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "AreaAccuracy.hpp" -using namespace wmtk; -using namespace wmtk::energy; - -AreaAccuracy::AreaAccuracy(const TriMesh& mesh1, const TriMesh& mesh2) - : DifferentiableEnergy(mesh1) - , m_position_mesh(mesh2) - , m_3d_position_handle(mesh2.get_attribute_handle("position", PrimitiveType::Vertex)) -{} - -template -T energy_eval(const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, const Eigen::Vector2d& uv3) -{} diff --git a/src/wmtk/energy/CMakeLists.txt b/src/wmtk/energy/CMakeLists.txt deleted file mode 100644 index 9b00c8bee9..0000000000 --- a/src/wmtk/energy/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ - -set(SRC_FILES - Energy.hpp - Energy.cpp - DifferentiableEnergy.hpp - AMIPS.hpp - AMIPS.cpp - TriMeshValenceEnergy.hpp - TriMeshValenceEnergy.cpp - AreaAccuracy.hpp - AreaAccuracy.cpp -) -target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) -add_subdirectory(utils) diff --git a/src/wmtk/energy/DifferentiableEnergy.hpp b/src/wmtk/energy/DifferentiableEnergy.hpp deleted file mode 100644 index 38debba6ca..0000000000 --- a/src/wmtk/energy/DifferentiableEnergy.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include -#include -#include -#include -// #include -#include "Energy.hpp" -namespace wmtk { -namespace energy { - -using DScalar = DScalar2, Eigen::Matrix>; -using Scalar = typename DScalar::Scalar; - -class DifferentiableEnergy : public Energy -{ -public: - DifferentiableEnergy(const Mesh& mesh) - : Energy(mesh){}; - - virtual ~DifferentiableEnergy() = default; - -public: - virtual DScalar energy_eval_autodiff(const Tuple& tuple) const = 0; -}; -} // namespace energy -} // namespace wmtk diff --git a/src/wmtk/energy/Energy.cpp b/src/wmtk/energy/Energy.cpp deleted file mode 100644 index 339415dd3c..0000000000 --- a/src/wmtk/energy/Energy.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "Energy.hpp" -namespace wmtk::energy { -Energy::Energy(const Mesh& mesh) - : m_mesh(mesh) - , m_position_handle(mesh.get_attribute_handle("position", PrimitiveType::Vertex)){}; - -Energy::~Energy() = default; -} // namespace wmtk::energy diff --git a/src/wmtk/energy/Energy.hpp b/src/wmtk/energy/Energy.hpp deleted file mode 100644 index de9f82586a..0000000000 --- a/src/wmtk/energy/Energy.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include -namespace wmtk { -namespace energy { -class Energy -{ -protected: - const Mesh& m_mesh; - const MeshAttributeHandle m_position_handle; - - -public: - Energy(const Mesh& mesh); - virtual ~Energy(); - -public: - virtual double energy_eval(const Tuple& tuple) const = 0; -}; -} // namespace energy -} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/TriMeshValenceEnergy.hpp b/src/wmtk/energy/TriMeshValenceEnergy.hpp deleted file mode 100644 index ccd116e2d2..0000000000 --- a/src/wmtk/energy/TriMeshValenceEnergy.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include "Energy.hpp" -namespace wmtk { -namespace energy { - -class TriMeshValenceEnergy : public Energy -{ -public: - TriMeshValenceEnergy(const TriMesh& mesh); - double energy_eval(const Tuple& tuple) const override; - -protected: - TriMesh& mesh() const; -}; -} // namespace energy -} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/utils/AutoDiffUtils.hpp b/src/wmtk/energy/utils/AutoDiffUtils.hpp deleted file mode 100644 index aa9fc2196b..0000000000 --- a/src/wmtk/energy/utils/AutoDiffUtils.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once -#include "autodiff.h" - -namespace wmtk { -namespace energy { -using DScalar = DScalar2, Eigen::Matrix>; -using Scalar = typename DScalar::Scalar; -template -class AutoDiffAllocator -{ -public: - T operator()(const int i, double v) const { return T(i, v); } -}; - -template <> -class AutoDiffAllocator -{ -public: - double operator()(const int i, double v) const { return v; } -}; - -inline double get_value(float x) -{ - return static_cast(x); -} -inline double get_value(double x) -{ - return x; -} -inline double get_value( - DScalar2, Eigen::Matrix> x) -{ - return x.getValue(); -} - -inline double get_value( - DScalar2, Eigen::Matrix> x) -{ - return x.getValue(); -} - -template -void get_T_vector(const Eigen::MatrixXd& data, const int size, AutoDiffVect& T_vector) -{ - typedef typename AutoDiffVect::Scalar T; - DiffScalarBase::setVariableCount(size); - const AutoDiffAllocator allocate_auto_diff_scalar; - T_vector.resize(size); - for (int i = 0; i < size; ++i) { - T_vector(i) = allocate_auto_diff_scalar(i, data(i)); - } -} -template -void get_double_vector(const AutoDiffVect& T_vector, const int size, Eigen::MatrixXd& double_t) -{ - double_t.resize(size, 1); - for (int i = 0; i < size; ++i) { - double_t(i) = T_vector(i).getValue(); - } -} -} // namespace energy -} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/utils/CMakeLists.txt b/src/wmtk/energy/utils/CMakeLists.txt deleted file mode 100644 index 9c70628599..0000000000 --- a/src/wmtk/energy/utils/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(SRC_FILES - AutoDiffUtils.hpp - autodiff.h - autodiff.cpp - DofsToPosition.hpp -) -target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/energy/utils/DofsToPosition.hpp b/src/wmtk/energy/utils/DofsToPosition.hpp deleted file mode 100644 index ce7cc36a77..0000000000 --- a/src/wmtk/energy/utils/DofsToPosition.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "AutoDiffUtils.hpp" -#include "autodiff.h" - -namespace wmtk { -namespace energy { -class DofsToPosition -{ // size = 2: uv position, size =1, t of boundary curve - using DofVectorX = Eigen::Matrix; - -protected: - std::unique_ptr m_sampling; - -public: - DofsToPosition() = default; - ~DofsToPosition() = default; - DofsToPosition& operator=(const DofsToPosition&) = default; // copy assignment operator - DofsToPosition& operator=(DofsToPosition&&) = default; // move assignment operator - - /** - * @brief Construct a new Dofs To Position object using a displacement map (requires a sampler) - * - * @param image - */ - DofsToPosition(const image::Image& image) - { - m_sampling = std::make_unique(image); - } - - DofsToPosition( - const wmtk::image::SamplingAnalyticFunction::FunctionType type, - const double a, - const double b, - const double c) - { - m_sampling = std::make_unique(type, a, b, c); - } - - - template - Eigen::Matrix dof_to_pos(const Eigen::Matrix& dofT) const - { - Eigen::Matrix pos; - int size = dofT.rows(); - - if (size == 2) { - // TODO retrive position using displacement map - // for now just return itself - pos << dofT(0), dofT(1), m_sampling->sample(dofT(0), dofT(1)); - - } else - //(dofx.size() == 1) - { - // curve parameterization - // TODO covert to uv first and sample using the uv - - pos << dofT(0), static_cast(0.0), static_cast(0.0); - } - return pos; - } -}; - -} // namespace energy -} // namespace wmtk diff --git a/src/wmtk/energy/AMIPS.cpp b/src/wmtk/function/AMIPS.cpp similarity index 54% rename from src/wmtk/energy/AMIPS.cpp rename to src/wmtk/function/AMIPS.cpp index 0282abe074..6a613b358d 100644 --- a/src/wmtk/energy/AMIPS.cpp +++ b/src/wmtk/function/AMIPS.cpp @@ -1,70 +1,85 @@ +#pragma once #include "AMIPS.hpp" #include using namespace wmtk; -using namespace wmtk::energy; +using namespace wmtk::function; -AMIPS::AMIPS(const TriMesh& mesh) - : DifferentiableEnergy(mesh) +AMIPS::AMIPS(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) + : AutodiffFunction(mesh, vertex_attribute_handle) {} -AMIPS_2D::AMIPS_2D(const TriMesh& mesh) - : AMIPS(mesh) +AMIPS_2D::AMIPS_2D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) + : AMIPS(mesh, vertex_attribute_handle) +{ + // assert(m_vertex_attribute_handle); + // check the dimension of the position + // assert(m_mesh.get_attribute_size(m_vertex_attribute_handle) == 2); +} + +AMIPS_3DEmbedded::AMIPS_3DEmbedded( + const TriMesh& mesh, + const MeshAttributeHandle& vertex_uv_handle, + const image::Image& image) + : AMIPS(mesh, vertex_uv_handle) + , m_dofs_to_pos(image) +{} + +AMIPS_3DEmbedded::AMIPS_3DEmbedded( + const TriMesh& mesh, + const MeshAttributeHandle& vertex_uv_handle, + const wmtk::image::SamplingAnalyticFunction::FunctionType type, + const double a, + const double b, + const double c) + : AMIPS(mesh, vertex_uv_handle) + , m_dofs_to_pos(type, a, b, c) {} -Eigen::Matrix AMIPS::get_target_triangle(double scaling) const +Eigen::Matrix AMIPS::get_target_triangle(double scaling) { const static std::array m_target_triangle = {0., 1., 1. / 2., 0., 0., sqrt(3) / 2.}; return scaling * Eigen::Matrix::ConstMapType(m_target_triangle.data()); } -double AMIPS_2D::energy_eval(const Tuple& tuple) const +DScalar AMIPS_2D::get_value_autodiff(const Tuple& tuple) const { // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); - Eigen::Vector2d uv1 = pos.vector_attribute(tuple).head<2>(); - Eigen::Vector2d uv2 = - pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))).head<2>(); - Eigen::Vector2d uv3 = - pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))).head<2>(); + ConstAccessor pos = m_mesh.create_const_accessor(m_vertex_attribute_handle); - // return the energy - return energy_eval(uv1, uv2, uv3); -} -DScalar AMIPS_2D::energy_eval_autodiff(const Tuple& tuple) const -{ - // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); + Eigen::Vector2d uv0 = pos.vector_attribute(tuple); + int size = 2; + Eigen::Matrix dofT = get_T_vector>(uv0, size); - Eigen::Vector2d uv1 = pos.vector_attribute(tuple); - Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); - Eigen::Vector2d uv3 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); + Eigen::Vector2d uv1 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); + Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); // return the energy - return energy_eval(uv1, uv2, uv3); + return function_eval(dofT, uv1, uv2); } + template -auto AMIPS_2D::energy_eval( +auto AMIPS_2D::function_eval( + const Eigen::Matrix& uv0, const Eigen::Vector2d& uv1, - const Eigen::Vector2d& uv2, - const Eigen::Vector2d& uv3) const -> T + const Eigen::Vector2d& uv2) const -> T { // (x0 - x1, y0 - y1, x0 - x2, y0 - y2).transpose - Eigen::Matrix Dm; - typedef Eigen::Matrix Vec2T; - Vec2T uv1_; - get_T_vector(uv1, 2, uv1_); - - Dm << uv2(0) - uv1_(0), uv3(0) - uv1_(0), uv2(1) - uv1_(1), uv3(1) - uv1_(1); + Eigen::Matrix Dm; + Dm.row(0) = uv1.template cast() - uv0; + Dm.row(1) = uv2.template cast() - uv0; + // Dm << uv1(0) - uv0(0), uv2(0) - uv0(0), uv1(1) - uv0(1), uv2(1) - uv0(1); Eigen::Matrix2d Ds, Dsinv; Eigen::Matrix target_triangle = get_target_triangle(1.0); - Eigen::Vector2d target_A, target_B, target_C; - target_A = target_triangle.row(0); - target_B = target_triangle.row(1); - target_C = target_triangle.row(2); - Ds << target_B.x() - target_A.x(), target_C.x() - target_A.x(), target_B.y() - target_A.y(), - target_C.y() - target_A.y(); + Eigen::Vector2d target0, target1, target2; + target0 = target_triangle.row(0); + target1 = target_triangle.row(1); + target2 = target_triangle.row(2); + Ds.row(0) = target1 - target0; + Ds.row(1) = target2 - target0; + // Ds << target1.x() - target0.x(), target2.x() - target0.x(), target1.y() - target0.y(), + // target2.y() - target0.y(); auto Dsdet = Ds.determinant(); if (abs(Dsdet) < std::numeric_limits::denorm_min()) { @@ -74,10 +89,11 @@ auto AMIPS_2D::energy_eval( // define of transform matrix F = Dm@Ds.inv Eigen::Matrix F; - F << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), - (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), - (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), - (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); + F = Dm.transpose() * Dsinv.transpose().template cast(); + // F << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), + // (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), + // (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), + // (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); auto Fdet = F.determinant(); if (abs(Fdet) < std::numeric_limits::denorm_min()) { @@ -86,59 +102,37 @@ auto AMIPS_2D::energy_eval( return (F.transpose() * F).trace() / Fdet; } -double AMIPS_3DEmbedded::energy_eval(const Tuple& tuple) const +DScalar AMIPS_3DEmbedded::get_value_autodiff(const Tuple& tuple) const { // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); - Eigen::Vector2d uv1 = pos.vector_attribute(tuple).head<2>(); - // TODO curve mesh uv -> t conversion happens here - // check if it's on the curve mesh or not - // if curve mesh, convert to t - // else - int size = 2; - Eigen::Matrix dofT; - get_T_vector>(uv1, size, dofT); - - Eigen::Vector2d uv2 = - pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))).head<2>(); - Eigen::Vector2d uv3 = - pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))).head<2>(); - - // return the energy - return energy_eval(dofT, uv2, uv3); -} - -DScalar AMIPS_3DEmbedded::energy_eval_autodiff(const Tuple& tuple) const -{ - // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_const_accessor(m_position_handle); + ConstAccessor pos = m_mesh.create_const_accessor(m_vertex_attribute_handle); // TODO curve mesh uv -> t conversion happens here - Eigen::Vector2d uv1 = pos.vector_attribute(tuple); + Eigen::Vector2d uv0 = pos.vector_attribute(tuple); int size = 2; - Eigen::Matrix dofT; - get_T_vector>(uv1, size, dofT); + Eigen::Matrix dofT = get_T_vector>(uv0, size); - Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); - Eigen::Vector2d uv3 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); + Eigen::Vector2d uv1 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); + Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); // return the energy - return energy_eval(dofT, uv2, uv3); + return function_eval(dofT, uv1, uv2); } + template -auto AMIPS_3DEmbedded::energy_eval( - const Eigen::Matrix& dofT, - const Eigen::Vector2d& uv2, - const Eigen::Vector2d& uv3) const -> T +auto AMIPS_3DEmbedded::function_eval( + const Eigen::Matrix& uv0, + const Eigen::Vector2d& uv1, + const Eigen::Vector2d& uv2) const -> T { - Eigen::Matrix pos1 = m_dofs_to_pos.dof_to_pos(dofT); + Eigen::Matrix pos0 = m_dofs_to_pos.dof_to_pos(uv0); + Eigen::Matrix pos1 = m_dofs_to_pos.dof_to_pos(uv1); Eigen::Matrix pos2 = m_dofs_to_pos.dof_to_pos(uv2); - Eigen::Matrix pos3 = m_dofs_to_pos.dof_to_pos(uv3); Eigen::Matrix V2_V1; - V2_V1 << pos2(0) - pos1(0), pos2(1) - pos1(1), pos2(2) - pos1(2); + V2_V1 << pos1(0) - pos0(0), pos1(1) - pos0(1), pos1(2) - pos0(2); Eigen::Matrix V3_V1; - V3_V1 << pos3(0) - pos1(0), pos3(1) - pos1(1), pos3(2) - pos1(2); + V3_V1 << pos2(0) - pos0(0), pos2(1) - pos0(1), pos2(2) - pos0(2); // tangent bases // e1 = (V2 - V1).normalize() @@ -153,7 +147,7 @@ auto AMIPS_3DEmbedded::energy_eval( // if (double_n.lpNorm() < std::numeric_limits::denorm_min()) { // wmtk::logger().critical("n.lpNorm {}", double_n.lpNorm()); // std::cout << "V1 " << std::endl; - // std::cout << std::hexfloat << get_value(pos1(0))<< " " << y1 << v1(2) << std::endl; + // std::cout << std::hexfloat << get_value(pos0(0))<< " " << y1 << v1(2) << std::endl; // std::cout << "V2 " << std::endl; // std::cout << std::hexfloat << v2(0) << " " << v2(1) << v2(2) << std::endl; // std::cout << "V3 " << std::endl; @@ -182,7 +176,7 @@ auto AMIPS_3DEmbedded::energy_eval( Dm << VT2.x() - VT1.x(), VT3.x() - VT1.x(), VT2.y() - VT1.y(), VT3.y() - VT1.y(); T Dmdet = Dm.determinant(); - assert(wmtk::energy::get_value(Dmdet) > 0); + assert(wmtk::function::get_value(Dmdet) > 0); Eigen::Matrix2d Ds, Dsinv; Eigen::Matrix target_triangle = get_target_triangle(1.0); diff --git a/src/wmtk/energy/AMIPS.hpp b/src/wmtk/function/AMIPS.hpp similarity index 51% rename from src/wmtk/energy/AMIPS.hpp rename to src/wmtk/function/AMIPS.hpp index 247a75674d..d62f6a09bd 100644 --- a/src/wmtk/energy/AMIPS.hpp +++ b/src/wmtk/function/AMIPS.hpp @@ -1,40 +1,39 @@ #pragma once #include -#include "DifferentiableEnergy.hpp" +#include "AutodiffFunction.hpp" #include "utils/AutoDiffUtils.hpp" #include "utils/DofsToPosition.hpp" namespace wmtk { -namespace energy { -class AMIPS : public DifferentiableEnergy +namespace function { +class AMIPS : public AutodiffFunction { public: - AMIPS(const TriMesh& mesh); - Eigen::Matrix get_target_triangle(double scaling) const; + AMIPS(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); + static Eigen::Matrix get_target_triangle(double scaling); }; class AMIPS_2D : public AMIPS { public: - AMIPS_2D(const TriMesh& mesh); + AMIPS_2D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); -public: - double energy_eval(const Tuple& tuple) const override; - DScalar energy_eval_autodiff(const Tuple& tuple) const override; +protected: + DScalar get_value_autodiff(const Tuple& tuple) const override; /** * @brief gradient defined wrt the first vertex * + * @param uv0 * @param uv1 * @param uv2 - * @param uv3 * @return can be double or DScalar */ template - T energy_eval( + T function_eval( + const Eigen::Matrix& uv0, const Eigen::Vector2d& uv1, - const Eigen::Vector2d& uv2, - const Eigen::Vector2d& uv3) const; -}; // namespace energy + const Eigen::Vector2d& uv2) const; +}; /** * @brief 3D AMIPS uses uv and displacement map to get the 3d cooridnates then evaluate @@ -43,27 +42,24 @@ class AMIPS_2D : public AMIPS class AMIPS_3DEmbedded : public AMIPS { public: - AMIPS_3DEmbedded(const TriMesh& mesh, const image::Image& image) - : AMIPS(mesh) - , m_dofs_to_pos(image) - {} AMIPS_3DEmbedded( const TriMesh& mesh, + const MeshAttributeHandle& vertex_uv_handle, + const image::Image& image); + AMIPS_3DEmbedded( + const TriMesh& mesh, + const MeshAttributeHandle& vertex_uv_handle, const wmtk::image::SamplingAnalyticFunction::FunctionType type, const double a, const double b, - const double c) - : AMIPS(mesh) - , m_dofs_to_pos(type, a, b, c) - {} - + const double c); public: - double energy_eval(const Tuple& tuple) const override; - DScalar energy_eval_autodiff(const Tuple& tuple) const override; + DScalar get_value_autodiff(const Tuple& tuple) const override; +protected: template - T energy_eval( + T function_eval( const Eigen::Matrix& dofT, const Eigen::Vector2d& uv2, const Eigen::Vector2d& uv3) const; @@ -71,5 +67,5 @@ class AMIPS_3DEmbedded : public AMIPS protected: DofsToPosition m_dofs_to_pos; }; -} // namespace energy +} // namespace function } // namespace wmtk diff --git a/src/wmtk/function/AreaAccuracy.cpp b/src/wmtk/function/AreaAccuracy.cpp new file mode 100644 index 0000000000..4fa89ed358 --- /dev/null +++ b/src/wmtk/function/AreaAccuracy.cpp @@ -0,0 +1,13 @@ +#include "AreaAccuracy.hpp" +using namespace wmtk; +using namespace wmtk::function; + +// AreaAccuracy::AreaAccuracy(const TriMesh& mesh1, const TriMesh& mesh2) +// : AutodiffFunction(mesh1) +// , m_position_mesh(mesh2) +// , m_3d_position_handle(mesh2.get_attribute_handle("position", PrimitiveType::Vertex)) +// {} + +// template +// T energy_eval(const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, const Eigen::Vector2d& uv3) +// {} diff --git a/src/wmtk/energy/AreaAccuracy.hpp b/src/wmtk/function/AreaAccuracy.hpp similarity index 56% rename from src/wmtk/energy/AreaAccuracy.hpp rename to src/wmtk/function/AreaAccuracy.hpp index 52400c20e2..43f03c2b33 100644 --- a/src/wmtk/energy/AreaAccuracy.hpp +++ b/src/wmtk/function/AreaAccuracy.hpp @@ -1,16 +1,15 @@ #include #include -#include "DifferentiableEnergy.hpp" +#include "AutodiffFunction.hpp" namespace wmtk { -namespace energy { +namespace function { -class AreaAccuracy : public DifferentiableEnergy +class AreaAccuracy : public AutodiffFunction { public: AreaAccuracy(const TriMesh& mesh1, const TriMesh& mesh2); - double energy_eval(const Tuple& tuple) const override; - DScalar energy_eval_autodiff(const Tuple& tuple) const override; + DScalar get_value_autodiff(const Tuple& tuple) const override; /** * @brief gradient defined wrt the first vertex @@ -21,12 +20,14 @@ class AreaAccuracy : public DifferentiableEnergy * @return can be double or DScalar */ template - static T - energy_eval(const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, const Eigen::Vector2d& uv3); + static T function_eval( + const Eigen::Vector2d& uv1, + const Eigen::Vector2d& uv2, + const Eigen::Vector2d& uv3); protected: const TriMesh& m_position_mesh; const MeshAttributeHandle m_3d_position_handle; }; -} // namespace energy +} // namespace function } // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/AutodiffFunction.cpp b/src/wmtk/function/AutodiffFunction.cpp new file mode 100644 index 0000000000..448a07435c --- /dev/null +++ b/src/wmtk/function/AutodiffFunction.cpp @@ -0,0 +1,22 @@ +#include "AutodiffFunction.hpp" + +using namespace wmtk; +using namespace wmtk::function; + +AutodiffFunction::AutodiffFunction( + const Mesh& mesh, + const MeshAttributeHandle& vertex_attribute_handle) + : DifferentiableFunction(mesh, vertex_attribute_handle){}; + +double AutodiffFunction::get_value(const Tuple& tuple) const +{ + return get_value_autodiff(tuple).getValue(); +} +Eigen::VectorXd AutodiffFunction::get_gradient(const Tuple& tuple) const +{ + return get_value_autodiff(tuple).getGradient(); +} +Eigen::MatrixXd AutodiffFunction::get_hessian(const Tuple& tuple) const +{ + return get_value_autodiff(tuple).getHessian(); +} \ No newline at end of file diff --git a/src/wmtk/function/AutodiffFunction.hpp b/src/wmtk/function/AutodiffFunction.hpp new file mode 100644 index 0000000000..c2bf5540e6 --- /dev/null +++ b/src/wmtk/function/AutodiffFunction.hpp @@ -0,0 +1,25 @@ +#pragma once +#include "DifferentiableFunction.hpp" +namespace wmtk { +namespace function { + +using DScalar = DScalar2, Eigen::Matrix>; +using Scalar = typename DScalar::Scalar; + +class AutodiffFunction : public DifferentiableFunction +{ +public: + AutodiffFunction(const Mesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); + + virtual ~AutodiffFunction() = default; + +public: + double get_value(const Tuple& tuple) const override; + Eigen::VectorXd get_gradient(const Tuple& tuple) const override; + Eigen::MatrixXd get_hessian(const Tuple& tuple) const override; + +protected: + virtual DScalar get_value_autodiff(const Tuple& tuple) const = 0; +}; +} // namespace function +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/CMakeLists.txt b/src/wmtk/function/CMakeLists.txt new file mode 100644 index 0000000000..079cfda905 --- /dev/null +++ b/src/wmtk/function/CMakeLists.txt @@ -0,0 +1,17 @@ + +set(SRC_FILES + Function.hpp + Function.cpp + DifferentiableFunction.cpp + DifferentiableFunction.hpp + AMIPS.hpp + AMIPS.cpp + TriMeshValenceFunction.hpp + TriMeshValenceFunction.cpp + AreaAccuracy.hpp + AreaAccuracy.cpp + AutodiffFunction.hpp + AutodiffFunction.cpp +) +target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) +add_subdirectory(utils) \ No newline at end of file diff --git a/src/wmtk/function/DifferentiableFunction.cpp b/src/wmtk/function/DifferentiableFunction.cpp new file mode 100644 index 0000000000..1cc457a08f --- /dev/null +++ b/src/wmtk/function/DifferentiableFunction.cpp @@ -0,0 +1,9 @@ +#include "DifferentiableFunction.hpp" +using namespace wmtk; +using namespace wmtk::function; + +DifferentiableFunction::DifferentiableFunction( + const Mesh& mesh, + const MeshAttributeHandle& vertex_attribute_handle) + : Function(mesh) + , m_vertex_attribute_handle(vertex_attribute_handle){}; diff --git a/src/wmtk/function/DifferentiableFunction.hpp b/src/wmtk/function/DifferentiableFunction.hpp new file mode 100644 index 0000000000..ee3fc13ff6 --- /dev/null +++ b/src/wmtk/function/DifferentiableFunction.hpp @@ -0,0 +1,26 @@ +#pragma once +#include +#include +#include +#include +// #include +#include "Function.hpp" +namespace wmtk { +namespace function { + +class DifferentiableFunction : public Function +{ +public: + DifferentiableFunction( + const Mesh& mesh, + const MeshAttributeHandle& vertex_attribute_handle); + + virtual ~DifferentiableFunction() = default; + + const MeshAttributeHandle m_vertex_attribute_handle; + + virtual Eigen::VectorXd get_gradient(const Tuple& tuple) const = 0; + virtual Eigen::MatrixXd get_hessian(const Tuple& tuple) const = 0; +}; +} // namespace function +} // namespace wmtk diff --git a/src/wmtk/function/Function.cpp b/src/wmtk/function/Function.cpp new file mode 100644 index 0000000000..d4192b85d3 --- /dev/null +++ b/src/wmtk/function/Function.cpp @@ -0,0 +1,9 @@ +#include "Function.hpp" +using namespace wmtk; +using namespace wmtk::function; + +Function::Function(const Mesh& mesh) + : m_mesh(mesh) +{} + +Function::~Function() = default; diff --git a/src/wmtk/function/Function.hpp b/src/wmtk/function/Function.hpp new file mode 100644 index 0000000000..903e7e1e4b --- /dev/null +++ b/src/wmtk/function/Function.hpp @@ -0,0 +1,21 @@ +#pragma once +#include +#include +namespace wmtk { +namespace function { +class Function +{ +protected: + const Mesh& m_mesh; + + +public: + Function(const Mesh& mesh); + virtual ~Function(); + +public: + // evaluate the function on the top level simplex of the tuple + virtual double get_value(const Tuple& tuple) const = 0; +}; +} // namespace function +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/TriMeshValenceEnergy.cpp b/src/wmtk/function/TriMeshValenceFunction.cpp similarity index 88% rename from src/wmtk/energy/TriMeshValenceEnergy.cpp rename to src/wmtk/function/TriMeshValenceFunction.cpp index 975d5ddb7f..cc3f91e6a3 100644 --- a/src/wmtk/energy/TriMeshValenceEnergy.cpp +++ b/src/wmtk/function/TriMeshValenceFunction.cpp @@ -1,14 +1,14 @@ -#include "TriMeshValenceEnergy.hpp" +#include "TriMeshValenceFunction.hpp" #include #include #include namespace wmtk { -namespace energy { -TriMeshValenceEnergy::TriMeshValenceEnergy(const TriMesh& mesh) - : Energy(mesh) +namespace function { +TriMeshValenceFunction::TriMeshValenceFunction(const TriMesh& mesh) + : Function(mesh) {} -double TriMeshValenceEnergy::energy_eval(const Tuple& tuple) const +double TriMeshValenceFunction::get_value(const Tuple& tuple) const { // assume tuple is not a boundary edge const Tuple current_v = tuple; @@ -51,5 +51,5 @@ double TriMeshValenceEnergy::energy_eval(const Tuple& tuple) const return static_cast(val_energy); } -} // namespace energy +} // namespace function } // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/TriMeshValenceFunction.hpp b/src/wmtk/function/TriMeshValenceFunction.hpp new file mode 100644 index 0000000000..8076114ec5 --- /dev/null +++ b/src/wmtk/function/TriMeshValenceFunction.hpp @@ -0,0 +1,16 @@ +#include +#include "Function.hpp" +namespace wmtk { +namespace function { + +class TriMeshValenceFunction : public Function +{ +public: + TriMeshValenceFunction(const TriMesh& mesh); + double get_value(const Tuple& tuple) const override; + +protected: + TriMesh& mesh() const; +}; +} // namespace function +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/energy/utils/autodiff.cpp b/src/wmtk/function/utils/autodiff.cpp similarity index 100% rename from src/wmtk/energy/utils/autodiff.cpp rename to src/wmtk/function/utils/autodiff.cpp diff --git a/src/wmtk/energy/utils/autodiff.h b/src/wmtk/function/utils/autodiff.h similarity index 100% rename from src/wmtk/energy/utils/autodiff.h rename to src/wmtk/function/utils/autodiff.h From 6e598ff781de3e1ee3511835ef531b00ddee0db1 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Mon, 11 Sep 2023 23:18:08 -0400 Subject: [PATCH 051/134] add tests for AMIPS2d. compilation details --- src/wmtk/function/utils/AutoDiffUtils.hpp | 64 +++++++++++++++++ src/wmtk/function/utils/CMakeLists.txt | 7 ++ src/wmtk/function/utils/DofsToPosition.hpp | 69 +++++++++++++++++++ src/wmtk/image/Image.hpp | 4 +- src/wmtk/image/Sampling.hpp | 3 +- src/wmtk/image/bicubic_interpolation.hpp | 2 +- tests/CMakeLists.txt | 4 +- tests/{energy => function}/CMakeLists.txt | 0 tests/{energy => function}/test_2d_energy.cpp | 43 ++++++------ tests/tools/TriMesh_examples.cpp | 7 +- tests/tools/TriMesh_examples.hpp | 2 +- 11 files changed, 177 insertions(+), 28 deletions(-) create mode 100644 src/wmtk/function/utils/AutoDiffUtils.hpp create mode 100644 src/wmtk/function/utils/CMakeLists.txt create mode 100644 src/wmtk/function/utils/DofsToPosition.hpp rename tests/{energy => function}/CMakeLists.txt (100%) rename tests/{energy => function}/test_2d_energy.cpp (59%) diff --git a/src/wmtk/function/utils/AutoDiffUtils.hpp b/src/wmtk/function/utils/AutoDiffUtils.hpp new file mode 100644 index 0000000000..b9cc7ba6e1 --- /dev/null +++ b/src/wmtk/function/utils/AutoDiffUtils.hpp @@ -0,0 +1,64 @@ +#pragma once +#include "autodiff.h" + +namespace wmtk { +namespace function { +using DScalar = DScalar2, Eigen::Matrix>; +using Scalar = typename DScalar::Scalar; +template +class AutoDiffAllocator +{ +public: + T operator()(const int i, double v) const { return T(i, v); } +}; + +template <> +class AutoDiffAllocator +{ +public: + double operator()(const int i, double v) const { return v; } +}; + +inline double get_value(float x) +{ + return static_cast(x); +} +inline double get_value(double x) +{ + return x; +} +inline double get_value( + DScalar2, Eigen::Matrix> x) +{ + return x.getValue(); +} + +inline double get_value( + DScalar2, Eigen::Matrix> x) +{ + return x.getValue(); +} + +template +AutoDiffVect get_T_vector(const Eigen::MatrixXd& data, const int size) +{ + typedef typename AutoDiffVect::Scalar T; + AutoDiffVect T_vector; + DiffScalarBase::setVariableCount(size); + const AutoDiffAllocator allocate_auto_diff_scalar; + T_vector.resize(size); + for (int i = 0; i < size; ++i) { + T_vector(i) = allocate_auto_diff_scalar(i, data(i)); + } + return T_vector; +} +template +void get_double_vector(const AutoDiffVect& T_vector, const int size, Eigen::MatrixXd& double_t) +{ + double_t.resize(size, 1); + for (int i = 0; i < size; ++i) { + double_t(i) = T_vector(i).getValue(); + } +} +} // namespace function +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/utils/CMakeLists.txt b/src/wmtk/function/utils/CMakeLists.txt new file mode 100644 index 0000000000..9c70628599 --- /dev/null +++ b/src/wmtk/function/utils/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SRC_FILES + AutoDiffUtils.hpp + autodiff.h + autodiff.cpp + DofsToPosition.hpp +) +target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/function/utils/DofsToPosition.hpp b/src/wmtk/function/utils/DofsToPosition.hpp new file mode 100644 index 0000000000..3208cd4d22 --- /dev/null +++ b/src/wmtk/function/utils/DofsToPosition.hpp @@ -0,0 +1,69 @@ +#pragma once +#include +#include +#include +#include +#include +#include "AutoDiffUtils.hpp" +#include "autodiff.h" + +namespace wmtk { +namespace function { +class DofsToPosition +{ // size = 2: uv position, size =1, t of boundary curve + using DofVectorX = Eigen::Matrix; + +protected: + std::unique_ptr m_sampling; + +public: + DofsToPosition() = default; + ~DofsToPosition() = default; + DofsToPosition& operator=(const DofsToPosition&) = default; // copy assignment operator + DofsToPosition& operator=(DofsToPosition&&) = default; // move assignment operator + + /** + * @brief Construct a new Dofs To Position object using a displacement map (requires a sampler) + * + * @param image + */ + DofsToPosition(const image::Image& image) + { + m_sampling = std::make_unique(image); + } + + DofsToPosition( + const wmtk::image::SamplingAnalyticFunction::FunctionType type, + const double a, + const double b, + const double c) + { + m_sampling = std::make_unique(type, a, b, c); + } + + + template + Eigen::Matrix dof_to_pos(const Eigen::Matrix& dofT) const + { + Eigen::Matrix pos; + int size = dofT.rows(); + + if (size == 2) { + // TODO retrive position using displacement map + // for now just return itself + pos << dofT(0), dofT(1), m_sampling->sample(dofT(0), dofT(1)); + + } else + //(dofx.size() == 1) + { + // curve parameterization + // TODO covert to uv first and sample using the uv + + pos << dofT(0), static_cast(0.0), static_cast(0.0); + } + return pos; + } +}; + +} // namespace energy +} // namespace wmtk diff --git a/src/wmtk/image/Image.hpp b/src/wmtk/image/Image.hpp index 1fce7ae1a5..283aee4cc5 100644 --- a/src/wmtk/image/Image.hpp +++ b/src/wmtk/image/Image.hpp @@ -2,13 +2,13 @@ #include #include -#include +#include #include #include #include #include #include -#include +#include #include "bicubic_interpolation.hpp" #include "load_image_exr.h" #include "save_image_exr.h" diff --git a/src/wmtk/image/Sampling.hpp b/src/wmtk/image/Sampling.hpp index 12cd4def76..0dbc9f9dff 100644 --- a/src/wmtk/image/Sampling.hpp +++ b/src/wmtk/image/Sampling.hpp @@ -34,7 +34,8 @@ class SamplingAnalyticFunction : public Sampling { if (m_type == Linear) { return _evaluate_linear(u, v); - } + } else + return static_cast(0.0); } template diff --git a/src/wmtk/image/bicubic_interpolation.hpp b/src/wmtk/image/bicubic_interpolation.hpp index 18016eca96..aa7c14e138 100644 --- a/src/wmtk/image/bicubic_interpolation.hpp +++ b/src/wmtk/image/bicubic_interpolation.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include #include diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7c005f6c82..d06e0f0728 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -46,8 +46,8 @@ add_subdirectory(components) source_group("components" REGULAR_EXPRESSION "components\/.*\.(cpp|h|hpp)?$") -add_subdirectory(energy) -source_group("energy" REGULAR_EXPRESSION "energy\/.*\.(cpp|h|hpp)?$") +add_subdirectory(function) +source_group("function" REGULAR_EXPRESSION "function\/.*\.(cpp|h|hpp)?$") wmtk_copy_dll(wmtk_tests) diff --git a/tests/energy/CMakeLists.txt b/tests/function/CMakeLists.txt similarity index 100% rename from tests/energy/CMakeLists.txt rename to tests/function/CMakeLists.txt diff --git a/tests/energy/test_2d_energy.cpp b/tests/function/test_2d_energy.cpp similarity index 59% rename from tests/energy/test_2d_energy.cpp rename to tests/function/test_2d_energy.cpp index 651d10ddac..341528cb04 100644 --- a/tests/energy/test_2d_energy.cpp +++ b/tests/function/test_2d_energy.cpp @@ -1,14 +1,14 @@ - +#pragma once #include #include #include -#include -#include -#include +#include +#include +#include #include "../tools/DEBUG_TriMesh.hpp" #include "../tools/TriMesh_examples.hpp" using namespace wmtk; -using namespace wmtk::energy; +using namespace wmtk::function; using namespace wmtk::tests; TEST_CASE("energy_valence") { @@ -26,26 +26,29 @@ TEST_CASE("energy_valence") const TriMesh tri_mesh = static_cast(example_mesh); - TriMeshValenceEnergy valence_energy(tri_mesh); + TriMeshValenceFunction valence_energy(tri_mesh); - REQUIRE(valence_energy.energy_eval(e1) == 2); - REQUIRE(valence_energy.energy_eval(e2) == 2); - REQUIRE(valence_energy.energy_eval(e3) == 2); - REQUIRE(valence_energy.energy_eval(e4) == 2); + REQUIRE(valence_energy.get_value(e1) == 2); + REQUIRE(valence_energy.get_value(e2) == 2); + REQUIRE(valence_energy.get_value(e3) == 2); + REQUIRE(valence_energy.get_value(e4) == 2); } TEST_CASE("amips2d") { SECTION("equilateral triangle") { - const DEBUG_TriMesh example_mesh = single_equilateral_triangle(); + const DEBUG_TriMesh example_mesh = single_equilateral_triangle(2); + + auto uv_handle = + example_mesh.get_attribute_handle("position", PrimitiveType::Vertex); auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); const TriMesh tri_mesh = static_cast(example_mesh); - AMIPS_2D amips2d(tri_mesh); + AMIPS_2D amips2d(tri_mesh, uv_handle); - REQUIRE(amips2d.energy_eval(e1) == 2.0); + REQUIRE(amips2d.get_value(e1) == 2.0); } } @@ -58,13 +61,13 @@ TEST_CASE("amips3d") const TriMesh tri_mesh = static_cast(example_mesh); - AMIPS_3DEmbedded amips3d( - tri_mesh, - wmtk::image::SamplingAnalyticFunction::FunctionType::Linear, - 0.0, - 0.0, - 1.0); + // AMIPS_3DEmbedded amips3d( + // tri_mesh, + // wmtk::image::SamplingAnalyticFunction::FunctionType::Linear, + // 0.0, + // 0.0, + // 1.0); - REQUIRE(amips3d.energy_eval(e1) == 2.0); + // REQUIRE(amips3d.get_value(e1) == 2.0); } } \ No newline at end of file diff --git a/tests/tools/TriMesh_examples.cpp b/tests/tools/TriMesh_examples.cpp index 623d52413d..cce3882d40 100644 --- a/tests/tools/TriMesh_examples.cpp +++ b/tests/tools/TriMesh_examples.cpp @@ -13,14 +13,19 @@ TriMesh single_triangle() return m; } -TriMesh single_equilateral_triangle() +TriMesh single_equilateral_triangle(int dimension) { TriMesh m = single_triangle(); Eigen::MatrixXd V; + V.resize(3, 3); V.row(0) << 0., 0., 0; V.row(1) << 1., 0, 0; V.row(2) << 0.5, sqrt(3) / 2., 0; + + if (dimension != 2 && dimension != 3) assert(false); + + V.conservativeResize(3, dimension); mesh_utils::set_matrix_attribute(V, "position", PrimitiveType::Vertex, m); return m; } diff --git a/tests/tools/TriMesh_examples.hpp b/tests/tools/TriMesh_examples.hpp index 7879ae47fd..6d6ce0caaf 100644 --- a/tests/tools/TriMesh_examples.hpp +++ b/tests/tools/TriMesh_examples.hpp @@ -16,7 +16,7 @@ TriMesh single_triangle(); // a single triangle with position // (0,0,0), (1,0,0), (0.5, sqrt(3)/2, 0)ß -TriMesh single_equilateral_triangle(); +TriMesh single_equilateral_triangle(int dimension = 3); // 3--1--- 0 // | / \ . From d45def49a63c72dd0a8c7c94fa25cbb026aec8ad Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 13 Sep 2023 08:32:32 -0400 Subject: [PATCH 052/134] small changes for compilation of AMIPS --- src/wmtk/CMakeLists.txt | 3 +-- src/wmtk/function/AMIPS.cpp | 16 ++++++++++------ src/wmtk/operations/tri_mesh/EdgeSwap.cpp | 4 ++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/wmtk/CMakeLists.txt b/src/wmtk/CMakeLists.txt index ca9121470a..7f1e641679 100644 --- a/src/wmtk/CMakeLists.txt +++ b/src/wmtk/CMakeLists.txt @@ -40,8 +40,7 @@ add_subdirectory(utils) add_subdirectory(attribute) add_subdirectory(operations) add_subdirectory(autogen) -add_subdirectory(energy) -add_subdirectory(image) + add_subdirectory(invariants) add_subdirectory(function) diff --git a/src/wmtk/function/AMIPS.cpp b/src/wmtk/function/AMIPS.cpp index 6a613b358d..ced2efd57e 100644 --- a/src/wmtk/function/AMIPS.cpp +++ b/src/wmtk/function/AMIPS.cpp @@ -45,12 +45,14 @@ DScalar AMIPS_2D::get_value_autodiff(const Tuple& tuple) const // get the uv coordinates of the triangle ConstAccessor pos = m_mesh.create_const_accessor(m_vertex_attribute_handle); - Eigen::Vector2d uv0 = pos.vector_attribute(tuple); + Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); int size = 2; Eigen::Matrix dofT = get_T_vector>(uv0, size); - Eigen::Vector2d uv1 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); - Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); + Eigen::Vector2d uv1 = + pos.const_vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); + Eigen::Vector2d uv2 = + pos.const_vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); // return the energy return function_eval(dofT, uv1, uv2); @@ -108,13 +110,15 @@ DScalar AMIPS_3DEmbedded::get_value_autodiff(const Tuple& tuple) const ConstAccessor pos = m_mesh.create_const_accessor(m_vertex_attribute_handle); // TODO curve mesh uv -> t conversion happens here - Eigen::Vector2d uv0 = pos.vector_attribute(tuple); + Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); int size = 2; Eigen::Matrix dofT = get_T_vector>(uv0, size); - Eigen::Vector2d uv1 = pos.vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); - Eigen::Vector2d uv2 = pos.vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); + Eigen::Vector2d uv1 = + pos.const_vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); + Eigen::Vector2d uv2 = + pos.const_vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); // return the energy return function_eval(dofT, uv1, uv2); diff --git a/src/wmtk/operations/tri_mesh/EdgeSwap.cpp b/src/wmtk/operations/tri_mesh/EdgeSwap.cpp index 3180f9f07d..5c20833656 100644 --- a/src/wmtk/operations/tri_mesh/EdgeSwap.cpp +++ b/src/wmtk/operations/tri_mesh/EdgeSwap.cpp @@ -29,10 +29,10 @@ bool EdgeSwap::before() const const Tuple v1 = mesh().switch_vertex(input_tuple()); long val0 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), v0).size()); long val1 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), v1).size()); - if (m_mesh.is_boundary_vertex(v0)) { + if (mesh().is_boundary_vertex(v0)) { val0 += 2; } - if (m_mesh.is_boundary_vertex(v1)) { + if (mesh().is_boundary_vertex(v1)) { val1 += 2; } if (val0 < 4 || val1 < 4) { From 151197d660880f95ff48443114468a9aa7bb0869 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 13 Sep 2023 08:33:31 -0400 Subject: [PATCH 053/134] traingle area helper functions in utils --- src/wmtk/utils/CMakeLists.txt | 1 + src/wmtk/utils/triangle_helper_functions.hpp | 32 ++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/wmtk/utils/triangle_helper_functions.hpp diff --git a/src/wmtk/utils/CMakeLists.txt b/src/wmtk/utils/CMakeLists.txt index 3b06117a0c..0fc3c5961c 100644 --- a/src/wmtk/utils/CMakeLists.txt +++ b/src/wmtk/utils/CMakeLists.txt @@ -11,5 +11,6 @@ set(SRC_FILES Rational.hpp mesh_utils.hpp mesh_utils.cpp + triangle_helper_functions.hpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/utils/triangle_helper_functions.hpp b/src/wmtk/utils/triangle_helper_functions.hpp new file mode 100644 index 0000000000..dc489f9d9e --- /dev/null +++ b/src/wmtk/utils/triangle_helper_functions.hpp @@ -0,0 +1,32 @@ +#pragma once +#include +namespace wmtk { + +// template get 3d tri area +template +T triangle_3d_area( + const Eigen::Matrix& a, + const Eigen::Matrix& b, + const Eigen::Matrix& c) +{ + const T n0 = (a[1] - b[1]) * (a[2] - c[2]) - (a[1] - c[1]) * (a[2] - b[2]); + const T n1 = -(a[0] - b[0]) * (a[2] - c[2]) + (a[0] - c[0]) * (a[2] - b[2]); + const T n2 = (a[0] - b[0]) * (a[1] - c[1]) - (a[0] - c[0]) * (a[1] - b[1]); + + return sqrt(n0 * n0 + n1 * n1 + n2 * n2) * static_cast(0.5); +} + +// template get 3d tri area +template +T triangle_2d_area( + const Eigen::Matrix& A, + const Eigen::Matrix& B, + const Eigen::Matrix& C) +{ + auto B_A = B - A; + auto C_A = C - A; + T area = static_cast(0.5) * abs(B_A.x() * C_A.y() - B_A.y() * C_A.x()); + return area; +} + +} // namespace wmtk \ No newline at end of file From 81198b004218b3cb4bfb40f268a26a47a7a12223 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 13 Sep 2023 08:33:59 -0400 Subject: [PATCH 054/134] add trinagle inversion invariants --- src/wmtk/invariants/CMakeLists.txt | 2 ++ .../invariants/TriangleInversionInvariant.cpp | 24 +++++++++++++++++++ .../invariants/TriangleInversionInvariant.hpp | 20 ++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 src/wmtk/invariants/TriangleInversionInvariant.cpp create mode 100644 src/wmtk/invariants/TriangleInversionInvariant.hpp diff --git a/src/wmtk/invariants/CMakeLists.txt b/src/wmtk/invariants/CMakeLists.txt index 2c62749a9f..56c59c0a04 100644 --- a/src/wmtk/invariants/CMakeLists.txt +++ b/src/wmtk/invariants/CMakeLists.txt @@ -21,5 +21,7 @@ set(SRC_FILES MaxEdgeLengthInvariant.cpp MinEdgeLengthInvariant.hpp MinEdgeLengthInvariant.cpp + TriangleInversionInvariant.hpp + TriangleInversionInvariant.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/invariants/TriangleInversionInvariant.cpp b/src/wmtk/invariants/TriangleInversionInvariant.cpp new file mode 100644 index 0000000000..beeb937a60 --- /dev/null +++ b/src/wmtk/invariants/TriangleInversionInvariant.cpp @@ -0,0 +1,24 @@ + +#include "TriangleInversionInvariant.hpp" +#include +#include + +namespace wmtk { +TriangleInversionInvariant::TriangleInversionInvariant( + const Mesh& m, + const MeshAttributeHandle& uv_coordinate) + : MeshInvariant(m) + , m_uv_coordinate_handle(uv_coordinate) +{} +bool TriangleInversionInvariant::before(const Tuple& t) const +{ + ConstAccessor accessor = mesh().create_accessor(m_uv_coordinate_handle); + + Eigen::Vector2d p0 = accessor.const_vector_attribute(t); + Eigen::Vector2d p1 = accessor.const_vector_attribute(mesh().switch_vertex(t)); + Eigen::Vector2d p2 = + accessor.const_vector_attribute(mesh().switch_vertex(mesh().switch_edge(t))); + + return (triangle_2d_area(p0, p1, p2) < 0); +} +} // namespace wmtk diff --git a/src/wmtk/invariants/TriangleInversionInvariant.hpp b/src/wmtk/invariants/TriangleInversionInvariant.hpp new file mode 100644 index 0000000000..51287c50d7 --- /dev/null +++ b/src/wmtk/invariants/TriangleInversionInvariant.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "MeshInvariant.hpp" + +namespace wmtk { +class TriangleInversionInvariant : public MeshInvariant +{ +public: + // NOTE: this takes in the threshold squared rather than the threshold itself + TriangleInversionInvariant( + const Mesh& m, + const MeshAttributeHandle& uv_coordinate); + using MeshInvariant::MeshInvariant; + bool before(const Tuple& t) const override; + +private: + const MeshAttributeHandle m_uv_coordinate_handle; +}; +} // namespace wmtk From 10e13dc4eef31b7ecb392679eccb42953e77d9df Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 14 Sep 2023 03:14:26 -0400 Subject: [PATCH 055/134] add inversion check in smooth base operation --- src/wmtk/operations/tri_mesh/VertexSmooth.cpp | 18 +++++++++++++----- src/wmtk/operations/tri_mesh/VertexSmooth.hpp | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/wmtk/operations/tri_mesh/VertexSmooth.cpp b/src/wmtk/operations/tri_mesh/VertexSmooth.cpp index 82d8bbe948..110c78465d 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmooth.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmooth.cpp @@ -1,13 +1,21 @@ #include "VertexSmooth.hpp" - #include #include +#include + +namespace wmtk::operations { + +void OperationSettings::initialize_invariants(const TriMesh& m) +{ + invariants = basic_invariant_collection(m); + if (!smooth_boundary) { + invariants.add(std::make_shared(m)); + } +} +} // namespace wmtk::operations namespace wmtk::operations::tri_mesh { -VertexSmooth::VertexSmooth( - Mesh& m, - const Tuple& t, - const OperationSettings& settings) +VertexSmooth::VertexSmooth(Mesh& m, const Tuple& t, const OperationSettings& settings) : TriMeshOperation(m) , TupleOperation(settings.invariants, t) , m_pos_accessor(m.create_accessor(settings.position)) diff --git a/src/wmtk/operations/tri_mesh/VertexSmooth.hpp b/src/wmtk/operations/tri_mesh/VertexSmooth.hpp index bef7e74a55..c125b31966 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmooth.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmooth.hpp @@ -15,6 +15,7 @@ struct OperationSettings MeshAttributeHandle position; bool smooth_boundary = false; InvariantCollection invariants; + void initialize_invariants(const TriMesh& m); }; namespace tri_mesh { From e9cadbea1617cf7d4e4a6c7778ae833e3ac8705e Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 14 Sep 2023 03:15:11 -0400 Subject: [PATCH 056/134] VertexSmoothing using diff energy --- src/wmtk/operations/tri_mesh/CMakeLists.txt | 2 + .../VertexSmoothUsingDifferentiableEnergy.cpp | 99 +++++++++++++++++++ .../VertexSmoothUsingDifferentiableEnergy.hpp | 47 +++++++++ src/wmtk/utils/triangle_helper_functions.cpp | 15 +++ src/wmtk/utils/triangle_helper_functions.hpp | 7 ++ 5 files changed, 170 insertions(+) create mode 100644 src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp create mode 100644 src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp create mode 100644 src/wmtk/utils/triangle_helper_functions.cpp diff --git a/src/wmtk/operations/tri_mesh/CMakeLists.txt b/src/wmtk/operations/tri_mesh/CMakeLists.txt index e8249a90ea..898f4cae07 100644 --- a/src/wmtk/operations/tri_mesh/CMakeLists.txt +++ b/src/wmtk/operations/tri_mesh/CMakeLists.txt @@ -15,5 +15,7 @@ set(SRC_FILES VertexSmooth.cpp VertexTangentialSmooth.hpp VertexTangentialSmooth.cpp + VertexSmoothUsingDifferentiableEnergy.hpp + VertexSmoothUsingDifferentiableEnergy.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp new file mode 100644 index 0000000000..79f05bec49 --- /dev/null +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp @@ -0,0 +1,99 @@ +#include "VertexSmoothUsingDifferentiableEnergy.hpp" +#include +#include +#include +#include +#include +#include "VertexSmooth.hpp" + +namespace wmtk::operations { +void OperationSettings::initialize_invariants( + const TriMesh& m) +{ + smooth_settings.initialize_invariants(m); + smooth_settings.invariants.add( + std::make_shared(m, smooth_settings.position)); +} +} // namespace wmtk::operations + +namespace wmtk::operations::tri_mesh { +VertexSmoothUsingDifferentiableEnergy::VertexSmoothUsingDifferentiableEnergy( + Mesh& m, + const Tuple& t, + const OperationSettings& settings) + : TriMeshOperation(m) + , TupleOperation(settings.smooth_settings.invariants, t) + , m_uv_pos_accessor{m.create_accessor(settings.smooth_settings.position)} + , m_settings{settings} +{} + +std::string VertexSmoothUsingDifferentiableEnergy::name() const +{ + return "tri_mesh_vertex_smooth_using_differentiable_energy"; +} + +bool VertexSmoothUsingDifferentiableEnergy::before() const +{ + if (!mesh().is_valid_slow(input_tuple())) { + return false; + } + return true; +} + +bool VertexSmoothUsingDifferentiableEnergy::execute() +{ + const Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); + OperationSettings op_settings; + tri_mesh::VertexSmooth smooth_op(mesh(), input_tuple(), m_settings.smooth_settings); + if (!smooth_op()) { + return false; + } + + const Tuple tup = smooth_op.return_tuple(); + Eigen::Vector2d new_pos = p; + + assert(mesh().is_valid_slow(tup)); + // start scope + // auto scope = m_mesh.create_scope(); + + if (m_settings.smooth_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { + // do curve mesh smoothing + + } else { + // get one ring energy wrt to the vertex + const std::vector one_ring = + SimplicialComplex::vertex_one_ring(mesh(), input_tuple()); + double total_energy = 0; + Eigen::Vector2d gradient = Eigen::Vector2d::Zero(); + Eigen::Matrix2d hessian = Eigen::Matrix2d::Zero(); + for (const Simplex& s : one_ring) { + total_energy += m_settings.energy->get_value(s.tuple()); + gradient += m_settings.energy->get_gradient(s.tuple()); + if (m_settings.second_order) hessian += m_settings.energy->get_hessian(s.tuple()); + } + // get descent direction + Eigen::Vector2d descent_dir = Eigen::Vector2d::Zero(); + if (m_settings.second_order) { + // newton's method + descent_dir = -hessian.ldlt().solve(gradient); + } else { + // gradient descent + descent_dir = -gradient; + } + new_pos = p + descent_dir; + // set new position + m_uv_pos_accessor.vector_attribute(tup) = new_pos; + // check if the new position is valid + // for (const Simplex& s : one_ring) { + // if (triangle_2d_area(s.tuple()) < 0) { + // scope.mark_failed(); + // break; + // } + // } + } + + return true; +} + + +} // namespace wmtk::operations::tri_mesh diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp new file mode 100644 index 0000000000..e31cdac63f --- /dev/null +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp @@ -0,0 +1,47 @@ +#pragma once +#include +#include +#include +#include +#include +#include "TriMeshOperation.hpp" +#include "VertexSmooth.hpp" + +namespace wmtk::operations { +namespace tri_mesh { +class VertexSmoothUsingDifferentiableEnergy; +} + +template <> +struct OperationSettings +{ + OperationSettings smooth_settings; + std::unique_ptr energy; + bool second_order = true; + void initialize_invariants(const TriMesh& m); +}; + +namespace tri_mesh { +class VertexSmoothUsingDifferentiableEnergy : public TriMeshOperation, private TupleOperation +{ +public: + VertexSmoothUsingDifferentiableEnergy( + Mesh& m, + const Tuple& t, + const OperationSettings& settings); + + std::string name() const override; + + static PrimitiveType primitive_type() { return PrimitiveType::Vertex; } + +protected: + bool before() const override; + bool execute() override; + +private: + Accessor m_uv_pos_accessor; + const OperationSettings& m_settings; +}; + +} // namespace tri_mesh +} // namespace wmtk::operations diff --git a/src/wmtk/utils/triangle_helper_functions.cpp b/src/wmtk/utils/triangle_helper_functions.cpp new file mode 100644 index 0000000000..b5a710314b --- /dev/null +++ b/src/wmtk/utils/triangle_helper_functions.cpp @@ -0,0 +1,15 @@ +#include "triangle_helper_functions.hpp" +using namespace wmtk; + +double triangle_2d_area( + const TriMesh& m, + const MeshAttributeHandle& vertex_uv_handle, + const Tuple& tuple) +{ + // assuming traingle is ccw + ConstAccessor pos = m.create_const_accessor(vertex_uv_handle); + Eigen::Vector2d p0 = pos.const_vector_attribute(tuple); + Eigen::Vector2d p1 = pos.const_vector_attribute(m.switch_edge(m.switch_vertex(tuple))); + Eigen::Vector2d p2 = pos.const_vector_attribute(m.switch_vertex(tuple)); + return triangle_2d_area(p0, p1, p2); +} \ No newline at end of file diff --git a/src/wmtk/utils/triangle_helper_functions.hpp b/src/wmtk/utils/triangle_helper_functions.hpp index dc489f9d9e..673abe5089 100644 --- a/src/wmtk/utils/triangle_helper_functions.hpp +++ b/src/wmtk/utils/triangle_helper_functions.hpp @@ -1,6 +1,12 @@ #pragma once #include +#include +#include namespace wmtk { +double triangle_2d_area( + const TriMesh& m, + const MeshAttributeHandle& vertex_uv_handle, + const Tuple& tuple); // template get 3d tri area template @@ -29,4 +35,5 @@ T triangle_2d_area( return area; } + } // namespace wmtk \ No newline at end of file From 89154e80a7a1249da2d144fcb5c226cbab4fa526 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 14 Sep 2023 03:15:43 -0400 Subject: [PATCH 057/134] triangle helper function cmake --- src/wmtk/utils/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wmtk/utils/CMakeLists.txt b/src/wmtk/utils/CMakeLists.txt index 0fc3c5961c..90ecb580ba 100644 --- a/src/wmtk/utils/CMakeLists.txt +++ b/src/wmtk/utils/CMakeLists.txt @@ -12,5 +12,6 @@ set(SRC_FILES mesh_utils.hpp mesh_utils.cpp triangle_helper_functions.hpp + triangle_helper_functions.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) From 5d9a88467f1c9a609fae1fbe8d8ce32cd011b0d8 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 14 Sep 2023 03:20:21 -0400 Subject: [PATCH 058/134] amips3d test --- tests/function/test_2d_energy.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/function/test_2d_energy.cpp b/tests/function/test_2d_energy.cpp index 341528cb04..47273d8b25 100644 --- a/tests/function/test_2d_energy.cpp +++ b/tests/function/test_2d_energy.cpp @@ -59,15 +59,17 @@ TEST_CASE("amips3d") const DEBUG_TriMesh example_mesh = single_equilateral_triangle(); auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); const TriMesh tri_mesh = static_cast(example_mesh); + auto uv_handle = + example_mesh.get_attribute_handle("position", PrimitiveType::Vertex); + AMIPS_3DEmbedded amips3d( + tri_mesh, + uv_handle, + wmtk::image::SamplingAnalyticFunction::FunctionType::Linear, + 0.0, + 0.0, + 1.0); - // AMIPS_3DEmbedded amips3d( - // tri_mesh, - // wmtk::image::SamplingAnalyticFunction::FunctionType::Linear, - // 0.0, - // 0.0, - // 1.0); - - // REQUIRE(amips3d.get_value(e1) == 2.0); + REQUIRE(amips3d.get_value(e1) == 2.0); } } \ No newline at end of file From f395b98691c600cc72313ad9183588d4ae140b59 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 14 Sep 2023 05:07:28 -0400 Subject: [PATCH 059/134] test for random traingle on amips energy --- tests/function/test_2d_energy.cpp | 40 +++++++++++++++++++++++++++++-- tests/tools/TriMesh_examples.cpp | 27 +++++++++++++++++++++ tests/tools/TriMesh_examples.hpp | 3 +++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/tests/function/test_2d_energy.cpp b/tests/function/test_2d_energy.cpp index 47273d8b25..868b53b42d 100644 --- a/tests/function/test_2d_energy.cpp +++ b/tests/function/test_2d_energy.cpp @@ -37,7 +37,7 @@ TEST_CASE("energy_valence") TEST_CASE("amips2d") { - SECTION("equilateral triangle") + SECTION("equilateral_triangle") { const DEBUG_TriMesh example_mesh = single_equilateral_triangle(2); @@ -50,11 +50,26 @@ TEST_CASE("amips2d") REQUIRE(amips2d.get_value(e1) == 2.0); } + SECTION("random_triangle") + { + for (int i = 0; i < 50; i++) { + const DEBUG_TriMesh example_mesh = single_triangle_with_position(2); + + auto uv_handle = + example_mesh.get_attribute_handle("position", PrimitiveType::Vertex); + auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); + const TriMesh tri_mesh = static_cast(example_mesh); + + AMIPS_2D amips2d(tri_mesh, uv_handle); + + REQUIRE((amips2d.get_value(e1) > 2. || amips2d.get_value(e1) == 2.)); + } + } } TEST_CASE("amips3d") { - SECTION("equilateral triangle") + SECTION("equilateral_triangle") { const DEBUG_TriMesh example_mesh = single_equilateral_triangle(); auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); @@ -72,4 +87,25 @@ TEST_CASE("amips3d") REQUIRE(amips3d.get_value(e1) == 2.0); } + SECTION("random_triangle") + { + for (int i = 0; i < 50; i++) { + const DEBUG_TriMesh example_mesh = single_triangle_with_position(2); + + auto uv_handle = + example_mesh.get_attribute_handle("position", PrimitiveType::Vertex); + auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); + const TriMesh tri_mesh = static_cast(example_mesh); + + AMIPS_3DEmbedded amips3d( + tri_mesh, + uv_handle, + wmtk::image::SamplingAnalyticFunction::FunctionType::Linear, + 0.0, + 0.0, + 1.0); + + REQUIRE((amips3d.get_value(e1) > 2. || amips3d.get_value(e1) == 2.)); + } + } } \ No newline at end of file diff --git a/tests/tools/TriMesh_examples.cpp b/tests/tools/TriMesh_examples.cpp index cce3882d40..e8a07db4b5 100644 --- a/tests/tools/TriMesh_examples.cpp +++ b/tests/tools/TriMesh_examples.cpp @@ -1,5 +1,7 @@ #include "TriMesh_examples.hpp" +#include #include +#include namespace wmtk::tests { @@ -30,6 +32,31 @@ TriMesh single_equilateral_triangle(int dimension) return m; } +TriMesh single_triangle_with_position(int dimension) +{ + TriMesh m = single_triangle(); + Eigen::MatrixXd V; + V.resize(3, 3); + V.setZero(); + + std::mt19937 generator(123); + std::uniform_int_distribution distribution(1, 100); + + while (!( + triangle_3d_area(V.row(0).transpose(), V.row(1).transpose(), V.row(2).transpose()) > + 0)) { + V.row(0) << distribution(generator), distribution(generator), 0.; + V.row(1) << distribution(generator), distribution(generator), 0.; + V.row(2) << distribution(generator), distribution(generator), 0.; + } + + if (dimension != 2 && dimension != 3) assert(false); + + V.conservativeResize(3, dimension); + mesh_utils::set_matrix_attribute(V, "position", PrimitiveType::Vertex, m); + return m; +} + TriMesh quad() { TriMesh m; diff --git a/tests/tools/TriMesh_examples.hpp b/tests/tools/TriMesh_examples.hpp index 6d6ce0caaf..cc2690f32f 100644 --- a/tests/tools/TriMesh_examples.hpp +++ b/tests/tools/TriMesh_examples.hpp @@ -18,6 +18,9 @@ TriMesh single_triangle(); // (0,0,0), (1,0,0), (0.5, sqrt(3)/2, 0)ß TriMesh single_equilateral_triangle(int dimension = 3); +// a single triangle with position +TriMesh single_triangle_with_position(int dimension = 3); + // 3--1--- 0 // | / \ . // 2 f1 /2 1 From e0288271ed4c89f1173d9b130750cb1b08c97509 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 19 Sep 2023 11:17:13 -0400 Subject: [PATCH 060/134] add triangle inversion invariants --- .../invariants/TriangleInversionInvariant.cpp | 18 +++++++++++------- .../invariants/TriangleInversionInvariant.hpp | 6 ++---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/wmtk/invariants/TriangleInversionInvariant.cpp b/src/wmtk/invariants/TriangleInversionInvariant.cpp index beeb937a60..e3e9181627 100644 --- a/src/wmtk/invariants/TriangleInversionInvariant.cpp +++ b/src/wmtk/invariants/TriangleInversionInvariant.cpp @@ -10,15 +10,19 @@ TriangleInversionInvariant::TriangleInversionInvariant( : MeshInvariant(m) , m_uv_coordinate_handle(uv_coordinate) {} -bool TriangleInversionInvariant::before(const Tuple& t) const +bool TriangleInversionInvariant::after(PrimitiveType type, const std::vector& t) const { + if (type != PrimitiveType::Face) return true; + // assume conterclockwise ConstAccessor accessor = mesh().create_accessor(m_uv_coordinate_handle); + for (auto& tuple : t) { + Eigen::Vector2d p0 = accessor.const_vector_attribute(tuple); + Eigen::Vector2d p1 = accessor.const_vector_attribute(mesh().switch_vertex(tuple)); + Eigen::Vector2d p2 = + accessor.const_vector_attribute(mesh().switch_vertex(mesh().switch_edge(tuple))); - Eigen::Vector2d p0 = accessor.const_vector_attribute(t); - Eigen::Vector2d p1 = accessor.const_vector_attribute(mesh().switch_vertex(t)); - Eigen::Vector2d p2 = - accessor.const_vector_attribute(mesh().switch_vertex(mesh().switch_edge(t))); - - return (triangle_2d_area(p0, p1, p2) < 0); + if (triangle_2d_area(p0, p1, p2) < 0) return false; + } + return true; } } // namespace wmtk diff --git a/src/wmtk/invariants/TriangleInversionInvariant.hpp b/src/wmtk/invariants/TriangleInversionInvariant.hpp index 51287c50d7..e6cd384444 100644 --- a/src/wmtk/invariants/TriangleInversionInvariant.hpp +++ b/src/wmtk/invariants/TriangleInversionInvariant.hpp @@ -8,11 +8,9 @@ class TriangleInversionInvariant : public MeshInvariant { public: // NOTE: this takes in the threshold squared rather than the threshold itself - TriangleInversionInvariant( - const Mesh& m, - const MeshAttributeHandle& uv_coordinate); + TriangleInversionInvariant(const Mesh& m, const MeshAttributeHandle& uv_coordinate); using MeshInvariant::MeshInvariant; - bool before(const Tuple& t) const override; + bool after(PrimitiveType type, const std::vector& t) const override; private: const MeshAttributeHandle m_uv_coordinate_handle; From dd96ea0638ad85b154dad72e47825456263487c7 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 19 Sep 2023 11:17:58 -0400 Subject: [PATCH 061/134] add modified primitives for vertex smooth> only has modified faces for now --- src/wmtk/operations/tri_mesh/VertexSmooth.cpp | 14 ++++++++++++++ src/wmtk/operations/tri_mesh/VertexSmooth.hpp | 1 + 2 files changed, 15 insertions(+) diff --git a/src/wmtk/operations/tri_mesh/VertexSmooth.cpp b/src/wmtk/operations/tri_mesh/VertexSmooth.cpp index 110c78465d..56669451e0 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmooth.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmooth.cpp @@ -73,5 +73,19 @@ bool VertexSmooth::execute() return true; } +std::vector VertexSmooth::modified_primitives(PrimitiveType type) const +{ + if (type == PrimitiveType::Face) { + auto one_ring = SimplicialComplex::vertex_one_ring(mesh(), input_tuple()); + std::vector ret; + for (const auto& s : one_ring) { + ret.emplace_back(s.tuple()); + } + return ret; + } else { + return {}; + } +} + } // namespace wmtk::operations::tri_mesh diff --git a/src/wmtk/operations/tri_mesh/VertexSmooth.hpp b/src/wmtk/operations/tri_mesh/VertexSmooth.hpp index c125b31966..a81c321919 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmooth.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmooth.hpp @@ -29,6 +29,7 @@ class VertexSmooth : public TriMeshOperation, private TupleOperation static PrimitiveType primitive_type() { return PrimitiveType::Vertex; } const Tuple& return_tuple() const; + std::vector modified_primitives(PrimitiveType) const override; protected: bool before() const override; From 5c3fe4d9b61af2a3fadcfef39fd739da0132747a Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 19 Sep 2023 11:19:12 -0400 Subject: [PATCH 062/134] helper function for optimization. probably not useful in the end --- src/wmtk/utils/CMakeLists.txt | 3 ++ src/wmtk/utils/FunctionInterface.hpp | 31 ++++++++++++++ src/wmtk/utils/Optimization.cpp | 60 ++++++++++++++++++++++++++++ src/wmtk/utils/Optimization.hpp | 42 +++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 src/wmtk/utils/FunctionInterface.hpp create mode 100644 src/wmtk/utils/Optimization.cpp create mode 100644 src/wmtk/utils/Optimization.hpp diff --git a/src/wmtk/utils/CMakeLists.txt b/src/wmtk/utils/CMakeLists.txt index 90ecb580ba..59bbc5de44 100644 --- a/src/wmtk/utils/CMakeLists.txt +++ b/src/wmtk/utils/CMakeLists.txt @@ -13,5 +13,8 @@ set(SRC_FILES mesh_utils.cpp triangle_helper_functions.hpp triangle_helper_functions.cpp + FunctionInterface.hpp + Optimization.hpp + Optimization.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/utils/FunctionInterface.hpp b/src/wmtk/utils/FunctionInterface.hpp new file mode 100644 index 0000000000..fe55d5dd31 --- /dev/null +++ b/src/wmtk/utils/FunctionInterface.hpp @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace wmtk { +class FunctionInterface +{ +public: + FunctionInterface( + const Tuple& tuple, + Accessor& accessor, + const function::DifferentiableFunction& function) + : m_tuple(tuple) + , m_accessor(accessor) + , m_function(function) + {} + + const Tuple& m_tuple; + Accessor& m_accessor; + const function::DifferentiableFunction& m_function; + + void store(Eigen::Vector2d& v) { m_accessor.vector_attribute(m_tuple) = v; } + double eval() const { return m_function.get_value(m_tuple); } + Eigen::Vector2d grad() const { return m_function.get_gradient(m_tuple); } + Eigen::Matrix2d hess() const { return m_function.get_hessian(m_tuple); } +}; +} // namespace wmtk diff --git a/src/wmtk/utils/Optimization.cpp b/src/wmtk/utils/Optimization.cpp new file mode 100644 index 0000000000..bd319ebaac --- /dev/null +++ b/src/wmtk/utils/Optimization.cpp @@ -0,0 +1,60 @@ +#include "Optimization.hpp" +#include + +using namespace wmtk; + + +Optimization::Optimization( + FunctionInterface& function_interface, + const TriMesh& m, + const InvariantCollection& invariants, + const bool second_order, + const bool line_search) + : m_function_interface(function_interface) + , m_mesh(m) + , m_invariants(invariants) + , m_second_order(second_order) + , m_line_search(line_search) +{} + +Optimization::Optimization( + const Tuple& tuple, + Accessor& accessor, + const function::DifferentiableFunction& function, + const TriMesh& m, + const InvariantCollection& invariants, + const bool second_order, + const bool line_search) + : m_function_interface(tuple, accessor, function) + , m_mesh(m) + , m_invariants(invariants) + , m_second_order(second_order) + , m_line_search(line_search) +{} + +void Optimization::optimize2d(const Eigen::Vector2d& current_pos) +{ + Eigen::Vector2d new_pos = Eigen::Vector2d::Zero(); + Eigen::Vector2d search_direction = Eigen::Vector2d::Zero(); + if (m_second_order) { + // solve optimization problem + search_direction = -(m_function_interface.hess()).ldlt().solve(m_function_interface.grad()); + } else { + // solve optimization problem + search_direction = -m_function_interface.grad(); + } + new_pos = current_pos + search_direction; + m_function_interface.store(new_pos); + double step_size = 1.0; + // get the modified primitives and convert to tuple manully + auto one_ring = SimplicialComplex::vertex_one_ring(m_mesh, m_function_interface.m_tuple); + std::vector modified_faces; + for (const auto& s : one_ring) { + modified_faces.emplace_back(s.tuple()); + } + while (m_line_search && !m_invariants.after(PrimitiveType::Face, modified_faces)) { + step_size /= 2; + new_pos = current_pos + search_direction * step_size; + m_function_interface.store(new_pos); + } +} \ No newline at end of file diff --git a/src/wmtk/utils/Optimization.hpp b/src/wmtk/utils/Optimization.hpp new file mode 100644 index 0000000000..e356704ecf --- /dev/null +++ b/src/wmtk/utils/Optimization.hpp @@ -0,0 +1,42 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +namespace wmtk { +class Optimization +{ +public: + Optimization() = default; + ~Optimization(); + + Optimization( + FunctionInterface& function_interface, + const TriMesh& m, + const InvariantCollection& invariants, + const bool second_order, + const bool line_search); + + Optimization( + const Tuple& tuple, + Accessor& accessor, + const function::DifferentiableFunction& function, + const TriMesh& m, + const InvariantCollection& invariants, + const bool second_order, + const bool line_search); + + + FunctionInterface m_function_interface; + TriMesh m_mesh; + const InvariantCollection& m_invariants; + const bool m_second_order; + const bool m_line_search; + + + void optimize2d(const Eigen::Vector2d& current_pos); +}; +} // namespace wmtk \ No newline at end of file From 6e5e29ed3b96b2a8acc5e9a02fc9bb49fe99f644 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 19 Sep 2023 11:19:50 -0400 Subject: [PATCH 063/134] vertex smoothing that uses energy optimization --- src/wmtk/operations/tri_mesh/CMakeLists.txt | 2 + .../tri_mesh/VertexSmoothNewtonMethod.cpp | 34 ++++++++++++ .../tri_mesh/VertexSmoothNewtonMethod.hpp | 16 ++++++ ...VertexSmoothNewtonMethodWithLineSearch.cpp | 50 +++++++++++++++++ ...VertexSmoothNewtonMethodWithLineSearch.hpp | 16 ++++++ .../VertexSmoothUsingDifferentiableEnergy.cpp | 53 +++++++------------ .../VertexSmoothUsingDifferentiableEnergy.hpp | 6 ++- 7 files changed, 142 insertions(+), 35 deletions(-) create mode 100644 src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp create mode 100644 src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.hpp create mode 100644 src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp create mode 100644 src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp diff --git a/src/wmtk/operations/tri_mesh/CMakeLists.txt b/src/wmtk/operations/tri_mesh/CMakeLists.txt index 898f4cae07..a8509714b1 100644 --- a/src/wmtk/operations/tri_mesh/CMakeLists.txt +++ b/src/wmtk/operations/tri_mesh/CMakeLists.txt @@ -17,5 +17,7 @@ set(SRC_FILES VertexTangentialSmooth.cpp VertexSmoothUsingDifferentiableEnergy.hpp VertexSmoothUsingDifferentiableEnergy.cpp + VertexSmoothNewtonMethod.hpp + VertexSmoothNewtonMethod.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp new file mode 100644 index 0000000000..8330e73437 --- /dev/null +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp @@ -0,0 +1,34 @@ +#include "VertexSmoothNewtonMethod.hpp" +namespace wmtk::operations::tri_mesh { +VertexSmoothNewtonMethod::VertexSmoothNewtonMethod( + Mesh& m, + const Tuple& t, + const OperationSettings& settings) + : VertexSmoothUsingDifferentiableEnergy(m, t, settings) +{} + +bool VertexSmoothNewtonMethod::execute() +{ + const Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); + OperationSettings op_settings; + tri_mesh::VertexSmooth smooth_op(mesh(), input_tuple(), m_settings.smooth_settings); + if (!smooth_op()) { + return false; + } + + const Tuple tup = smooth_op.return_tuple(); + assert(mesh().is_valid_slow(tup)); + // check if it is a boundary vertex + if (!m_settings.smooth_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { + } + // do curve mesh smoothing + else { + Eigen::Vector2d search_dir = Eigen::Vector2d::Zero(); + search_dir = + -m_settings.energy->get_hessian(tup).ldlt().solve(m_settings.energy->get_gradient(tup)); + Eigen::Vector2d new_pos = p + search_dir; + m_uv_pos_accessor.vector_attribute(tup) = new_pos; + } + return true; +} +} // namespace wmtk::operations::tri_mesh \ No newline at end of file diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.hpp new file mode 100644 index 0000000000..d7b89d9e64 --- /dev/null +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.hpp @@ -0,0 +1,16 @@ +#include "VertexSmoothUsingDifferentiableEnergy.hpp" + +namespace wmtk::operations::tri_mesh { + +class VertexSmoothNewtonMethod : public VertexSmoothUsingDifferentiableEnergy +{ +public: + VertexSmoothNewtonMethod( + Mesh& m, + const Tuple& t, + const OperationSettings& settings); + +protected: + bool execute() override; +}; +} // namespace wmtk::operations::tri_mesh \ No newline at end of file diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp new file mode 100644 index 0000000000..1e4ff3dab2 --- /dev/null +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp @@ -0,0 +1,50 @@ +#include "VertexSmoothNewtonMethodWithLineSearch.hpp" + +namespace wmtk::operations { +namespace tri_mesh { +VertexSmoothNewtonMethodWithLineSearch::VertexSmoothNewtonMethodWithLineSearch( + Mesh& m, + const Tuple& t, + const OperationSettings& settings) + : VertexSmoothNewTonMethod(m, t, settings) +{} + +bool VertexSmoothNewtonMethodWithLineSearch::execute() +{ + const Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); + OperationSettings op_settings; + tri_mesh::VertexSmooth smooth_op(mesh(), input_tuple(), m_settings.smooth_settings); + if (!smooth_op()) { + return false; + } + + const Tuple tup = smooth_op.return_tuple(); + assert(mesh().is_valid_slow(tup)); + // start scope + // auto scope = mesh().create_scope(); + + // fix boundary curve + if (!m_settings.smooth_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { + // do curve mesh smoothing + + } else { + Optimization opt( + input_tuple(), + m_uv_pos_accessor, + *m_settings.energy.get(), + mesh(), + m_settings.second_order, + m_settings.line_search); + opt.step_size = m_settings.step_size; + opt.execute(); + if (!opt.success) { + return false; + } + m_uv_pos_accessor.set_vector_attribute(input_tuple(), opt.x); + } + + return true; +} +} // namespace tri_mesh + +} // namespace wmtk::operations \ No newline at end of file diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp new file mode 100644 index 0000000000..014839ec5f --- /dev/null +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp @@ -0,0 +1,16 @@ +#include "VertexSmoothNewtonMethod.hpp" +namespace wmtk::operations { +namespace tri_mesh { +class VertexSmoothNewtonMethodWithLineSearch : public VertexSmoothNewtonMethod +{ +public: + VertexSmoothNewtonMethodWithLineSearch( + Mesh& m, + const Tuple& t, + const OperationSettings& settings); + +protected: + bool execute() override; +} +} // namespace tri_mesh +} // namespace wmtk::operations \ No newline at end of file diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp index 79f05bec49..089077db8f 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include "VertexSmooth.hpp" @@ -50,45 +51,31 @@ bool VertexSmoothUsingDifferentiableEnergy::execute() } const Tuple tup = smooth_op.return_tuple(); - Eigen::Vector2d new_pos = p; - assert(mesh().is_valid_slow(tup)); // start scope - // auto scope = m_mesh.create_scope(); + // auto scope = mesh().create_scope(); - if (m_settings.smooth_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { + // fix boundary curve + if (!m_settings.smooth_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { // do curve mesh smoothing } else { - // get one ring energy wrt to the vertex - const std::vector one_ring = - SimplicialComplex::vertex_one_ring(mesh(), input_tuple()); - double total_energy = 0; - Eigen::Vector2d gradient = Eigen::Vector2d::Zero(); - Eigen::Matrix2d hessian = Eigen::Matrix2d::Zero(); - for (const Simplex& s : one_ring) { - total_energy += m_settings.energy->get_value(s.tuple()); - gradient += m_settings.energy->get_gradient(s.tuple()); - if (m_settings.second_order) hessian += m_settings.energy->get_hessian(s.tuple()); - } - // get descent direction - Eigen::Vector2d descent_dir = Eigen::Vector2d::Zero(); - if (m_settings.second_order) { - // newton's method - descent_dir = -hessian.ldlt().solve(gradient); - } else { - // gradient descent - descent_dir = -gradient; - } - new_pos = p + descent_dir; - // set new position - m_uv_pos_accessor.vector_attribute(tup) = new_pos; - // check if the new position is valid - // for (const Simplex& s : one_ring) { - // if (triangle_2d_area(s.tuple()) < 0) { - // scope.mark_failed(); - // break; - // } + Optimization opt( + input_tuple(), + m_uv_pos_accessor, + *m_settings.energy.get(), + mesh(), + m_settings.smooth_settings.invariants, + m_settings.second_order, + m_settings.line_search); + opt.optimize2d(p); + // double step_size = m_settings.step_size; + // while (m_settings.line_search && !m_settings.smooth_settings.invariants.after( + // PrimitiveType::Face, + // modified_primitives(PrimitiveType::Face))) { + // step_size /= 2; + // opt.optimize2d(m_uv_pos_accessor.vector_attribute(smooth_op.return_tuple()), + // step_size); // } } diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp index e31cdac63f..1c2105223a 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp @@ -18,11 +18,13 @@ struct OperationSettings OperationSettings smooth_settings; std::unique_ptr energy; bool second_order = true; + bool line_search = false; void initialize_invariants(const TriMesh& m); + double step_size = 1.0; }; namespace tri_mesh { -class VertexSmoothUsingDifferentiableEnergy : public TriMeshOperation, private TupleOperation +class VertexSmoothUsingDifferentiableEnergy : public TriMeshOperation, protected TupleOperation { public: VertexSmoothUsingDifferentiableEnergy( @@ -38,7 +40,7 @@ class VertexSmoothUsingDifferentiableEnergy : public TriMeshOperation, private T bool before() const override; bool execute() override; -private: +protected: Accessor m_uv_pos_accessor; const OperationSettings& m_settings; }; From 329cbe0a90638d92f00692a87c1da032c3183c2f Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 19 Sep 2023 21:28:31 -0400 Subject: [PATCH 064/134] test with trimesh examples --- tests/components/test_smoothing.cpp | 13 ++++++++++++ tests/tools/TriMesh_examples.cpp | 33 +++++++++++++++++++++++++++++ tests/tools/TriMesh_examples.hpp | 2 ++ 3 files changed, 48 insertions(+) create mode 100644 tests/components/test_smoothing.cpp diff --git a/tests/components/test_smoothing.cpp b/tests/components/test_smoothing.cpp new file mode 100644 index 0000000000..46fcca8807 --- /dev/null +++ b/tests/components/test_smoothing.cpp @@ -0,0 +1,13 @@ + +TEST_CASE("smoothing_using_differentiable_energy") +{ + TriMesh mesh = ten_triangles_with_position(); + OperationSettings op_settings; + op_settings.smooth_settings.position = + mesh.get_attribute_handle("position", PrimitiveType::Vertex); + op_settings.smooth_settings.smooth_boundary = false; + + auto all_vertices = mesh.get_all(PrimitiveType::Vertex); + for (const Tuple& v : all_vertices) { + } +} \ No newline at end of file diff --git a/tests/tools/TriMesh_examples.cpp b/tests/tools/TriMesh_examples.cpp index e8a07db4b5..79de8295cf 100644 --- a/tests/tools/TriMesh_examples.cpp +++ b/tests/tools/TriMesh_examples.cpp @@ -340,6 +340,39 @@ TriMesh nine_triangles_with_a_hole() m.initialize(tris); return m; } + +TriMesh ten_triangles_with_position() +{ + TriMesh m; + RowVectors3l tris; + tris.resize(10, 3); + tris.row(0) << 0, 1, 2; + tris.row(1) << 0, 2, 3; + tris.row(2) << 1, 4, 2; + tris.row(3) << 1, 6, 4; + tris.row(4) << 6, 7, 4; + tris.row(5) << 4, 7, 5; + tris.row(6) << 7, 8, 5; + tris.row(7) << 5, 8, 3; + tris.row(8) << 5, 3, 2; + tris.row(9) << 2, 4, 5; + m.initialize(tris); + + Eigen::MatrixXd V; + V.resize(9, 3); + V.row(0) << 0, 1, 0; + V.row(1) << -1, 0, 0; + V.row(2) << 0, 0, 0; + V.row(3) << 1, 0, 0; + V.row(4) << -0.8, -0.3, 0; + V.row(5) << 1, -1, 0; + V.row(6) << -3, -3, 0; + V.row(7) << 0, -3, 0; + V.row(8) << 1.5, -2, 0; + mesh_utils::set_matrix_attribute(V, "position", PrimitiveType::Vertex, m); + return m; +} + TriMesh three_individuals() { TriMesh m; diff --git a/tests/tools/TriMesh_examples.hpp b/tests/tools/TriMesh_examples.hpp index cc2690f32f..d940646f23 100644 --- a/tests/tools/TriMesh_examples.hpp +++ b/tests/tools/TriMesh_examples.hpp @@ -129,6 +129,8 @@ TriMesh three_triangles_with_two_components(); // ⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠙⠿⠋⠛7⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠢⠤⠼⢦⡇⠀8⠀ TriMesh nine_triangles_with_a_hole(); +TriMesh ten_triangles_with_position(); + TriMesh edge_region_with_position(); // 1---2 From 91e83cd224144458ddf025e4c77d4a31d0ff0014 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 19 Sep 2023 21:28:53 -0400 Subject: [PATCH 065/134] structure for line search --- ...VertexSmoothNewtonMethodWithLineSearch.cpp | 39 +++++-------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp index 1e4ff3dab2..20dadbac80 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp @@ -11,36 +11,15 @@ VertexSmoothNewtonMethodWithLineSearch::VertexSmoothNewtonMethodWithLineSearch( bool VertexSmoothNewtonMethodWithLineSearch::execute() { - const Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); - OperationSettings op_settings; - tri_mesh::VertexSmooth smooth_op(mesh(), input_tuple(), m_settings.smooth_settings); - if (!smooth_op()) { - return false; - } - - const Tuple tup = smooth_op.return_tuple(); - assert(mesh().is_valid_slow(tup)); - // start scope - // auto scope = mesh().create_scope(); - - // fix boundary curve - if (!m_settings.smooth_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { - // do curve mesh smoothing - - } else { - Optimization opt( - input_tuple(), - m_uv_pos_accessor, - *m_settings.energy.get(), - mesh(), - m_settings.second_order, - m_settings.line_search); - opt.step_size = m_settings.step_size; - opt.execute(); - if (!opt.success) { - return false; - } - m_uv_pos_accessor.set_vector_attribute(input_tuple(), opt.x); + Tuple smooth_ret; + { + OperationSettings op_settings; + op_settings.initialize_invariants(mesh()); + tri_mesh::VertexSmoothNewtonMethod smooth_op(mesh(), input_tuple(), op_settings); + if (!smooth_op()) { + // line search + while () } + smooth_ret = smooth_op.return_tuple(); } return true; From 80227d4c7e8190e030e3085a6556df050e44f9fb Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 20 Sep 2023 10:00:14 -0400 Subject: [PATCH 066/134] change vertex smooth to be the parent class --- src/wmtk/operations/tri_mesh/VertexSmooth.hpp | 2 +- .../VertexSmoothUsingDifferentiableEnergy.cpp | 47 +++++++++---------- .../VertexSmoothUsingDifferentiableEnergy.hpp | 2 +- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/wmtk/operations/tri_mesh/VertexSmooth.hpp b/src/wmtk/operations/tri_mesh/VertexSmooth.hpp index a81c321919..fff3974aa7 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmooth.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmooth.hpp @@ -19,7 +19,7 @@ struct OperationSettings }; namespace tri_mesh { -class VertexSmooth : public TriMeshOperation, private TupleOperation +class VertexSmooth : public TriMeshOperation, protected TupleOperation { public: VertexSmooth(Mesh& m, const Tuple& t, const OperationSettings& settings); diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp index 089077db8f..2d215407a4 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp @@ -22,8 +22,7 @@ VertexSmoothUsingDifferentiableEnergy::VertexSmoothUsingDifferentiableEnergy( Mesh& m, const Tuple& t, const OperationSettings& settings) - : TriMeshOperation(m) - , TupleOperation(settings.smooth_settings.invariants, t) + : VertexSmooth(m, t, settings.smooth_settings) , m_uv_pos_accessor{m.create_accessor(settings.smooth_settings.position)} , m_settings{settings} {} @@ -55,29 +54,29 @@ bool VertexSmoothUsingDifferentiableEnergy::execute() // start scope // auto scope = mesh().create_scope(); - // fix boundary curve - if (!m_settings.smooth_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { - // do curve mesh smoothing + // // fix boundary curve + // if (!m_settings.smooth_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { + // // do curve mesh smoothing - } else { - Optimization opt( - input_tuple(), - m_uv_pos_accessor, - *m_settings.energy.get(), - mesh(), - m_settings.smooth_settings.invariants, - m_settings.second_order, - m_settings.line_search); - opt.optimize2d(p); - // double step_size = m_settings.step_size; - // while (m_settings.line_search && !m_settings.smooth_settings.invariants.after( - // PrimitiveType::Face, - // modified_primitives(PrimitiveType::Face))) { - // step_size /= 2; - // opt.optimize2d(m_uv_pos_accessor.vector_attribute(smooth_op.return_tuple()), - // step_size); - // } - } + // } else { + // Optimization opt( + // input_tuple(), + // m_uv_pos_accessor, + // *m_settings.energy.get(), + // mesh(), + // m_settings.smooth_settings.invariants, + // m_settings.second_order, + // m_settings.line_search); + // opt.optimize2d(p); + // // double step_size = m_settings.step_size; + // // while (m_settings.line_search && !m_settings.smooth_settings.invariants.after( + // // PrimitiveType::Face, + // // modified_primitives(PrimitiveType::Face))) { + // // step_size /= 2; + // // opt.optimize2d(m_uv_pos_accessor.vector_attribute(smooth_op.return_tuple()), + // // step_size); + // // } + // } return true; } diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp index 1c2105223a..fce93f71d8 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp @@ -24,7 +24,7 @@ struct OperationSettings }; namespace tri_mesh { -class VertexSmoothUsingDifferentiableEnergy : public TriMeshOperation, protected TupleOperation +class VertexSmoothUsingDifferentiableEnergy : public VertexSmooth { public: VertexSmoothUsingDifferentiableEnergy( From 846cd4c12bdb1551a517f9ca0e1c1d2eafe825ef Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 20 Sep 2023 10:02:10 -0400 Subject: [PATCH 067/134] vertex smoothing newton method with line search --- src/wmtk/operations/tri_mesh/CMakeLists.txt | 2 ++ ...VertexSmoothNewtonMethodWithLineSearch.cpp | 29 +++++++++++++++---- ...VertexSmoothNewtonMethodWithLineSearch.hpp | 4 +-- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/wmtk/operations/tri_mesh/CMakeLists.txt b/src/wmtk/operations/tri_mesh/CMakeLists.txt index a8509714b1..714381e8c7 100644 --- a/src/wmtk/operations/tri_mesh/CMakeLists.txt +++ b/src/wmtk/operations/tri_mesh/CMakeLists.txt @@ -19,5 +19,7 @@ set(SRC_FILES VertexSmoothUsingDifferentiableEnergy.cpp VertexSmoothNewtonMethod.hpp VertexSmoothNewtonMethod.cpp + VertexSmoothNewtonMethodWithLineSearch.hpp + VertexSmoothNewtonMethodWithLineSearch.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp index 20dadbac80..948f61b82b 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp @@ -5,21 +5,38 @@ namespace tri_mesh { VertexSmoothNewtonMethodWithLineSearch::VertexSmoothNewtonMethodWithLineSearch( Mesh& m, const Tuple& t, - const OperationSettings& settings) - : VertexSmoothNewTonMethod(m, t, settings) + const OperationSettings& settings) + : VertexSmoothNewtonMethod(m, t, settings) {} bool VertexSmoothNewtonMethodWithLineSearch::execute() { - Tuple smooth_ret; { - OperationSettings op_settings; + OperationSettings op_settings; op_settings.initialize_invariants(mesh()); tri_mesh::VertexSmoothNewtonMethod smooth_op(mesh(), input_tuple(), op_settings); + Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); if (!smooth_op()) { // line search - while () } - smooth_ret = smooth_op.return_tuple(); + Tuple tup = smooth_op.return_tuple(); + double step_size = 1; + double minimum_step_size = 1e-6; + Eigen::Vector2d search_dir = Eigen::Vector2d::Zero(); + search_dir = -op_settings.energy->get_hessian(tup).ldlt().solve( + op_settings.energy->get_gradient(tup)); + Eigen::Vector2d new_pos = p + search_dir; + while (!op_settings.smooth_settings.invariants.after( + PrimitiveType::Face, + smooth_op.modified_primitives(PrimitiveType::Face)) && + (step_size > minimum_step_size)) { + step_size /= 2; + search_dir = -op_settings.energy->get_hessian(tup).ldlt().solve( + op_settings.energy->get_gradient(tup)); + new_pos = p + search_dir; + + m_uv_pos_accessor.vector_attribute(tup) = new_pos; + } + } } return true; diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp index 014839ec5f..6e85deec12 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp @@ -7,10 +7,10 @@ class VertexSmoothNewtonMethodWithLineSearch : public VertexSmoothNewtonMethod VertexSmoothNewtonMethodWithLineSearch( Mesh& m, const Tuple& t, - const OperationSettings& settings); + const OperationSettings& settings); protected: bool execute() override; -} +}; } // namespace tri_mesh } // namespace wmtk::operations \ No newline at end of file From 349840e5357ded1e0d4a0edad2eae3575c946aec Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 20 Sep 2023 10:16:15 -0400 Subject: [PATCH 068/134] bounadry situation not implemented --- ...VertexSmoothNewtonMethodWithLineSearch.cpp | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp index 948f61b82b..f0966874ea 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp @@ -11,16 +11,17 @@ VertexSmoothNewtonMethodWithLineSearch::VertexSmoothNewtonMethodWithLineSearch( bool VertexSmoothNewtonMethodWithLineSearch::execute() { - { - OperationSettings op_settings; - op_settings.initialize_invariants(mesh()); - tri_mesh::VertexSmoothNewtonMethod smooth_op(mesh(), input_tuple(), op_settings); - Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); - if (!smooth_op()) { - // line search - Tuple tup = smooth_op.return_tuple(); - double step_size = 1; - double minimum_step_size = 1e-6; + OperationSettings op_settings; + op_settings.initialize_invariants(mesh()); + tri_mesh::VertexSmoothNewtonMethod smooth_op(mesh(), input_tuple(), op_settings); + Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); + if (!smooth_op()) { + // line search + Tuple tup = smooth_op.return_tuple(); + double step_size = 1; + double minimum_step_size = 1e-6; + if (!m_settings.smooth_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { + } else { Eigen::Vector2d search_dir = Eigen::Vector2d::Zero(); search_dir = -op_settings.energy->get_hessian(tup).ldlt().solve( op_settings.energy->get_gradient(tup)); From b4a29f1b3ec9daa57bc732267491d905e5da6258 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 27 Sep 2023 12:19:22 -0400 Subject: [PATCH 069/134] scheduler with operation factory specific for an operation --- src/wmtk/Scheduler.hpp | 26 +++++++++++--- src/wmtk/operations/CMakeLists.txt | 1 + .../OperationDifferentiableSmoothFactory.hpp | 36 +++++++++++++++++++ src/wmtk/operations/OperationFactory.hpp | 4 +++ 4 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp diff --git a/src/wmtk/Scheduler.hpp b/src/wmtk/Scheduler.hpp index e54db87b0a..87d23bbd49 100644 --- a/src/wmtk/Scheduler.hpp +++ b/src/wmtk/Scheduler.hpp @@ -1,11 +1,12 @@ #pragma once #include #include +#include #include "Mesh.hpp" #include "operations/Operation.hpp" +#include "operations/OperationDifferentiableSmoothFactory.hpp" #include "operations/OperationFactory.hpp" - namespace wmtk { // Scheduler scheduler; @@ -31,20 +32,35 @@ class Scheduler // primitive_type, // std::forward(args)...); //} + template - void add_operation_type(const std::string& name) + void add_operation_type( + const std::string& name, + const operations::OperationSettings& settings) { - m_factories[name] = std::make_unique>(); + m_factories[name] = std::make_unique>(settings); } template void add_operation_type( const std::string& name, - const operations::OperationSettings& settings) + operations::OperationSettings&& settings) { - m_factories[name] = std::make_unique>(settings); + m_factories[name] = + std::make_unique>(std::move(settings)); } + template + void add_operation_type( + const std::string& name, + const operations::OperationSettings< + operations::tri_mesh::VertexSmoothUsingDifferentiableEnergy>& settings) + { + m_factories[name] = + std::make_unique(settings); + } + + void enqueue_operations(std::vector>&& ops); diff --git a/src/wmtk/operations/CMakeLists.txt b/src/wmtk/operations/CMakeLists.txt index d8dfcee1bb..77c66eb723 100644 --- a/src/wmtk/operations/CMakeLists.txt +++ b/src/wmtk/operations/CMakeLists.txt @@ -5,6 +5,7 @@ set(SRC_FILES TupleOperation.cpp OperationFactory.hpp OperationFactory.cpp + OperationDifferentiableSmoothFactory.hpp OperationQueue.hpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp b/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp new file mode 100644 index 0000000000..9a0f470e08 --- /dev/null +++ b/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include +#include +#include +#include "Operation.hpp" +#include "OperationFactory.hpp" + +namespace wmtk { +namespace operations { + +class OperationDifferentiableSmoothFactory : public OperationFactoryBase +{ +public: + OperationDifferentiableSmoothFactory( + const OperationSettings& settings) + : OperationFactoryBase(tri_mesh::VertexSmoothUsingDifferentiableEnergy::primitive_type()) + , m_settings(settings) + {} + + std::unique_ptr create(wmtk::Mesh& m, const Tuple& t) const override + { + if (m_settings.second_order) { + if (m_settings.line_search) { + return std::make_unique(m, t, m_settings); + } + } + return std::make_unique(m, t, m_settings); + } + +protected: + const OperationSettings& m_settings; +}; +} // namespace operations +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/operations/OperationFactory.hpp b/src/wmtk/operations/OperationFactory.hpp index 33fb5a77f3..1608ed286f 100644 --- a/src/wmtk/operations/OperationFactory.hpp +++ b/src/wmtk/operations/OperationFactory.hpp @@ -24,6 +24,10 @@ class OperationFactory : public OperationFactoryBase : OperationFactoryBase(OperationType::primitive_type()) , m_settings(settings) {} + OperationFactory(OperationSettings&& settings) + : OperationFactoryBase(OperationType::primitive_type()) + , m_settings(std::move(settings)) + {} std::unique_ptr create(wmtk::Mesh& m, const Tuple& t) const override { From 3a186f503958f7e4c30121d8832a9b56a42ee27f Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 27 Sep 2023 12:20:10 -0400 Subject: [PATCH 070/134] small changes for operations --- .../tri_mesh/VertexSmoothNewtonMethod.cpp | 7 ++++++- .../tri_mesh/VertexSmoothNewtonMethod.hpp | 8 ++++++-- .../VertexSmoothNewtonMethodWithLineSearch.cpp | 15 +++++++-------- .../VertexSmoothNewtonMethodWithLineSearch.hpp | 1 + .../VertexSmoothUsingDifferentiableEnergy.hpp | 2 +- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp index 8330e73437..ee9d8ece57 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp @@ -1,4 +1,5 @@ #include "VertexSmoothNewtonMethod.hpp" + namespace wmtk::operations::tri_mesh { VertexSmoothNewtonMethod::VertexSmoothNewtonMethod( Mesh& m, @@ -6,11 +7,15 @@ VertexSmoothNewtonMethod::VertexSmoothNewtonMethod( const OperationSettings& settings) : VertexSmoothUsingDifferentiableEnergy(m, t, settings) {} +std::string VertexSmoothNewtonMethod::name() const +{ + return "tri_mesh_vertex_smooth_newton_method"; +} bool VertexSmoothNewtonMethod::execute() { const Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); - OperationSettings op_settings; + tri_mesh::VertexSmooth smooth_op(mesh(), input_tuple(), m_settings.smooth_settings); if (!smooth_op()) { return false; diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.hpp index d7b89d9e64..5ba295a8b2 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.hpp @@ -1,7 +1,9 @@ +#pragma once #include "VertexSmoothUsingDifferentiableEnergy.hpp" -namespace wmtk::operations::tri_mesh { +namespace wmtk::operations { +namespace tri_mesh { class VertexSmoothNewtonMethod : public VertexSmoothUsingDifferentiableEnergy { public: @@ -12,5 +14,7 @@ class VertexSmoothNewtonMethod : public VertexSmoothUsingDifferentiableEnergy protected: bool execute() override; + std::string name() const; }; -} // namespace wmtk::operations::tri_mesh \ No newline at end of file +} // namespace tri_mesh +} // namespace wmtk::operations \ No newline at end of file diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp index f0966874ea..80daf0f085 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp @@ -1,6 +1,7 @@ #include "VertexSmoothNewtonMethodWithLineSearch.hpp" namespace wmtk::operations { + namespace tri_mesh { VertexSmoothNewtonMethodWithLineSearch::VertexSmoothNewtonMethodWithLineSearch( Mesh& m, @@ -11,9 +12,7 @@ VertexSmoothNewtonMethodWithLineSearch::VertexSmoothNewtonMethodWithLineSearch( bool VertexSmoothNewtonMethodWithLineSearch::execute() { - OperationSettings op_settings; - op_settings.initialize_invariants(mesh()); - tri_mesh::VertexSmoothNewtonMethod smooth_op(mesh(), input_tuple(), op_settings); + tri_mesh::VertexSmoothNewtonMethod smooth_op(mesh(), input_tuple(), m_settings); Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); if (!smooth_op()) { // line search @@ -23,16 +22,16 @@ bool VertexSmoothNewtonMethodWithLineSearch::execute() if (!m_settings.smooth_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { } else { Eigen::Vector2d search_dir = Eigen::Vector2d::Zero(); - search_dir = -op_settings.energy->get_hessian(tup).ldlt().solve( - op_settings.energy->get_gradient(tup)); + search_dir = -m_settings.energy->get_hessian(tup).ldlt().solve( + m_settings.energy->get_gradient(tup)); Eigen::Vector2d new_pos = p + search_dir; - while (!op_settings.smooth_settings.invariants.after( + while (!m_settings.smooth_settings.invariants.after( PrimitiveType::Face, smooth_op.modified_primitives(PrimitiveType::Face)) && (step_size > minimum_step_size)) { step_size /= 2; - search_dir = -op_settings.energy->get_hessian(tup).ldlt().solve( - op_settings.energy->get_gradient(tup)); + search_dir = -m_settings.energy->get_hessian(tup).ldlt().solve( + m_settings.energy->get_gradient(tup)); new_pos = p + search_dir; m_uv_pos_accessor.vector_attribute(tup) = new_pos; diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp index 6e85deec12..9714e17a30 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp @@ -1,3 +1,4 @@ +#pragma once #include "VertexSmoothNewtonMethod.hpp" namespace wmtk::operations { namespace tri_mesh { diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp index fce93f71d8..a6bdabbc6c 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp @@ -20,7 +20,7 @@ struct OperationSettings bool second_order = true; bool line_search = false; void initialize_invariants(const TriMesh& m); - double step_size = 1.0; + // double step_size = 1.0; }; namespace tri_mesh { From f9fa357d3afeeb6301d61e948b8264da2ad36482 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 27 Sep 2023 14:57:03 -0400 Subject: [PATCH 071/134] the triangles with positions --- tests/tools/TriMesh_examples.cpp | 7 ++++++- tests/tools/TriMesh_examples.hpp | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/tools/TriMesh_examples.cpp b/tests/tools/TriMesh_examples.cpp index 79de8295cf..5be1b9d3e8 100644 --- a/tests/tools/TriMesh_examples.cpp +++ b/tests/tools/TriMesh_examples.cpp @@ -341,7 +341,7 @@ TriMesh nine_triangles_with_a_hole() return m; } -TriMesh ten_triangles_with_position() +TriMesh ten_triangles_with_position(int dimension) { TriMesh m; RowVectors3l tris; @@ -369,6 +369,11 @@ TriMesh ten_triangles_with_position() V.row(6) << -3, -3, 0; V.row(7) << 0, -3, 0; V.row(8) << 1.5, -2, 0; + + if (dimension != 2 && dimension != 3) assert(false); + + V.conservativeResize(9, dimension); + mesh_utils::set_matrix_attribute(V, "position", PrimitiveType::Vertex, m); return m; } diff --git a/tests/tools/TriMesh_examples.hpp b/tests/tools/TriMesh_examples.hpp index d940646f23..558f283f9f 100644 --- a/tests/tools/TriMesh_examples.hpp +++ b/tests/tools/TriMesh_examples.hpp @@ -129,7 +129,7 @@ TriMesh three_triangles_with_two_components(); // ⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠙⠿⠋⠛7⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠢⠤⠼⢦⡇⠀8⠀ TriMesh nine_triangles_with_a_hole(); -TriMesh ten_triangles_with_position(); +TriMesh ten_triangles_with_position(int dimension); TriMesh edge_region_with_position(); From a8ff1717442081379bd9d3dea0bf54b8d86e6ce2 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 27 Sep 2023 14:57:32 -0400 Subject: [PATCH 072/134] smoothing test --- tests/components/CMakeLists.txt | 1 + tests/components/test_smoothing.cpp | 28 ++++++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/tests/components/CMakeLists.txt b/tests/components/CMakeLists.txt index 49a4f4cdb0..7bef5bf0d9 100644 --- a/tests/components/CMakeLists.txt +++ b/tests/components/CMakeLists.txt @@ -4,6 +4,7 @@ set(TEST_SOURCES test_component_mesh_info.cpp test_component_output.cpp test_component_isotropic_remeshing.cpp + test_smoothing.cpp ) target_sources(wmtk_tests PRIVATE ${TEST_SOURCES}) diff --git a/tests/components/test_smoothing.cpp b/tests/components/test_smoothing.cpp index 46fcca8807..e06ac511c4 100644 --- a/tests/components/test_smoothing.cpp +++ b/tests/components/test_smoothing.cpp @@ -1,13 +1,29 @@ +#pragma once +#include +#include +#include +#include +#include +#include "../tools/TriMesh_examples.hpp" +using namespace wmtk; +using namespace wmtk::tests; +using namespace wmtk::operations; TEST_CASE("smoothing_using_differentiable_energy") { - TriMesh mesh = ten_triangles_with_position(); - OperationSettings op_settings; + TriMesh mesh = ten_triangles_with_position(2); + OperationSettings op_settings; op_settings.smooth_settings.position = mesh.get_attribute_handle("position", PrimitiveType::Vertex); op_settings.smooth_settings.smooth_boundary = false; - - auto all_vertices = mesh.get_all(PrimitiveType::Vertex); - for (const Tuple& v : all_vertices) { - } + op_settings.second_order = true; + op_settings.line_search = false; + op_settings.energy = std::make_unique( + mesh, + mesh.get_attribute_handle("position", PrimitiveType::Vertex)); + Scheduler scheduler(mesh); + scheduler.add_operation_type( + "tri_mesh_smooth_vertex_newton_method", + op_settings); + scheduler.run_operation_on_all(PrimitiveType::Vertex, "tri_mesh_smooth_vertex_newton_method"); } \ No newline at end of file From 6220d3238c2c76acffcde37673b68f656cade88d Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 28 Sep 2023 17:42:51 -0400 Subject: [PATCH 073/134] modified primitives in VertexAttributesUpdateBase --- .../tri_mesh/VertexAttributesUpdateBase.cpp | 16 ++++++++++++++++ .../tri_mesh/VertexAttributesUpdateBase.hpp | 2 ++ 2 files changed, 18 insertions(+) diff --git a/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.cpp b/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.cpp index 93fd19a5af..829ed4eaa2 100644 --- a/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.cpp +++ b/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.cpp @@ -31,6 +31,22 @@ const Tuple& VertexAttributesUpdateBase::return_tuple() const return m_output_tuple; } +std::vector VertexAttributesUpdateBase::modified_primitives(PrimitiveType type) const +{ + if (type == PrimitiveType::Face) { + Simplex v(PrimitiveType::Vertex, m_output_tuple); + auto sc = SimplicialComplex::open_star(mesh(), v); + auto faces = sc.get_simplices(PrimitiveType::Face); + std::vector ret; + for (const auto& face : faces) { + ret.emplace_back(face.tuple()); + } + return ret; + } else { + return {}; + } +} + bool VertexAttributesUpdateBase::execute() { diff --git a/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.hpp b/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.hpp index 41f2e22c1a..6ceae3f550 100644 --- a/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.hpp +++ b/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.hpp @@ -2,6 +2,7 @@ #include #include #include + #include "TriMeshOperation.hpp" namespace wmtk::operations { @@ -30,6 +31,7 @@ class VertexAttributesUpdateBase : public TriMeshOperation, protected TupleOpera static PrimitiveType primitive_type() { return PrimitiveType::Vertex; } const Tuple& return_tuple() const; + std::vector modified_primitives(PrimitiveType) const override; protected: bool execute() override; From 6c531061033bfd496afdc4c385adccab5478b489 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 28 Sep 2023 17:45:45 -0400 Subject: [PATCH 074/134] initialize invariants for LaplacianSmooth --- .../tri_mesh/VertexLaplacianSmooth.cpp | 29 +++++++++---------- .../tri_mesh/VertexLaplacianSmooth.hpp | 5 +--- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/wmtk/operations/tri_mesh/VertexLaplacianSmooth.cpp b/src/wmtk/operations/tri_mesh/VertexLaplacianSmooth.cpp index de6fc742bd..f2ca5f61ab 100644 --- a/src/wmtk/operations/tri_mesh/VertexLaplacianSmooth.cpp +++ b/src/wmtk/operations/tri_mesh/VertexLaplacianSmooth.cpp @@ -1,9 +1,19 @@ #include "VertexLaplacianSmooth.hpp" - #include #include +#include + +namespace wmtk::operations { -namespace wmtk::operations::tri_mesh { +void OperationSettings::initialize_invariants(const TriMesh& m) +{ + base_settings.initialize_invariants(m); + if (!smooth_boundary) { + base_settings.invariants.add(std::make_unique(m)); + } +} // namespace wmtk::operations + +namespace tri_mesh { VertexLaplacianSmooth::VertexLaplacianSmooth( Mesh& m, const Tuple& t, @@ -19,17 +29,6 @@ std::string VertexLaplacianSmooth::name() const } -bool VertexLaplacianSmooth::before() const -{ - if (!mesh().is_valid_slow(input_tuple())) { - return false; - } - if (!m_settings.smooth_boundary && mesh().is_boundary_vertex(input_tuple())) { - return false; - } - return true; -} - bool VertexLaplacianSmooth::execute() { if (!tri_mesh::VertexAttributesUpdateBase::execute()) { @@ -47,5 +46,5 @@ bool VertexLaplacianSmooth::execute() return true; } - -} // namespace wmtk::operations::tri_mesh +} // namespace tri_mesh +} // namespace wmtk::operations diff --git a/src/wmtk/operations/tri_mesh/VertexLaplacianSmooth.hpp b/src/wmtk/operations/tri_mesh/VertexLaplacianSmooth.hpp index 1c5e680f73..9aea049103 100644 --- a/src/wmtk/operations/tri_mesh/VertexLaplacianSmooth.hpp +++ b/src/wmtk/operations/tri_mesh/VertexLaplacianSmooth.hpp @@ -16,6 +16,7 @@ struct OperationSettings OperationSettings base_settings; MeshAttributeHandle position; bool smooth_boundary = false; + void initialize_invariants(const TriMesh& m); }; namespace tri_mesh { @@ -31,11 +32,7 @@ class VertexLaplacianSmooth : public VertexAttributesUpdateBase static PrimitiveType primitive_type() { return PrimitiveType::Vertex; } - const Tuple& return_tuple() const; - std::vector modified_primitives(PrimitiveType) const override; - protected: - bool before() const override; bool execute() override; protected: From edb9085c4d2ff94c2f3d1e55bb3397273b8f6708 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 28 Sep 2023 17:46:55 -0400 Subject: [PATCH 075/134] compilation fix for differentiable smooth and its child class --- .../OperationDifferentiableSmoothFactory.hpp | 2 +- .../tri_mesh/VertexSmoothNewtonMethod.cpp | 7 +++--- ...VertexSmoothNewtonMethodWithLineSearch.cpp | 4 +-- .../VertexSmoothUsingDifferentiableEnergy.cpp | 25 +++++-------------- .../VertexSmoothUsingDifferentiableEnergy.hpp | 11 +++++--- .../VertexTangentialLaplacianSmooth.cpp | 11 -------- .../VertexTangentialLaplacianSmooth.hpp | 1 - 7 files changed, 19 insertions(+), 42 deletions(-) diff --git a/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp b/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp index 9a0f470e08..46e42a019f 100644 --- a/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp +++ b/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include +#include #include #include #include diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp index ee9d8ece57..7ce6688c62 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp @@ -16,15 +16,14 @@ bool VertexSmoothNewtonMethod::execute() { const Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); - tri_mesh::VertexSmooth smooth_op(mesh(), input_tuple(), m_settings.smooth_settings); - if (!smooth_op()) { + if (!tri_mesh::VertexSmoothUsingDifferentiableEnergy::execute()) { return false; } - const Tuple tup = smooth_op.return_tuple(); + const Tuple tup = tri_mesh::VertexAttributesUpdateBase::return_tuple(); assert(mesh().is_valid_slow(tup)); // check if it is a boundary vertex - if (!m_settings.smooth_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { + if (!m_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { } // do curve mesh smoothing else { diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp index 80daf0f085..770fa46798 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp @@ -19,13 +19,13 @@ bool VertexSmoothNewtonMethodWithLineSearch::execute() Tuple tup = smooth_op.return_tuple(); double step_size = 1; double minimum_step_size = 1e-6; - if (!m_settings.smooth_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { + if (!m_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { } else { Eigen::Vector2d search_dir = Eigen::Vector2d::Zero(); search_dir = -m_settings.energy->get_hessian(tup).ldlt().solve( m_settings.energy->get_gradient(tup)); Eigen::Vector2d new_pos = p + search_dir; - while (!m_settings.smooth_settings.invariants.after( + while (!m_settings.base_settings.invariants.after( PrimitiveType::Face, smooth_op.modified_primitives(PrimitiveType::Face)) && (step_size > minimum_step_size)) { diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp index 2d215407a4..ccd67aa39b 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp @@ -5,15 +5,13 @@ #include #include #include -#include "VertexSmooth.hpp" namespace wmtk::operations { void OperationSettings::initialize_invariants( const TriMesh& m) { - smooth_settings.initialize_invariants(m); - smooth_settings.invariants.add( - std::make_shared(m, smooth_settings.position)); + base_settings.initialize_invariants(m); + base_settings.invariants.add(std::make_shared(m, uv_position)); } } // namespace wmtk::operations @@ -22,8 +20,8 @@ VertexSmoothUsingDifferentiableEnergy::VertexSmoothUsingDifferentiableEnergy( Mesh& m, const Tuple& t, const OperationSettings& settings) - : VertexSmooth(m, t, settings.smooth_settings) - , m_uv_pos_accessor{m.create_accessor(settings.smooth_settings.position)} + : VertexAttributesUpdateBase(m, t, settings.base_settings) + , m_uv_pos_accessor{m.create_accessor(settings.uv_position)} , m_settings{settings} {} @@ -32,24 +30,13 @@ std::string VertexSmoothUsingDifferentiableEnergy::name() const return "tri_mesh_vertex_smooth_using_differentiable_energy"; } -bool VertexSmoothUsingDifferentiableEnergy::before() const -{ - if (!mesh().is_valid_slow(input_tuple())) { - return false; - } - return true; -} - bool VertexSmoothUsingDifferentiableEnergy::execute() { - const Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); - OperationSettings op_settings; - tri_mesh::VertexSmooth smooth_op(mesh(), input_tuple(), m_settings.smooth_settings); - if (!smooth_op()) { + if (!tri_mesh::VertexAttributesUpdateBase::execute()) { return false; } - const Tuple tup = smooth_op.return_tuple(); + const Tuple tup = tri_mesh::VertexAttributesUpdateBase::return_tuple(); assert(mesh().is_valid_slow(tup)); // start scope // auto scope = mesh().create_scope(); diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp index a6bdabbc6c..4264f4f4b2 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp @@ -5,7 +5,7 @@ #include #include #include "TriMeshOperation.hpp" -#include "VertexSmooth.hpp" +#include "VertexAttributesUpdateBase.hpp" namespace wmtk::operations { namespace tri_mesh { @@ -15,8 +15,12 @@ class VertexSmoothUsingDifferentiableEnergy; template <> struct OperationSettings { - OperationSettings smooth_settings; + OperationSettings base_settings; std::unique_ptr energy; + // uv_positioin accesor + MeshAttributeHandle uv_position; + bool smooth_boundary = false; + bool second_order = true; bool line_search = false; void initialize_invariants(const TriMesh& m); @@ -24,7 +28,7 @@ struct OperationSettings }; namespace tri_mesh { -class VertexSmoothUsingDifferentiableEnergy : public VertexSmooth +class VertexSmoothUsingDifferentiableEnergy : public VertexAttributesUpdateBase { public: VertexSmoothUsingDifferentiableEnergy( @@ -37,7 +41,6 @@ class VertexSmoothUsingDifferentiableEnergy : public VertexSmooth static PrimitiveType primitive_type() { return PrimitiveType::Vertex; } protected: - bool before() const override; bool execute() override; protected: diff --git a/src/wmtk/operations/tri_mesh/VertexTangentialLaplacianSmooth.cpp b/src/wmtk/operations/tri_mesh/VertexTangentialLaplacianSmooth.cpp index 09b9b946d8..a317e2da5c 100644 --- a/src/wmtk/operations/tri_mesh/VertexTangentialLaplacianSmooth.cpp +++ b/src/wmtk/operations/tri_mesh/VertexTangentialLaplacianSmooth.cpp @@ -19,21 +19,10 @@ std::string VertexTangentialLaplacianSmooth::name() const return "tri_mesh_vertex_tangential_smooth"; } -bool VertexTangentialLaplacianSmooth::before() const -{ - if (!mesh().is_valid_slow(input_tuple())) { - return false; - } - return true; -} - bool VertexTangentialLaplacianSmooth::execute() { const Eigen::Vector3d p = m_pos_accessor.vector_attribute(input_tuple()); - if (!tri_mesh::VertexLaplacianSmooth::before()) { - return false; - } if (!tri_mesh::VertexLaplacianSmooth::execute()) { return false; } diff --git a/src/wmtk/operations/tri_mesh/VertexTangentialLaplacianSmooth.hpp b/src/wmtk/operations/tri_mesh/VertexTangentialLaplacianSmooth.hpp index 356108defe..20061dfb1a 100644 --- a/src/wmtk/operations/tri_mesh/VertexTangentialLaplacianSmooth.hpp +++ b/src/wmtk/operations/tri_mesh/VertexTangentialLaplacianSmooth.hpp @@ -33,7 +33,6 @@ class VertexTangentialLaplacianSmooth : public VertexLaplacianSmooth static PrimitiveType primitive_type() { return PrimitiveType::Vertex; } protected: - bool before() const override; bool execute() override; private: From d350ef510c4a7baf32ded93a47f150a20d3980bd Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 28 Sep 2023 17:47:13 -0400 Subject: [PATCH 076/134] compilation fix for test smoothing --- tests/components/test_smoothing.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/components/test_smoothing.cpp b/tests/components/test_smoothing.cpp index e06ac511c4..6c0bea0639 100644 --- a/tests/components/test_smoothing.cpp +++ b/tests/components/test_smoothing.cpp @@ -13,9 +13,8 @@ TEST_CASE("smoothing_using_differentiable_energy") { TriMesh mesh = ten_triangles_with_position(2); OperationSettings op_settings; - op_settings.smooth_settings.position = - mesh.get_attribute_handle("position", PrimitiveType::Vertex); - op_settings.smooth_settings.smooth_boundary = false; + op_settings.uv_position = mesh.get_attribute_handle("position", PrimitiveType::Vertex); + op_settings.smooth_boundary = false; op_settings.second_order = true; op_settings.line_search = false; op_settings.energy = std::make_unique( From abb43f695459d155f55fec88eb50d8daf8de7262 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Tue, 3 Oct 2023 16:27:18 -0400 Subject: [PATCH 077/134] adding a add_operation_factory to allow for direct passing of factories in scheduler --- src/wmtk/Scheduler.hpp | 20 ++++++++------------ tests/components/test_smoothing.cpp | 6 +++--- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/wmtk/Scheduler.hpp b/src/wmtk/Scheduler.hpp index 87d23bbd49..19a5a2fbf7 100644 --- a/src/wmtk/Scheduler.hpp +++ b/src/wmtk/Scheduler.hpp @@ -33,31 +33,27 @@ class Scheduler // std::forward(args)...); //} - template - void add_operation_type( + void add_operation_factory( const std::string& name, - const operations::OperationSettings& settings) + std::unique_ptr&& ptr) { - m_factories[name] = std::make_unique>(settings); + m_factories[name] = std::move(ptr); } - template void add_operation_type( const std::string& name, - operations::OperationSettings&& settings) + const operations::OperationSettings& settings) { - m_factories[name] = - std::make_unique>(std::move(settings)); + add_operation_factory( name, std::make_unique>(settings)); } template void add_operation_type( const std::string& name, - const operations::OperationSettings< - operations::tri_mesh::VertexSmoothUsingDifferentiableEnergy>& settings) + operations::OperationSettings&& settings) { - m_factories[name] = - std::make_unique(settings); + add_operation_factory( name, + std::make_unique>(std::move(settings))); } diff --git a/tests/components/test_smoothing.cpp b/tests/components/test_smoothing.cpp index 6c0bea0639..f776852bc2 100644 --- a/tests/components/test_smoothing.cpp +++ b/tests/components/test_smoothing.cpp @@ -21,8 +21,8 @@ TEST_CASE("smoothing_using_differentiable_energy") mesh, mesh.get_attribute_handle("position", PrimitiveType::Vertex)); Scheduler scheduler(mesh); - scheduler.add_operation_type( + scheduler.add_operation_factory( "tri_mesh_smooth_vertex_newton_method", - op_settings); + std::make_unique>(op_settings)); scheduler.run_operation_on_all(PrimitiveType::Vertex, "tri_mesh_smooth_vertex_newton_method"); -} \ No newline at end of file +} From b358ada2cd5c6308f376c0b5b6c6b30ec1385031 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 3 Oct 2023 17:20:49 -0400 Subject: [PATCH 078/134] compiled new operation creation --- src/wmtk/Scheduler.hpp | 7 +++++-- tests/components/test_smoothing.cpp | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/wmtk/Scheduler.hpp b/src/wmtk/Scheduler.hpp index 19a5a2fbf7..e953a77791 100644 --- a/src/wmtk/Scheduler.hpp +++ b/src/wmtk/Scheduler.hpp @@ -44,7 +44,9 @@ class Scheduler const std::string& name, const operations::OperationSettings& settings) { - add_operation_factory( name, std::make_unique>(settings)); + add_operation_factory( + name, + std::make_unique>(settings)); } template @@ -52,7 +54,8 @@ class Scheduler const std::string& name, operations::OperationSettings&& settings) { - add_operation_factory( name, + add_operation_factory( + name, std::make_unique>(std::move(settings))); } diff --git a/tests/components/test_smoothing.cpp b/tests/components/test_smoothing.cpp index f776852bc2..7a74391ff7 100644 --- a/tests/components/test_smoothing.cpp +++ b/tests/components/test_smoothing.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include "../tools/TriMesh_examples.hpp" @@ -23,6 +24,6 @@ TEST_CASE("smoothing_using_differentiable_energy") Scheduler scheduler(mesh); scheduler.add_operation_factory( "tri_mesh_smooth_vertex_newton_method", - std::make_unique>(op_settings)); + std::make_unique(op_settings)); scheduler.run_operation_on_all(PrimitiveType::Vertex, "tri_mesh_smooth_vertex_newton_method"); } From 523529e89c6a72cb2c690dc60484b2dbfdb2dd31 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 3 Oct 2023 17:45:58 -0400 Subject: [PATCH 079/134] It's doing something but newton method is still not totally correct --- .../OperationDifferentiableSmoothFactory.hpp | 8 +++++++- .../tri_mesh/VertexAttributesUpdateBase.cpp | 5 ++++- .../tri_mesh/VertexSmoothNewtonMethod.cpp | 7 +++++-- .../VertexSmoothUsingDifferentiableEnergy.cpp | 1 + src/wmtk/simplex/closed_star.cpp | 2 +- src/wmtk/simplex/top_level_cofaces.cpp | 2 ++ tests/components/test_smoothing.cpp | 13 ++++++++++++- 7 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp b/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp index 46e42a019f..c86431aa2f 100644 --- a/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp +++ b/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp @@ -22,9 +22,15 @@ class OperationDifferentiableSmoothFactory : public OperationFactoryBase std::unique_ptr create(wmtk::Mesh& m, const Tuple& t) const override { if (m_settings.second_order) { - if (m_settings.line_search) { + if (!m_settings.line_search) { return std::make_unique(m, t, m_settings); } + if (m_settings.line_search) { + return std::make_unique( + m, + t, + m_settings); + } } return std::make_unique(m, t, m_settings); } diff --git a/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.cpp b/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.cpp index 829ed4eaa2..437cd96d40 100644 --- a/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.cpp +++ b/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.cpp @@ -19,7 +19,10 @@ VertexAttributesUpdateBase::VertexAttributesUpdateBase( : TriMeshOperation(m) , TupleOperation(settings.invariants, t) , m_settings{settings} -{} +{ + assert(m.is_valid_slow(t)); + assert(m.is_valid_slow(input_tuple())); +} std::string VertexAttributesUpdateBase::name() const { diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp index 7ce6688c62..f9076df5bc 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp @@ -1,5 +1,5 @@ #include "VertexSmoothNewtonMethod.hpp" - +#include namespace wmtk::operations::tri_mesh { VertexSmoothNewtonMethod::VertexSmoothNewtonMethod( Mesh& m, @@ -15,8 +15,9 @@ std::string VertexSmoothNewtonMethod::name() const bool VertexSmoothNewtonMethod::execute() { const Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); - + wmtk::logger().info("p {}", p.transpose()); if (!tri_mesh::VertexSmoothUsingDifferentiableEnergy::execute()) { + wmtk::logger().info("execute failed"); return false; } @@ -30,6 +31,8 @@ bool VertexSmoothNewtonMethod::execute() Eigen::Vector2d search_dir = Eigen::Vector2d::Zero(); search_dir = -m_settings.energy->get_hessian(tup).ldlt().solve(m_settings.energy->get_gradient(tup)); + wmtk::logger().info("gradient {}", m_settings.energy->get_gradient(tup).transpose()); + wmtk::logger().info("search_dir {}", search_dir.transpose()); Eigen::Vector2d new_pos = p + search_dir; m_uv_pos_accessor.vector_attribute(tup) = new_pos; } diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp index ccd67aa39b..1400e562ec 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp @@ -32,6 +32,7 @@ std::string VertexSmoothUsingDifferentiableEnergy::name() const bool VertexSmoothUsingDifferentiableEnergy::execute() { + assert(mesh().is_valid_slow(input_tuple())); if (!tri_mesh::VertexAttributesUpdateBase::execute()) { return false; } diff --git a/src/wmtk/simplex/closed_star.cpp b/src/wmtk/simplex/closed_star.cpp index 9f7927d780..dc265558c3 100644 --- a/src/wmtk/simplex/closed_star.cpp +++ b/src/wmtk/simplex/closed_star.cpp @@ -13,7 +13,7 @@ SimplexCollection closed_star(const Mesh& mesh, const Simplex& simplex, const bo SimplexCollection collection(mesh); collection.add(simplex); - + assert(mesh.is_valid_slow(simplex.tuple())); const SimplexCollection top_level_cofaces_collection = mesh.top_simplex_type() == PrimitiveType::Face ? top_level_cofaces(static_cast(mesh), simplex, false) diff --git a/src/wmtk/simplex/top_level_cofaces.cpp b/src/wmtk/simplex/top_level_cofaces.cpp index 72d0cde670..87c4b59459 100644 --- a/src/wmtk/simplex/top_level_cofaces.cpp +++ b/src/wmtk/simplex/top_level_cofaces.cpp @@ -28,6 +28,8 @@ std::vector top_level_cofaces_tuples_vertex(const TriMesh& mesh, const Tu { std::vector collection; + assert(mesh.is_valid_slow(t)); + std::set touched_cells; std::queue q; q.push(t); diff --git a/tests/components/test_smoothing.cpp b/tests/components/test_smoothing.cpp index 7a74391ff7..bfc85682a8 100644 --- a/tests/components/test_smoothing.cpp +++ b/tests/components/test_smoothing.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include "../tools/DEBUG_TriMesh.hpp" #include "../tools/TriMesh_examples.hpp" using namespace wmtk; using namespace wmtk::tests; @@ -12,7 +14,7 @@ using namespace wmtk::operations; TEST_CASE("smoothing_using_differentiable_energy") { - TriMesh mesh = ten_triangles_with_position(2); + DEBUG_TriMesh mesh = ten_triangles_with_position(2); OperationSettings op_settings; op_settings.uv_position = mesh.get_attribute_handle("position", PrimitiveType::Vertex); op_settings.smooth_boundary = false; @@ -21,9 +23,18 @@ TEST_CASE("smoothing_using_differentiable_energy") op_settings.energy = std::make_unique( mesh, mesh.get_attribute_handle("position", PrimitiveType::Vertex)); + op_settings.initialize_invariants(mesh); Scheduler scheduler(mesh); scheduler.add_operation_factory( "tri_mesh_smooth_vertex_newton_method", std::make_unique(op_settings)); scheduler.run_operation_on_all(PrimitiveType::Vertex, "tri_mesh_smooth_vertex_newton_method"); + ConstAccessor pos = mesh.create_const_accessor(op_settings.uv_position); + Tuple tuple = mesh.face_tuple_from_vids(2, 4, 5); + Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); + Eigen::Vector2d uv1 = pos.const_vector_attribute(mesh.switch_vertex(tuple)); + Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh.switch_vertex(mesh.switch_edge(tuple))); + wmtk::logger().info("uv0 {}", uv0.transpose()); + wmtk::logger().info("uv1 {}", uv1.transpose()); + wmtk::logger().info("uv2 {}", uv2.transpose()); } From 0953dd2df65498f8b2d2dd17c6fa3a3dcedbdde8 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 4 Oct 2023 16:02:53 -0400 Subject: [PATCH 080/134] paased test for Newton Method smooth --- tests/components/test_smoothing.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/tests/components/test_smoothing.cpp b/tests/components/test_smoothing.cpp index bfc85682a8..2de451504a 100644 --- a/tests/components/test_smoothing.cpp +++ b/tests/components/test_smoothing.cpp @@ -25,16 +25,21 @@ TEST_CASE("smoothing_using_differentiable_energy") mesh.get_attribute_handle("position", PrimitiveType::Vertex)); op_settings.initialize_invariants(mesh); Scheduler scheduler(mesh); - scheduler.add_operation_factory( - "tri_mesh_smooth_vertex_newton_method", - std::make_unique(op_settings)); - scheduler.run_operation_on_all(PrimitiveType::Vertex, "tri_mesh_smooth_vertex_newton_method"); - ConstAccessor pos = mesh.create_const_accessor(op_settings.uv_position); Tuple tuple = mesh.face_tuple_from_vids(2, 4, 5); + while (op_settings.energy->get_gradient(tuple).norm() > 1e-6) { + scheduler.add_operation_factory( + "tri_mesh_smooth_vertex_newton_method", + std::make_unique(op_settings)); + scheduler.run_operation_on_all( + PrimitiveType::Vertex, + "tri_mesh_smooth_vertex_newton_method"); + tuple = mesh.face_tuple_from_vids(2, 4, 5); + } + ConstAccessor pos = mesh.create_const_accessor(op_settings.uv_position); + Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); Eigen::Vector2d uv1 = pos.const_vector_attribute(mesh.switch_vertex(tuple)); Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh.switch_vertex(mesh.switch_edge(tuple))); - wmtk::logger().info("uv0 {}", uv0.transpose()); - wmtk::logger().info("uv1 {}", uv1.transpose()); - wmtk::logger().info("uv2 {}", uv2.transpose()); + + REQUIRE((uv0 - uv1).norm() - (uv1 - uv2).norm() < 1e-6); } From c4813b1951ac0197aa23dbacbea3887b8a3c374f Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 5 Oct 2023 16:20:00 -0400 Subject: [PATCH 081/134] m_output needs to be resurrected --- src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp | 5 ++--- src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.cpp | 1 + src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.hpp | 2 +- src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp | 1 + .../tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp | 4 ++-- src/wmtk/simplex/top_level_cofaces.cpp | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp b/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp index c86431aa2f..95e441e55c 100644 --- a/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp +++ b/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp @@ -22,14 +22,13 @@ class OperationDifferentiableSmoothFactory : public OperationFactoryBase std::unique_ptr create(wmtk::Mesh& m, const Tuple& t) const override { if (m_settings.second_order) { - if (!m_settings.line_search) { - return std::make_unique(m, t, m_settings); - } if (m_settings.line_search) { return std::make_unique( m, t, m_settings); + } else { + return std::make_unique(m, t, m_settings); } } return std::make_unique(m, t, m_settings); diff --git a/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.cpp b/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.cpp index 437cd96d40..5252835deb 100644 --- a/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.cpp +++ b/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.cpp @@ -37,6 +37,7 @@ const Tuple& VertexAttributesUpdateBase::return_tuple() const std::vector VertexAttributesUpdateBase::modified_primitives(PrimitiveType type) const { if (type == PrimitiveType::Face) { + assert(mesh().is_valid_slow(m_output_tuple)); Simplex v(PrimitiveType::Vertex, m_output_tuple); auto sc = SimplicialComplex::open_star(mesh(), v); auto faces = sc.get_simplices(PrimitiveType::Face); diff --git a/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.hpp b/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.hpp index 6ceae3f550..c0e0b30ff6 100644 --- a/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.hpp +++ b/src/wmtk/operations/tri_mesh/VertexAttributesUpdateBase.hpp @@ -36,7 +36,7 @@ class VertexAttributesUpdateBase : public TriMeshOperation, protected TupleOpera protected: bool execute() override; -private: +protected: Tuple m_output_tuple; const OperationSettings& m_settings; }; diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp index f9076df5bc..297148b177 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp @@ -36,6 +36,7 @@ bool VertexSmoothNewtonMethod::execute() Eigen::Vector2d new_pos = p + search_dir; m_uv_pos_accessor.vector_attribute(tup) = new_pos; } + m_output_tuple = resurrect_tuple(input_tuple()); return true; } } // namespace wmtk::operations::tri_mesh \ No newline at end of file diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp index 770fa46798..50b37c7f33 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp @@ -32,13 +32,13 @@ bool VertexSmoothNewtonMethodWithLineSearch::execute() step_size /= 2; search_dir = -m_settings.energy->get_hessian(tup).ldlt().solve( m_settings.energy->get_gradient(tup)); - new_pos = p + search_dir; + new_pos = p + search_dir * step_size; m_uv_pos_accessor.vector_attribute(tup) = new_pos; } } } - + m_output_tuple = resurrect_tuple(input_tuple()); return true; } } // namespace tri_mesh diff --git a/src/wmtk/simplex/top_level_cofaces.cpp b/src/wmtk/simplex/top_level_cofaces.cpp index 87c4b59459..0d2e216163 100644 --- a/src/wmtk/simplex/top_level_cofaces.cpp +++ b/src/wmtk/simplex/top_level_cofaces.cpp @@ -28,7 +28,7 @@ std::vector top_level_cofaces_tuples_vertex(const TriMesh& mesh, const Tu { std::vector collection; - assert(mesh.is_valid_slow(t)); + assert(mesh.is_valid_slow(t)); std::set touched_cells; std::queue q; From 3b1f81ebcfc669f928c043bea081d45df49f45ba Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 5 Oct 2023 16:21:31 -0400 Subject: [PATCH 082/134] add newton with line search --- tests/components/test_smoothing.cpp | 34 ++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/components/test_smoothing.cpp b/tests/components/test_smoothing.cpp index 2de451504a..049aac4cc2 100644 --- a/tests/components/test_smoothing.cpp +++ b/tests/components/test_smoothing.cpp @@ -12,7 +12,7 @@ using namespace wmtk; using namespace wmtk::tests; using namespace wmtk::operations; -TEST_CASE("smoothing_using_differentiable_energy") +TEST_CASE("smoothing_Newton_Method") { DEBUG_TriMesh mesh = ten_triangles_with_position(2); OperationSettings op_settings; @@ -43,3 +43,35 @@ TEST_CASE("smoothing_using_differentiable_energy") REQUIRE((uv0 - uv1).norm() - (uv1 - uv2).norm() < 1e-6); } + +TEST_CASE("smoothing_Newton_Method_line_search") +{ + DEBUG_TriMesh mesh = ten_triangles_with_position(2); + OperationSettings op_settings; + op_settings.uv_position = mesh.get_attribute_handle("position", PrimitiveType::Vertex); + op_settings.smooth_boundary = false; + op_settings.second_order = true; + op_settings.line_search = true; + op_settings.energy = std::make_unique( + mesh, + mesh.get_attribute_handle("position", PrimitiveType::Vertex)); + op_settings.initialize_invariants(mesh); + Scheduler scheduler(mesh); + Tuple tuple = mesh.face_tuple_from_vids(2, 4, 5); + while (op_settings.energy->get_gradient(tuple).norm() > 1e-6) { + scheduler.add_operation_factory( + "tri_mesh_smooth_vertex_newton_method", + std::make_unique(op_settings)); + scheduler.run_operation_on_all( + PrimitiveType::Vertex, + "tri_mesh_smooth_vertex_newton_method"); + tuple = mesh.face_tuple_from_vids(2, 4, 5); + } + ConstAccessor pos = mesh.create_const_accessor(op_settings.uv_position); + + Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); + Eigen::Vector2d uv1 = pos.const_vector_attribute(mesh.switch_vertex(tuple)); + Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh.switch_vertex(mesh.switch_edge(tuple))); + + REQUIRE((uv0 - uv1).norm() - (uv1 - uv2).norm() < 1e-6); +} From d5f31b46e8c0b01dbacbfee3b246788c4b69a39e Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 5 Oct 2023 16:28:59 -0400 Subject: [PATCH 083/134] cleaning print messages, add more tests --- src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp | 5 +---- tests/components/test_smoothing.cpp | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp index 297148b177..8873e12b46 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp @@ -15,9 +15,8 @@ std::string VertexSmoothNewtonMethod::name() const bool VertexSmoothNewtonMethod::execute() { const Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); - wmtk::logger().info("p {}", p.transpose()); if (!tri_mesh::VertexSmoothUsingDifferentiableEnergy::execute()) { - wmtk::logger().info("execute failed"); + wmtk::logger().debug("execute failed"); return false; } @@ -31,8 +30,6 @@ bool VertexSmoothNewtonMethod::execute() Eigen::Vector2d search_dir = Eigen::Vector2d::Zero(); search_dir = -m_settings.energy->get_hessian(tup).ldlt().solve(m_settings.energy->get_gradient(tup)); - wmtk::logger().info("gradient {}", m_settings.energy->get_gradient(tup).transpose()); - wmtk::logger().info("search_dir {}", search_dir.transpose()); Eigen::Vector2d new_pos = p + search_dir; m_uv_pos_accessor.vector_attribute(tup) = new_pos; } diff --git a/tests/components/test_smoothing.cpp b/tests/components/test_smoothing.cpp index 049aac4cc2..15b2b5debd 100644 --- a/tests/components/test_smoothing.cpp +++ b/tests/components/test_smoothing.cpp @@ -42,6 +42,8 @@ TEST_CASE("smoothing_Newton_Method") Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh.switch_vertex(mesh.switch_edge(tuple))); REQUIRE((uv0 - uv1).norm() - (uv1 - uv2).norm() < 1e-6); + REQUIRE((uv0 - uv1).norm() - (uv0 - uv2).norm() < 1e-6); + REQUIRE((uv1 - uv2).norm() - (uv0 - uv2).norm() < 1e-6); } TEST_CASE("smoothing_Newton_Method_line_search") @@ -74,4 +76,6 @@ TEST_CASE("smoothing_Newton_Method_line_search") Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh.switch_vertex(mesh.switch_edge(tuple))); REQUIRE((uv0 - uv1).norm() - (uv1 - uv2).norm() < 1e-6); + REQUIRE((uv0 - uv1).norm() - (uv0 - uv2).norm() < 1e-6); + REQUIRE((uv1 - uv2).norm() - (uv0 - uv2).norm() < 1e-6); } From e97fc1a8cca8af7cef8713964a8fa75a368bed25 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 10 Oct 2023 14:20:15 -0400 Subject: [PATCH 084/134] change AMIPS code style --- src/wmtk/function/AMIPS.cpp | 50 +++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/src/wmtk/function/AMIPS.cpp b/src/wmtk/function/AMIPS.cpp index ced2efd57e..b863b9afe7 100644 --- a/src/wmtk/function/AMIPS.cpp +++ b/src/wmtk/function/AMIPS.cpp @@ -1,6 +1,6 @@ -#pragma once #include "AMIPS.hpp" #include +#include using namespace wmtk; using namespace wmtk::function; @@ -66,7 +66,6 @@ auto AMIPS_2D::function_eval( { // (x0 - x1, y0 - y1, x0 - x2, y0 - y2).transpose - Eigen::Matrix Dm; Dm.row(0) = uv1.template cast() - uv0; Dm.row(1) = uv2.template cast() - uv0; @@ -90,18 +89,18 @@ auto AMIPS_2D::function_eval( Dsinv = Ds.inverse(); // define of transform matrix F = Dm@Ds.inv - Eigen::Matrix F; - F = Dm.transpose() * Dsinv.transpose().template cast(); - // F << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), + Eigen::Matrix J; + J = Dm * Dsinv.template cast(); + // J << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), // (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), // (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), // (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); - auto Fdet = F.determinant(); - if (abs(Fdet) < std::numeric_limits::denorm_min()) { + auto Jdet = J.determinant(); + if (abs(Jdet) < std::numeric_limits::denorm_min()) { return static_cast(std::numeric_limits::infinity()); } - return (F.transpose() * F).trace() / Fdet; + return (J.transpose() * J).trace() / Jdet; } DScalar AMIPS_3DEmbedded::get_value_autodiff(const Tuple& tuple) const @@ -111,7 +110,6 @@ DScalar AMIPS_3DEmbedded::get_value_autodiff(const Tuple& tuple) const // TODO curve mesh uv -> t conversion happens here Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); - int size = 2; Eigen::Matrix dofT = get_T_vector>(uv0, size); @@ -134,12 +132,12 @@ auto AMIPS_3DEmbedded::function_eval( Eigen::Matrix pos1 = m_dofs_to_pos.dof_to_pos(uv1); Eigen::Matrix pos2 = m_dofs_to_pos.dof_to_pos(uv2); Eigen::Matrix V2_V1; - V2_V1 << pos1(0) - pos0(0), pos1(1) - pos0(1), pos1(2) - pos0(2); + V2_V1 = pos1.template cast() - pos0; Eigen::Matrix V3_V1; - V3_V1 << pos2(0) - pos0(0), pos2(1) - pos0(1), pos2(2) - pos0(2); + V3_V1 = pos2.template cast() - pos0; // tangent bases - // e1 = (V2 - V1).normalize() + // e1 = (V2 - V1).normalize() (this is buggy due to autodiff normalization implementation) // e1 = V2_V1.stableNormalized(); assert(V2_V1.norm() > 0); // check norm is not 0 Eigen::Matrix e1 = V2_V1 / V2_V1.norm(); @@ -169,27 +167,28 @@ auto AMIPS_3DEmbedded::function_eval( // project V1, V2, V3 to tangent plane to VT1, VT2, VT3 - Eigen::Matrix VT1, VT2, VT3; - VT1 << static_cast(0.), static_cast(0.); // the origin - VT2 << V2_V1.dot(e1), V2_V1.dot(e2); - VT3 << V3_V1.dot(e1), V3_V1.dot(e2); + Eigen::Matrix VT0, VT1, VT2; + VT0 << static_cast(0.), static_cast(0.); // the origin + VT1 << V2_V1.dot(e1), V2_V1.dot(e2); + VT2 << V3_V1.dot(e1), V3_V1.dot(e2); // now construct Dm as before in tangent plane // (x2 - x1, y2 - y1, x3 - x1, y2 - y1).transpose Eigen::Matrix Dm; - Dm << VT2.x() - VT1.x(), VT3.x() - VT1.x(), VT2.y() - VT1.y(), VT3.y() - VT1.y(); + Dm.row(0) = VT1 - VT0; + Dm.row(1) = VT2 - VT0; T Dmdet = Dm.determinant(); assert(wmtk::function::get_value(Dmdet) > 0); Eigen::Matrix2d Ds, Dsinv; Eigen::Matrix target_triangle = get_target_triangle(1.0); - Eigen::Vector2d target_A, target_B, target_C; - target_A = target_triangle.row(0); - target_B = target_triangle.row(1); - target_C = target_triangle.row(2); - Ds << target_B.x() - target_A.x(), target_C.x() - target_A.x(), target_B.y() - target_A.y(), - target_C.y() - target_A.y(); + Eigen::Vector2d target0, target1, target2; + target0 = target_triangle.row(0); + target1 = target_triangle.row(1); + target2 = target_triangle.row(2); + Ds.row(0) = target1 - target0; + Ds.row(1) = target2 - target0; auto Dsdet = Ds.determinant(); if (abs(Dsdet) < std::numeric_limits::denorm_min()) { @@ -199,10 +198,7 @@ auto AMIPS_3DEmbedded::function_eval( // define of transform matrix F = Dm@Ds.inv Eigen::Matrix F; - F << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), - (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), - (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), - (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); + F = Dm * Dsinv.template cast(); auto Fdet = F.determinant(); if (abs(Fdet) < std::numeric_limits::denorm_min()) { From cc67e1e6b1e93cb4fb586c3b0f258e5ddbbf7a29 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 10 Oct 2023 14:21:24 -0400 Subject: [PATCH 085/134] small changes to format --- src/wmtk/function/utils/DofsToPosition.hpp | 4 ++-- src/wmtk/image/Sampling.hpp | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wmtk/function/utils/DofsToPosition.hpp b/src/wmtk/function/utils/DofsToPosition.hpp index 3208cd4d22..510284aaa1 100644 --- a/src/wmtk/function/utils/DofsToPosition.hpp +++ b/src/wmtk/function/utils/DofsToPosition.hpp @@ -49,7 +49,7 @@ class DofsToPosition int size = dofT.rows(); if (size == 2) { - // TODO retrive position using displacement map + // TODO retrive position using displacement map for the frist two coordinates too // for now just return itself pos << dofT(0), dofT(1), m_sampling->sample(dofT(0), dofT(1)); @@ -65,5 +65,5 @@ class DofsToPosition } }; -} // namespace energy +} // namespace function } // namespace wmtk diff --git a/src/wmtk/image/Sampling.hpp b/src/wmtk/image/Sampling.hpp index 0dbc9f9dff..65b7871a1b 100644 --- a/src/wmtk/image/Sampling.hpp +++ b/src/wmtk/image/Sampling.hpp @@ -30,16 +30,16 @@ class SamplingAnalyticFunction : public Sampling double C = 0.0; template - auto _evaluate(const S& u, const S& v) const + auto evaluate(const S& u, const S& v) const { if (m_type == Linear) { - return _evaluate_linear(u, v); + return evaluate_linear(u, v); } else return static_cast(0.0); } template - auto _evaluate_linear(const S& u, const S& v) const + auto evaluate_linear(const S& u, const S& v) const { return A * u + B * v + C; } @@ -64,10 +64,10 @@ class SamplingAnalyticFunction : public Sampling B = b; C = c; } - double sample(const double u, const double v) const override { return _evaluate(u, v); } + double sample(const double u, const double v) const override { return evaluate(u, v); } DScalar sample(const DScalar& u, const DScalar& v) const override { - return _evaluate(u, v); + return evaluate(u, v); } }; From 10481dd214d7b4d11e6cc3eb99d854937993d369 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 10 Oct 2023 14:23:05 -0400 Subject: [PATCH 086/134] single 2d tri with pos example. And change 2d energy test for amips unit test to use correctly oriented tri --- tests/function/test_2d_energy.cpp | 13 ++++++++----- tests/tools/TriMesh_examples.cpp | 23 +++++++++++++++++++++++ tests/tools/TriMesh_examples.hpp | 2 +- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/tests/function/test_2d_energy.cpp b/tests/function/test_2d_energy.cpp index 868b53b42d..60a560f86a 100644 --- a/tests/function/test_2d_energy.cpp +++ b/tests/function/test_2d_energy.cpp @@ -52,8 +52,8 @@ TEST_CASE("amips2d") } SECTION("random_triangle") { - for (int i = 0; i < 50; i++) { - const DEBUG_TriMesh example_mesh = single_triangle_with_position(2); + for (int i = 0; i < 1; i++) { + const DEBUG_TriMesh example_mesh = single_2d_triangle_with_position(); auto uv_handle = example_mesh.get_attribute_handle("position", PrimitiveType::Vertex); @@ -61,8 +61,10 @@ TEST_CASE("amips2d") const TriMesh tri_mesh = static_cast(example_mesh); AMIPS_2D amips2d(tri_mesh, uv_handle); - - REQUIRE((amips2d.get_value(e1) > 2. || amips2d.get_value(e1) == 2.)); + if (amips2d.get_value(e1) < 2.) { + wmtk::logger().critical("wrong value"); + REQUIRE((amips2d.get_value(e1) > 2. || amips2d.get_value(e1) == 2.)); + } } } } @@ -71,7 +73,8 @@ TEST_CASE("amips3d") { SECTION("equilateral_triangle") { - const DEBUG_TriMesh example_mesh = single_equilateral_triangle(); + const DEBUG_TriMesh example_mesh = single_equilateral_triangle(2); + auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); const TriMesh tri_mesh = static_cast(example_mesh); auto uv_handle = diff --git a/tests/tools/TriMesh_examples.cpp b/tests/tools/TriMesh_examples.cpp index 5be1b9d3e8..1f27dd0ee4 100644 --- a/tests/tools/TriMesh_examples.cpp +++ b/tests/tools/TriMesh_examples.cpp @@ -1,5 +1,6 @@ #include "TriMesh_examples.hpp" #include +#include #include #include @@ -57,6 +58,28 @@ TriMesh single_triangle_with_position(int dimension) return m; } +TriMesh single_2d_triangle_with_position() +{ + TriMesh m = single_triangle(); + Eigen::MatrixXd V; + V.resize(3, 2); + V.setZero(); + + std::mt19937 generator(123); + std::uniform_int_distribution distribution(1, 100); + + while (!triangle_2d_orientation( + V.row(0).transpose(), + V.row(1).transpose(), + V.row(2).transpose())) { + V.row(0) << distribution(generator), distribution(generator); + V.row(1) << distribution(generator), distribution(generator); + V.row(2) << distribution(generator), distribution(generator); + } + mesh_utils::set_matrix_attribute(V, "position", PrimitiveType::Vertex, m); + return m; +} + TriMesh quad() { TriMesh m; diff --git a/tests/tools/TriMesh_examples.hpp b/tests/tools/TriMesh_examples.hpp index 558f283f9f..843af9e41c 100644 --- a/tests/tools/TriMesh_examples.hpp +++ b/tests/tools/TriMesh_examples.hpp @@ -20,7 +20,7 @@ TriMesh single_equilateral_triangle(int dimension = 3); // a single triangle with position TriMesh single_triangle_with_position(int dimension = 3); - +TriMesh single_2d_triangle_with_position(); // 3--1--- 0 // | / \ . // 2 f1 /2 1 From 67835c658cc78b17176054b47664253dcc42f850 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 10 Oct 2023 14:23:38 -0400 Subject: [PATCH 087/134] add helper function for signed 2d tri --- src/wmtk/utils/triangle_helper_functions.hpp | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/wmtk/utils/triangle_helper_functions.hpp b/src/wmtk/utils/triangle_helper_functions.hpp index 673abe5089..2320fd015a 100644 --- a/src/wmtk/utils/triangle_helper_functions.hpp +++ b/src/wmtk/utils/triangle_helper_functions.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -35,5 +36,30 @@ T triangle_2d_area( return area; } +// template get 3d tri area +template +T triangle_signed_2d_area( + const Eigen::Matrix& A, + const Eigen::Matrix& B, + const Eigen::Matrix& C) +{ + auto B_A = B - A; + auto C_A = C - A; + T area = static_cast(0.5) * (B_A.x() * C_A.y() - B_A.y() * C_A.x()); + return area; +} + +template +bool triangle_2d_orientation( + const Eigen::Matrix& A, + const Eigen::Matrix& B, + const Eigen::Matrix& C) +{ + auto res = igl::predicates::orient2d(A, B, C); + if (res == igl::predicates::Orientation::POSITIVE) + return true; + else + return false; +} } // namespace wmtk \ No newline at end of file From bba7525f1be554a8c34ac13c03302cf13870b837 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 11 Oct 2023 18:50:29 -0400 Subject: [PATCH 088/134] transposing matrices per row major or col major --- src/wmtk/function/AMIPS.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/wmtk/function/AMIPS.cpp b/src/wmtk/function/AMIPS.cpp index b863b9afe7..dfe2a69eb9 100644 --- a/src/wmtk/function/AMIPS.cpp +++ b/src/wmtk/function/AMIPS.cpp @@ -65,7 +65,6 @@ auto AMIPS_2D::function_eval( const Eigen::Vector2d& uv2) const -> T { // (x0 - x1, y0 - y1, x0 - x2, y0 - y2).transpose - Eigen::Matrix Dm; Dm.row(0) = uv1.template cast() - uv0; Dm.row(1) = uv2.template cast() - uv0; @@ -90,7 +89,7 @@ auto AMIPS_2D::function_eval( // define of transform matrix F = Dm@Ds.inv Eigen::Matrix J; - J = Dm * Dsinv.template cast(); + J = Dm.transpose() * Dsinv.template cast().transpose(); // J << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), // (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), // (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), @@ -100,7 +99,7 @@ auto AMIPS_2D::function_eval( if (abs(Jdet) < std::numeric_limits::denorm_min()) { return static_cast(std::numeric_limits::infinity()); } - return (J.transpose() * J).trace() / Jdet; + return (J * J.transpose()).trace() / Jdet; } DScalar AMIPS_3DEmbedded::get_value_autodiff(const Tuple& tuple) const @@ -198,11 +197,11 @@ auto AMIPS_3DEmbedded::function_eval( // define of transform matrix F = Dm@Ds.inv Eigen::Matrix F; - F = Dm * Dsinv.template cast(); + F = Dm.transpose() * Dsinv.template cast().transpose(); auto Fdet = F.determinant(); if (abs(Fdet) < std::numeric_limits::denorm_min()) { return static_cast(std::numeric_limits::infinity()); } - return (F.transpose() * F).trace() / Fdet; + return (F * F.transpose()).trace() / Fdet; } From 35dee5e67a0fb4c903a52d8d6e3c55d3a35e5fc8 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 11 Oct 2023 18:50:41 -0400 Subject: [PATCH 089/134] small changes to test --- tests/components/test_smoothing.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/components/test_smoothing.cpp b/tests/components/test_smoothing.cpp index 15b2b5debd..dc1193c64a 100644 --- a/tests/components/test_smoothing.cpp +++ b/tests/components/test_smoothing.cpp @@ -26,7 +26,7 @@ TEST_CASE("smoothing_Newton_Method") op_settings.initialize_invariants(mesh); Scheduler scheduler(mesh); Tuple tuple = mesh.face_tuple_from_vids(2, 4, 5); - while (op_settings.energy->get_gradient(tuple).norm() > 1e-6) { + while (op_settings.energy->get_gradient(tuple).norm() > 1e-10) { scheduler.add_operation_factory( "tri_mesh_smooth_vertex_newton_method", std::make_unique(op_settings)); @@ -60,7 +60,7 @@ TEST_CASE("smoothing_Newton_Method_line_search") op_settings.initialize_invariants(mesh); Scheduler scheduler(mesh); Tuple tuple = mesh.face_tuple_from_vids(2, 4, 5); - while (op_settings.energy->get_gradient(tuple).norm() > 1e-6) { + while (op_settings.energy->get_gradient(tuple).norm() > 1e-10) { scheduler.add_operation_factory( "tri_mesh_smooth_vertex_newton_method", std::make_unique(op_settings)); @@ -74,7 +74,6 @@ TEST_CASE("smoothing_Newton_Method_line_search") Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); Eigen::Vector2d uv1 = pos.const_vector_attribute(mesh.switch_vertex(tuple)); Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh.switch_vertex(mesh.switch_edge(tuple))); - REQUIRE((uv0 - uv1).norm() - (uv1 - uv2).norm() < 1e-6); REQUIRE((uv0 - uv1).norm() - (uv0 - uv2).norm() < 1e-6); REQUIRE((uv1 - uv2).norm() - (uv0 - uv2).norm() < 1e-6); From 5fd038c225acbe839392417572d5fd83e177578a Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Fri, 13 Oct 2023 19:51:11 -0400 Subject: [PATCH 090/134] middle of a bunch of refactoring --- src/wmtk/Scheduler.hpp | 2 - src/wmtk/function/AMIPS.cpp | 203 +------------------ src/wmtk/function/AMIPS.hpp | 62 +----- src/wmtk/function/AMIPS2D.cpp | 81 ++++++++ src/wmtk/function/AMIPS2D.hpp | 30 +++ src/wmtk/function/AreaAccuracy.cpp | 13 -- src/wmtk/function/AreaAccuracy.hpp | 33 --- src/wmtk/function/AutodiffFunction.cpp | 14 +- src/wmtk/function/AutodiffFunction.hpp | 14 +- src/wmtk/function/CMakeLists.txt | 6 +- src/wmtk/function/DifferentiableFunction.cpp | 16 +- src/wmtk/function/DifferentiableFunction.hpp | 21 +- src/wmtk/function/Function.cpp | 10 +- src/wmtk/function/Function.hpp | 6 +- src/wmtk/function/PositionMapAMIPS3D.cpp | 129 ++++++++++++ src/wmtk/function/PositionMapAMIPS3D.hpp | 35 ++++ src/wmtk/function/TriMeshValenceFunction.cpp | 36 ++-- src/wmtk/function/TriMeshValenceFunction.hpp | 6 +- src/wmtk/function/utils/AutoDiffRAII.cpp | 14 ++ src/wmtk/function/utils/AutoDiffRAII.hpp | 17 ++ src/wmtk/function/utils/AutoDiffUtils.hpp | 108 +++++----- src/wmtk/function/utils/CMakeLists.txt | 2 + src/wmtk/operations/tri_mesh/CMakeLists.txt | 12 +- tests/components/CMakeLists.txt | 4 +- tests/function/CMakeLists.txt | 6 +- 25 files changed, 455 insertions(+), 425 deletions(-) create mode 100644 src/wmtk/function/AMIPS2D.cpp create mode 100644 src/wmtk/function/AMIPS2D.hpp delete mode 100644 src/wmtk/function/AreaAccuracy.cpp delete mode 100644 src/wmtk/function/AreaAccuracy.hpp create mode 100644 src/wmtk/function/PositionMapAMIPS3D.cpp create mode 100644 src/wmtk/function/PositionMapAMIPS3D.hpp create mode 100644 src/wmtk/function/utils/AutoDiffRAII.cpp create mode 100644 src/wmtk/function/utils/AutoDiffRAII.hpp diff --git a/src/wmtk/Scheduler.hpp b/src/wmtk/Scheduler.hpp index e953a77791..ab41a94530 100644 --- a/src/wmtk/Scheduler.hpp +++ b/src/wmtk/Scheduler.hpp @@ -1,10 +1,8 @@ #pragma once #include #include -#include #include "Mesh.hpp" #include "operations/Operation.hpp" -#include "operations/OperationDifferentiableSmoothFactory.hpp" #include "operations/OperationFactory.hpp" namespace wmtk { diff --git a/src/wmtk/function/AMIPS.cpp b/src/wmtk/function/AMIPS.cpp index dfe2a69eb9..5eb8161f56 100644 --- a/src/wmtk/function/AMIPS.cpp +++ b/src/wmtk/function/AMIPS.cpp @@ -1,207 +1,8 @@ #include "AMIPS.hpp" -#include -#include -using namespace wmtk; -using namespace wmtk::function; +namespace wmtk::function { AMIPS::AMIPS(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) : AutodiffFunction(mesh, vertex_attribute_handle) {} -AMIPS_2D::AMIPS_2D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) - : AMIPS(mesh, vertex_attribute_handle) -{ - // assert(m_vertex_attribute_handle); - // check the dimension of the position - // assert(m_mesh.get_attribute_size(m_vertex_attribute_handle) == 2); -} -AMIPS_3DEmbedded::AMIPS_3DEmbedded( - const TriMesh& mesh, - const MeshAttributeHandle& vertex_uv_handle, - const image::Image& image) - : AMIPS(mesh, vertex_uv_handle) - , m_dofs_to_pos(image) -{} - -AMIPS_3DEmbedded::AMIPS_3DEmbedded( - const TriMesh& mesh, - const MeshAttributeHandle& vertex_uv_handle, - const wmtk::image::SamplingAnalyticFunction::FunctionType type, - const double a, - const double b, - const double c) - : AMIPS(mesh, vertex_uv_handle) - , m_dofs_to_pos(type, a, b, c) -{} - -Eigen::Matrix AMIPS::get_target_triangle(double scaling) -{ - const static std::array m_target_triangle = {0., 1., 1. / 2., 0., 0., sqrt(3) / 2.}; - return scaling * Eigen::Matrix::ConstMapType(m_target_triangle.data()); -} - -DScalar AMIPS_2D::get_value_autodiff(const Tuple& tuple) const -{ - // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_const_accessor(m_vertex_attribute_handle); - - Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); - int size = 2; - Eigen::Matrix dofT = get_T_vector>(uv0, size); - - Eigen::Vector2d uv1 = - pos.const_vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); - Eigen::Vector2d uv2 = - pos.const_vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); - - // return the energy - return function_eval(dofT, uv1, uv2); -} - -template -auto AMIPS_2D::function_eval( - const Eigen::Matrix& uv0, - const Eigen::Vector2d& uv1, - const Eigen::Vector2d& uv2) const -> T -{ - // (x0 - x1, y0 - y1, x0 - x2, y0 - y2).transpose - Eigen::Matrix Dm; - Dm.row(0) = uv1.template cast() - uv0; - Dm.row(1) = uv2.template cast() - uv0; - // Dm << uv1(0) - uv0(0), uv2(0) - uv0(0), uv1(1) - uv0(1), uv2(1) - uv0(1); - - Eigen::Matrix2d Ds, Dsinv; - Eigen::Matrix target_triangle = get_target_triangle(1.0); - Eigen::Vector2d target0, target1, target2; - target0 = target_triangle.row(0); - target1 = target_triangle.row(1); - target2 = target_triangle.row(2); - Ds.row(0) = target1 - target0; - Ds.row(1) = target2 - target0; - // Ds << target1.x() - target0.x(), target2.x() - target0.x(), target1.y() - target0.y(), - // target2.y() - target0.y(); - - auto Dsdet = Ds.determinant(); - if (abs(Dsdet) < std::numeric_limits::denorm_min()) { - return static_cast(std::numeric_limits::infinity()); - } - Dsinv = Ds.inverse(); - - // define of transform matrix F = Dm@Ds.inv - Eigen::Matrix J; - J = Dm.transpose() * Dsinv.template cast().transpose(); - // J << (Dm(0, 0) * Dsinv(0, 0) + Dm(0, 1) * Dsinv(1, 0)), - // (Dm(0, 0) * Dsinv(0, 1) + Dm(0, 1) * Dsinv(1, 1)), - // (Dm(1, 0) * Dsinv(0, 0) + Dm(1, 1) * Dsinv(1, 0)), - // (Dm(1, 0) * Dsinv(0, 1) + Dm(1, 1) * Dsinv(1, 1)); - - auto Jdet = J.determinant(); - if (abs(Jdet) < std::numeric_limits::denorm_min()) { - return static_cast(std::numeric_limits::infinity()); - } - return (J * J.transpose()).trace() / Jdet; -} - -DScalar AMIPS_3DEmbedded::get_value_autodiff(const Tuple& tuple) const -{ - // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_const_accessor(m_vertex_attribute_handle); - - // TODO curve mesh uv -> t conversion happens here - Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); - int size = 2; - Eigen::Matrix dofT = get_T_vector>(uv0, size); - - Eigen::Vector2d uv1 = - pos.const_vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); - Eigen::Vector2d uv2 = - pos.const_vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); - - // return the energy - return function_eval(dofT, uv1, uv2); -} - -template -auto AMIPS_3DEmbedded::function_eval( - const Eigen::Matrix& uv0, - const Eigen::Vector2d& uv1, - const Eigen::Vector2d& uv2) const -> T -{ - Eigen::Matrix pos0 = m_dofs_to_pos.dof_to_pos(uv0); - Eigen::Matrix pos1 = m_dofs_to_pos.dof_to_pos(uv1); - Eigen::Matrix pos2 = m_dofs_to_pos.dof_to_pos(uv2); - Eigen::Matrix V2_V1; - V2_V1 = pos1.template cast() - pos0; - Eigen::Matrix V3_V1; - V3_V1 = pos2.template cast() - pos0; - - // tangent bases - // e1 = (V2 - V1).normalize() (this is buggy due to autodiff normalization implementation) - // e1 = V2_V1.stableNormalized(); - assert(V2_V1.norm() > 0); // check norm is not 0 - Eigen::Matrix e1 = V2_V1 / V2_V1.norm(); - Eigen::Matrix n = V2_V1.cross(V3_V1); - - // #ifdef DEBUG - // Eigen::MatrixXd double_n; - // get_double_vecto(n, 3, double_n); - // if (double_n.lpNorm() < std::numeric_limits::denorm_min()) { - // wmtk::logger().critical("n.lpNorm {}", double_n.lpNorm()); - // std::cout << "V1 " << std::endl; - // std::cout << std::hexfloat << get_value(pos0(0))<< " " << y1 << v1(2) << std::endl; - // std::cout << "V2 " << std::endl; - // std::cout << std::hexfloat << v2(0) << " " << v2(1) << v2(2) << std::endl; - // std::cout << "V3 " << std::endl; - // std::cout << std::hexfloat << v3(0) << " " << v3(1) << v3(2) << std::endl; - // assert(false); - // } - // #endif - // n = n.stableNormalized(); - assert(n.norm() > 0); // check norm is not 0 - n = n / n.norm(); - Eigen::Matrix e2 = n.cross(e1); - // Eigen::Matrix e2_stableNormalized = e2.stableNormalized(); - assert(e2.norm() > 0); // check norm is not 0 - e2 = e2 / e2.norm(); - - // project V1, V2, V3 to tangent plane to VT1, VT2, VT3 - - Eigen::Matrix VT0, VT1, VT2; - VT0 << static_cast(0.), static_cast(0.); // the origin - VT1 << V2_V1.dot(e1), V2_V1.dot(e2); - VT2 << V3_V1.dot(e1), V3_V1.dot(e2); - - // now construct Dm as before in tangent plane - // (x2 - x1, y2 - y1, x3 - x1, y2 - y1).transpose - Eigen::Matrix Dm; - Dm.row(0) = VT1 - VT0; - Dm.row(1) = VT2 - VT0; - - T Dmdet = Dm.determinant(); - assert(wmtk::function::get_value(Dmdet) > 0); - - Eigen::Matrix2d Ds, Dsinv; - Eigen::Matrix target_triangle = get_target_triangle(1.0); - Eigen::Vector2d target0, target1, target2; - target0 = target_triangle.row(0); - target1 = target_triangle.row(1); - target2 = target_triangle.row(2); - Ds.row(0) = target1 - target0; - Ds.row(1) = target2 - target0; - - auto Dsdet = Ds.determinant(); - if (abs(Dsdet) < std::numeric_limits::denorm_min()) { - return static_cast(std::numeric_limits::infinity()); - } - Dsinv = Ds.inverse(); - - // define of transform matrix F = Dm@Ds.inv - Eigen::Matrix F; - F = Dm.transpose() * Dsinv.template cast().transpose(); - - auto Fdet = F.determinant(); - if (abs(Fdet) < std::numeric_limits::denorm_min()) { - return static_cast(std::numeric_limits::infinity()); - } - return (F * F.transpose()).trace() / Fdet; -} +} // namespace wmtk::function diff --git a/src/wmtk/function/AMIPS.hpp b/src/wmtk/function/AMIPS.hpp index d62f6a09bd..398ca1e1d7 100644 --- a/src/wmtk/function/AMIPS.hpp +++ b/src/wmtk/function/AMIPS.hpp @@ -3,69 +3,11 @@ #include "AutodiffFunction.hpp" #include "utils/AutoDiffUtils.hpp" #include "utils/DofsToPosition.hpp" -namespace wmtk { -namespace function { +namespace wmtk::function { class AMIPS : public AutodiffFunction { public: AMIPS(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); - static Eigen::Matrix get_target_triangle(double scaling); }; -class AMIPS_2D : public AMIPS -{ -public: - AMIPS_2D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); - -protected: - DScalar get_value_autodiff(const Tuple& tuple) const override; - - /** - * @brief gradient defined wrt the first vertex - * - * @param uv0 - * @param uv1 - * @param uv2 - * @return can be double or DScalar - */ - template - T function_eval( - const Eigen::Matrix& uv0, - const Eigen::Vector2d& uv1, - const Eigen::Vector2d& uv2) const; -}; - -/** - * @brief 3D AMIPS uses uv and displacement map to get the 3d cooridnates then evaluate - * - */ -class AMIPS_3DEmbedded : public AMIPS -{ -public: - AMIPS_3DEmbedded( - const TriMesh& mesh, - const MeshAttributeHandle& vertex_uv_handle, - const image::Image& image); - AMIPS_3DEmbedded( - const TriMesh& mesh, - const MeshAttributeHandle& vertex_uv_handle, - const wmtk::image::SamplingAnalyticFunction::FunctionType type, - const double a, - const double b, - const double c); - -public: - DScalar get_value_autodiff(const Tuple& tuple) const override; - -protected: - template - T function_eval( - const Eigen::Matrix& dofT, - const Eigen::Vector2d& uv2, - const Eigen::Vector2d& uv3) const; - -protected: - DofsToPosition m_dofs_to_pos; -}; -} // namespace function -} // namespace wmtk +} // namespace wmtk::function diff --git a/src/wmtk/function/AMIPS2D.cpp b/src/wmtk/function/AMIPS2D.cpp new file mode 100644 index 0000000000..85f059e4df --- /dev/null +++ b/src/wmtk/function/AMIPS2D.cpp @@ -0,0 +1,81 @@ +#pragma once +#include "AMIPS2D.hpp" +#include +#include +AMIPS2D::AMIPS2D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) + : AMIPS(mesh, vertex_attribute_handle) +{ + // assert(m_vertex_attribute_handle); + // check the dimension of the position + // assert(m_mesh.get_attribute_size(m_vertex_attribute_handle) == 2); +} + +Eigen::Matrix AMIPS2D::get_target_triangle(double scaling) +{ + const static std::array m_target_triangle = {0., 1., 1. / 2., 0., 0., sqrt(3) / 2.}; + return scaling * Eigen::Matrix::ConstMapType(m_target_triangle.data()); +} + + +DScalar AMIPS2D::get_value_autodiff(const Tuple& tuple) const +{ + return function_eval(tuple); +} + +template +T AMIPS2D::function_eval(const Tuple& tuple) const +{ + // get_autodiff_value sets the autodiff size if necessary + // get the uv coordinates of the triangle + ConstAccessor pos = m_mesh.create_const_accessor(m_vertex_attribute_handle); + + auto tuple_value = pos.const_vector_attribute(tuple); + Vector2 uv0; + if constexpr (std::is_same_v < T, DScalar >>) { + uv0 = as_DScalar(tuple_value); + } else { + uv0 = tuple_value; + } + constexpr static Primitive PV = PrimitiveType::Vertex; + constexpr static Primitive PE = PrimitiveType::Edge; + + Eigen::Vector2d uv1 = pos.const_vector_attribute(m_mesh.switch_tuples(tuple, {PE, PV})); + Eigen::Vector2d uv2 = pos.const_vector_attribute(m_mesh.switch_tuples(tuple, {PV, PE})); + + // return the energy + return function_eval(uv0, uv1, uv2); +} + +template +auto AMIPS2D::function_eval( + const Eigen::Matrix& uv0, + const Eigen::Vector2d& uv1, + const Eigen::Vector2d& uv2) const -> T +{ + Eigen::Matrix Dm; + Dm.row(0) = uv1.template cast() - uv0; + Dm.row(1) = uv2.template cast() - uv0; + // Dm << uv1(0) - uv0(0), uv2(0) - uv0(0), uv1(1) - uv0(1), uv2(1) - uv0(1); + + Eigen::Matrix2d Ds, Dsinv; + Eigen::Matrix target_triangle = get_target_triangle(1.0); + Eigen::Vector2d target0; + target0 = target_triangle.row(0); + Ds = target_triangle.bottomRows<2>().rowwise() - target0; + + auto Dsdet = Ds.determinant(); + if (abs(Dsdet) < std::numeric_limits::denorm_min()) { + return static_cast(std::numeric_limits::infinity()); + } + Dsinv = Ds.inverse(); + + // define of transform matrix F = Dm@Ds.inv + Eigen::Matrix J; + J = Dm.transpose() * Dsinv.template cast().transpose(); + + auto Jdet = J.determinant(); + if (abs(Jdet) < std::numeric_limits::denorm_min()) { + return static_cast(std::numeric_limits::infinity()); + } + return (J * J.transpose()).trace() / Jdet; +} diff --git a/src/wmtk/function/AMIPS2D.hpp b/src/wmtk/function/AMIPS2D.hpp new file mode 100644 index 0000000000..53aeff22c8 --- /dev/null +++ b/src/wmtk/function/AMIPS2D.hpp @@ -0,0 +1,30 @@ +#pragma once +#include "AMIPS.hpp" +namespace wmtk::function { +class AMIPS2D : public AMIPS +{ +public: + AMIPS2D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); + +protected: + DScalar get_value_autodiff(const Tuple& tuple) const override; + + /** + * @brief gradient defined wrt the first vertex + * + * @param uv0 + * @param uv1 + * @param uv2 + * @return can be double or DScalar + */ + template + T function_eval( + const Eigen::Matrix& uv0, + const Eigen::Vector2d& uv1, + const Eigen::Vector2d& uv2) const; + template + T function_eval(const Tuple& tuple) const; + +private: + static Eigen::Matrix get_target_triangle(double scaling); +}; diff --git a/src/wmtk/function/AreaAccuracy.cpp b/src/wmtk/function/AreaAccuracy.cpp deleted file mode 100644 index 4fa89ed358..0000000000 --- a/src/wmtk/function/AreaAccuracy.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "AreaAccuracy.hpp" -using namespace wmtk; -using namespace wmtk::function; - -// AreaAccuracy::AreaAccuracy(const TriMesh& mesh1, const TriMesh& mesh2) -// : AutodiffFunction(mesh1) -// , m_position_mesh(mesh2) -// , m_3d_position_handle(mesh2.get_attribute_handle("position", PrimitiveType::Vertex)) -// {} - -// template -// T energy_eval(const Eigen::Vector2d& uv1, const Eigen::Vector2d& uv2, const Eigen::Vector2d& uv3) -// {} diff --git a/src/wmtk/function/AreaAccuracy.hpp b/src/wmtk/function/AreaAccuracy.hpp deleted file mode 100644 index 43f03c2b33..0000000000 --- a/src/wmtk/function/AreaAccuracy.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include "AutodiffFunction.hpp" -namespace wmtk { -namespace function { - -class AreaAccuracy : public AutodiffFunction -{ -public: - AreaAccuracy(const TriMesh& mesh1, const TriMesh& mesh2); - - DScalar get_value_autodiff(const Tuple& tuple) const override; - - /** - * @brief gradient defined wrt the first vertex - * - * @param uv1 - * @param uv2 - * @param uv3 - * @return can be double or DScalar - */ - template - static T function_eval( - const Eigen::Vector2d& uv1, - const Eigen::Vector2d& uv2, - const Eigen::Vector2d& uv3); - -protected: - const TriMesh& m_position_mesh; - const MeshAttributeHandle m_3d_position_handle; -}; -} // namespace function -} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/AutodiffFunction.cpp b/src/wmtk/function/AutodiffFunction.cpp index 448a07435c..c1f36bde06 100644 --- a/src/wmtk/function/AutodiffFunction.cpp +++ b/src/wmtk/function/AutodiffFunction.cpp @@ -1,22 +1,28 @@ #include "AutodiffFunction.hpp" +#include -using namespace wmtk; -using namespace wmtk::function; +namespace wmtk::function { AutodiffFunction::AutodiffFunction( const Mesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) - : DifferentiableFunction(mesh, vertex_attribute_handle){}; + : DifferentiableFunction(mesh, vertex_attribute_handle) +{} +AutodiffFunction::~AutodiffFunction() = default; double AutodiffFunction::get_value(const Tuple& tuple) const { + auto scope = AutoDiffRAII(embedded_dimension()); return get_value_autodiff(tuple).getValue(); } Eigen::VectorXd AutodiffFunction::get_gradient(const Tuple& tuple) const { + auto scope = AutoDiffRAII(embedded_dimension()); return get_value_autodiff(tuple).getGradient(); } Eigen::MatrixXd AutodiffFunction::get_hessian(const Tuple& tuple) const { + auto scope = AutoDiffRAII(embedded_dimension()); return get_value_autodiff(tuple).getHessian(); -} \ No newline at end of file +} +} // namespace wmtk::function diff --git a/src/wmtk/function/AutodiffFunction.hpp b/src/wmtk/function/AutodiffFunction.hpp index c2bf5540e6..4421023de9 100644 --- a/src/wmtk/function/AutodiffFunction.hpp +++ b/src/wmtk/function/AutodiffFunction.hpp @@ -1,17 +1,14 @@ #pragma once +#include #include "DifferentiableFunction.hpp" -namespace wmtk { -namespace function { - -using DScalar = DScalar2, Eigen::Matrix>; -using Scalar = typename DScalar::Scalar; +namespace wmtk::function { class AutodiffFunction : public DifferentiableFunction { public: AutodiffFunction(const Mesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); - virtual ~AutodiffFunction() = default; + virtual ~AutodiffFunction(); public: double get_value(const Tuple& tuple) const override; @@ -19,7 +16,8 @@ class AutodiffFunction : public DifferentiableFunction Eigen::MatrixXd get_hessian(const Tuple& tuple) const override; protected: + using DScalar = DScalar2, Eigen::Matrix>; + using Scalar = typename DScalar::Scalar; virtual DScalar get_value_autodiff(const Tuple& tuple) const = 0; }; -} // namespace function -} // namespace wmtk \ No newline at end of file +} // namespace wmtk::function diff --git a/src/wmtk/function/CMakeLists.txt b/src/wmtk/function/CMakeLists.txt index 079cfda905..9ec1938495 100644 --- a/src/wmtk/function/CMakeLists.txt +++ b/src/wmtk/function/CMakeLists.txt @@ -6,12 +6,12 @@ set(SRC_FILES DifferentiableFunction.hpp AMIPS.hpp AMIPS.cpp + AMIPS2D.hpp + AMIPS2D.cpp TriMeshValenceFunction.hpp TriMeshValenceFunction.cpp - AreaAccuracy.hpp - AreaAccuracy.cpp AutodiffFunction.hpp AutodiffFunction.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) -add_subdirectory(utils) \ No newline at end of file +add_subdirectory(utils) diff --git a/src/wmtk/function/DifferentiableFunction.cpp b/src/wmtk/function/DifferentiableFunction.cpp index 1cc457a08f..3a691f3860 100644 --- a/src/wmtk/function/DifferentiableFunction.cpp +++ b/src/wmtk/function/DifferentiableFunction.cpp @@ -1,9 +1,19 @@ #include "DifferentiableFunction.hpp" -using namespace wmtk; -using namespace wmtk::function; +namespace wmtk::function { DifferentiableFunction::DifferentiableFunction( const Mesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) : Function(mesh) - , m_vertex_attribute_handle(vertex_attribute_handle){}; + , m_vertex_attribute_handle(vertex_attribute_handle) +{} + +const MeshAttributeHandle DifferentiableFunction::get_vertex_attribute_handle() const +{ + return m_vertex_attribute_handle; +} +long DifferentiableFunction::embedded_dimension() const +{ + return mesh().get_attribute_dimension(get_vertex_attribute_handle()); +} +} // namespace wmtk::function diff --git a/src/wmtk/function/DifferentiableFunction.hpp b/src/wmtk/function/DifferentiableFunction.hpp index ee3fc13ff6..b0d7d1c8c8 100644 --- a/src/wmtk/function/DifferentiableFunction.hpp +++ b/src/wmtk/function/DifferentiableFunction.hpp @@ -1,12 +1,8 @@ #pragma once -#include -#include -#include #include -// #include +#include #include "Function.hpp" -namespace wmtk { -namespace function { +namespace wmtk::function { class DifferentiableFunction : public Function { @@ -17,10 +13,17 @@ class DifferentiableFunction : public Function virtual ~DifferentiableFunction() = default; - const MeshAttributeHandle m_vertex_attribute_handle; virtual Eigen::VectorXd get_gradient(const Tuple& tuple) const = 0; virtual Eigen::MatrixXd get_hessian(const Tuple& tuple) const = 0; + const MeshAttributeHandle get_vertex_attribute_handle() const; + + // The dimension that this differentiable function lies in + // (this is the dimension of the vector space the vertex attribute lies in) + long embedded_dimension() const; + + +private: + const MeshAttributeHandle m_vertex_attribute_handle; }; -} // namespace function -} // namespace wmtk +} // namespace wmtk::function diff --git a/src/wmtk/function/Function.cpp b/src/wmtk/function/Function.cpp index d4192b85d3..e7d8e46583 100644 --- a/src/wmtk/function/Function.cpp +++ b/src/wmtk/function/Function.cpp @@ -1,9 +1,15 @@ #include "Function.hpp" -using namespace wmtk; -using namespace wmtk::function; +namespace wmtk::function { Function::Function(const Mesh& mesh) : m_mesh(mesh) {} Function::~Function() = default; + + +const Mesh& Function::mesh() const +{ + return m_mesh; +} +} // namespace wmtk::function diff --git a/src/wmtk/function/Function.hpp b/src/wmtk/function/Function.hpp index 903e7e1e4b..b19bf8f0c7 100644 --- a/src/wmtk/function/Function.hpp +++ b/src/wmtk/function/Function.hpp @@ -5,7 +5,7 @@ namespace wmtk { namespace function { class Function { -protected: +private: const Mesh& m_mesh; @@ -13,9 +13,11 @@ class Function Function(const Mesh& mesh); virtual ~Function(); + const Mesh& mesh() const; + public: // evaluate the function on the top level simplex of the tuple virtual double get_value(const Tuple& tuple) const = 0; }; } // namespace function -} // namespace wmtk \ No newline at end of file +} // namespace wmtk diff --git a/src/wmtk/function/PositionMapAMIPS3D.cpp b/src/wmtk/function/PositionMapAMIPS3D.cpp new file mode 100644 index 0000000000..ee638b8340 --- /dev/null +++ b/src/wmtk/function/PositionMapAMIPS3D.cpp @@ -0,0 +1,129 @@ +#pragma once +#include "PositionMapAMIPS3D.hpp" +#include + +namespace wmtk::function { +PositionMapsAMIPS3D::PositionMapsAMIPS3D( + const TriMesh& mesh, + const MeshAttributeHandle& vertex_uv_handle, + const image::Image& image) + : AMIPS(mesh, vertex_uv_handle) + , m_dofs_to_pos(image) +{} + +PositionMapsAMIPS3D::PositionMapsAMIPS3D( + const TriMesh& mesh, + const MeshAttributeHandle& vertex_uv_handle, + const wmtk::image::SamplingAnalyticFunction::FunctionType type, + const double a, + const double b, + const double c) + : AMIPS(mesh, vertex_uv_handle) + , m_dofs_to_pos(type, a, b, c) +{} + + +DScalar PositionMapsAMIPS3D::get_value_autodiff(const Tuple& tuple) const +{ + // get the uv coordinates of the triangle + ConstAccessor pos = m_mesh.create_const_accessor(m_vertex_attribute_handle); + + // TODO curve mesh uv -> t conversion happens here + Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); + int size = 2; + Eigen::Matrix dofT = get_T_vector>(uv0, size); + + Eigen::Vector2d uv1 = + pos.const_vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); + Eigen::Vector2d uv2 = + pos.const_vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); + + // return the energy + return function_eval(dofT, uv1, uv2); +} + +template +auto PositionMapsAMIPS3D::function_eval( + const Eigen::Matrix& uv0, + const Eigen::Vector2d& uv1, + const Eigen::Vector2d& uv2) const -> T +{ + Eigen::Matrix pos0 = m_dofs_to_pos.dof_to_pos(uv0); + Eigen::Matrix pos1 = m_dofs_to_pos.dof_to_pos(uv1); + Eigen::Matrix pos2 = m_dofs_to_pos.dof_to_pos(uv2); + Eigen::Matrix V2_V1; + V2_V1 = pos1.template cast() - pos0; + Eigen::Matrix V3_V1; + V3_V1 = pos2.template cast() - pos0; + + // tangent bases + // e1 = (V2 - V1).normalize() (this is buggy due to autodiff normalization implementation) + // e1 = V2_V1.stableNormalized(); + assert(V2_V1.norm() > 0); // check norm is not 0 + Eigen::Matrix e1 = V2_V1 / V2_V1.norm(); + Eigen::Matrix n = V2_V1.cross(V3_V1); + + // #ifdef DEBUG + // Eigen::MatrixXd double_n; + // get_double_vecto(n, 3, double_n); + // if (double_n.lpNorm() < std::numeric_limits::denorm_min()) { + // wmtk::logger().critical("n.lpNorm {}", double_n.lpNorm()); + // std::cout << "V1 " << std::endl; + // std::cout << std::hexfloat << get_value(pos0(0))<< " " << y1 << v1(2) << std::endl; + // std::cout << "V2 " << std::endl; + // std::cout << std::hexfloat << v2(0) << " " << v2(1) << v2(2) << std::endl; + // std::cout << "V3 " << std::endl; + // std::cout << std::hexfloat << v3(0) << " " << v3(1) << v3(2) << std::endl; + // assert(false); + // } + // #endif + // n = n.stableNormalized(); + assert(n.norm() > 0); // check norm is not 0 + n = n / n.norm(); + Eigen::Matrix e2 = n.cross(e1); + // Eigen::Matrix e2_stableNormalized = e2.stableNormalized(); + assert(e2.norm() > 0); // check norm is not 0 + e2 = e2 / e2.norm(); + + // project V1, V2, V3 to tangent plane to VT1, VT2, VT3 + + Eigen::Matrix VT0, VT1, VT2; + VT0 << static_cast(0.), static_cast(0.); // the origin + VT1 << V2_V1.dot(e1), V2_V1.dot(e2); + VT2 << V3_V1.dot(e1), V3_V1.dot(e2); + + // now construct Dm as before in tangent plane + // (x2 - x1, y2 - y1, x3 - x1, y2 - y1).transpose + Eigen::Matrix Dm; + Dm.row(0) = VT1 - VT0; + Dm.row(1) = VT2 - VT0; + + T Dmdet = Dm.determinant(); + assert(wmtk::function::get_value(Dmdet) > 0); + + Eigen::Matrix2d Ds, Dsinv; + Eigen::Matrix target_triangle = get_target_triangle(1.0); + Eigen::Vector2d target0, target1, target2; + target0 = target_triangle.row(0); + target1 = target_triangle.row(1); + target2 = target_triangle.row(2); + Ds.row(0) = target1 - target0; + Ds.row(1) = target2 - target0; + + auto Dsdet = Ds.determinant(); + if (abs(Dsdet) < std::numeric_limits::denorm_min()) { + return static_cast(std::numeric_limits::infinity()); + } + Dsinv = Ds.inverse(); + + // define of transform matrix F = Dm@Ds.inv + Eigen::Matrix F; + F = Dm.transpose() * Dsinv.template cast().transpose(); + + auto Fdet = F.determinant(); + if (abs(Fdet) < std::numeric_limits::denorm_min()) { + return static_cast(std::numeric_limits::infinity()); + } + return (F * F.transpose()).trace() / Fdet; +} +} // namespace wmtk::function diff --git a/src/wmtk/function/PositionMapAMIPS3D.hpp b/src/wmtk/function/PositionMapAMIPS3D.hpp new file mode 100644 index 0000000000..dbc2a0631a --- /dev/null +++ b/src/wmtk/function/PositionMapAMIPS3D.hpp @@ -0,0 +1,35 @@ +#pragma once +#include "AMIPS.hpp" + +/** + * @brief 3D AMIPS uses uv and position map to get the 3d cooridnates then evaluate + * + */ +class PositionMapAMIPS3D : public AMIPS +{ +public: + PositionMapAMIPS3D( + const TriMesh& mesh, + const MeshAttributeHandle& vertex_uv_handle, + const image::Image& image); + PositionMapAMIPS3D( + const TriMesh& mesh, + const MeshAttributeHandle& vertex_uv_handle, + const wmtk::image::SamplingAnalyticFunction::FunctionType type, + const double a, + const double b, + const double c); + +public: + DScalar get_value_autodiff(const Tuple& tuple) const override; + +protected: + template + T function_eval( + const Eigen::Matrix& dofT, + const Eigen::Vector2d& uv2, + const Eigen::Vector2d& uv3) const; + +protected: + DofsToPosition m_dofs_to_pos; +}; diff --git a/src/wmtk/function/TriMeshValenceFunction.cpp b/src/wmtk/function/TriMeshValenceFunction.cpp index cc3f91e6a3..d1659e0ebb 100644 --- a/src/wmtk/function/TriMeshValenceFunction.cpp +++ b/src/wmtk/function/TriMeshValenceFunction.cpp @@ -1,9 +1,8 @@ #include "TriMeshValenceFunction.hpp" #include #include -#include -namespace wmtk { -namespace function { +#include +namespace wmtk::function { TriMeshValenceFunction::TriMeshValenceFunction(const TriMesh& mesh) : Function(mesh) {} @@ -12,13 +11,13 @@ double TriMeshValenceFunction::get_value(const Tuple& tuple) const { // assume tuple is not a boundary edge const Tuple current_v = tuple; - const Tuple other_v = m_mesh.switch_vertex(tuple); - long val0 = static_cast(SimplicialComplex::vertex_one_ring(m_mesh, current_v).size()); - long val1 = static_cast(SimplicialComplex::vertex_one_ring(m_mesh, other_v).size()); - if (m_mesh.is_boundary_vertex(current_v)) { + const Tuple other_v = mesh().switch_vertex(tuple); + long val0 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), current_v).size()); + long val1 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), other_v).size()); + if (mesh().is_boundary_vertex(current_v)) { val0 += 2; } - if (m_mesh.is_boundary_vertex(other_v)) { + if (mesh().is_boundary_vertex(other_v)) { val1 += 2; } if (val0 < 4 || val1 < 4) { @@ -32,15 +31,15 @@ double TriMeshValenceFunction::get_value(const Tuple& tuple) const // \ / // \ / // bottom_v - const Tuple top_v = m_mesh.switch_vertex(m_mesh.switch_edge(tuple)); - const Tuple bottom_v = m_mesh.switch_vertex(m_mesh.switch_edge(m_mesh.switch_face(tuple))); - long val2 = static_cast(SimplicialComplex::vertex_one_ring(m_mesh, top_v).size()); - long val3 = static_cast(SimplicialComplex::vertex_one_ring(m_mesh, bottom_v).size()); + const Tuple top_v = mesh().switch_vertex(mesh().switch_edge(tuple)); + const Tuple bottom_v = mesh().switch_vertex(mesh().switch_edge(mesh().switch_face(tuple))); + long val2 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), top_v).size()); + long val3 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), bottom_v).size()); - if (m_mesh.is_boundary_vertex(top_v)) { + if (mesh().is_boundary_vertex(top_v)) { val2 += 2; } - if (m_mesh.is_boundary_vertex(bottom_v)) { + if (mesh().is_boundary_vertex(bottom_v)) { val3 += 2; } // formula from: https://github.com/daniel-zint/hpmeshgen/blob/cdfb9163ed92523fcf41a127c8173097e935c0a3/src/HPMeshGen2/TriRemeshing.cpp#L315 @@ -51,5 +50,10 @@ double TriMeshValenceFunction::get_value(const Tuple& tuple) const return static_cast(val_energy); } -} // namespace function -} // namespace wmtk \ No newline at end of file + +const TriMesh& TriMeshValenceFunction::mesh() const +{ + return static_cast(Function::mesh()); +} + +} // namespace wmtk::function diff --git a/src/wmtk/function/TriMeshValenceFunction.hpp b/src/wmtk/function/TriMeshValenceFunction.hpp index 8076114ec5..1526f32ccf 100644 --- a/src/wmtk/function/TriMeshValenceFunction.hpp +++ b/src/wmtk/function/TriMeshValenceFunction.hpp @@ -1,6 +1,6 @@ -#include #include "Function.hpp" namespace wmtk { +class TriMesh; namespace function { class TriMeshValenceFunction : public Function @@ -10,7 +10,7 @@ class TriMeshValenceFunction : public Function double get_value(const Tuple& tuple) const override; protected: - TriMesh& mesh() const; + const TriMesh& mesh() const; }; } // namespace function -} // namespace wmtk \ No newline at end of file +} // namespace wmtk diff --git a/src/wmtk/function/utils/AutoDiffRAII.cpp b/src/wmtk/function/utils/AutoDiffRAII.cpp new file mode 100644 index 0000000000..a6ad9314d6 --- /dev/null +++ b/src/wmtk/function/utils/AutoDiffRAII.cpp @@ -0,0 +1,14 @@ +#include "AutoDiffRAII.hpp" +#include "autodiff.h" + +namespace wmtk::function { +AutoDiffRAII::AutoDiffRAII(size_t size) + : m_previous_variable_count(DiffScalarBase::getVariableCount()) +{ + DiffScalarBase::setVariableCount(size); +} +AutoDiffRAII::~AutoDiffRAII() +{ + DiffScalarBase::setVariableCount(m_previous_variable_count); +} +} // namespace wmtk::function diff --git a/src/wmtk/function/utils/AutoDiffRAII.hpp b/src/wmtk/function/utils/AutoDiffRAII.hpp new file mode 100644 index 0000000000..50d272f32d --- /dev/null +++ b/src/wmtk/function/utils/AutoDiffRAII.hpp @@ -0,0 +1,17 @@ +#pragma once +#include + + +namespace wmtk::function { + + +class AutoDiffRAII +{ +public: + AutoDiffRAII(size_t size); + ~AutoDiffRAII(); + +private: + size_t m_previous_variable_count; +}; +} // namespace wmtk::function diff --git a/src/wmtk/function/utils/AutoDiffUtils.hpp b/src/wmtk/function/utils/AutoDiffUtils.hpp index b9cc7ba6e1..0d1a984f94 100644 --- a/src/wmtk/function/utils/AutoDiffUtils.hpp +++ b/src/wmtk/function/utils/AutoDiffUtils.hpp @@ -1,64 +1,66 @@ #pragma once #include "autodiff.h" -namespace wmtk { -namespace function { -using DScalar = DScalar2, Eigen::Matrix>; -using Scalar = typename DScalar::Scalar; -template -class AutoDiffAllocator -{ -public: - T operator()(const int i, double v) const { return T(i, v); } -}; +namespace wmtk::function { -template <> -class AutoDiffAllocator -{ -public: - double operator()(const int i, double v) const { return v; } -}; -inline double get_value(float x) -{ - return static_cast(x); -} -inline double get_value(double x) -{ - return x; -} -inline double get_value( - DScalar2, Eigen::Matrix> x) +template +auto make_DScalar_matrix(int rows = 0, int cols = 0) { - return x.getValue(); -} - -inline double get_value( - DScalar2, Eigen::Matrix> x) -{ - return x.getValue(); -} + if constexpr (Rows != Eigen::Dynamic) { + rows = Rows; + } + if constexpr (Cols != Eigen::Dynamic) { + cols = Cols; + } + assert(rows * cols == DiffScalarBase::getVariableCount()); -template -AutoDiffVect get_T_vector(const Eigen::MatrixXd& data, const int size) -{ - typedef typename AutoDiffVect::Scalar T; - AutoDiffVect T_vector; - DiffScalarBase::setVariableCount(size); - const AutoDiffAllocator allocate_auto_diff_scalar; - T_vector.resize(size); - for (int i = 0; i < size; ++i) { - T_vector(i) = allocate_auto_diff_scalar(i, data(i)); + using RetType = Eigen::Matrix; + if constexpr (Rows != Eigen::Dynamic && Cols != Eigen::Dynamic) { + return RetType::NullaryExpr([](int row, int col) { + int index; + if constexpr (RetType::IsRowMajor) { + index = Rows * col + row; + } else { + index = Cols * row + col; + } + return DScalarType(index); + }) + .eval(); + } else { + return RetType::NullaryExpr( + rows, + cols, + [&](int row, int col) { + int index; + if constexpr (RetType::IsRowMajor) { + index = rows * col + row; + } else { + index = cols * row + col; + } + return DScalarType(index); + }) + .eval(); } - return T_vector; } -template -void get_double_vector(const AutoDiffVect& T_vector, const int size, Eigen::MatrixXd& double_t) + +template +auto as_DScalar(const Eigen::MatrixBase& data) { - double_t.resize(size, 1); - for (int i = 0; i < size; ++i) { - double_t(i) = T_vector(i).getValue(); - } + constexpr static int Rows = Derived::RowsAtCompileTime; + constexpr static int Cols = Derived::ColsAtCompileTime; + + Eigen::Matrix M = + make_DScalar_matrix(data.rows(), data.cols()); + + M.noalias() = M.binaryExpr(data, [](DScalarType v, const auto& d) { + v = d; + return v; + }); + + + return M; } -} // namespace function -} // namespace wmtk \ No newline at end of file + + +} // namespace wmtk::function diff --git a/src/wmtk/function/utils/CMakeLists.txt b/src/wmtk/function/utils/CMakeLists.txt index 9c70628599..5cdc01861c 100644 --- a/src/wmtk/function/utils/CMakeLists.txt +++ b/src/wmtk/function/utils/CMakeLists.txt @@ -3,5 +3,7 @@ set(SRC_FILES autodiff.h autodiff.cpp DofsToPosition.hpp + AutoDiffRAII.hpp + AutoDiffRAII.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/operations/tri_mesh/CMakeLists.txt b/src/wmtk/operations/tri_mesh/CMakeLists.txt index 666376b36c..b8ba0145db 100644 --- a/src/wmtk/operations/tri_mesh/CMakeLists.txt +++ b/src/wmtk/operations/tri_mesh/CMakeLists.txt @@ -11,12 +11,12 @@ set(SRC_FILES EdgeCollapseToMidpoint.cpp EdgeSwap.hpp EdgeSwap.cpp - VertexSmoothUsingDifferentiableEnergy.hpp - VertexSmoothUsingDifferentiableEnergy.cpp - VertexSmoothNewtonMethod.hpp - VertexSmoothNewtonMethod.cpp - VertexSmoothNewtonMethodWithLineSearch.hpp - VertexSmoothNewtonMethodWithLineSearch.cpp + #VertexSmoothUsingDifferentiableEnergy.hpp + #VertexSmoothUsingDifferentiableEnergy.cpp + #VertexSmoothNewtonMethod.hpp + #VertexSmoothNewtonMethod.cpp + #VertexSmoothNewtonMethodWithLineSearch.hpp + #VertexSmoothNewtonMethodWithLineSearch.cpp VertexAttributesUpdateBase.hpp VertexAttributesUpdateBase.cpp VertexLaplacianSmooth.hpp diff --git a/tests/components/CMakeLists.txt b/tests/components/CMakeLists.txt index 8961359751..16d05cd686 100644 --- a/tests/components/CMakeLists.txt +++ b/tests/components/CMakeLists.txt @@ -5,10 +5,10 @@ set(TEST_SOURCES test_component_mesh_info.cpp test_component_output.cpp test_component_isotropic_remeshing.cpp - test_smoothing.cpp + #test_smoothing.cpp ) target_sources(wmtk_tests PRIVATE ${TEST_SOURCES}) target_link_libraries(wmtk_tests PUBLIC wildmeshing_components -) \ No newline at end of file +) diff --git a/tests/function/CMakeLists.txt b/tests/function/CMakeLists.txt index f05c9ed59b..d07dc5c0e9 100644 --- a/tests/function/CMakeLists.txt +++ b/tests/function/CMakeLists.txt @@ -1,11 +1,7 @@ # Sources set(TEST_SOURCES - test_2d_energy.cpp + # test_2d_energy.cpp ) target_sources(wmtk_tests PRIVATE ${TEST_SOURCES}) -target_link_libraries(wmtk_tests PUBLIC - wildmeshing_toolkit -) - From 8447cf8122d511acdf6fa769a354478298062f61 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 14 Oct 2023 13:08:12 -0400 Subject: [PATCH 091/134] new amips structure in place, with basic 2d and 3d --- src/wmtk/Types.hpp | 7 + src/wmtk/function/AMIPS2D.cpp | 69 +++------- src/wmtk/function/AMIPS2D.hpp | 16 +-- src/wmtk/function/AMIPS3D.cpp | 49 +++++++ src/wmtk/function/AMIPS3D.hpp | 19 +++ src/wmtk/function/CMakeLists.txt | 5 + src/wmtk/function/PositionMapAMIPS2D.cpp | 57 ++++++++ ...nMapAMIPS3D.hpp => PositionMapAMIPS2D.hpp} | 14 +- src/wmtk/function/PositionMapAMIPS3D.cpp | 129 ------------------ src/wmtk/function/utils/AutoDiffUtils.hpp | 16 ++- src/wmtk/function/utils/CMakeLists.txt | 2 + src/wmtk/function/utils/amips.cpp | 35 +++++ src/wmtk/function/utils/amips.hpp | 109 +++++++++++++++ tests/function/CMakeLists.txt | 1 + tests/function/test_2d_energy.cpp | 7 +- tests/function/test_amips.cpp | 30 ++++ tests/test_execution.cpp | 12 +- tests/tools/TriMesh_examples.cpp | 6 +- tests/tools/TriMesh_examples.hpp | 6 +- 19 files changed, 365 insertions(+), 224 deletions(-) create mode 100644 src/wmtk/function/AMIPS3D.cpp create mode 100644 src/wmtk/function/AMIPS3D.hpp create mode 100644 src/wmtk/function/PositionMapAMIPS2D.cpp rename src/wmtk/function/{PositionMapAMIPS3D.hpp => PositionMapAMIPS2D.hpp} (63%) delete mode 100644 src/wmtk/function/PositionMapAMIPS3D.cpp create mode 100644 src/wmtk/function/utils/amips.cpp create mode 100644 src/wmtk/function/utils/amips.hpp create mode 100644 tests/function/test_amips.cpp diff --git a/src/wmtk/Types.hpp b/src/wmtk/Types.hpp index 0f9a9926ba..feab31c298 100644 --- a/src/wmtk/Types.hpp +++ b/src/wmtk/Types.hpp @@ -11,6 +11,13 @@ using Vector = Eigen::Matrix; template using VectorX = Vector; +template +using Vector2 = Vector; +template +using Vector3 = Vector; +template +using Vector4 = Vector; + template using RowVector = Eigen::Matrix; template diff --git a/src/wmtk/function/AMIPS2D.cpp b/src/wmtk/function/AMIPS2D.cpp index 85f059e4df..c286e5ee27 100644 --- a/src/wmtk/function/AMIPS2D.cpp +++ b/src/wmtk/function/AMIPS2D.cpp @@ -1,23 +1,22 @@ -#pragma once #include "AMIPS2D.hpp" +#include #include +#include +#include #include + + +namespace wmtk::function { AMIPS2D::AMIPS2D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) : AMIPS(mesh, vertex_attribute_handle) { - // assert(m_vertex_attribute_handle); + assert(get_vertex_attribute_handle()); // check the dimension of the position - // assert(m_mesh.get_attribute_size(m_vertex_attribute_handle) == 2); + assert(embedding_dimension() == 2); } -Eigen::Matrix AMIPS2D::get_target_triangle(double scaling) -{ - const static std::array m_target_triangle = {0., 1., 1. / 2., 0., 0., sqrt(3) / 2.}; - return scaling * Eigen::Matrix::ConstMapType(m_target_triangle.data()); -} - -DScalar AMIPS2D::get_value_autodiff(const Tuple& tuple) const +auto AMIPS2D::get_value_autodiff(const Tuple& tuple) const -> DScalar { return function_eval(tuple); } @@ -27,55 +26,23 @@ T AMIPS2D::function_eval(const Tuple& tuple) const { // get_autodiff_value sets the autodiff size if necessary // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_const_accessor(m_vertex_attribute_handle); + ConstAccessor pos = mesh().create_const_accessor(get_vertex_attribute_handle()); auto tuple_value = pos.const_vector_attribute(tuple); Vector2 uv0; - if constexpr (std::is_same_v < T, DScalar >>) { - uv0 = as_DScalar(tuple_value); + if constexpr (std::is_same_v) { + uv0 = utils::as_DScalar(tuple_value); } else { uv0 = tuple_value; } - constexpr static Primitive PV = PrimitiveType::Vertex; - constexpr static Primitive PE = PrimitiveType::Edge; + constexpr static PrimitiveType PV = PrimitiveType::Vertex; + constexpr static PrimitiveType PE = PrimitiveType::Edge; - Eigen::Vector2d uv1 = pos.const_vector_attribute(m_mesh.switch_tuples(tuple, {PE, PV})); - Eigen::Vector2d uv2 = pos.const_vector_attribute(m_mesh.switch_tuples(tuple, {PV, PE})); + Eigen::Vector2d uv1 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PE, PV})); + Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PV, PE})); // return the energy - return function_eval(uv0, uv1, uv2); + return utils::amips(uv0, uv1, uv2); } -template -auto AMIPS2D::function_eval( - const Eigen::Matrix& uv0, - const Eigen::Vector2d& uv1, - const Eigen::Vector2d& uv2) const -> T -{ - Eigen::Matrix Dm; - Dm.row(0) = uv1.template cast() - uv0; - Dm.row(1) = uv2.template cast() - uv0; - // Dm << uv1(0) - uv0(0), uv2(0) - uv0(0), uv1(1) - uv0(1), uv2(1) - uv0(1); - - Eigen::Matrix2d Ds, Dsinv; - Eigen::Matrix target_triangle = get_target_triangle(1.0); - Eigen::Vector2d target0; - target0 = target_triangle.row(0); - Ds = target_triangle.bottomRows<2>().rowwise() - target0; - - auto Dsdet = Ds.determinant(); - if (abs(Dsdet) < std::numeric_limits::denorm_min()) { - return static_cast(std::numeric_limits::infinity()); - } - Dsinv = Ds.inverse(); - - // define of transform matrix F = Dm@Ds.inv - Eigen::Matrix J; - J = Dm.transpose() * Dsinv.template cast().transpose(); - - auto Jdet = J.determinant(); - if (abs(Jdet) < std::numeric_limits::denorm_min()) { - return static_cast(std::numeric_limits::infinity()); - } - return (J * J.transpose()).trace() / Jdet; -} +} // namespace wmtk::function diff --git a/src/wmtk/function/AMIPS2D.hpp b/src/wmtk/function/AMIPS2D.hpp index 53aeff22c8..022d816a2a 100644 --- a/src/wmtk/function/AMIPS2D.hpp +++ b/src/wmtk/function/AMIPS2D.hpp @@ -9,22 +9,10 @@ class AMIPS2D : public AMIPS protected: DScalar get_value_autodiff(const Tuple& tuple) const override; - /** - * @brief gradient defined wrt the first vertex - * - * @param uv0 - * @param uv1 - * @param uv2 - * @return can be double or DScalar - */ - template - T function_eval( - const Eigen::Matrix& uv0, - const Eigen::Vector2d& uv1, - const Eigen::Vector2d& uv2) const; + template T function_eval(const Tuple& tuple) const; private: - static Eigen::Matrix get_target_triangle(double scaling); }; +} // namespace wmtk::function diff --git a/src/wmtk/function/AMIPS3D.cpp b/src/wmtk/function/AMIPS3D.cpp new file mode 100644 index 0000000000..850039767a --- /dev/null +++ b/src/wmtk/function/AMIPS3D.cpp @@ -0,0 +1,49 @@ +#include "AMIPS3D.hpp" +#include +#include +#include +#include +#include + +namespace wmtk::function { +AMIPS3D::AMIPS3D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) + : AMIPS(mesh, vertex_attribute_handle) +{ + assert(get_vertex_attribute_handle()); + // check the dimension of the position + assert(embedding_dimension() == 3); +} + + +auto AMIPS3D::get_value_autodiff(const Tuple& tuple) const -> DScalar +{ + return function_eval(tuple); +} + +template +T AMIPS3D::function_eval(const Tuple& tuple) const +{ + // get_autodiff_value sets the autodiff size if necessary + // get the pos coordinates of the triangle + ConstAccessor pos = mesh().create_const_accessor(get_vertex_attribute_handle()); + + auto tuple_value = pos.const_vector_attribute(tuple); + Vector3 pos0; + if constexpr (std::is_same_v) { + pos0 = utils::as_DScalar(tuple_value); + } else { + pos0 = tuple_value; + } + constexpr static PrimitiveType PV = PrimitiveType::Vertex; + constexpr static PrimitiveType PE = PrimitiveType::Edge; + + Eigen::Vector3d pos1 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PE, PV})); + Eigen::Vector3d pos2 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PV, PE})); + + // return the energy + return utils::amips(pos0, pos1, pos2); +} + + + +} // namespace wmtk::function diff --git a/src/wmtk/function/AMIPS3D.hpp b/src/wmtk/function/AMIPS3D.hpp new file mode 100644 index 0000000000..4d6e19b0c2 --- /dev/null +++ b/src/wmtk/function/AMIPS3D.hpp @@ -0,0 +1,19 @@ +#pragma once +#include +#include "AMIPS.hpp" +namespace wmtk::function { +class AMIPS3D : public AMIPS +{ +public: + AMIPS3D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); + +protected: + DScalar get_value_autodiff(const Tuple& tuple) const override; + + template + T function_eval(const Tuple& tuple) const; + + +private: +}; +} // namespace wmtk::function diff --git a/src/wmtk/function/CMakeLists.txt b/src/wmtk/function/CMakeLists.txt index 9ec1938495..35f1de559a 100644 --- a/src/wmtk/function/CMakeLists.txt +++ b/src/wmtk/function/CMakeLists.txt @@ -8,6 +8,11 @@ set(SRC_FILES AMIPS.cpp AMIPS2D.hpp AMIPS2D.cpp + AMIPS3D.hpp + AMIPS3D.cpp + #PositionMapAMIPS3D.hpp + #PositionMapAMIPS3D.cpp + TriMeshValenceFunction.hpp TriMeshValenceFunction.cpp AutodiffFunction.hpp diff --git a/src/wmtk/function/PositionMapAMIPS2D.cpp b/src/wmtk/function/PositionMapAMIPS2D.cpp new file mode 100644 index 0000000000..40078df1d4 --- /dev/null +++ b/src/wmtk/function/PositionMapAMIPS2D.cpp @@ -0,0 +1,57 @@ +#pragma once +#include +#include "PositionMapAMIPS2D.hpp" + +namespace wmtk::function { +PositionMapsAMIPS2D::PositionMapsAMIPS2D( + const TriMesh& mesh, + const MeshAttributeHandle& vertex_uv_handle, + const image::Image& image) + : AMIPS(mesh, vertex_uv_handle) + , m_dofs_to_pos(image) +{} + +PositionMapsAMIPS2D::PositionMapsAMIPS2D( + const TriMesh& mesh, + const MeshAttributeHandle& vertex_uv_handle, + const wmtk::image::SamplingAnalyticFunction::FunctionType type, + const double a, + const double b, + const double c) + : AMIPS(mesh, vertex_uv_handle) + , m_dofs_to_pos(type, a, b, c) +{} + + +DScalar PositionMapsAMIPS2D::get_value_autodiff(const Tuple& tuple) const +{ + // get the uv coordinates of the triangle + ConstAccessor pos = m_mesh.create_const_accessor(m_vertex_attribute_handle); + + // TODO curve mesh uv -> t conversion happens here + Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); + int size = 2; + Eigen::Matrix dofT = get_T_vector>(uv0, size); + + Eigen::Vector2d uv1 = + pos.const_vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); + Eigen::Vector2d uv2 = + pos.const_vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); + + // return the energy + return function_eval(dofT, uv1, uv2); +} + +template +auto PositionMapsAMIPS2D::function_eval( + const Eigen::Matrix& uv0, + const Eigen::Vector2d& uv1, + const Eigen::Vector2d& uv2) const -> T +{ + Eigen::Matrix pos0 = m_dofs_to_pos.dof_to_pos(uv0); + Eigen::Matrix pos1 = m_dofs_to_pos.dof_to_pos(uv1); + Eigen::Matrix pos2 = m_dofs_to_pos.dof_to_pos(uv2); + + return utils::amips(pos0, pos1, pos2); +} +} // namespace wmtk::function diff --git a/src/wmtk/function/PositionMapAMIPS3D.hpp b/src/wmtk/function/PositionMapAMIPS2D.hpp similarity index 63% rename from src/wmtk/function/PositionMapAMIPS3D.hpp rename to src/wmtk/function/PositionMapAMIPS2D.hpp index dbc2a0631a..cb21be913b 100644 --- a/src/wmtk/function/PositionMapAMIPS3D.hpp +++ b/src/wmtk/function/PositionMapAMIPS2D.hpp @@ -2,17 +2,17 @@ #include "AMIPS.hpp" /** - * @brief 3D AMIPS uses uv and position map to get the 3d cooridnates then evaluate + * @brief 2D AMIPS uses uv and position map to get the 3d cooridnates then evaluate * */ -class PositionMapAMIPS3D : public AMIPS +class PositionMapAMIPS2D : public AMIPS2D { public: - PositionMapAMIPS3D( + PositionMapAMIPS2D( const TriMesh& mesh, const MeshAttributeHandle& vertex_uv_handle, const image::Image& image); - PositionMapAMIPS3D( + PositionMapAMIPS2D( const TriMesh& mesh, const MeshAttributeHandle& vertex_uv_handle, const wmtk::image::SamplingAnalyticFunction::FunctionType type, @@ -24,12 +24,6 @@ class PositionMapAMIPS3D : public AMIPS DScalar get_value_autodiff(const Tuple& tuple) const override; protected: - template - T function_eval( - const Eigen::Matrix& dofT, - const Eigen::Vector2d& uv2, - const Eigen::Vector2d& uv3) const; - protected: DofsToPosition m_dofs_to_pos; }; diff --git a/src/wmtk/function/PositionMapAMIPS3D.cpp b/src/wmtk/function/PositionMapAMIPS3D.cpp deleted file mode 100644 index ee638b8340..0000000000 --- a/src/wmtk/function/PositionMapAMIPS3D.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once -#include "PositionMapAMIPS3D.hpp" -#include - -namespace wmtk::function { -PositionMapsAMIPS3D::PositionMapsAMIPS3D( - const TriMesh& mesh, - const MeshAttributeHandle& vertex_uv_handle, - const image::Image& image) - : AMIPS(mesh, vertex_uv_handle) - , m_dofs_to_pos(image) -{} - -PositionMapsAMIPS3D::PositionMapsAMIPS3D( - const TriMesh& mesh, - const MeshAttributeHandle& vertex_uv_handle, - const wmtk::image::SamplingAnalyticFunction::FunctionType type, - const double a, - const double b, - const double c) - : AMIPS(mesh, vertex_uv_handle) - , m_dofs_to_pos(type, a, b, c) -{} - - -DScalar PositionMapsAMIPS3D::get_value_autodiff(const Tuple& tuple) const -{ - // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_const_accessor(m_vertex_attribute_handle); - - // TODO curve mesh uv -> t conversion happens here - Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); - int size = 2; - Eigen::Matrix dofT = get_T_vector>(uv0, size); - - Eigen::Vector2d uv1 = - pos.const_vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); - Eigen::Vector2d uv2 = - pos.const_vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); - - // return the energy - return function_eval(dofT, uv1, uv2); -} - -template -auto PositionMapsAMIPS3D::function_eval( - const Eigen::Matrix& uv0, - const Eigen::Vector2d& uv1, - const Eigen::Vector2d& uv2) const -> T -{ - Eigen::Matrix pos0 = m_dofs_to_pos.dof_to_pos(uv0); - Eigen::Matrix pos1 = m_dofs_to_pos.dof_to_pos(uv1); - Eigen::Matrix pos2 = m_dofs_to_pos.dof_to_pos(uv2); - Eigen::Matrix V2_V1; - V2_V1 = pos1.template cast() - pos0; - Eigen::Matrix V3_V1; - V3_V1 = pos2.template cast() - pos0; - - // tangent bases - // e1 = (V2 - V1).normalize() (this is buggy due to autodiff normalization implementation) - // e1 = V2_V1.stableNormalized(); - assert(V2_V1.norm() > 0); // check norm is not 0 - Eigen::Matrix e1 = V2_V1 / V2_V1.norm(); - Eigen::Matrix n = V2_V1.cross(V3_V1); - - // #ifdef DEBUG - // Eigen::MatrixXd double_n; - // get_double_vecto(n, 3, double_n); - // if (double_n.lpNorm() < std::numeric_limits::denorm_min()) { - // wmtk::logger().critical("n.lpNorm {}", double_n.lpNorm()); - // std::cout << "V1 " << std::endl; - // std::cout << std::hexfloat << get_value(pos0(0))<< " " << y1 << v1(2) << std::endl; - // std::cout << "V2 " << std::endl; - // std::cout << std::hexfloat << v2(0) << " " << v2(1) << v2(2) << std::endl; - // std::cout << "V3 " << std::endl; - // std::cout << std::hexfloat << v3(0) << " " << v3(1) << v3(2) << std::endl; - // assert(false); - // } - // #endif - // n = n.stableNormalized(); - assert(n.norm() > 0); // check norm is not 0 - n = n / n.norm(); - Eigen::Matrix e2 = n.cross(e1); - // Eigen::Matrix e2_stableNormalized = e2.stableNormalized(); - assert(e2.norm() > 0); // check norm is not 0 - e2 = e2 / e2.norm(); - - // project V1, V2, V3 to tangent plane to VT1, VT2, VT3 - - Eigen::Matrix VT0, VT1, VT2; - VT0 << static_cast(0.), static_cast(0.); // the origin - VT1 << V2_V1.dot(e1), V2_V1.dot(e2); - VT2 << V3_V1.dot(e1), V3_V1.dot(e2); - - // now construct Dm as before in tangent plane - // (x2 - x1, y2 - y1, x3 - x1, y2 - y1).transpose - Eigen::Matrix Dm; - Dm.row(0) = VT1 - VT0; - Dm.row(1) = VT2 - VT0; - - T Dmdet = Dm.determinant(); - assert(wmtk::function::get_value(Dmdet) > 0); - - Eigen::Matrix2d Ds, Dsinv; - Eigen::Matrix target_triangle = get_target_triangle(1.0); - Eigen::Vector2d target0, target1, target2; - target0 = target_triangle.row(0); - target1 = target_triangle.row(1); - target2 = target_triangle.row(2); - Ds.row(0) = target1 - target0; - Ds.row(1) = target2 - target0; - - auto Dsdet = Ds.determinant(); - if (abs(Dsdet) < std::numeric_limits::denorm_min()) { - return static_cast(std::numeric_limits::infinity()); - } - Dsinv = Ds.inverse(); - - // define of transform matrix F = Dm@Ds.inv - Eigen::Matrix F; - F = Dm.transpose() * Dsinv.template cast().transpose(); - - auto Fdet = F.determinant(); - if (abs(Fdet) < std::numeric_limits::denorm_min()) { - return static_cast(std::numeric_limits::infinity()); - } - return (F * F.transpose()).trace() / Fdet; -} -} // namespace wmtk::function diff --git a/src/wmtk/function/utils/AutoDiffUtils.hpp b/src/wmtk/function/utils/AutoDiffUtils.hpp index 0d1a984f94..71adbbf9f8 100644 --- a/src/wmtk/function/utils/AutoDiffUtils.hpp +++ b/src/wmtk/function/utils/AutoDiffUtils.hpp @@ -1,7 +1,7 @@ #pragma once #include "autodiff.h" -namespace wmtk::function { +namespace wmtk::function::utils { template @@ -53,14 +53,18 @@ auto as_DScalar(const Eigen::MatrixBase& data) Eigen::Matrix M = make_DScalar_matrix(data.rows(), data.cols()); - M.noalias() = M.binaryExpr(data, [](DScalarType v, const auto& d) { - v = d; - return v; - }); + + for (int j = 0; j < M.size(); ++j) { + M(j) = data(j); + } + // M.noalias() = M.binaryExpr(data, [](DScalarType v, const auto& d) -> DScalarType { + // v = d; + // return v; + // }); return M; } -} // namespace wmtk::function +} // namespace wmtk::function::utils diff --git a/src/wmtk/function/utils/CMakeLists.txt b/src/wmtk/function/utils/CMakeLists.txt index 5cdc01861c..b033a711ce 100644 --- a/src/wmtk/function/utils/CMakeLists.txt +++ b/src/wmtk/function/utils/CMakeLists.txt @@ -5,5 +5,7 @@ set(SRC_FILES DofsToPosition.hpp AutoDiffRAII.hpp AutoDiffRAII.cpp + amips.hpp + amips.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/function/utils/amips.cpp b/src/wmtk/function/utils/amips.cpp new file mode 100644 index 0000000000..c69d817a61 --- /dev/null +++ b/src/wmtk/function/utils/amips.cpp @@ -0,0 +1,35 @@ +#include "amips.hpp" +#include +#include +namespace wmtk::function::utils { +namespace detail { +namespace { +auto make_amips_target_triangle() +{ + const static std::array, 3> m_target_triangle{{ + // comments to keep formatting + std::array{{0., 0.}}, // + std::array{{1., 0.}}, // + std::array{{0.5, sqrt(3) / 2.}}, + // + }}; + auto map = Eigen::Matrix::ConstMapType(m_target_triangle[0].data()); + return map; +} + +} // namespace +const Eigen::Matrix amips_target_triangle = make_amips_target_triangle(); + +namespace { +Eigen::Matrix2d make_amips_reference_to_barycentric() +{ + const auto& A = amips_target_triangle; + Eigen::Matrix2d Ds = (A.rightCols<2>().colwise() - A.col(0)); + + return Ds.inverse(); +} + +} // namespace +const Eigen::Matrix2d amips_reference_to_barycentric = make_amips_reference_to_barycentric(); +} // namespace detail +} // namespace wmtk::function::utils diff --git a/src/wmtk/function/utils/amips.hpp b/src/wmtk/function/utils/amips.hpp new file mode 100644 index 0000000000..70a0617d20 --- /dev/null +++ b/src/wmtk/function/utils/amips.hpp @@ -0,0 +1,109 @@ +#pragma once +#include +#include + +namespace wmtk::function::utils { + +namespace detail { +// returns v0,v1,v2 of the target triangle as row vectors +extern const Eigen::Matrix amips_target_triangle; +// maps from the embedding of the reference triangle to the barycentric coordinates +extern const Eigen::Matrix2d amips_reference_to_barycentric; + +} // namespace detail + + +// Given an basis vectors following a "v1-v0,v2-v0" convention for a triangle (v0,v1,v2) +// return the AMIPS energy. +// Input are assumed to be column vectors, opposite of the standard IGL formation +template +auto amips(const Eigen::MatrixBase& B) +{ + using Scalar = typename Derived::Scalar; + constexpr static int Rows = Derived::RowsAtCompileTime; + constexpr static int Cols = Derived::ColsAtCompileTime; + + + // check that these are vectors + static_assert(Cols == 2); + static_assert(Rows == 2); + + + // MTAO: Why can't this work with more than 2 rows? + // define of transform matrix F = Dm@Ds.inv + Eigen::Matrix J; + J = B * detail::amips_reference_to_barycentric.template cast(); + + auto Jdet = J.determinant(); + if (abs(Jdet) < std::numeric_limits::denorm_min()) { + return static_cast(std::numeric_limits::infinity()); + } + return (J * J.transpose()).trace() / Jdet; +} + + +// +template +auto amips( + const Eigen::MatrixBase& v0, + const Eigen::MatrixBase& v1, + const Eigen::MatrixBase& v2) +{ + using Scalar = typename V0Type::Scalar; + constexpr static int Rows = V0Type::RowsAtCompileTime; + constexpr static int Cols0 = V0Type::ColsAtCompileTime; + constexpr static int Cols1 = V1Type::ColsAtCompileTime; + constexpr static int Cols2 = V1Type::ColsAtCompileTime; + + + // check that these are vectors + static_assert(Cols0 == 1); + static_assert(Cols1 == 1); + static_assert(Cols2 == 1); + + // just check that the inputs had the right dimensions + constexpr static int Rows1 = V1Type::RowsAtCompileTime; + constexpr static int Rows2 = V1Type::RowsAtCompileTime; + static_assert(Rows == Rows1); + static_assert(Rows == Rows2); + + Eigen::Matrix Dm; + + + static_assert(Rows == 2 || Rows == 3); + + if constexpr (Rows == 2) { + Dm.col(0) = (v1.template cast() - v0); + Dm.col(1) = (v2.template cast() - v0); + } else if constexpr (Rows == 3) { + // in 3d we compute a basis + // local vectors + Eigen::Matrix V; + V.col(0) = v1.template cast() - v0; + V.col(1) = v2.template cast() - v0; + + // compute a basis plane + Eigen::Matrix B = V; + + auto e0 = B.col(0); + auto e1 = B.col(1); + + // TODO: shouldnt we make sure the normms are over some eps instead of 0? + auto e0norm = e0.norm(); + assert(e0norm > 0); // check norm is not 0 + e0 = e0 / e0norm; + + Vector3 n = e0.cross(e1); + e1 = n.cross(e0); + auto e1norm = e1.norm(); + assert(e1norm > 0); // check norm is not 0 + e1 = e1 / e1norm; + + + Dm = (B.transpose() * V).eval(); + } + + + return amips(Dm); +} +} // namespace wmtk::function::utils diff --git a/tests/function/CMakeLists.txt b/tests/function/CMakeLists.txt index d07dc5c0e9..80a4dec409 100644 --- a/tests/function/CMakeLists.txt +++ b/tests/function/CMakeLists.txt @@ -2,6 +2,7 @@ set(TEST_SOURCES # test_2d_energy.cpp + test_amips.cpp ) target_sources(wmtk_tests PRIVATE ${TEST_SOURCES}) diff --git a/tests/function/test_2d_energy.cpp b/tests/function/test_2d_energy.cpp index 60a560f86a..cc20c26926 100644 --- a/tests/function/test_2d_energy.cpp +++ b/tests/function/test_2d_energy.cpp @@ -1,4 +1,3 @@ -#pragma once #include #include #include @@ -50,6 +49,7 @@ TEST_CASE("amips2d") REQUIRE(amips2d.get_value(e1) == 2.0); } + /* SECTION("random_triangle") { for (int i = 0; i < 1; i++) { @@ -67,6 +67,7 @@ TEST_CASE("amips2d") } } } + */ } TEST_CASE("amips3d") @@ -90,6 +91,7 @@ TEST_CASE("amips3d") REQUIRE(amips3d.get_value(e1) == 2.0); } + /* SECTION("random_triangle") { for (int i = 0; i < 50; i++) { @@ -111,4 +113,5 @@ TEST_CASE("amips3d") REQUIRE((amips3d.get_value(e1) > 2. || amips3d.get_value(e1) == 2.)); } } -} \ No newline at end of file + */ +} diff --git a/tests/function/test_amips.cpp b/tests/function/test_amips.cpp new file mode 100644 index 0000000000..dd23789d5d --- /dev/null +++ b/tests/function/test_amips.cpp @@ -0,0 +1,30 @@ +#include +#include +#include + +TEST_CASE("amips2d") +{ + SECTION("equilateral_triangle") + { + Eigen::Vector2d uv0 = {0,0}; + Eigen::Vector2d uv1 = {1,0}; + Eigen::Vector2d uv2 = {0.5,std::sqrt(3)/2}; + + + std::cout << uv0 << std::endl; + + CHECK(wmtk::function::utils::amips(uv0,uv1,uv2) == 2.0); + } +} + +TEST_CASE("amips3d") +{ + SECTION("equilateral_triangle") + { + Eigen::Vector2d uv0 = {0,0}; + Eigen::Vector2d uv1 = {1,0}; + Eigen::Vector2d uv2 = {0.5,std::sqrt(3)/2}; + + CHECK(wmtk::function::utils::amips(uv0,uv1,uv2) == 2.0); + } +} diff --git a/tests/test_execution.cpp b/tests/test_execution.cpp index 79fd7997fc..84d94a8877 100644 --- a/tests/test_execution.cpp +++ b/tests/test_execution.cpp @@ -74,14 +74,14 @@ TEST_CASE("scheduler_success_report", "[scheduler][operations][2D]") operations::OperationSettings op_settings; SECTION("single_triangle_with_boundary") { - m = single_triangle_with_position(); + m = single_equilateral_triangle(); expected_op_success = 1; expected_op_fail = 2; op_settings.smooth_boundary = true; } SECTION("single_triangle_without_boundary") { - m = single_triangle_with_position(); + m = single_equilateral_triangle(); expected_op_success = 0; expected_op_fail = 3; op_settings.smooth_boundary = false; @@ -96,7 +96,7 @@ TEST_CASE("scheduler_success_report", "[scheduler][operations][2D]") const long expected_op_sum = expected_op_success + expected_op_fail; op_settings.position = m.get_attribute_handle("position", PrimitiveType::Vertex); - op_settings.base_settings.initialize_invariants(m); + op_settings.initialize_invariants(m); Scheduler scheduler(m); scheduler.add_operation_type("vertex_smooth", op_settings); @@ -109,11 +109,11 @@ TEST_CASE("scheduler_success_report", "[scheduler][operations][2D]") } SECTION("multiple_runs") { - DEBUG_TriMesh m = single_triangle_with_position(); + DEBUG_TriMesh m = single_equilateral_triangle(); operations::OperationSettings op_settings; op_settings.smooth_boundary = true; op_settings.position = m.get_attribute_handle("position", PrimitiveType::Vertex); - op_settings.base_settings.initialize_invariants(m); + op_settings.initialize_invariants(m); Scheduler scheduler(m); scheduler.add_operation_type("vertex_smooth", op_settings); @@ -125,4 +125,4 @@ TEST_CASE("scheduler_success_report", "[scheduler][operations][2D]") CHECK(scheduler.number_of_failed_operations() == 2); } } -} \ No newline at end of file +} diff --git a/tests/tools/TriMesh_examples.cpp b/tests/tools/TriMesh_examples.cpp index 8e1ad6efb4..9c657cff2b 100644 --- a/tests/tools/TriMesh_examples.cpp +++ b/tests/tools/TriMesh_examples.cpp @@ -40,8 +40,8 @@ TriMesh single_2d_triangle_with_random_position(size_t seed) V.resize(3, 2); V.setZero(); - std::mt19937 generator(123); - std::uniform_int_distribution distribution(0., 1.); + std::mt19937 generator(seed); + std::uniform_real_distribution distribution(0., 1.); auto xt = V.row(0); auto yt = V.row(1); @@ -50,7 +50,7 @@ TriMesh single_2d_triangle_with_random_position(size_t seed) auto x = xt.transpose(); auto y = yt.transpose(); auto z = zt.transpose(); - auto gen = [&](int row, int col) { return distribution(generator) }; + auto gen = [&](int , int ) { return distribution(generator); }; do { V = Eigen::MatrixXd::NullaryExpr(V.rows(), V.cols(), gen); } while (triangle_2d_area(x, y, z) <= 0); diff --git a/tests/tools/TriMesh_examples.hpp b/tests/tools/TriMesh_examples.hpp index 70d5517fe2..629bc0c730 100644 --- a/tests/tools/TriMesh_examples.hpp +++ b/tests/tools/TriMesh_examples.hpp @@ -14,15 +14,15 @@ namespace wmtk::tests { // TriMesh single_triangle(); -TriMesh single_equilateral_triangle(int dimension); +TriMesh single_equilateral_triangle(int dimension = 3); // a single triangle with position -TriMesh single_2d_triangle_with_random_positions(long seed); +TriMesh single_2d_triangle_with_random_positions(long seed = 123); // 3--1--- 0 // | / \ . // 2 f1 /2 1 // | 0/ f0 \ . -// | / \ +// | / \ . // 1 ----0---- 2 // TriMesh one_ear(); // an alias for quad From e230791169fbe12ce540fc1bf3f337497310fb65 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 14 Oct 2023 13:47:38 -0400 Subject: [PATCH 092/134] refactoring position map and cleaning up headers --- src/wmtk/function/AMIPS.cpp | 1 + src/wmtk/function/AMIPS.hpp | 3 - src/wmtk/function/AMIPS2D.cpp | 1 - src/wmtk/function/AutodiffFunction.hpp | 6 +- src/wmtk/function/CMakeLists.txt | 4 +- src/wmtk/function/PositionMapAMIPS2D.cpp | 51 +++++++------- src/wmtk/function/PositionMapAMIPS2D.hpp | 7 +- src/wmtk/function/utils/CMakeLists.txt | 3 +- src/wmtk/function/utils/DofsToPosition.hpp | 69 ------------------- .../function/utils/PositionMapEvaluator.cpp | 49 +++++++++++++ .../function/utils/PositionMapEvaluator.hpp | 43 ++++++++++++ src/wmtk/image/Sampling.hpp | 3 +- 12 files changed, 131 insertions(+), 109 deletions(-) delete mode 100644 src/wmtk/function/utils/DofsToPosition.hpp create mode 100644 src/wmtk/function/utils/PositionMapEvaluator.cpp create mode 100644 src/wmtk/function/utils/PositionMapEvaluator.hpp diff --git a/src/wmtk/function/AMIPS.cpp b/src/wmtk/function/AMIPS.cpp index 5eb8161f56..acbc2ec9f2 100644 --- a/src/wmtk/function/AMIPS.cpp +++ b/src/wmtk/function/AMIPS.cpp @@ -1,4 +1,5 @@ #include "AMIPS.hpp" +#include namespace wmtk::function { AMIPS::AMIPS(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) diff --git a/src/wmtk/function/AMIPS.hpp b/src/wmtk/function/AMIPS.hpp index 398ca1e1d7..e2fa9692c3 100644 --- a/src/wmtk/function/AMIPS.hpp +++ b/src/wmtk/function/AMIPS.hpp @@ -1,8 +1,5 @@ #pragma once -#include #include "AutodiffFunction.hpp" -#include "utils/AutoDiffUtils.hpp" -#include "utils/DofsToPosition.hpp" namespace wmtk::function { class AMIPS : public AutodiffFunction { diff --git a/src/wmtk/function/AMIPS2D.cpp b/src/wmtk/function/AMIPS2D.cpp index c286e5ee27..208e5ac24e 100644 --- a/src/wmtk/function/AMIPS2D.cpp +++ b/src/wmtk/function/AMIPS2D.cpp @@ -1,6 +1,5 @@ #include "AMIPS2D.hpp" #include -#include #include #include #include diff --git a/src/wmtk/function/AutodiffFunction.hpp b/src/wmtk/function/AutodiffFunction.hpp index 4421023de9..6ac22f501e 100644 --- a/src/wmtk/function/AutodiffFunction.hpp +++ b/src/wmtk/function/AutodiffFunction.hpp @@ -6,6 +6,10 @@ namespace wmtk::function { class AutodiffFunction : public DifferentiableFunction { public: + using DScalar = DScalar2, Eigen::Matrix>; + using Scalar = typename DScalar::Scalar; + static_assert( + std::is_same_v); // MTAO: i'm leaving scalar here but is it ever not double? AutodiffFunction(const Mesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); virtual ~AutodiffFunction(); @@ -16,8 +20,6 @@ class AutodiffFunction : public DifferentiableFunction Eigen::MatrixXd get_hessian(const Tuple& tuple) const override; protected: - using DScalar = DScalar2, Eigen::Matrix>; - using Scalar = typename DScalar::Scalar; virtual DScalar get_value_autodiff(const Tuple& tuple) const = 0; }; } // namespace wmtk::function diff --git a/src/wmtk/function/CMakeLists.txt b/src/wmtk/function/CMakeLists.txt index 35f1de559a..657d16074a 100644 --- a/src/wmtk/function/CMakeLists.txt +++ b/src/wmtk/function/CMakeLists.txt @@ -10,8 +10,8 @@ set(SRC_FILES AMIPS2D.cpp AMIPS3D.hpp AMIPS3D.cpp - #PositionMapAMIPS3D.hpp - #PositionMapAMIPS3D.cpp + PositionMapAMIPS2D.hpp + PositionMapAMIPS2D.cpp TriMeshValenceFunction.hpp TriMeshValenceFunction.cpp diff --git a/src/wmtk/function/PositionMapAMIPS2D.cpp b/src/wmtk/function/PositionMapAMIPS2D.cpp index 40078df1d4..789aa1d2c4 100644 --- a/src/wmtk/function/PositionMapAMIPS2D.cpp +++ b/src/wmtk/function/PositionMapAMIPS2D.cpp @@ -1,17 +1,19 @@ -#pragma once -#include #include "PositionMapAMIPS2D.hpp" +#include +#include +#include +#include namespace wmtk::function { -PositionMapsAMIPS2D::PositionMapsAMIPS2D( +PositionMapAMIPS2D::PositionMapAMIPS2D( const TriMesh& mesh, const MeshAttributeHandle& vertex_uv_handle, const image::Image& image) : AMIPS(mesh, vertex_uv_handle) - , m_dofs_to_pos(image) + , m_pos_evaluator(image) {} -PositionMapsAMIPS2D::PositionMapsAMIPS2D( +PositionMapAMIPS2D::PositionMapAMIPS2D( const TriMesh& mesh, const MeshAttributeHandle& vertex_uv_handle, const wmtk::image::SamplingAnalyticFunction::FunctionType type, @@ -19,39 +21,32 @@ PositionMapsAMIPS2D::PositionMapsAMIPS2D( const double b, const double c) : AMIPS(mesh, vertex_uv_handle) - , m_dofs_to_pos(type, a, b, c) + , m_pos_evaluator(type, a, b, c) {} -DScalar PositionMapsAMIPS2D::get_value_autodiff(const Tuple& tuple) const +auto PositionMapAMIPS2D::get_value_autodiff(const Tuple& tuple) const -> DScalar { + // get_autodiff_value sets the autodiff size if necessary // get the uv coordinates of the triangle - ConstAccessor pos = m_mesh.create_const_accessor(m_vertex_attribute_handle); + ConstAccessor pos = mesh().create_const_accessor(get_vertex_attribute_handle()); - // TODO curve mesh uv -> t conversion happens here - Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); - int size = 2; - Eigen::Matrix dofT = get_T_vector>(uv0, size); + auto tuple_value = pos.const_vector_attribute(tuple); - Eigen::Vector2d uv1 = - pos.const_vector_attribute(m_mesh.switch_edge(m_mesh.switch_vertex(tuple))); - Eigen::Vector2d uv2 = - pos.const_vector_attribute(m_mesh.switch_vertex(m_mesh.switch_edge(tuple))); + Vector2 uv0; + uv0 = utils::as_DScalar(tuple_value); - // return the energy - return function_eval(dofT, uv1, uv2); -} + constexpr static PrimitiveType PV = PrimitiveType::Vertex; + constexpr static PrimitiveType PE = PrimitiveType::Edge; -template -auto PositionMapsAMIPS2D::function_eval( - const Eigen::Matrix& uv0, - const Eigen::Vector2d& uv1, - const Eigen::Vector2d& uv2) const -> T -{ - Eigen::Matrix pos0 = m_dofs_to_pos.dof_to_pos(uv0); - Eigen::Matrix pos1 = m_dofs_to_pos.dof_to_pos(uv1); - Eigen::Matrix pos2 = m_dofs_to_pos.dof_to_pos(uv2); + Eigen::Vector2d uv1 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PE, PV})); + Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PV, PE})); + + Vector3 pos0 = m_pos_evaluator.uv_to_pos(uv0); + Eigen::Vector3d pos1 = m_pos_evaluator.uv_to_pos(uv1); + Eigen::Vector3d pos2 = m_pos_evaluator.uv_to_pos(uv2); return utils::amips(pos0, pos1, pos2); } + } // namespace wmtk::function diff --git a/src/wmtk/function/PositionMapAMIPS2D.hpp b/src/wmtk/function/PositionMapAMIPS2D.hpp index cb21be913b..a9c4ca2ea0 100644 --- a/src/wmtk/function/PositionMapAMIPS2D.hpp +++ b/src/wmtk/function/PositionMapAMIPS2D.hpp @@ -1,11 +1,13 @@ #pragma once +#include #include "AMIPS.hpp" +namespace wmtk::function { /** * @brief 2D AMIPS uses uv and position map to get the 3d cooridnates then evaluate * */ -class PositionMapAMIPS2D : public AMIPS2D +class PositionMapAMIPS2D : public AMIPS { public: PositionMapAMIPS2D( @@ -25,5 +27,6 @@ class PositionMapAMIPS2D : public AMIPS2D protected: protected: - DofsToPosition m_dofs_to_pos; + utils::PositionMapEvaluator m_pos_evaluator; }; +} // namespace wmtk::function diff --git a/src/wmtk/function/utils/CMakeLists.txt b/src/wmtk/function/utils/CMakeLists.txt index b033a711ce..66abd05be8 100644 --- a/src/wmtk/function/utils/CMakeLists.txt +++ b/src/wmtk/function/utils/CMakeLists.txt @@ -2,7 +2,8 @@ set(SRC_FILES AutoDiffUtils.hpp autodiff.h autodiff.cpp - DofsToPosition.hpp + PositionMapEvaluator.hpp + PositionMapEvaluator.cpp AutoDiffRAII.hpp AutoDiffRAII.cpp amips.hpp diff --git a/src/wmtk/function/utils/DofsToPosition.hpp b/src/wmtk/function/utils/DofsToPosition.hpp deleted file mode 100644 index 510284aaa1..0000000000 --- a/src/wmtk/function/utils/DofsToPosition.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "AutoDiffUtils.hpp" -#include "autodiff.h" - -namespace wmtk { -namespace function { -class DofsToPosition -{ // size = 2: uv position, size =1, t of boundary curve - using DofVectorX = Eigen::Matrix; - -protected: - std::unique_ptr m_sampling; - -public: - DofsToPosition() = default; - ~DofsToPosition() = default; - DofsToPosition& operator=(const DofsToPosition&) = default; // copy assignment operator - DofsToPosition& operator=(DofsToPosition&&) = default; // move assignment operator - - /** - * @brief Construct a new Dofs To Position object using a displacement map (requires a sampler) - * - * @param image - */ - DofsToPosition(const image::Image& image) - { - m_sampling = std::make_unique(image); - } - - DofsToPosition( - const wmtk::image::SamplingAnalyticFunction::FunctionType type, - const double a, - const double b, - const double c) - { - m_sampling = std::make_unique(type, a, b, c); - } - - - template - Eigen::Matrix dof_to_pos(const Eigen::Matrix& dofT) const - { - Eigen::Matrix pos; - int size = dofT.rows(); - - if (size == 2) { - // TODO retrive position using displacement map for the frist two coordinates too - // for now just return itself - pos << dofT(0), dofT(1), m_sampling->sample(dofT(0), dofT(1)); - - } else - //(dofx.size() == 1) - { - // curve parameterization - // TODO covert to uv first and sample using the uv - - pos << dofT(0), static_cast(0.0), static_cast(0.0); - } - return pos; - } -}; - -} // namespace function -} // namespace wmtk diff --git a/src/wmtk/function/utils/PositionMapEvaluator.cpp b/src/wmtk/function/utils/PositionMapEvaluator.cpp new file mode 100644 index 0000000000..2705f91c7a --- /dev/null +++ b/src/wmtk/function/utils/PositionMapEvaluator.cpp @@ -0,0 +1,49 @@ +#include "PositionMapEvaluator.hpp" +#include +#include +#include +#include + + +namespace wmtk::function::utils { + +PositionMapEvaluator::PositionMapEvaluator() = default; +PositionMapEvaluator::~PositionMapEvaluator() = default; +PositionMapEvaluator::PositionMapEvaluator(PositionMapEvaluator&&) = + default; // move assignment operator +PositionMapEvaluator& PositionMapEvaluator::operator=(PositionMapEvaluator&&) = + default; // move assignment operator + +/** + * @brief Construct a new Dofs To Position object using a displacement map (requires a + * sampler) + * + * @param image + */ +PositionMapEvaluator::PositionMapEvaluator(const image::Image& image) +{ + m_sampling = std::make_unique(image); +} + +PositionMapEvaluator::PositionMapEvaluator( + const wmtk::image::SamplingAnalyticFunction::FunctionType type, + const double a, + const double b, + const double c) +{ + m_sampling = std::make_unique(type, a, b, c); +} +template +Vector3 PositionMapEvaluator::uv_to_pos(const Vector2& uv) const +{ + return Vector3(uv.x(), uv.y(), m_sampling->sample(uv.x(), uv.y())); +} + +template <> +Vector3 PositionMapEvaluator::uv_to_pos(const Vector2& uv) const; + + +template <> +auto PositionMapEvaluator::uv_to_pos(const Vector2& uv) + const -> Vector3; +} // namespace wmtk::function::utils diff --git a/src/wmtk/function/utils/PositionMapEvaluator.hpp b/src/wmtk/function/utils/PositionMapEvaluator.hpp new file mode 100644 index 0000000000..1a3ff29c5b --- /dev/null +++ b/src/wmtk/function/utils/PositionMapEvaluator.hpp @@ -0,0 +1,43 @@ +#pragma once +#include +#include +#include + +namespace wmtk::image { +class Image; +class SamplingAnalyticFunction; +} // namespace wmtk::image + +namespace wmtk::function::utils { +class PositionMapEvaluator +{ +protected: + std::unique_ptr m_sampling; + +public: + PositionMapEvaluator(); + ~PositionMapEvaluator(); + PositionMapEvaluator(PositionMapEvaluator&&); // move assignment operator + PositionMapEvaluator& operator=(PositionMapEvaluator&&); // move assignment operator + + /** + * @brief Construct a new Dofs To Position object using a displacement map (requires a + * sampler) + * + * @param image + */ + PositionMapEvaluator(const image::Image& image); + + PositionMapEvaluator( + const wmtk::image::SamplingAnalyticFunction::FunctionType type, + const double a, + const double b, + const double c); + + + // Dont forget to update this if we change autodiff tyeps (add declarations in the cpp) + template + Vector3 uv_to_pos(const Vector2& uv) const; +}; + +} // namespace wmtk::function::utils diff --git a/src/wmtk/image/Sampling.hpp b/src/wmtk/image/Sampling.hpp index 65b7871a1b..4d24943cc4 100644 --- a/src/wmtk/image/Sampling.hpp +++ b/src/wmtk/image/Sampling.hpp @@ -18,10 +18,11 @@ class Sampling }; +enum SamplingAnalyticFunction_FunctionType { Linear, Quadratic }; class SamplingAnalyticFunction : public Sampling { public: - enum FunctionType { Linear, Quadratic }; + using FunctionType = SamplingAnalyticFunction_FunctionType; protected: FunctionType m_type = FunctionType::Linear; From eb512faca9231e9e381547be82a65adf1c1d644a Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 14 Oct 2023 14:06:28 -0400 Subject: [PATCH 093/134] making function unit tests build --- src/wmtk/attribute/AttributeHandle.hpp | 5 ++- src/wmtk/function/AMIPS2D.cpp | 4 +- src/wmtk/function/AMIPS3D.cpp | 5 +-- .../function/utils/PositionMapEvaluator.cpp | 2 + .../function/utils/PositionMapEvaluator.hpp | 5 ++- tests/function/CMakeLists.txt | 3 +- tests/function/test_2d_energy.cpp | 37 ++++++++----------- tests/tools/TriMesh_examples.cpp | 8 +++- tests/tools/TriMesh_examples.hpp | 2 +- 9 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/wmtk/attribute/AttributeHandle.hpp b/src/wmtk/attribute/AttributeHandle.hpp index 0c91ca501a..1d6bfebe87 100644 --- a/src/wmtk/attribute/AttributeHandle.hpp +++ b/src/wmtk/attribute/AttributeHandle.hpp @@ -2,7 +2,7 @@ #include #include "wmtk/Primitive.hpp" namespace wmtk { - class Mesh; +class Mesh; namespace attribute { template class MeshAttributes; @@ -36,6 +36,8 @@ class AttributeHandle bool operator==(const AttributeHandle& other) const { return index == other.index; } + + bool is_valid() const { return index != -1; } }; template @@ -71,6 +73,7 @@ class MeshAttributeHandle return std::is_same_v && m_base_handle == o.m_base_handle && m_primitive_type == o.m_primitive_type; } + bool is_valid() const { return m_base_handle.is_valid(); } }; } // namespace attribute using AttributeHandle = attribute::AttributeHandle; diff --git a/src/wmtk/function/AMIPS2D.cpp b/src/wmtk/function/AMIPS2D.cpp index 208e5ac24e..2345ec71ad 100644 --- a/src/wmtk/function/AMIPS2D.cpp +++ b/src/wmtk/function/AMIPS2D.cpp @@ -9,9 +9,9 @@ namespace wmtk::function { AMIPS2D::AMIPS2D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) : AMIPS(mesh, vertex_attribute_handle) { - assert(get_vertex_attribute_handle()); + assert(get_vertex_attribute_handle().is_valid()); // check the dimension of the position - assert(embedding_dimension() == 2); + assert(embedded_dimension() == 2); } diff --git a/src/wmtk/function/AMIPS3D.cpp b/src/wmtk/function/AMIPS3D.cpp index 850039767a..6f77e2844e 100644 --- a/src/wmtk/function/AMIPS3D.cpp +++ b/src/wmtk/function/AMIPS3D.cpp @@ -9,9 +9,9 @@ namespace wmtk::function { AMIPS3D::AMIPS3D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) : AMIPS(mesh, vertex_attribute_handle) { - assert(get_vertex_attribute_handle()); + assert(get_vertex_attribute_handle().is_valid()); // check the dimension of the position - assert(embedding_dimension() == 3); + assert(embedded_dimension() == 3); } @@ -45,5 +45,4 @@ T AMIPS3D::function_eval(const Tuple& tuple) const } - } // namespace wmtk::function diff --git a/src/wmtk/function/utils/PositionMapEvaluator.cpp b/src/wmtk/function/utils/PositionMapEvaluator.cpp index 2705f91c7a..1ad636ad7f 100644 --- a/src/wmtk/function/utils/PositionMapEvaluator.cpp +++ b/src/wmtk/function/utils/PositionMapEvaluator.cpp @@ -33,6 +33,7 @@ PositionMapEvaluator::PositionMapEvaluator( { m_sampling = std::make_unique(type, a, b, c); } +/* template Vector3 PositionMapEvaluator::uv_to_pos(const Vector2& uv) const { @@ -46,4 +47,5 @@ Vector3 PositionMapEvaluator::uv_to_pos(const Vector2& uv) const template <> auto PositionMapEvaluator::uv_to_pos(const Vector2& uv) const -> Vector3; + */ } // namespace wmtk::function::utils diff --git a/src/wmtk/function/utils/PositionMapEvaluator.hpp b/src/wmtk/function/utils/PositionMapEvaluator.hpp index 1a3ff29c5b..25846a6051 100644 --- a/src/wmtk/function/utils/PositionMapEvaluator.hpp +++ b/src/wmtk/function/utils/PositionMapEvaluator.hpp @@ -37,7 +37,10 @@ class PositionMapEvaluator // Dont forget to update this if we change autodiff tyeps (add declarations in the cpp) template - Vector3 uv_to_pos(const Vector2& uv) const; + Vector3 uv_to_pos(const Vector2& uv) const +{ + return Vector3(uv.x(), uv.y(), m_sampling->sample(uv.x(), uv.y())); +} }; } // namespace wmtk::function::utils diff --git a/tests/function/CMakeLists.txt b/tests/function/CMakeLists.txt index 80a4dec409..216eb0e86a 100644 --- a/tests/function/CMakeLists.txt +++ b/tests/function/CMakeLists.txt @@ -1,7 +1,6 @@ # Sources set(TEST_SOURCES - # test_2d_energy.cpp - + test_2d_energy.cpp test_amips.cpp ) target_sources(wmtk_tests PRIVATE ${TEST_SOURCES}) diff --git a/tests/function/test_2d_energy.cpp b/tests/function/test_2d_energy.cpp index cc20c26926..3eb223c986 100644 --- a/tests/function/test_2d_energy.cpp +++ b/tests/function/test_2d_energy.cpp @@ -1,9 +1,9 @@ #include #include #include -#include +#include +#include #include -#include #include "../tools/DEBUG_TriMesh.hpp" #include "../tools/TriMesh_examples.hpp" using namespace wmtk; @@ -34,7 +34,7 @@ TEST_CASE("energy_valence") REQUIRE(valence_energy.get_value(e4) == 2); } -TEST_CASE("amips2d") +TEST_CASE("amips2d_values") { SECTION("equilateral_triangle") { @@ -45,32 +45,27 @@ TEST_CASE("amips2d") auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); const TriMesh tri_mesh = static_cast(example_mesh); - AMIPS_2D amips2d(tri_mesh, uv_handle); + AMIPS2D amips2d(tri_mesh, uv_handle); - REQUIRE(amips2d.get_value(e1) == 2.0); + CHECK(amips2d.get_value(e1) == 2.0); } - /* SECTION("random_triangle") { - for (int i = 0; i < 1; i++) { - const DEBUG_TriMesh example_mesh = single_2d_triangle_with_position(); + for (int i = 0; i < 50; i++) { + const DEBUG_TriMesh example_mesh = single_2d_triangle_with_random_positions(123); auto uv_handle = example_mesh.get_attribute_handle("position", PrimitiveType::Vertex); auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); const TriMesh tri_mesh = static_cast(example_mesh); - AMIPS_2D amips2d(tri_mesh, uv_handle); - if (amips2d.get_value(e1) < 2.) { - wmtk::logger().critical("wrong value"); - REQUIRE((amips2d.get_value(e1) > 2. || amips2d.get_value(e1) == 2.)); - } + AMIPS2D amips2d(tri_mesh, uv_handle); + CHECK(amips2d.get_value(e1) >= 2.); } } - */ } -TEST_CASE("amips3d") +TEST_CASE("PositionMapAMIPS_values") { SECTION("equilateral_triangle") { @@ -81,7 +76,7 @@ TEST_CASE("amips3d") auto uv_handle = example_mesh.get_attribute_handle("position", PrimitiveType::Vertex); - AMIPS_3DEmbedded amips3d( + PositionMapAMIPS2D amips3d( tri_mesh, uv_handle, wmtk::image::SamplingAnalyticFunction::FunctionType::Linear, @@ -89,20 +84,19 @@ TEST_CASE("amips3d") 0.0, 1.0); - REQUIRE(amips3d.get_value(e1) == 2.0); + CHECK(amips3d.get_value(e1) == 2.0); } - /* SECTION("random_triangle") { for (int i = 0; i < 50; i++) { - const DEBUG_TriMesh example_mesh = single_triangle_with_position(2); + const DEBUG_TriMesh example_mesh = single_2d_triangle_with_random_positions(123); auto uv_handle = example_mesh.get_attribute_handle("position", PrimitiveType::Vertex); auto e1 = example_mesh.edge_tuple_between_v1_v2(0, 1, 0); const TriMesh tri_mesh = static_cast(example_mesh); - AMIPS_3DEmbedded amips3d( + PositionMapAMIPS2D amips3d( tri_mesh, uv_handle, wmtk::image::SamplingAnalyticFunction::FunctionType::Linear, @@ -110,8 +104,7 @@ TEST_CASE("amips3d") 0.0, 1.0); - REQUIRE((amips3d.get_value(e1) > 2. || amips3d.get_value(e1) == 2.)); + CHECK(amips3d.get_value(e1) >= 2.0); } } - */ } diff --git a/tests/tools/TriMesh_examples.cpp b/tests/tools/TriMesh_examples.cpp index 9c657cff2b..654ecf77ce 100644 --- a/tests/tools/TriMesh_examples.cpp +++ b/tests/tools/TriMesh_examples.cpp @@ -27,13 +27,17 @@ TriMesh single_equilateral_triangle(int dimension) V.row(1) << 1., 0, 0; V.row(2) << 0.5, sqrt(3) / 2., 0; + auto x = V.row(0); + auto y = V.row(1); + auto z = V.row(2); + assert(triangle_2d_area(x, y, z) <= 0); V.conservativeResize(3, dimension); mesh_utils::set_matrix_attribute(V, "position", PrimitiveType::Vertex, m); return m; } -TriMesh single_2d_triangle_with_random_position(size_t seed) +TriMesh single_2d_triangle_with_random_positions(size_t seed) { TriMesh m = single_triangle(); Eigen::MatrixXd V; @@ -50,7 +54,7 @@ TriMesh single_2d_triangle_with_random_position(size_t seed) auto x = xt.transpose(); auto y = yt.transpose(); auto z = zt.transpose(); - auto gen = [&](int , int ) { return distribution(generator); }; + auto gen = [&](int, int) { return distribution(generator); }; do { V = Eigen::MatrixXd::NullaryExpr(V.rows(), V.cols(), gen); } while (triangle_2d_area(x, y, z) <= 0); diff --git a/tests/tools/TriMesh_examples.hpp b/tests/tools/TriMesh_examples.hpp index 629bc0c730..249b8f8719 100644 --- a/tests/tools/TriMesh_examples.hpp +++ b/tests/tools/TriMesh_examples.hpp @@ -17,7 +17,7 @@ TriMesh single_triangle(); TriMesh single_equilateral_triangle(int dimension = 3); // a single triangle with position -TriMesh single_2d_triangle_with_random_positions(long seed = 123); +TriMesh single_2d_triangle_with_random_positions(size_t seed = 123); // 3--1--- 0 // | / \ . // 2 f1 /2 1 From 1fe84cd5206faeb34f409bae37c3af893133a297 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 14 Oct 2023 14:53:10 -0400 Subject: [PATCH 094/134] changing ampis functions to match positive area FV structures --- src/wmtk/function/AMIPS2D.cpp | 5 +- src/wmtk/function/AMIPS3D.cpp | 5 +- src/wmtk/function/PositionMapAMIPS2D.cpp | 5 +- src/wmtk/function/utils/amips.cpp | 9 +++ .../invariants/TriangleInversionInvariant.cpp | 4 +- src/wmtk/utils/CMakeLists.txt | 4 +- src/wmtk/utils/triangle_areas.cpp | 6 ++ src/wmtk/utils/triangle_areas.hpp | 62 ++++++++++++++++++ src/wmtk/utils/triangle_helper_functions.cpp | 15 ----- src/wmtk/utils/triangle_helper_functions.hpp | 65 ------------------- tests/tools/TriMesh_examples.cpp | 33 ++++++---- 11 files changed, 106 insertions(+), 107 deletions(-) create mode 100644 src/wmtk/utils/triangle_areas.cpp create mode 100644 src/wmtk/utils/triangle_areas.hpp delete mode 100644 src/wmtk/utils/triangle_helper_functions.cpp delete mode 100644 src/wmtk/utils/triangle_helper_functions.hpp diff --git a/src/wmtk/function/AMIPS2D.cpp b/src/wmtk/function/AMIPS2D.cpp index 2345ec71ad..1aaa6e4a66 100644 --- a/src/wmtk/function/AMIPS2D.cpp +++ b/src/wmtk/function/AMIPS2D.cpp @@ -2,7 +2,6 @@ #include #include #include -#include namespace wmtk::function { @@ -37,8 +36,8 @@ T AMIPS2D::function_eval(const Tuple& tuple) const constexpr static PrimitiveType PV = PrimitiveType::Vertex; constexpr static PrimitiveType PE = PrimitiveType::Edge; - Eigen::Vector2d uv1 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PE, PV})); - Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PV, PE})); + Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PE, PV})); + Eigen::Vector2d uv1 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PV, PE})); // return the energy return utils::amips(uv0, uv1, uv2); diff --git a/src/wmtk/function/AMIPS3D.cpp b/src/wmtk/function/AMIPS3D.cpp index 6f77e2844e..1eef7151d5 100644 --- a/src/wmtk/function/AMIPS3D.cpp +++ b/src/wmtk/function/AMIPS3D.cpp @@ -3,7 +3,6 @@ #include #include #include -#include namespace wmtk::function { AMIPS3D::AMIPS3D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) @@ -37,8 +36,8 @@ T AMIPS3D::function_eval(const Tuple& tuple) const constexpr static PrimitiveType PV = PrimitiveType::Vertex; constexpr static PrimitiveType PE = PrimitiveType::Edge; - Eigen::Vector3d pos1 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PE, PV})); - Eigen::Vector3d pos2 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PV, PE})); + Eigen::Vector3d pos2 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PE, PV})); + Eigen::Vector3d pos1 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PV, PE})); // return the energy return utils::amips(pos0, pos1, pos2); diff --git a/src/wmtk/function/PositionMapAMIPS2D.cpp b/src/wmtk/function/PositionMapAMIPS2D.cpp index 789aa1d2c4..49a8ea8f50 100644 --- a/src/wmtk/function/PositionMapAMIPS2D.cpp +++ b/src/wmtk/function/PositionMapAMIPS2D.cpp @@ -2,7 +2,6 @@ #include #include #include -#include namespace wmtk::function { PositionMapAMIPS2D::PositionMapAMIPS2D( @@ -39,8 +38,8 @@ auto PositionMapAMIPS2D::get_value_autodiff(const Tuple& tuple) const -> DScalar constexpr static PrimitiveType PV = PrimitiveType::Vertex; constexpr static PrimitiveType PE = PrimitiveType::Edge; - Eigen::Vector2d uv1 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PE, PV})); - Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PV, PE})); + Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PE, PV})); + Eigen::Vector2d uv1 = pos.const_vector_attribute(mesh().switch_tuples(tuple, {PV, PE})); Vector3 pos0 = m_pos_evaluator.uv_to_pos(uv0); Eigen::Vector3d pos1 = m_pos_evaluator.uv_to_pos(uv1); diff --git a/src/wmtk/function/utils/amips.cpp b/src/wmtk/function/utils/amips.cpp index c69d817a61..f3c3501eb3 100644 --- a/src/wmtk/function/utils/amips.cpp +++ b/src/wmtk/function/utils/amips.cpp @@ -1,6 +1,7 @@ #include "amips.hpp" #include #include +#include namespace wmtk::function::utils { namespace detail { namespace { @@ -14,6 +15,14 @@ auto make_amips_target_triangle() // }}; auto map = Eigen::Matrix::ConstMapType(m_target_triangle[0].data()); + + +#if !defined(NDEBUG) + auto x = map.col(0); + auto y = map.col(1); + auto z = map.col(2); + assert(wmtk::utils::triangle_signed_2d_area(x,y,z) > 0); +#endif return map; } diff --git a/src/wmtk/invariants/TriangleInversionInvariant.cpp b/src/wmtk/invariants/TriangleInversionInvariant.cpp index e3e9181627..a252df69ae 100644 --- a/src/wmtk/invariants/TriangleInversionInvariant.cpp +++ b/src/wmtk/invariants/TriangleInversionInvariant.cpp @@ -1,7 +1,7 @@ #include "TriangleInversionInvariant.hpp" #include -#include +#include namespace wmtk { TriangleInversionInvariant::TriangleInversionInvariant( @@ -21,7 +21,7 @@ bool TriangleInversionInvariant::after(PrimitiveType type, const std::vector(p0, p1, p2) < 0) return false; + if (wmtk::utils::triangle_signed_2d_area(p0, p1, p2) < 0) return false; } return true; } diff --git a/src/wmtk/utils/CMakeLists.txt b/src/wmtk/utils/CMakeLists.txt index 7887f93fb6..664d7a1915 100644 --- a/src/wmtk/utils/CMakeLists.txt +++ b/src/wmtk/utils/CMakeLists.txt @@ -15,8 +15,8 @@ set(SRC_FILES mesh_utils.cpp TupleInspector.hpp - triangle_helper_functions.hpp - triangle_helper_functions.cpp + triangle_areas.hpp + triangle_areas.cpp FunctionInterface.hpp Optimization.hpp Optimization.cpp diff --git a/src/wmtk/utils/triangle_areas.cpp b/src/wmtk/utils/triangle_areas.cpp new file mode 100644 index 0000000000..491135a446 --- /dev/null +++ b/src/wmtk/utils/triangle_areas.cpp @@ -0,0 +1,6 @@ +#include "triangle_areas.hpp" +#include +#include + +namespace wmtk::utils { +} diff --git a/src/wmtk/utils/triangle_areas.hpp b/src/wmtk/utils/triangle_areas.hpp new file mode 100644 index 0000000000..f9740dc472 --- /dev/null +++ b/src/wmtk/utils/triangle_areas.hpp @@ -0,0 +1,62 @@ +#pragma once +#include +#include +namespace wmtk { + class Tuple; + class TriMesh; + namespace attribute { + template + class MeshAttributeHandle; + } +} +namespace wmtk::utils { + +// template get 3d tri area +template +auto triangle_3d_area( + const Eigen::MatrixBase& a, + const Eigen::MatrixBase& b, + const Eigen::MatrixBase& c) -> typename ADerived::Scalar +{ + + auto ba = b-a; + auto ca = c-a; + return typename ADerived::Scalar(.5) * ba.cross(ca).norm(); + +} + +// template get 3d tri area +template +auto triangle_signed_2d_area( + const Eigen::MatrixBase& a, + const Eigen::MatrixBase& b, + const Eigen::MatrixBase& c) -> typename ADerived::Scalar +{ + auto ba = (b - a).eval(); + auto ca = (c - a).eval(); + return typename ADerived::Scalar(.5) * ba.homogeneous().cross(ca.homogeneous()).z(); +} + +template +auto triangle_unsigned_2d_area( + const Eigen::MatrixBase& a, + const Eigen::MatrixBase& b, + const Eigen::MatrixBase& c) -> typename ADerived::Scalar +{ + return std::abs(triangle_signed_2d_area(a,b,c)); +} + +template +bool triangle_2d_orientation( + const Eigen::MatrixBase& a, + const Eigen::MatrixBase& b, + const Eigen::MatrixBase& c) +{ + auto res = igl::predicates::orient2d(a, b, c); + if (res == igl::predicates::Orientation::POSITIVE) + return true; + else + return false; +} + +} // namespace wmtk diff --git a/src/wmtk/utils/triangle_helper_functions.cpp b/src/wmtk/utils/triangle_helper_functions.cpp deleted file mode 100644 index b5a710314b..0000000000 --- a/src/wmtk/utils/triangle_helper_functions.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "triangle_helper_functions.hpp" -using namespace wmtk; - -double triangle_2d_area( - const TriMesh& m, - const MeshAttributeHandle& vertex_uv_handle, - const Tuple& tuple) -{ - // assuming traingle is ccw - ConstAccessor pos = m.create_const_accessor(vertex_uv_handle); - Eigen::Vector2d p0 = pos.const_vector_attribute(tuple); - Eigen::Vector2d p1 = pos.const_vector_attribute(m.switch_edge(m.switch_vertex(tuple))); - Eigen::Vector2d p2 = pos.const_vector_attribute(m.switch_vertex(tuple)); - return triangle_2d_area(p0, p1, p2); -} \ No newline at end of file diff --git a/src/wmtk/utils/triangle_helper_functions.hpp b/src/wmtk/utils/triangle_helper_functions.hpp deleted file mode 100644 index 2320fd015a..0000000000 --- a/src/wmtk/utils/triangle_helper_functions.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once -#include -#include -#include -#include -namespace wmtk { -double triangle_2d_area( - const TriMesh& m, - const MeshAttributeHandle& vertex_uv_handle, - const Tuple& tuple); - -// template get 3d tri area -template -T triangle_3d_area( - const Eigen::Matrix& a, - const Eigen::Matrix& b, - const Eigen::Matrix& c) -{ - const T n0 = (a[1] - b[1]) * (a[2] - c[2]) - (a[1] - c[1]) * (a[2] - b[2]); - const T n1 = -(a[0] - b[0]) * (a[2] - c[2]) + (a[0] - c[0]) * (a[2] - b[2]); - const T n2 = (a[0] - b[0]) * (a[1] - c[1]) - (a[0] - c[0]) * (a[1] - b[1]); - - return sqrt(n0 * n0 + n1 * n1 + n2 * n2) * static_cast(0.5); -} - -// template get 3d tri area -template -T triangle_2d_area( - const Eigen::Matrix& A, - const Eigen::Matrix& B, - const Eigen::Matrix& C) -{ - auto B_A = B - A; - auto C_A = C - A; - T area = static_cast(0.5) * abs(B_A.x() * C_A.y() - B_A.y() * C_A.x()); - return area; -} - -// template get 3d tri area -template -T triangle_signed_2d_area( - const Eigen::Matrix& A, - const Eigen::Matrix& B, - const Eigen::Matrix& C) -{ - auto B_A = B - A; - auto C_A = C - A; - T area = static_cast(0.5) * (B_A.x() * C_A.y() - B_A.y() * C_A.x()); - return area; -} - -template -bool triangle_2d_orientation( - const Eigen::Matrix& A, - const Eigen::Matrix& B, - const Eigen::Matrix& C) -{ - auto res = igl::predicates::orient2d(A, B, C); - if (res == igl::predicates::Orientation::POSITIVE) - return true; - else - return false; -} - -} // namespace wmtk \ No newline at end of file diff --git a/tests/tools/TriMesh_examples.cpp b/tests/tools/TriMesh_examples.cpp index 654ecf77ce..036f7e29dc 100644 --- a/tests/tools/TriMesh_examples.cpp +++ b/tests/tools/TriMesh_examples.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include namespace wmtk::tests { @@ -20,29 +20,34 @@ TriMesh single_equilateral_triangle(int dimension) { assert(dimension == 2 || dimension == 3); TriMesh m = single_triangle(); - Eigen::MatrixXd V; + Eigen::Matrix V; - V.resize(3, 3); V.row(0) << 0., 0., 0; V.row(1) << 1., 0, 0; V.row(2) << 0.5, sqrt(3) / 2., 0; - auto x = V.row(0); - auto y = V.row(1); - auto z = V.row(2); - assert(triangle_2d_area(x, y, z) <= 0); - - V.conservativeResize(3, dimension); - mesh_utils::set_matrix_attribute(V, "position", PrimitiveType::Vertex, m); +#if !defined(NDEBUG) + auto xt = V.row(0); + auto yt = V.row(1); + auto zt = V.row(2); + auto xth = xt.head<2>(); + auto yth = yt.head<2>(); + auto zth = zt.head<2>(); + auto x = xth.transpose(); + auto y = yth.transpose(); + auto z = zth.transpose(); + assert(wmtk::utils::triangle_signed_2d_area(x, y, z) >= 0); +#endif + + auto V2 = V.leftCols(dimension).eval(); + mesh_utils::set_matrix_attribute(V2, "position", PrimitiveType::Vertex, m); return m; } TriMesh single_2d_triangle_with_random_positions(size_t seed) { TriMesh m = single_triangle(); - Eigen::MatrixXd V; - V.resize(3, 2); - V.setZero(); + Eigen::Matrix V; std::mt19937 generator(seed); std::uniform_real_distribution distribution(0., 1.); @@ -57,7 +62,7 @@ TriMesh single_2d_triangle_with_random_positions(size_t seed) auto gen = [&](int, int) { return distribution(generator); }; do { V = Eigen::MatrixXd::NullaryExpr(V.rows(), V.cols(), gen); - } while (triangle_2d_area(x, y, z) <= 0); + } while (wmtk::utils::triangle_signed_2d_area(x, y, z) <= 0); mesh_utils::set_matrix_attribute(V, "position", PrimitiveType::Vertex, m); From 19f6a993ffa15d6221a679dc97cf3477a79dc684 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 14 Oct 2023 15:02:55 -0400 Subject: [PATCH 095/134] centralizing wmtk image deps into the main cmake --- CMakeLists.txt | 12 +++++++++++- src/wmtk/CMakeLists.txt | 12 ------------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dbcacd606..50c2adf1bf 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,11 @@ include(metis) include(gmp) include(probabilistic_quadrics) +# Dependencies for images +include(stb) +include(tinyexr) + + include(lagrange) lagrange_include_modules(bvh) @@ -134,7 +139,12 @@ wmtk_target_link_system_libraries(wildmeshing_toolkit PUBLIC probabilistic_quadrics::probabilistic_quadrics paraviewo::paraviewo gmp::gmp -) + + + miniz # MTAO: I had a build issue with windows not finding miniz at linktime - adding here to make sure it's there? + tinyexr + stb::image + ) include(nlohmann_json) diff --git a/src/wmtk/CMakeLists.txt b/src/wmtk/CMakeLists.txt index a5b494071a..c0fa5e784d 100644 --- a/src/wmtk/CMakeLists.txt +++ b/src/wmtk/CMakeLists.txt @@ -34,12 +34,6 @@ target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) -include(stb) -include(tinyexr) -target_link_libraries(wildmeshing_toolkit PUBLIC - miniz # MTAO: I had a build issue with windows not finding miniz at linktime - adding here to make sure it's there? - tinyexr - stb::image) add_subdirectory(io) add_subdirectory(utils) @@ -54,9 +48,3 @@ add_subdirectory(multimesh) add_subdirectory(function) add_subdirectory(image) -include(stb) -include(tinyexr) -target_link_libraries(wildmeshing_toolkit PUBLIC - miniz # MTAO: I had a build issue with windows not finding miniz at linktime - adding here to make sure it's there? - tinyexr - stb::image) From de64bb31a736102d96e13b856a153777295b9157 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 14 Oct 2023 15:08:11 -0400 Subject: [PATCH 096/134] adding invariant initialization in some meshes --- tests/components/test_component_isotropic_remeshing.cpp | 3 +++ tests/test_execution.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/components/test_component_isotropic_remeshing.cpp b/tests/components/test_component_isotropic_remeshing.cpp index 3ee561e187..dfab7ad1a1 100644 --- a/tests/components/test_component_isotropic_remeshing.cpp +++ b/tests/components/test_component_isotropic_remeshing.cpp @@ -75,6 +75,7 @@ TEST_CASE("smoothing_simple_examples", "[components][isotropic_remeshing][2D]") OperationSettings op_settings; op_settings.position = mesh.get_attribute_handle("position", PrimitiveType::Vertex); + op_settings.initialize_invariants(mesh); // offset interior vertex auto pos = mesh.create_accessor(op_settings.position); @@ -97,6 +98,7 @@ TEST_CASE("smoothing_simple_examples", "[components][isotropic_remeshing][2D]") OperationSettings op_settings; op_settings.position = mesh.get_attribute_handle("position", PrimitiveType::Vertex); + op_settings.initialize_invariants(mesh); // offset interior vertex auto pos = mesh.create_accessor(op_settings.position); @@ -133,6 +135,7 @@ TEST_CASE("tangential_smoothing", "[components][isotropic_remeshing][2D]") OperationSettings op_settings; op_settings.smooth_settings.position = mesh.get_attribute_handle("position", PrimitiveType::Vertex); + op_settings.smooth_settings.initialize_invariants(mesh); // offset interior vertex auto pos = mesh.create_accessor(op_settings.smooth_settings.position); diff --git a/tests/test_execution.cpp b/tests/test_execution.cpp index 84d94a8877..2ee0287bac 100644 --- a/tests/test_execution.cpp +++ b/tests/test_execution.cpp @@ -54,7 +54,7 @@ TEST_CASE("operation_with_settings", "[scheduler][operations][2D]") operations::OperationSettings op_settings; op_settings.position = m.get_attribute_handle("position", PrimitiveType::Vertex); - op_settings.base_settings.initialize_invariants(m); + op_settings.initialize_invariants(m); Scheduler scheduler(m); scheduler.add_operation_type("vertex_smooth", op_settings); From 13aa0d7303735bbe1b1c619258e717d5f083f2ca Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sun, 15 Oct 2023 11:16:08 -0400 Subject: [PATCH 097/134] optimization operations all build --- src/wmtk/Types.hpp | 3 + src/wmtk/attribute/MutableAccessor.hpp | 2 + src/wmtk/operations/CMakeLists.txt | 3 +- .../OperationDifferentiableSmoothFactory.hpp | 41 ------- src/wmtk/operations/TupleOperation.hpp | 3 + src/wmtk/operations/tri_mesh/CMakeLists.txt | 9 +- .../tri_mesh/VertexSmoothNewtonMethod.cpp | 39 ------- ...VertexSmoothNewtonMethodWithLineSearch.cpp | 46 -------- .../VertexSmoothUsingDifferentiableEnergy.cpp | 61 ++++------ .../VertexSmoothUsingDifferentiableEnergy.hpp | 24 ++-- .../tri_mesh/internal/CMakeLists.txt | 12 ++ .../internal/VertexSmoothGradientDescent.cpp | 38 ++++++ .../internal/VertexSmoothGradientDescent.hpp | 20 ++++ .../internal/VertexSmoothNewtonMethod.cpp | 44 +++++++ .../VertexSmoothNewtonMethod.hpp | 10 +- ...VertexSmoothNewtonMethodWithLineSearch.cpp | 38 ++++++ ...VertexSmoothNewtonMethodWithLineSearch.hpp | 6 +- ...SmoothUsingDifferentiableEnergyFactory.cpp | 27 +++++ ...SmoothUsingDifferentiableEnergyFactory.hpp | 13 +++ src/wmtk/optimization/CMakeLists.txt | 6 + src/wmtk/optimization/FunctionInterface.hpp | 67 +++++++++++ src/wmtk/optimization/GradientDescent.hpp | 1 + src/wmtk/optimization/LineSearch.cpp | 14 +++ src/wmtk/optimization/LineSearch.hpp | 109 ++++++++++++++++++ src/wmtk/utils/CMakeLists.txt | 5 +- src/wmtk/utils/FunctionInterface.hpp | 31 ----- src/wmtk/utils/Optimization.cpp | 60 ---------- src/wmtk/utils/Optimization.hpp | 42 ------- tests/components/test_smoothing.cpp | 5 +- 29 files changed, 445 insertions(+), 334 deletions(-) delete mode 100644 src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp delete mode 100644 src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp delete mode 100644 src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp create mode 100644 src/wmtk/operations/tri_mesh/internal/CMakeLists.txt create mode 100644 src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp create mode 100644 src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.hpp create mode 100644 src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp rename src/wmtk/operations/tri_mesh/{ => internal}/VertexSmoothNewtonMethod.hpp (52%) create mode 100644 src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethodWithLineSearch.cpp rename src/wmtk/operations/tri_mesh/{ => internal}/VertexSmoothNewtonMethodWithLineSearch.hpp (77%) create mode 100644 src/wmtk/operations/tri_mesh/internal/VertexSmoothUsingDifferentiableEnergyFactory.cpp create mode 100644 src/wmtk/operations/tri_mesh/internal/VertexSmoothUsingDifferentiableEnergyFactory.hpp create mode 100644 src/wmtk/optimization/CMakeLists.txt create mode 100644 src/wmtk/optimization/FunctionInterface.hpp create mode 100644 src/wmtk/optimization/GradientDescent.hpp create mode 100644 src/wmtk/optimization/LineSearch.cpp create mode 100644 src/wmtk/optimization/LineSearch.hpp delete mode 100644 src/wmtk/utils/FunctionInterface.hpp delete mode 100644 src/wmtk/utils/Optimization.cpp delete mode 100644 src/wmtk/utils/Optimization.hpp diff --git a/src/wmtk/Types.hpp b/src/wmtk/Types.hpp index feab31c298..cb83fe50c8 100644 --- a/src/wmtk/Types.hpp +++ b/src/wmtk/Types.hpp @@ -6,6 +6,9 @@ namespace wmtk { template using RowVectors = Eigen::Matrix; +template +using SquareMatrix = Eigen::Matrix; + template using Vector = Eigen::Matrix; template diff --git a/src/wmtk/attribute/MutableAccessor.hpp b/src/wmtk/attribute/MutableAccessor.hpp index 4524854774..1e01c119de 100644 --- a/src/wmtk/attribute/MutableAccessor.hpp +++ b/src/wmtk/attribute/MutableAccessor.hpp @@ -34,6 +34,8 @@ class MutableAccessor : public ConstAccessor using CachingBaseType::has_stack; using CachingBaseType::stack_depth; + using ConstAccessorType::mesh; + protected: using ConstAccessorType::base_type; using ConstAccessorType::caching_base_type; diff --git a/src/wmtk/operations/CMakeLists.txt b/src/wmtk/operations/CMakeLists.txt index e2ab45d6e4..b03763f61e 100644 --- a/src/wmtk/operations/CMakeLists.txt +++ b/src/wmtk/operations/CMakeLists.txt @@ -5,11 +5,10 @@ set(SRC_FILES TupleOperation.cpp OperationFactory.hpp OperationFactory.cpp - OperationDifferentiableSmoothFactory.hpp OperationQueue.hpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) add_subdirectory(edge_mesh) add_subdirectory(tri_mesh) -add_subdirectory(tet_mesh) \ No newline at end of file +add_subdirectory(tet_mesh) diff --git a/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp b/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp deleted file mode 100644 index 95e441e55c..0000000000 --- a/src/wmtk/operations/OperationDifferentiableSmoothFactory.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "Operation.hpp" -#include "OperationFactory.hpp" - -namespace wmtk { -namespace operations { - -class OperationDifferentiableSmoothFactory : public OperationFactoryBase -{ -public: - OperationDifferentiableSmoothFactory( - const OperationSettings& settings) - : OperationFactoryBase(tri_mesh::VertexSmoothUsingDifferentiableEnergy::primitive_type()) - , m_settings(settings) - {} - - std::unique_ptr create(wmtk::Mesh& m, const Tuple& t) const override - { - if (m_settings.second_order) { - if (m_settings.line_search) { - return std::make_unique( - m, - t, - m_settings); - } else { - return std::make_unique(m, t, m_settings); - } - } - return std::make_unique(m, t, m_settings); - } - -protected: - const OperationSettings& m_settings; -}; -} // namespace operations -} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/operations/TupleOperation.hpp b/src/wmtk/operations/TupleOperation.hpp index 50c651a16c..6b694d1b54 100644 --- a/src/wmtk/operations/TupleOperation.hpp +++ b/src/wmtk/operations/TupleOperation.hpp @@ -23,6 +23,9 @@ class TupleOperation : virtual public Operation // Returns the set of tuples, organized by the type virtual std::vector modified_primitives(PrimitiveType) const; + + const InvariantCollection& invariants() const { return m_invariants; } + private: const InvariantCollection& m_invariants; Tuple m_input_tuple; diff --git a/src/wmtk/operations/tri_mesh/CMakeLists.txt b/src/wmtk/operations/tri_mesh/CMakeLists.txt index b8ba0145db..da21ffa07f 100644 --- a/src/wmtk/operations/tri_mesh/CMakeLists.txt +++ b/src/wmtk/operations/tri_mesh/CMakeLists.txt @@ -11,12 +11,8 @@ set(SRC_FILES EdgeCollapseToMidpoint.cpp EdgeSwap.hpp EdgeSwap.cpp - #VertexSmoothUsingDifferentiableEnergy.hpp - #VertexSmoothUsingDifferentiableEnergy.cpp - #VertexSmoothNewtonMethod.hpp - #VertexSmoothNewtonMethod.cpp - #VertexSmoothNewtonMethodWithLineSearch.hpp - #VertexSmoothNewtonMethodWithLineSearch.cpp + VertexSmoothUsingDifferentiableEnergy.hpp + VertexSmoothUsingDifferentiableEnergy.cpp VertexAttributesUpdateBase.hpp VertexAttributesUpdateBase.cpp VertexLaplacianSmooth.hpp @@ -25,3 +21,4 @@ set(SRC_FILES VertexTangentialLaplacianSmooth.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) +add_subdirectory(internal) diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp deleted file mode 100644 index 8873e12b46..0000000000 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "VertexSmoothNewtonMethod.hpp" -#include -namespace wmtk::operations::tri_mesh { -VertexSmoothNewtonMethod::VertexSmoothNewtonMethod( - Mesh& m, - const Tuple& t, - const OperationSettings& settings) - : VertexSmoothUsingDifferentiableEnergy(m, t, settings) -{} -std::string VertexSmoothNewtonMethod::name() const -{ - return "tri_mesh_vertex_smooth_newton_method"; -} - -bool VertexSmoothNewtonMethod::execute() -{ - const Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); - if (!tri_mesh::VertexSmoothUsingDifferentiableEnergy::execute()) { - wmtk::logger().debug("execute failed"); - return false; - } - - const Tuple tup = tri_mesh::VertexAttributesUpdateBase::return_tuple(); - assert(mesh().is_valid_slow(tup)); - // check if it is a boundary vertex - if (!m_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { - } - // do curve mesh smoothing - else { - Eigen::Vector2d search_dir = Eigen::Vector2d::Zero(); - search_dir = - -m_settings.energy->get_hessian(tup).ldlt().solve(m_settings.energy->get_gradient(tup)); - Eigen::Vector2d new_pos = p + search_dir; - m_uv_pos_accessor.vector_attribute(tup) = new_pos; - } - m_output_tuple = resurrect_tuple(input_tuple()); - return true; -} -} // namespace wmtk::operations::tri_mesh \ No newline at end of file diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp deleted file mode 100644 index 50b37c7f33..0000000000 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "VertexSmoothNewtonMethodWithLineSearch.hpp" - -namespace wmtk::operations { - -namespace tri_mesh { -VertexSmoothNewtonMethodWithLineSearch::VertexSmoothNewtonMethodWithLineSearch( - Mesh& m, - const Tuple& t, - const OperationSettings& settings) - : VertexSmoothNewtonMethod(m, t, settings) -{} - -bool VertexSmoothNewtonMethodWithLineSearch::execute() -{ - tri_mesh::VertexSmoothNewtonMethod smooth_op(mesh(), input_tuple(), m_settings); - Eigen::Vector2d p = m_uv_pos_accessor.vector_attribute(input_tuple()); - if (!smooth_op()) { - // line search - Tuple tup = smooth_op.return_tuple(); - double step_size = 1; - double minimum_step_size = 1e-6; - if (!m_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { - } else { - Eigen::Vector2d search_dir = Eigen::Vector2d::Zero(); - search_dir = -m_settings.energy->get_hessian(tup).ldlt().solve( - m_settings.energy->get_gradient(tup)); - Eigen::Vector2d new_pos = p + search_dir; - while (!m_settings.base_settings.invariants.after( - PrimitiveType::Face, - smooth_op.modified_primitives(PrimitiveType::Face)) && - (step_size > minimum_step_size)) { - step_size /= 2; - search_dir = -m_settings.energy->get_hessian(tup).ldlt().solve( - m_settings.energy->get_gradient(tup)); - new_pos = p + search_dir * step_size; - - m_uv_pos_accessor.vector_attribute(tup) = new_pos; - } - } - } - m_output_tuple = resurrect_tuple(input_tuple()); - return true; -} -} // namespace tri_mesh - -} // namespace wmtk::operations \ No newline at end of file diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp index 1400e562ec..ed856f0f7e 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp @@ -1,17 +1,14 @@ #include "VertexSmoothUsingDifferentiableEnergy.hpp" -#include #include #include -#include -#include -#include namespace wmtk::operations { void OperationSettings::initialize_invariants( const TriMesh& m) { base_settings.initialize_invariants(m); - base_settings.invariants.add(std::make_shared(m, uv_position)); + base_settings.invariants.add( + std::make_shared(m, coordinate_handle)); } } // namespace wmtk::operations @@ -21,7 +18,6 @@ VertexSmoothUsingDifferentiableEnergy::VertexSmoothUsingDifferentiableEnergy( const Tuple& t, const OperationSettings& settings) : VertexAttributesUpdateBase(m, t, settings.base_settings) - , m_uv_pos_accessor{m.create_accessor(settings.uv_position)} , m_settings{settings} {} @@ -30,44 +26,27 @@ std::string VertexSmoothUsingDifferentiableEnergy::name() const return "tri_mesh_vertex_smooth_using_differentiable_energy"; } -bool VertexSmoothUsingDifferentiableEnergy::execute() +template +optimization::FunctionInterface VertexSmoothUsingDifferentiableEnergy::get_function_interface( + Accessor& accessor) const { - assert(mesh().is_valid_slow(input_tuple())); - if (!tri_mesh::VertexAttributesUpdateBase::execute()) { - return false; - } - - const Tuple tup = tri_mesh::VertexAttributesUpdateBase::return_tuple(); - assert(mesh().is_valid_slow(tup)); - // start scope - // auto scope = mesh().create_scope(); + return optimization::FunctionInterface(input_tuple(), accessor, *m_settings.energy); +} +template <> +optimization::FunctionInterface<2> VertexSmoothUsingDifferentiableEnergy::get_function_interface<2>( + Accessor& accessor) const; - // // fix boundary curve - // if (!m_settings.smooth_settings.smooth_boundary && mesh().is_boundary_vertex(tup)) { - // // do curve mesh smoothing +template <> +optimization::FunctionInterface<3> VertexSmoothUsingDifferentiableEnergy::get_function_interface<3>( + Accessor& accessor) const; - // } else { - // Optimization opt( - // input_tuple(), - // m_uv_pos_accessor, - // *m_settings.energy.get(), - // mesh(), - // m_settings.smooth_settings.invariants, - // m_settings.second_order, - // m_settings.line_search); - // opt.optimize2d(p); - // // double step_size = m_settings.step_size; - // // while (m_settings.line_search && !m_settings.smooth_settings.invariants.after( - // // PrimitiveType::Face, - // // modified_primitives(PrimitiveType::Face))) { - // // step_size /= 2; - // // opt.optimize2d(m_uv_pos_accessor.vector_attribute(smooth_op.return_tuple()), - // // step_size); - // // } - // } - return true; +Accessor VertexSmoothUsingDifferentiableEnergy::coordinate_accessor() +{ + return mesh().create_accessor(m_settings.coordinate_handle); +} +ConstAccessor VertexSmoothUsingDifferentiableEnergy::const_coordinate_accessor() const +{ + return mesh().create_const_accessor(m_settings.coordinate_handle); } - - } // namespace wmtk::operations::tri_mesh diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp index 4264f4f4b2..786e9b56f9 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp @@ -1,10 +1,7 @@ #pragma once -#include -#include #include -#include -#include -#include "TriMeshOperation.hpp" +#include +#include #include "VertexAttributesUpdateBase.hpp" namespace wmtk::operations { @@ -17,14 +14,14 @@ struct OperationSettings { OperationSettings base_settings; std::unique_ptr energy; - // uv_positioin accesor - MeshAttributeHandle uv_position; + // coordinate for teh attribute used to evaluate the energy + MeshAttributeHandle coordinate_handle; bool smooth_boundary = false; bool second_order = true; bool line_search = false; void initialize_invariants(const TriMesh& m); - // double step_size = 1.0; + double step_size = 1.0; }; namespace tri_mesh { @@ -40,13 +37,18 @@ class VertexSmoothUsingDifferentiableEnergy : public VertexAttributesUpdateBase static PrimitiveType primitive_type() { return PrimitiveType::Vertex; } -protected: - bool execute() override; protected: - Accessor m_uv_pos_accessor; + template + optimization::FunctionInterface get_function_interface(Accessor& accessor) const; + MeshAttributeHandle coordinate_handle() const { return m_settings.coordinate_handle; } + + Accessor coordinate_accessor(); + ConstAccessor const_coordinate_accessor() const; const OperationSettings& m_settings; }; } // namespace tri_mesh } // namespace wmtk::operations +// provides overload for factory +#include diff --git a/src/wmtk/operations/tri_mesh/internal/CMakeLists.txt b/src/wmtk/operations/tri_mesh/internal/CMakeLists.txt new file mode 100644 index 0000000000..14eeb5db0d --- /dev/null +++ b/src/wmtk/operations/tri_mesh/internal/CMakeLists.txt @@ -0,0 +1,12 @@ + +set(SRC_FILES + VertexSmoothUsingDifferentiableEnergyFactory.hpp + VertexSmoothUsingDifferentiableEnergyFactory.cpp + VertexSmoothGradientDescent.hpp + VertexSmoothGradientDescent.cpp + VertexSmoothNewtonMethod.hpp + VertexSmoothNewtonMethod.cpp + VertexSmoothNewtonMethodWithLineSearch.hpp + VertexSmoothNewtonMethodWithLineSearch.cpp +) +target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp new file mode 100644 index 0000000000..2c745f58fe --- /dev/null +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp @@ -0,0 +1,38 @@ +#include "VertexSmoothGradientDescent.hpp" +#include +namespace wmtk::operations::tri_mesh::internal { +VertexSmoothGradientDescent::VertexSmoothGradientDescent( + Mesh& m, + const Tuple& t, + const OperationSettings& settings) + : VertexSmoothUsingDifferentiableEnergy(m, t, settings) +{} +std::string VertexSmoothGradientDescent::name() const +{ + return "tri_mesh_vertex_smooth_newton_method"; +} + + +template +Eigen::Vector VertexSmoothGradientDescent::get_descent_direction( + optimization::FunctionInterface& f) const +{ + return -f.get_gradient(); +} + +bool VertexSmoothGradientDescent::execute() +{ + auto accessor = coordinate_accessor(); + auto interface = get_function_interface<2>(accessor); + + auto pos = interface.get_coordinate(); + Eigen::Vector2d next_pos = pos + m_settings.step_size * get_descent_direction(interface); + interface.store(next_pos); + + if (!tri_mesh::VertexSmoothUsingDifferentiableEnergy::execute()) { + wmtk::logger().debug("execute failed"); + return false; + } + return true; +} +} // namespace wmtk::operations::tri_mesh::internal diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.hpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.hpp new file mode 100644 index 0000000000..24cdc56427 --- /dev/null +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.hpp @@ -0,0 +1,20 @@ + +#pragma once +#include + +namespace wmtk::operations::tri_mesh::internal { +class VertexSmoothGradientDescent : public VertexSmoothUsingDifferentiableEnergy +{ +public: + VertexSmoothGradientDescent( + Mesh& m, + const Tuple& t, + const OperationSettings& settings); + +protected: + template + Eigen::Vector get_descent_direction(optimization::FunctionInterface&) const; + bool execute() override; + std::string name() const; +}; +} // namespace wmtk::operations::tri_mesh::internal diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp new file mode 100644 index 0000000000..87697725d8 --- /dev/null +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp @@ -0,0 +1,44 @@ +#include "VertexSmoothNewtonMethod.hpp" +#include +namespace wmtk::operations::tri_mesh::internal { +VertexSmoothNewtonMethod::VertexSmoothNewtonMethod( + Mesh& m, + const Tuple& t, + const OperationSettings& settings) + : VertexSmoothUsingDifferentiableEnergy(m, t, settings) +{} +std::string VertexSmoothNewtonMethod::name() const +{ + return "tri_mesh_vertex_smooth_newton_method"; +} +template +Eigen::Vector VertexSmoothNewtonMethod::get_descent_direction( + optimization::FunctionInterface& f) const +{ + return -f.get_hessian().ldlt().solve(f.get_gradient()); +} +template <> +Eigen::Vector VertexSmoothNewtonMethod::get_descent_direction<2>( + optimization::FunctionInterface<2>& f) const; +template <> +Eigen::Vector VertexSmoothNewtonMethod::get_descent_direction<3>( + optimization::FunctionInterface<3>& f) const; + + +bool VertexSmoothNewtonMethod::execute() +{ + auto accessor = coordinate_accessor(); + auto interface = get_function_interface<2>(accessor); + + auto pos = interface.get_coordinate(); + Eigen::Vector2d next_pos = pos + m_settings.step_size * get_descent_direction(interface); + interface.store(next_pos); + + + if (!tri_mesh::VertexSmoothUsingDifferentiableEnergy::execute()) { + return false; + } + return true; +} + +} // namespace wmtk::operations::tri_mesh::internal diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.hpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.hpp similarity index 52% rename from src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.hpp rename to src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.hpp index 5ba295a8b2..5bcbc25730 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethod.hpp +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.hpp @@ -1,9 +1,8 @@ #pragma once -#include "VertexSmoothUsingDifferentiableEnergy.hpp" +#include -namespace wmtk::operations { +namespace wmtk::operations::tri_mesh::internal { -namespace tri_mesh { class VertexSmoothNewtonMethod : public VertexSmoothUsingDifferentiableEnergy { public: @@ -14,7 +13,8 @@ class VertexSmoothNewtonMethod : public VertexSmoothUsingDifferentiableEnergy protected: bool execute() override; + template + Eigen::Vector get_descent_direction(optimization::FunctionInterface&) const; std::string name() const; }; -} // namespace tri_mesh -} // namespace wmtk::operations \ No newline at end of file +} // namespace wmtk::operations::tri_mesh::internal diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethodWithLineSearch.cpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethodWithLineSearch.cpp new file mode 100644 index 0000000000..89ba75559b --- /dev/null +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethodWithLineSearch.cpp @@ -0,0 +1,38 @@ +#include "VertexSmoothNewtonMethodWithLineSearch.hpp" +#include + +namespace wmtk::operations::tri_mesh::internal { + +VertexSmoothNewtonMethodWithLineSearch::VertexSmoothNewtonMethodWithLineSearch( + Mesh& m, + const Tuple& t, + const OperationSettings& settings) + : VertexSmoothNewtonMethod(m, t, settings) +{} + +bool VertexSmoothNewtonMethodWithLineSearch::execute() +{ + auto accessor = coordinate_accessor(); + auto interface = get_function_interface<2>(accessor); + + Eigen::Vector2d direction = get_descent_direction(interface); + + optimization::LineSearch<2> line_search(interface, invariants()); + + line_search.set_create_scope( + false); // since we're in an operation we will fail if the seach doesn't do waht we want + double distance = line_search.run(direction, 1.0); + if (distance == 0.0) { + return false; + } + + + if (!tri_mesh::VertexSmoothUsingDifferentiableEnergy::execute()) { + return false; + } + return true; +} + + +} // namespace wmtk::operations::tri_mesh::internal + diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethodWithLineSearch.hpp similarity index 77% rename from src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp rename to src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethodWithLineSearch.hpp index 9714e17a30..c409542237 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothNewtonMethodWithLineSearch.hpp +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethodWithLineSearch.hpp @@ -1,7 +1,6 @@ #pragma once #include "VertexSmoothNewtonMethod.hpp" -namespace wmtk::operations { -namespace tri_mesh { +namespace wmtk::operations::tri_mesh::internal { class VertexSmoothNewtonMethodWithLineSearch : public VertexSmoothNewtonMethod { public: @@ -13,5 +12,4 @@ class VertexSmoothNewtonMethodWithLineSearch : public VertexSmoothNewtonMethod protected: bool execute() override; }; -} // namespace tri_mesh -} // namespace wmtk::operations \ No newline at end of file +} // namespace wmtk::operations::tri_mesh::internal diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothUsingDifferentiableEnergyFactory.cpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothUsingDifferentiableEnergyFactory.cpp new file mode 100644 index 0000000000..6c0a3442f5 --- /dev/null +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothUsingDifferentiableEnergyFactory.cpp @@ -0,0 +1,27 @@ +#include "VertexSmoothUsingDifferentiableEnergyFactory.hpp" +#include +#include +#include +#include + + +namespace wmtk::operations { + +template <> + +std::unique_ptr OperationFactory< + tri_mesh::VertexSmoothUsingDifferentiableEnergy>::create(wmtk::Mesh& m, const Tuple& t) const +{ + if (m_settings.second_order) { + if (m_settings.line_search) { + return std::make_unique( + m, + t, + m_settings); + } else { + return std::make_unique(m, t, m_settings); + } + } + return std::make_unique(m, t, m_settings); +} +} // namespace wmtk::operations diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothUsingDifferentiableEnergyFactory.hpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothUsingDifferentiableEnergyFactory.hpp new file mode 100644 index 0000000000..585c8edc25 --- /dev/null +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothUsingDifferentiableEnergyFactory.hpp @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +namespace wmtk::operations { +namespace tri_mesh { +class VertexSmoothUsingDifferentiableEnergy; +} + +template <> +std::unique_ptr OperationFactory< + tri_mesh::VertexSmoothUsingDifferentiableEnergy>::create(wmtk::Mesh& m, const Tuple& t) const; +} // namespace wmtk::operations diff --git a/src/wmtk/optimization/CMakeLists.txt b/src/wmtk/optimization/CMakeLists.txt new file mode 100644 index 0000000000..d6c9e7fbcf --- /dev/null +++ b/src/wmtk/optimization/CMakeLists.txt @@ -0,0 +1,6 @@ +set(SRC_FILES + FunctionInterface.hpp + LineSearch.hpp + LineSearch.cpp +) +target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/optimization/FunctionInterface.hpp b/src/wmtk/optimization/FunctionInterface.hpp new file mode 100644 index 0000000000..e0ffd7c9ca --- /dev/null +++ b/src/wmtk/optimization/FunctionInterface.hpp @@ -0,0 +1,67 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace wmtk::optimization { +template +class FunctionInterface +{ +public: + using Vector = wmtk::Vector; + using Matrix = wmtk::SquareMatrix; + FunctionInterface( + const Tuple& tuple, + Accessor& accessor, + const function::DifferentiableFunction& function) + : m_tuple(tuple) + , m_accessor(accessor) + , m_function(function) + {} + + + auto get_coordinate() { return m_accessor.vector_attribute(m_tuple); } + auto get_const_coordinate() const { return m_accessor.const_vector_attribute(m_tuple); } + template + void store(const Eigen::MatrixBase& v) + { + m_accessor.vector_attribute(m_tuple) = v; + } + auto get_coordinate() const { return get_const_coordinate(); } + + double get_value() const { return m_function.get_value(m_tuple); } + Vector get_gradient() const { return m_function.get_gradient(m_tuple); } + Matrix get_hessian() const { return m_function.get_hessian(m_tuple); } + + template + double get_value(const Eigen::MatrixBase& v) + { + store(v); + return get_value(); + } + template + Vector get_gradient(const Eigen::MatrixBase& v) + { + store(v); + return get_gradient(); + } + template + Matrix get_hessian(const Eigen::MatrixBase& v) + { + store(v); + return get_hessian(); + } + + + const Tuple& tuple() const { return m_tuple; } + Mesh& mesh() { return m_accessor.mesh(); } + +private: + const Tuple& m_tuple; + Accessor& m_accessor; + const function::DifferentiableFunction& m_function; +}; +} // namespace wmtk::optimization diff --git a/src/wmtk/optimization/GradientDescent.hpp b/src/wmtk/optimization/GradientDescent.hpp new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/wmtk/optimization/GradientDescent.hpp @@ -0,0 +1 @@ + diff --git a/src/wmtk/optimization/LineSearch.cpp b/src/wmtk/optimization/LineSearch.cpp new file mode 100644 index 0000000000..df5c1483c3 --- /dev/null +++ b/src/wmtk/optimization/LineSearch.cpp @@ -0,0 +1,14 @@ +#include +#include +namespace wmtk::optimization { +std::vector LineSearchBase::modified_top_simplices() const +{ + return simplex::top_level_cofaces_tuples(interface.mesh()); +} +bool LineSearchBase::check_state() const +{ + bool before_pass = m_invariants.before(m_interface.tuple()); + bool after_pass = m_invariants.after(top_type, modified_top_simplices); + return before_pass && after_pass; +} +} // namespace wmtk::optimization diff --git a/src/wmtk/optimization/LineSearch.hpp b/src/wmtk/optimization/LineSearch.hpp new file mode 100644 index 0000000000..56134e1bdc --- /dev/null +++ b/src/wmtk/optimization/LineSearch.hpp @@ -0,0 +1,109 @@ +#pragma once + +#include +#include "FunctionInterface.hpp" + + +namespace wmtk::optimization { + + +template +class LineSearch +{ +public: + using InvariantCollection = wmtk::InvariantCollection; + LineSearch(FunctionInterface& interface, const InvariantCollection& invariants) + : m_interface(interface) + , m_invariants(invariants) + {} + + void set_create_scope(bool enable) { m_create_scope = enable; } + void set_max_steps(long max_steps) { m_max_steps = max_steps; } + void set_min_step_size_ratio(long min_step_size_ratio) + { + m_min_step_size_ratio = min_step_size_ratio; + } + + + template + double run(const Eigen::MatrixBase& direction, double step_size); + template + double _run( + const Eigen::MatrixBase& direction, + double step_size, + const std::vector& modified_top_simplices); + +protected: + std::vector modified_top_simplices() const; + + // TODO: formally define what checking the state means + // we currently make sure that we pass before on the input tuple and after on all top level + // simplices, but should we be passing all of that every time? + bool check_state() const; + + FunctionInterface& m_interface; + const InvariantCollection& m_invariants; + + bool m_create_scope = true; + long m_max_steps = 10; + double m_min_step_size_ratio = 1e-6; +}; + +template +template +double LineSearch::run(const Eigen::MatrixBase& direction, double step_size) +{ + PrimitiveType top_type = m_interface.mesh().top_simplex_type(); + + std::vector modified_top_simplices = this->modified_top_simplices(); + if (!check_state()) { + return 0; + } + if (m_create_scope) { + { + auto scope = m_interface.mesh().create_scope(); + double retval = _run(direction, step_size, modified_top_simplices); + if (retval == 0) { + scope.mark_failed(); + } + return retval; + } + + } else { + return _run(direction, step_size, modified_top_simplices); + } +} + +template +template +double LineSearch::_run( + const Eigen::MatrixBase& direction, + double init_step_size, + const std::vector& modified_top_simplices) +{ + return 0; + /* + PrimitiveType top_type = interface.mesh().top_simplex_type(); + int steps = 0; + // just to make sure we try the initial stepsize + double step_size = init_step_size; + double next_step_size = step_size; + double min_step_size = m_min_step_size_ratio * step_size; + Vector current_pos = interface.get_const_coordinate(); + Vector new_pos; + do { + new_pos = current_pos + direction * step_size; + m_function_interface.store(new_pos); + + step_size = next_step_size; + next_step_size /= 2; + } while (steps++ < m_max_steps && step_size > min_step_size && !check_state()); + if (steps == m_max_steps || step_size < min_step_size) { + return 0; + } else { + return step_size; + } + */ +} +} // namespace wmtk::optimization + diff --git a/src/wmtk/utils/CMakeLists.txt b/src/wmtk/utils/CMakeLists.txt index 664d7a1915..d6761b5287 100644 --- a/src/wmtk/utils/CMakeLists.txt +++ b/src/wmtk/utils/CMakeLists.txt @@ -17,8 +17,7 @@ set(SRC_FILES triangle_areas.hpp triangle_areas.cpp - FunctionInterface.hpp - Optimization.hpp - Optimization.cpp + #Optimization.hpp + #Optimization.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/utils/FunctionInterface.hpp b/src/wmtk/utils/FunctionInterface.hpp deleted file mode 100644 index fe55d5dd31..0000000000 --- a/src/wmtk/utils/FunctionInterface.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -namespace wmtk { -class FunctionInterface -{ -public: - FunctionInterface( - const Tuple& tuple, - Accessor& accessor, - const function::DifferentiableFunction& function) - : m_tuple(tuple) - , m_accessor(accessor) - , m_function(function) - {} - - const Tuple& m_tuple; - Accessor& m_accessor; - const function::DifferentiableFunction& m_function; - - void store(Eigen::Vector2d& v) { m_accessor.vector_attribute(m_tuple) = v; } - double eval() const { return m_function.get_value(m_tuple); } - Eigen::Vector2d grad() const { return m_function.get_gradient(m_tuple); } - Eigen::Matrix2d hess() const { return m_function.get_hessian(m_tuple); } -}; -} // namespace wmtk diff --git a/src/wmtk/utils/Optimization.cpp b/src/wmtk/utils/Optimization.cpp deleted file mode 100644 index bd319ebaac..0000000000 --- a/src/wmtk/utils/Optimization.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "Optimization.hpp" -#include - -using namespace wmtk; - - -Optimization::Optimization( - FunctionInterface& function_interface, - const TriMesh& m, - const InvariantCollection& invariants, - const bool second_order, - const bool line_search) - : m_function_interface(function_interface) - , m_mesh(m) - , m_invariants(invariants) - , m_second_order(second_order) - , m_line_search(line_search) -{} - -Optimization::Optimization( - const Tuple& tuple, - Accessor& accessor, - const function::DifferentiableFunction& function, - const TriMesh& m, - const InvariantCollection& invariants, - const bool second_order, - const bool line_search) - : m_function_interface(tuple, accessor, function) - , m_mesh(m) - , m_invariants(invariants) - , m_second_order(second_order) - , m_line_search(line_search) -{} - -void Optimization::optimize2d(const Eigen::Vector2d& current_pos) -{ - Eigen::Vector2d new_pos = Eigen::Vector2d::Zero(); - Eigen::Vector2d search_direction = Eigen::Vector2d::Zero(); - if (m_second_order) { - // solve optimization problem - search_direction = -(m_function_interface.hess()).ldlt().solve(m_function_interface.grad()); - } else { - // solve optimization problem - search_direction = -m_function_interface.grad(); - } - new_pos = current_pos + search_direction; - m_function_interface.store(new_pos); - double step_size = 1.0; - // get the modified primitives and convert to tuple manully - auto one_ring = SimplicialComplex::vertex_one_ring(m_mesh, m_function_interface.m_tuple); - std::vector modified_faces; - for (const auto& s : one_ring) { - modified_faces.emplace_back(s.tuple()); - } - while (m_line_search && !m_invariants.after(PrimitiveType::Face, modified_faces)) { - step_size /= 2; - new_pos = current_pos + search_direction * step_size; - m_function_interface.store(new_pos); - } -} \ No newline at end of file diff --git a/src/wmtk/utils/Optimization.hpp b/src/wmtk/utils/Optimization.hpp deleted file mode 100644 index e356704ecf..0000000000 --- a/src/wmtk/utils/Optimization.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -namespace wmtk { -class Optimization -{ -public: - Optimization() = default; - ~Optimization(); - - Optimization( - FunctionInterface& function_interface, - const TriMesh& m, - const InvariantCollection& invariants, - const bool second_order, - const bool line_search); - - Optimization( - const Tuple& tuple, - Accessor& accessor, - const function::DifferentiableFunction& function, - const TriMesh& m, - const InvariantCollection& invariants, - const bool second_order, - const bool line_search); - - - FunctionInterface m_function_interface; - TriMesh m_mesh; - const InvariantCollection& m_invariants; - const bool m_second_order; - const bool m_line_search; - - - void optimize2d(const Eigen::Vector2d& current_pos); -}; -} // namespace wmtk \ No newline at end of file diff --git a/tests/components/test_smoothing.cpp b/tests/components/test_smoothing.cpp index dc1193c64a..af7ae84d4b 100644 --- a/tests/components/test_smoothing.cpp +++ b/tests/components/test_smoothing.cpp @@ -1,4 +1,3 @@ -#pragma once #include #include #include @@ -20,7 +19,7 @@ TEST_CASE("smoothing_Newton_Method") op_settings.smooth_boundary = false; op_settings.second_order = true; op_settings.line_search = false; - op_settings.energy = std::make_unique( + op_settings.energy = std::make_unique( mesh, mesh.get_attribute_handle("position", PrimitiveType::Vertex)); op_settings.initialize_invariants(mesh); @@ -54,7 +53,7 @@ TEST_CASE("smoothing_Newton_Method_line_search") op_settings.smooth_boundary = false; op_settings.second_order = true; op_settings.line_search = true; - op_settings.energy = std::make_unique( + op_settings.energy = std::make_unique( mesh, mesh.get_attribute_handle("position", PrimitiveType::Vertex)); op_settings.initialize_invariants(mesh); From 690171e9b7c1047867920e4c898ad2337e9c9476 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sun, 15 Oct 2023 14:14:22 -0400 Subject: [PATCH 098/134] let scheudler return const ref to factories created --- src/wmtk/Scheduler.hpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/wmtk/Scheduler.hpp b/src/wmtk/Scheduler.hpp index 4f42266d04..cb23fe3a93 100644 --- a/src/wmtk/Scheduler.hpp +++ b/src/wmtk/Scheduler.hpp @@ -31,30 +31,33 @@ class Scheduler // std::forward(args)...); //} - void add_operation_factory( + const operations::OperationFactoryBase& add_operation_factory( const std::string& name, std::unique_ptr&& ptr) { - m_factories[name] = std::move(ptr); + return *(m_factories[name] = std::move(ptr)); } template - void add_operation_type( + const operations::OperationFactory& add_operation_type( const std::string& name, const operations::OperationSettings& settings) { - add_operation_factory( - name, - std::make_unique>(settings)); + return static_cast&>( + add_operation_factory( + name, + std::make_unique>(settings))); } template - void add_operation_type( + const operations::OperationFactory& add_operation_type( const std::string& name, operations::OperationSettings&& settings) { - add_operation_factory( - name, - std::make_unique>(std::move(settings))); + return static_cast&>( + add_operation_factory( + name, + std::make_unique>( + std::move(settings)))); } From 78fed450deed95bbc6e14351800cc7e878ba124e Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sun, 15 Oct 2023 15:40:19 -0400 Subject: [PATCH 099/134] works? too big and too tired to proeprly comment --- .../internal/IsotropicRemeshing.cpp | 2 + src/wmtk/CMakeLists.txt | 1 + src/wmtk/Scheduler.cpp | 1 + src/wmtk/function/AutodiffFunction.cpp | 9 +- src/wmtk/function/DifferentiableFunction.cpp | 47 ++++++++- src/wmtk/function/DifferentiableFunction.hpp | 25 ++++- src/wmtk/function/Function.cpp | 15 +++ src/wmtk/function/Function.hpp | 4 +- src/wmtk/function/utils/AutoDiffRAII.cpp | 4 +- src/wmtk/function/utils/AutoDiffRAII.hpp | 2 +- src/wmtk/function/utils/AutoDiffUtils.hpp | 41 +++++--- src/wmtk/function/utils/CMakeLists.txt | 5 + .../utils/DifferentiableFunctionEvaluator.cpp | 34 +++++++ .../utils/DifferentiableFunctionEvaluator.hpp | 54 +++++++++++ src/wmtk/function/utils/FunctionEvaluator.cpp | 41 ++++++++ src/wmtk/function/utils/FunctionEvaluator.hpp | 70 ++++++++++++++ src/wmtk/operations/OperationFactory.hpp | 4 + .../tri_mesh/VertexLaplacianSmooth.cpp | 10 +- .../VertexSmoothUsingDifferentiableEnergy.cpp | 18 ++-- .../VertexSmoothUsingDifferentiableEnergy.hpp | 16 +++- .../internal/VertexSmoothGradientDescent.cpp | 20 ++-- .../internal/VertexSmoothGradientDescent.hpp | 5 +- .../internal/VertexSmoothNewtonMethod.cpp | 47 ++++++--- .../internal/VertexSmoothNewtonMethod.hpp | 5 +- ...VertexSmoothNewtonMethodWithLineSearch.cpp | 6 +- src/wmtk/optimization/CMakeLists.txt | 1 - src/wmtk/optimization/FunctionInterface.hpp | 67 ------------- src/wmtk/optimization/GradientDescent.hpp | 1 - src/wmtk/optimization/LineSearch.cpp | 63 ++++++++++-- src/wmtk/optimization/LineSearch.hpp | 95 ++++--------------- tests/CMakeLists.txt | 1 + .../test_component_isotropic_remeshing.cpp | 8 +- tests/components/test_smoothing.cpp | 72 +++++++------- tests/test_autodiff.cpp | 23 +++++ 34 files changed, 551 insertions(+), 266 deletions(-) create mode 100644 src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp create mode 100644 src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp create mode 100644 src/wmtk/function/utils/FunctionEvaluator.cpp create mode 100644 src/wmtk/function/utils/FunctionEvaluator.hpp delete mode 100644 src/wmtk/optimization/FunctionInterface.hpp delete mode 100644 src/wmtk/optimization/GradientDescent.hpp create mode 100644 tests/test_autodiff.cpp diff --git a/components/wmtk_components/isotropic_remeshing/internal/IsotropicRemeshing.cpp b/components/wmtk_components/isotropic_remeshing/internal/IsotropicRemeshing.cpp index ab62facb06..5740f47e1a 100644 --- a/components/wmtk_components/isotropic_remeshing/internal/IsotropicRemeshing.cpp +++ b/components/wmtk_components/isotropic_remeshing/internal/IsotropicRemeshing.cpp @@ -57,6 +57,8 @@ IsotropicRemeshing::IsotropicRemeshing(TriMesh& mesh, const double length, const op_settings.smooth_settings.position = m_position_handle; op_settings.smooth_settings.smooth_boundary = !m_lock_boundary; + op_settings.smooth_settings.initialize_invariants(m_mesh); + m_scheduler.add_operation_type( "smooth", op_settings); diff --git a/src/wmtk/CMakeLists.txt b/src/wmtk/CMakeLists.txt index c0fa5e784d..57e481bc6d 100644 --- a/src/wmtk/CMakeLists.txt +++ b/src/wmtk/CMakeLists.txt @@ -40,6 +40,7 @@ add_subdirectory(utils) add_subdirectory(attribute) add_subdirectory(simplex) add_subdirectory(operations) +add_subdirectory(optimization) add_subdirectory(autogen) add_subdirectory(invariants) diff --git a/src/wmtk/Scheduler.cpp b/src/wmtk/Scheduler.cpp index b79ad48fa6..f0b01cff12 100644 --- a/src/wmtk/Scheduler.cpp +++ b/src/wmtk/Scheduler.cpp @@ -27,6 +27,7 @@ void Scheduler::run_operation_on_all(PrimitiveType type, const std::string& name // enqueue_operations(ops); // TODO: pick some strategy for running these operations // tbb::parallel_for(ops, [&](const auto& ops) { (*op)(); }); + spdlog::info("Ran {} [{}] ops, {} succeeded, {} failed", number_of_performed_operations(), name, number_of_successful_operations(), number_of_failed_operations()); } void Scheduler::enqueue_operations(std::vector>&& ops) diff --git a/src/wmtk/function/AutodiffFunction.cpp b/src/wmtk/function/AutodiffFunction.cpp index c1f36bde06..cc07a45e8c 100644 --- a/src/wmtk/function/AutodiffFunction.cpp +++ b/src/wmtk/function/AutodiffFunction.cpp @@ -12,17 +12,18 @@ AutodiffFunction::AutodiffFunction( AutodiffFunction::~AutodiffFunction() = default; double AutodiffFunction::get_value(const Tuple& tuple) const { - auto scope = AutoDiffRAII(embedded_dimension()); + auto scope = utils::AutoDiffRAII(embedded_dimension()); return get_value_autodiff(tuple).getValue(); } Eigen::VectorXd AutodiffFunction::get_gradient(const Tuple& tuple) const { - auto scope = AutoDiffRAII(embedded_dimension()); - return get_value_autodiff(tuple).getGradient(); + auto scope = utils::AutoDiffRAII(embedded_dimension()); + auto v = get_value_autodiff(tuple); + return v.getGradient(); } Eigen::MatrixXd AutodiffFunction::get_hessian(const Tuple& tuple) const { - auto scope = AutoDiffRAII(embedded_dimension()); + auto scope = utils::AutoDiffRAII(embedded_dimension()); return get_value_autodiff(tuple).getHessian(); } } // namespace wmtk::function diff --git a/src/wmtk/function/DifferentiableFunction.cpp b/src/wmtk/function/DifferentiableFunction.cpp index 3a691f3860..9ae9b4907f 100644 --- a/src/wmtk/function/DifferentiableFunction.cpp +++ b/src/wmtk/function/DifferentiableFunction.cpp @@ -1,19 +1,58 @@ #include "DifferentiableFunction.hpp" +#include namespace wmtk::function { DifferentiableFunction::DifferentiableFunction( const Mesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) : Function(mesh) - , m_vertex_attribute_handle(vertex_attribute_handle) + , m_coordinate_vertex_attribute_handle(vertex_attribute_handle) {} -const MeshAttributeHandle DifferentiableFunction::get_vertex_attribute_handle() const + +Eigen::VectorXd DifferentiableFunction::get_one_ring_gradient(const Tuple& vertex) const +{ + auto simplices = + simplex::top_level_cofaces_tuples(mesh(), Simplex(PrimitiveType::Vertex, vertex)); + return get_gradient_sum(vertex, simplices); +} +Eigen::MatrixXd DifferentiableFunction::get_one_ring_hessian(const Tuple& vertex) const +{ + auto simplices = + simplex::top_level_cofaces_tuples(mesh(), Simplex(PrimitiveType::Vertex, vertex)); + return get_hessian_sum(vertex, simplices); +} +Eigen::VectorXd DifferentiableFunction::get_gradient_sum( + const Tuple& vertex, + const std::vector& top_level_simplices) const +{ + Eigen::VectorXd v = Eigen::VectorXd::Zero(embedded_dimension()); + for (const Tuple& cell : top_level_simplices) { + v += get_gradient(cell); + } + return v; +} +Eigen::MatrixXd DifferentiableFunction::get_hessian_sum( + const Tuple& vertex, + const std::vector& top_level_simplices) const +{ + Eigen::MatrixXd v = Eigen::MatrixXd::Zero(embedded_dimension(), embedded_dimension()); + for (const Tuple& cell : top_level_simplices) { + v += get_hessian(cell); + } + return v; +} + +const MeshAttributeHandle& DifferentiableFunction::get_vertex_attribute_handle() const +{ + return get_coordinate_attribute_handle(); +} +const MeshAttributeHandle& DifferentiableFunction::get_coordinate_attribute_handle() const { - return m_vertex_attribute_handle; + return m_coordinate_vertex_attribute_handle; } long DifferentiableFunction::embedded_dimension() const { - return mesh().get_attribute_dimension(get_vertex_attribute_handle()); + return mesh().get_attribute_dimension(get_coordinate_attribute_handle()); } } // namespace wmtk::function diff --git a/src/wmtk/function/DifferentiableFunction.hpp b/src/wmtk/function/DifferentiableFunction.hpp index b0d7d1c8c8..4adadbf04d 100644 --- a/src/wmtk/function/DifferentiableFunction.hpp +++ b/src/wmtk/function/DifferentiableFunction.hpp @@ -7,23 +7,38 @@ namespace wmtk::function { class DifferentiableFunction : public Function { public: + + // @param The mesh representing the domain the function is differentiated over + // @param The mesh's attribute that the function is differentiated against DifferentiableFunction( const Mesh& mesh, - const MeshAttributeHandle& vertex_attribute_handle); + const MeshAttributeHandle& coordinate_vertex_attribute_handle); virtual ~DifferentiableFunction() = default; - virtual Eigen::VectorXd get_gradient(const Tuple& tuple) const = 0; - virtual Eigen::MatrixXd get_hessian(const Tuple& tuple) const = 0; - const MeshAttributeHandle get_vertex_attribute_handle() const; + Eigen::VectorXd get_one_ring_gradient(const Tuple& vertex) const; + Eigen::MatrixXd get_one_ring_hessian(const Tuple& vertex) const; + + Eigen::VectorXd get_gradient_sum(const Tuple& tuple, const std::vector& top_level_simplices) const; + Eigen::MatrixXd get_hessian_sum(const Tuple& tuple, const std::vector& top_level_simplices) const; + + const MeshAttributeHandle& get_coordinate_attribute_handle() const; + + // alias for the coordinate_attribute_handle + const MeshAttributeHandle& get_vertex_attribute_handle() const; // The dimension that this differentiable function lies in // (this is the dimension of the vector space the vertex attribute lies in) long embedded_dimension() const; +protected: + virtual Eigen::VectorXd get_gradient(const Tuple& top_level_simplex) const = 0; + + // TODO: should differentiable function be required to be twice differentiable? + virtual Eigen::MatrixXd get_hessian(const Tuple& top_level_simplex) const = 0; private: - const MeshAttributeHandle m_vertex_attribute_handle; + const MeshAttributeHandle m_coordinate_vertex_attribute_handle; }; } // namespace wmtk::function diff --git a/src/wmtk/function/Function.cpp b/src/wmtk/function/Function.cpp index e7d8e46583..a98701efa1 100644 --- a/src/wmtk/function/Function.cpp +++ b/src/wmtk/function/Function.cpp @@ -1,4 +1,5 @@ #include "Function.hpp" +#include namespace wmtk::function { Function::Function(const Mesh& mesh) @@ -7,6 +8,20 @@ Function::Function(const Mesh& mesh) Function::~Function() = default; +double Function::get_one_ring_value(const Tuple& vertex) const +{ + auto simplices = + simplex::top_level_cofaces_tuples(mesh(), Simplex(PrimitiveType::Vertex, vertex)); + return get_value_sum(simplices); +} +double Function::get_value_sum(const std::vector& top_level_simplices) const +{ + double v = 0; + for (const Tuple& cell : top_level_simplices) { + v += get_value(cell); + } + return v; +} const Mesh& Function::mesh() const { diff --git a/src/wmtk/function/Function.hpp b/src/wmtk/function/Function.hpp index b19bf8f0c7..58368728d1 100644 --- a/src/wmtk/function/Function.hpp +++ b/src/wmtk/function/Function.hpp @@ -14,10 +14,12 @@ class Function virtual ~Function(); const Mesh& mesh() const; + double get_one_ring_value(const Tuple& vertex) const; + double get_value_sum(const std::vector& top_level_simplices) const; public: // evaluate the function on the top level simplex of the tuple - virtual double get_value(const Tuple& tuple) const = 0; + virtual double get_value(const Tuple& top_level_simplex) const = 0; }; } // namespace function } // namespace wmtk diff --git a/src/wmtk/function/utils/AutoDiffRAII.cpp b/src/wmtk/function/utils/AutoDiffRAII.cpp index a6ad9314d6..ad0db2a3ec 100644 --- a/src/wmtk/function/utils/AutoDiffRAII.cpp +++ b/src/wmtk/function/utils/AutoDiffRAII.cpp @@ -1,7 +1,7 @@ #include "AutoDiffRAII.hpp" #include "autodiff.h" -namespace wmtk::function { +namespace wmtk::function::utils { AutoDiffRAII::AutoDiffRAII(size_t size) : m_previous_variable_count(DiffScalarBase::getVariableCount()) { @@ -11,4 +11,4 @@ AutoDiffRAII::~AutoDiffRAII() { DiffScalarBase::setVariableCount(m_previous_variable_count); } -} // namespace wmtk::function +} // namespace wmtk::function::utils diff --git a/src/wmtk/function/utils/AutoDiffRAII.hpp b/src/wmtk/function/utils/AutoDiffRAII.hpp index 50d272f32d..25792a6db3 100644 --- a/src/wmtk/function/utils/AutoDiffRAII.hpp +++ b/src/wmtk/function/utils/AutoDiffRAII.hpp @@ -2,7 +2,7 @@ #include -namespace wmtk::function { +namespace wmtk::function::utils { class AutoDiffRAII diff --git a/src/wmtk/function/utils/AutoDiffUtils.hpp b/src/wmtk/function/utils/AutoDiffUtils.hpp index 71adbbf9f8..05ff287432 100644 --- a/src/wmtk/function/utils/AutoDiffUtils.hpp +++ b/src/wmtk/function/utils/AutoDiffUtils.hpp @@ -49,21 +49,38 @@ auto as_DScalar(const Eigen::MatrixBase& data) { constexpr static int Rows = Derived::RowsAtCompileTime; constexpr static int Cols = Derived::ColsAtCompileTime; + int rows = data.rows(); + int cols = data.cols(); - Eigen::Matrix M = - make_DScalar_matrix(data.rows(), data.cols()); - + assert(rows * cols == DiffScalarBase::getVariableCount()); - for (int j = 0; j < M.size(); ++j) { - M(j) = data(j); + using RetType = Eigen::Matrix; + if constexpr (Rows != Eigen::Dynamic && Cols != Eigen::Dynamic) { + return RetType::NullaryExpr([&](int row, int col) { + int index; + if constexpr (RetType::IsRowMajor) { + index = Rows * col + row; + } else { + index = Cols * row + col; + } + return DScalarType(index, data(row, col)); + }) + .eval(); + } else { + return RetType::NullaryExpr( + rows, + cols, + [&](int row, int col) { + int index; + if constexpr (RetType::IsRowMajor) { + index = rows * col + row; + } else { + index = cols * row + col; + } + return DScalarType(index, data(row, col)); + }) + .eval(); } - // M.noalias() = M.binaryExpr(data, [](DScalarType v, const auto& d) -> DScalarType { - // v = d; - // return v; - // }); - - - return M; } diff --git a/src/wmtk/function/utils/CMakeLists.txt b/src/wmtk/function/utils/CMakeLists.txt index 66abd05be8..c5dcdc0036 100644 --- a/src/wmtk/function/utils/CMakeLists.txt +++ b/src/wmtk/function/utils/CMakeLists.txt @@ -8,5 +8,10 @@ set(SRC_FILES AutoDiffRAII.cpp amips.hpp amips.cpp + + FunctionEvaluator.hpp + FunctionEvaluator.cpp + DifferentiableFunctionEvaluator.hpp + DifferentiableFunctionEvaluator.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp new file mode 100644 index 0000000000..880cc59fc7 --- /dev/null +++ b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp @@ -0,0 +1,34 @@ +#include "DifferentiableFunctionEvaluator.hpp" +namespace wmtk::function::utils { + +DifferentiableFunctionEvaluator::DifferentiableFunctionEvaluator( + const function::DifferentiableFunction& function, + Accessor& accessor, + const Tuple& tuple) + : FunctionEvaluator(function, accessor, tuple) +{} + +auto DifferentiableFunctionEvaluator::function() const -> const function::DifferentiableFunction& +{ + return static_cast(FunctionEvaluator::function()); +} + +auto DifferentiableFunctionEvaluator::get_gradient() const -> Vector +{ + return function().get_gradient_sum(tuple(), top_level_cofaces()); +} +auto DifferentiableFunctionEvaluator::get_hessian() const -> Matrix +{ + return function().get_hessian_sum(tuple(), top_level_cofaces()); +} +auto DifferentiableFunctionEvaluator::get_gradient(double v) -> Vector +{ + store(v); + return get_gradient(); +} +auto DifferentiableFunctionEvaluator::get_hessian(double v) -> Matrix +{ + store(v); + return get_hessian(); +} +} // namespace wmtk::function::utils diff --git a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp new file mode 100644 index 0000000000..db4e4dcebe --- /dev/null +++ b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp @@ -0,0 +1,54 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include "FunctionEvaluator.hpp" + +namespace wmtk::function::utils { + + +// Evaluates a function at a particular vertex of a mesh +// NOTE that this modifies attributes in the mesh. +// This should only be called from within a Scope so evaluates can be undone +// +class DifferentiableFunctionEvaluator : public FunctionEvaluator +{ +public: + DifferentiableFunctionEvaluator( + const function::DifferentiableFunction& function, + Accessor& accessor, + const Tuple& tuple); + using Vector = Eigen::VectorXd; + using Matrix = Eigen::MatrixXd; + + + Vector get_gradient() const; + Matrix get_hessian() const; + + template + Vector get_gradient(const Eigen::MatrixBase& v); + template + Matrix get_hessian(const Eigen::MatrixBase& v); + + Vector get_gradient(double v); + Matrix get_hessian(double v); + const function::DifferentiableFunction& function() const; +}; + + +template +auto DifferentiableFunctionEvaluator::get_gradient(const Eigen::MatrixBase& v) -> Vector +{ + store(v); + return get_gradient(); +} +template +auto DifferentiableFunctionEvaluator::get_hessian(const Eigen::MatrixBase& v) -> Matrix +{ + store(v); + return get_hessian(); +} +} // namespace wmtk::function::utils diff --git a/src/wmtk/function/utils/FunctionEvaluator.cpp b/src/wmtk/function/utils/FunctionEvaluator.cpp new file mode 100644 index 0000000000..ed7e952316 --- /dev/null +++ b/src/wmtk/function/utils/FunctionEvaluator.cpp @@ -0,0 +1,41 @@ +#include "FunctionEvaluator.hpp" +#include +namespace wmtk::function::utils { + +FunctionEvaluator::FunctionEvaluator( + const function::Function& function, + Accessor& accessor, + const Tuple& tuple) + : m_function(function) + , m_accessor(accessor) + , m_tuple(tuple) +{ + m_top_level_cofaces = compute_top_level_cofaces(); +} + + +void FunctionEvaluator::store(double v) +{ + m_accessor.scalar_attribute(m_tuple) = v; +} + + +double FunctionEvaluator::get_value() const +{ + return m_function.get_value_sum(m_top_level_cofaces); +} +auto FunctionEvaluator::get_value(double v) -> double +{ + store(v); + return get_value(); +} + +const std::vector& FunctionEvaluator::top_level_cofaces() const +{ + return m_top_level_cofaces; +} +std::vector FunctionEvaluator::compute_top_level_cofaces() const +{ + return simplex::top_level_cofaces_tuples(mesh(), Simplex(PrimitiveType::Vertex, tuple())); +} +} // namespace wmtk::function::utils diff --git a/src/wmtk/function/utils/FunctionEvaluator.hpp b/src/wmtk/function/utils/FunctionEvaluator.hpp new file mode 100644 index 0000000000..d9cd6b9e93 --- /dev/null +++ b/src/wmtk/function/utils/FunctionEvaluator.hpp @@ -0,0 +1,70 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace wmtk::function::utils { + + +// Evaluates a function at a particular vertex of a mesh +// NOTE that this modifies attributes in the mesh. +// This should only be called from within a Scope so evaluates can be undone +// +class FunctionEvaluator +{ +public: + FunctionEvaluator(const Function& function, Accessor& accessor, const Tuple& tuple); + + + auto get_coordinate() { return m_accessor.vector_attribute(m_tuple); } + auto get_const_coordinate() const { return m_accessor.const_vector_attribute(m_tuple); } + + void store(double v); + template + void store(const Eigen::MatrixBase& v); + + auto get_coordinate() const { return get_const_coordinate(); } + + double get_value() const; + + template + double get_value(const Eigen::MatrixBase& v); + + double get_value(double v); + + + const Tuple& tuple() const { return m_tuple; } + Mesh& mesh() { return m_accessor.mesh(); } + const Mesh& mesh() const { return m_accessor.mesh(); } + Accessor& accessor() { return m_accessor; } + + const Function& function() const { return m_function; } + + const std::vector& top_level_cofaces() const; + +private: + const Function& m_function; + Accessor& m_accessor; + const Tuple& m_tuple; + + // cache the top simplices + std::vector m_top_level_cofaces; + std::vector compute_top_level_cofaces() const; +}; + + +template +void FunctionEvaluator::store(const Eigen::MatrixBase& v) +{ + m_accessor.vector_attribute(m_tuple) = v; +} +template +double FunctionEvaluator::get_value(const Eigen::MatrixBase& v) +{ + store(v); + return get_value(); +} +} // namespace wmtk::function::utils diff --git a/src/wmtk/operations/OperationFactory.hpp b/src/wmtk/operations/OperationFactory.hpp index 1608ed286f..a2d024df1e 100644 --- a/src/wmtk/operations/OperationFactory.hpp +++ b/src/wmtk/operations/OperationFactory.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include "Operation.hpp" @@ -31,9 +32,12 @@ class OperationFactory : public OperationFactoryBase std::unique_ptr create(wmtk::Mesh& m, const Tuple& t) const override { + spdlog::info("Using default create"); return std::make_unique(m, t, m_settings); } + const OperationSettings& settings() const { return m_settings; } + protected: const OperationSettings m_settings; }; diff --git a/src/wmtk/operations/tri_mesh/VertexLaplacianSmooth.cpp b/src/wmtk/operations/tri_mesh/VertexLaplacianSmooth.cpp index f2ca5f61ab..b1826d979b 100644 --- a/src/wmtk/operations/tri_mesh/VertexLaplacianSmooth.cpp +++ b/src/wmtk/operations/tri_mesh/VertexLaplacianSmooth.cpp @@ -31,19 +31,15 @@ std::string VertexLaplacianSmooth::name() const bool VertexLaplacianSmooth::execute() { - if (!tri_mesh::VertexAttributesUpdateBase::execute()) { - return false; - } - const Tuple tup = tri_mesh::VertexAttributesUpdateBase::return_tuple(); - const std::vector one_ring = SimplicialComplex::vertex_one_ring(mesh(), tup); - auto p_mid = m_pos_accessor.vector_attribute(tup); + const std::vector one_ring = SimplicialComplex::vertex_one_ring(mesh(), input_tuple()); + auto p_mid = m_pos_accessor.vector_attribute(input_tuple()); p_mid = Eigen::Vector3d::Zero(); for (const Simplex& s : one_ring) { p_mid += m_pos_accessor.vector_attribute(s.tuple()); } p_mid /= one_ring.size(); - return true; + return tri_mesh::VertexAttributesUpdateBase::execute(); } } // namespace tri_mesh diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp index ed856f0f7e..ed6d6a567b 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp @@ -26,19 +26,15 @@ std::string VertexSmoothUsingDifferentiableEnergy::name() const return "tri_mesh_vertex_smooth_using_differentiable_energy"; } -template -optimization::FunctionInterface VertexSmoothUsingDifferentiableEnergy::get_function_interface( - Accessor& accessor) const +function::utils::DifferentiableFunctionEvaluator +VertexSmoothUsingDifferentiableEnergy::get_function_evaluator(Accessor& accessor) const { - return optimization::FunctionInterface(input_tuple(), accessor, *m_settings.energy); + return function::utils::DifferentiableFunctionEvaluator( + *m_settings.energy, + accessor, + input_tuple() + ); } -template <> -optimization::FunctionInterface<2> VertexSmoothUsingDifferentiableEnergy::get_function_interface<2>( - Accessor& accessor) const; - -template <> -optimization::FunctionInterface<3> VertexSmoothUsingDifferentiableEnergy::get_function_interface<3>( - Accessor& accessor) const; Accessor VertexSmoothUsingDifferentiableEnergy::coordinate_accessor() diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp index 786e9b56f9..5150924d5d 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp @@ -1,9 +1,14 @@ #pragma once -#include +#include #include -#include #include "VertexAttributesUpdateBase.hpp" +namespace wmtk { +namespace function { +class DifferentiableFunction; + +} +} // namespace wmtk namespace wmtk::operations { namespace tri_mesh { class VertexSmoothUsingDifferentiableEnergy; @@ -27,20 +32,21 @@ struct OperationSettings namespace tri_mesh { class VertexSmoothUsingDifferentiableEnergy : public VertexAttributesUpdateBase { -public: +protected: VertexSmoothUsingDifferentiableEnergy( Mesh& m, const Tuple& t, const OperationSettings& settings); +public: std::string name() const override; static PrimitiveType primitive_type() { return PrimitiveType::Vertex; } protected: - template - optimization::FunctionInterface get_function_interface(Accessor& accessor) const; + function::utils::DifferentiableFunctionEvaluator get_function_evaluator( + Accessor& accessor) const; MeshAttributeHandle coordinate_handle() const { return m_settings.coordinate_handle; } Accessor coordinate_accessor(); diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp index 2c745f58fe..e680087658 100644 --- a/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp @@ -13,9 +13,8 @@ std::string VertexSmoothGradientDescent::name() const } -template -Eigen::Vector VertexSmoothGradientDescent::get_descent_direction( - optimization::FunctionInterface& f) const +Eigen::VectorXd VertexSmoothGradientDescent::get_descent_direction( + function::utils::DifferentiableFunctionEvaluator& f) const { return -f.get_gradient(); } @@ -23,11 +22,11 @@ Eigen::Vector VertexSmoothGradientDescent::get_descent_direction( bool VertexSmoothGradientDescent::execute() { auto accessor = coordinate_accessor(); - auto interface = get_function_interface<2>(accessor); + auto evaluator = get_function_evaluator(accessor); - auto pos = interface.get_coordinate(); - Eigen::Vector2d next_pos = pos + m_settings.step_size * get_descent_direction(interface); - interface.store(next_pos); + auto pos = evaluator.get_coordinate(); + Eigen::Vector2d next_pos = pos + m_settings.step_size * get_descent_direction(evaluator); + evaluator.store(next_pos); if (!tri_mesh::VertexSmoothUsingDifferentiableEnergy::execute()) { wmtk::logger().debug("execute failed"); @@ -35,4 +34,11 @@ bool VertexSmoothGradientDescent::execute() } return true; } +std::vector VertexSmoothGradientDescent::priority() const +{ + double gradnorm = m_settings.energy->get_one_ring_gradient(input_tuple()).norm(); + std::vector r; + r.emplace_back(-gradnorm); + return r; +} } // namespace wmtk::operations::tri_mesh::internal diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.hpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.hpp index 24cdc56427..34ce7203e2 100644 --- a/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.hpp +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.hpp @@ -11,9 +11,10 @@ class VertexSmoothGradientDescent : public VertexSmoothUsingDifferentiableEnergy const Tuple& t, const OperationSettings& settings); + std::vector priority() const; + protected: - template - Eigen::Vector get_descent_direction(optimization::FunctionInterface&) const; + Eigen::VectorXd get_descent_direction(function::utils::DifferentiableFunctionEvaluator&) const; bool execute() override; std::string name() const; }; diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp index 87697725d8..387a4eab80 100644 --- a/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp @@ -6,33 +6,45 @@ VertexSmoothNewtonMethod::VertexSmoothNewtonMethod( const Tuple& t, const OperationSettings& settings) : VertexSmoothUsingDifferentiableEnergy(m, t, settings) -{} +{ +} std::string VertexSmoothNewtonMethod::name() const { return "tri_mesh_vertex_smooth_newton_method"; } -template -Eigen::Vector VertexSmoothNewtonMethod::get_descent_direction( - optimization::FunctionInterface& f) const +Eigen::VectorXd VertexSmoothNewtonMethod::get_descent_direction( + function::utils::DifferentiableFunctionEvaluator& f) const { return -f.get_hessian().ldlt().solve(f.get_gradient()); } -template <> -Eigen::Vector VertexSmoothNewtonMethod::get_descent_direction<2>( - optimization::FunctionInterface<2>& f) const; -template <> -Eigen::Vector VertexSmoothNewtonMethod::get_descent_direction<3>( - optimization::FunctionInterface<3>& f) const; bool VertexSmoothNewtonMethod::execute() { auto accessor = coordinate_accessor(); - auto interface = get_function_interface<2>(accessor); + auto evaluator = get_function_evaluator(accessor); + + auto pos = evaluator.get_coordinate().eval(); + double value = evaluator.get_value(pos); + auto dir = get_descent_direction(evaluator); + Eigen::Vector2d next_pos = pos + m_settings.step_size * dir; + double new_value = evaluator.get_value(next_pos); - auto pos = interface.get_coordinate(); - Eigen::Vector2d next_pos = pos + m_settings.step_size * get_descent_direction(interface); - interface.store(next_pos); + /* + spdlog::info( + "Went from f({},{})={} to f({},{})={} ====== +={} * {},{}", + pos.x(), + pos.y(), + value, + next_pos.x(), + next_pos.y(), + new_value, + m_settings.step_size, + dir.x(), + dir.y()); + + */ + evaluator.store(next_pos); if (!tri_mesh::VertexSmoothUsingDifferentiableEnergy::execute()) { @@ -40,5 +52,12 @@ bool VertexSmoothNewtonMethod::execute() } return true; } +std::vector VertexSmoothNewtonMethod::priority() const +{ + double gradnorm = m_settings.energy->get_one_ring_gradient(input_tuple()).norm(); + std::vector r; + r.emplace_back(-gradnorm); + return r; +} } // namespace wmtk::operations::tri_mesh::internal diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.hpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.hpp index 5bcbc25730..2fea5b2f0c 100644 --- a/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.hpp +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.hpp @@ -11,10 +11,11 @@ class VertexSmoothNewtonMethod : public VertexSmoothUsingDifferentiableEnergy const Tuple& t, const OperationSettings& settings); + std::vector priority() const; + protected: bool execute() override; - template - Eigen::Vector get_descent_direction(optimization::FunctionInterface&) const; + Eigen::VectorXd get_descent_direction(function::utils::DifferentiableFunctionEvaluator&) const; std::string name() const; }; } // namespace wmtk::operations::tri_mesh::internal diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethodWithLineSearch.cpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethodWithLineSearch.cpp index 89ba75559b..bc0bb36a37 100644 --- a/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethodWithLineSearch.cpp +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethodWithLineSearch.cpp @@ -13,11 +13,11 @@ VertexSmoothNewtonMethodWithLineSearch::VertexSmoothNewtonMethodWithLineSearch( bool VertexSmoothNewtonMethodWithLineSearch::execute() { auto accessor = coordinate_accessor(); - auto interface = get_function_interface<2>(accessor); + auto evaluator = get_function_evaluator(accessor); - Eigen::Vector2d direction = get_descent_direction(interface); + Eigen::Vector2d direction = get_descent_direction(evaluator); - optimization::LineSearch<2> line_search(interface, invariants()); + optimization::LineSearch line_search(evaluator, invariants()); line_search.set_create_scope( false); // since we're in an operation we will fail if the seach doesn't do waht we want diff --git a/src/wmtk/optimization/CMakeLists.txt b/src/wmtk/optimization/CMakeLists.txt index d6c9e7fbcf..3529f50732 100644 --- a/src/wmtk/optimization/CMakeLists.txt +++ b/src/wmtk/optimization/CMakeLists.txt @@ -1,5 +1,4 @@ set(SRC_FILES - FunctionInterface.hpp LineSearch.hpp LineSearch.cpp ) diff --git a/src/wmtk/optimization/FunctionInterface.hpp b/src/wmtk/optimization/FunctionInterface.hpp deleted file mode 100644 index e0ffd7c9ca..0000000000 --- a/src/wmtk/optimization/FunctionInterface.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -namespace wmtk::optimization { -template -class FunctionInterface -{ -public: - using Vector = wmtk::Vector; - using Matrix = wmtk::SquareMatrix; - FunctionInterface( - const Tuple& tuple, - Accessor& accessor, - const function::DifferentiableFunction& function) - : m_tuple(tuple) - , m_accessor(accessor) - , m_function(function) - {} - - - auto get_coordinate() { return m_accessor.vector_attribute(m_tuple); } - auto get_const_coordinate() const { return m_accessor.const_vector_attribute(m_tuple); } - template - void store(const Eigen::MatrixBase& v) - { - m_accessor.vector_attribute(m_tuple) = v; - } - auto get_coordinate() const { return get_const_coordinate(); } - - double get_value() const { return m_function.get_value(m_tuple); } - Vector get_gradient() const { return m_function.get_gradient(m_tuple); } - Matrix get_hessian() const { return m_function.get_hessian(m_tuple); } - - template - double get_value(const Eigen::MatrixBase& v) - { - store(v); - return get_value(); - } - template - Vector get_gradient(const Eigen::MatrixBase& v) - { - store(v); - return get_gradient(); - } - template - Matrix get_hessian(const Eigen::MatrixBase& v) - { - store(v); - return get_hessian(); - } - - - const Tuple& tuple() const { return m_tuple; } - Mesh& mesh() { return m_accessor.mesh(); } - -private: - const Tuple& m_tuple; - Accessor& m_accessor; - const function::DifferentiableFunction& m_function; -}; -} // namespace wmtk::optimization diff --git a/src/wmtk/optimization/GradientDescent.hpp b/src/wmtk/optimization/GradientDescent.hpp deleted file mode 100644 index 8b13789179..0000000000 --- a/src/wmtk/optimization/GradientDescent.hpp +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/wmtk/optimization/LineSearch.cpp b/src/wmtk/optimization/LineSearch.cpp index df5c1483c3..5f20bd4f8e 100644 --- a/src/wmtk/optimization/LineSearch.cpp +++ b/src/wmtk/optimization/LineSearch.cpp @@ -1,14 +1,65 @@ -#include -#include +#include "LineSearch.hpp" namespace wmtk::optimization { -std::vector LineSearchBase::modified_top_simplices() const + +LineSearch::LineSearch( + function::utils::FunctionEvaluator& interface, + const InvariantCollection& invariants) + : m_interface(interface) + , m_invariants(invariants) +{} + +const std::vector& LineSearch::top_level_cofaces() const { - return simplex::top_level_cofaces_tuples(interface.mesh()); + return m_interface.top_level_cofaces(); } -bool LineSearchBase::check_state() const + +bool LineSearch::check_state() const { + PrimitiveType top_type = m_interface.mesh().top_simplex_type(); bool before_pass = m_invariants.before(m_interface.tuple()); - bool after_pass = m_invariants.after(top_type, modified_top_simplices); + bool after_pass = m_invariants.after(top_type, top_level_cofaces()); return before_pass && after_pass; } +double LineSearch::run(const Eigen::VectorXd& direction, double step_size) +{ + if (!check_state()) { + return 0; + } + if (m_create_scope) { + { + auto scope = m_interface.mesh().create_scope(); + double retval = _run(direction, step_size); + if (retval == 0) { + scope.mark_failed(); + } + return retval; + } + + } else { + return _run(direction, step_size); + } +} + +double LineSearch::_run(const Eigen::VectorXd& direction, double init_step_size) +{ + int steps = 0; + // just to make sure we try the initial stepsize + double step_size = init_step_size; + double next_step_size = step_size; + double min_step_size = m_min_step_size_ratio * step_size; + Vector current_pos = m_interface.get_const_coordinate(); + Vector new_pos; + do { + new_pos = current_pos + direction * step_size; + m_interface.store(new_pos); + + step_size = next_step_size; + next_step_size /= 2; + } while (steps++ < m_max_steps && step_size > min_step_size && !check_state()); + if (steps == m_max_steps || step_size < min_step_size) { + return 0; + } else { + return step_size; + } +} } // namespace wmtk::optimization diff --git a/src/wmtk/optimization/LineSearch.hpp b/src/wmtk/optimization/LineSearch.hpp index 56134e1bdc..60551cb028 100644 --- a/src/wmtk/optimization/LineSearch.hpp +++ b/src/wmtk/optimization/LineSearch.hpp @@ -1,21 +1,24 @@ #pragma once +#include #include -#include "FunctionInterface.hpp" namespace wmtk::optimization { -template class LineSearch { public: using InvariantCollection = wmtk::InvariantCollection; - LineSearch(FunctionInterface& interface, const InvariantCollection& invariants) - : m_interface(interface) - , m_invariants(invariants) - {} + LineSearch( + function::utils::FunctionEvaluator& interface, + const InvariantCollection& invariants); + + using Vector = Eigen::VectorXd; + + double run(const Eigen::VectorXd& direction, double step_size); + double _run(const Eigen::VectorXd& direction, double step_size); void set_create_scope(bool enable) { m_create_scope = enable; } void set_max_steps(long max_steps) { m_max_steps = max_steps; } @@ -24,86 +27,24 @@ class LineSearch m_min_step_size_ratio = min_step_size_ratio; } - - template - double run(const Eigen::MatrixBase& direction, double step_size); - template - double _run( - const Eigen::MatrixBase& direction, - double step_size, - const std::vector& modified_top_simplices); - protected: - std::vector modified_top_simplices() const; - - // TODO: formally define what checking the state means - // we currently make sure that we pass before on the input tuple and after on all top level - // simplices, but should we be passing all of that every time? - bool check_state() const; - - FunctionInterface& m_interface; + function::utils::FunctionEvaluator& m_interface; const InvariantCollection& m_invariants; bool m_create_scope = true; long m_max_steps = 10; double m_min_step_size_ratio = 1e-6; -}; - -template -template -double LineSearch::run(const Eigen::MatrixBase& direction, double step_size) -{ - PrimitiveType top_type = m_interface.mesh().top_simplex_type(); - std::vector modified_top_simplices = this->modified_top_simplices(); - if (!check_state()) { - return 0; - } - if (m_create_scope) { - { - auto scope = m_interface.mesh().create_scope(); - double retval = _run(direction, step_size, modified_top_simplices); - if (retval == 0) { - scope.mark_failed(); - } - return retval; - } + const std::vector& top_level_cofaces() const; - } else { - return _run(direction, step_size, modified_top_simplices); - } -} + // TODO: formally define what checking the state means + // we currently make sure that we pass before on the input tuple and after on all top level + // simplices, but should we be passing all of that every time? + bool check_state() const; -template -template -double LineSearch::_run( - const Eigen::MatrixBase& direction, - double init_step_size, - const std::vector& modified_top_simplices) -{ - return 0; - /* - PrimitiveType top_type = interface.mesh().top_simplex_type(); - int steps = 0; - // just to make sure we try the initial stepsize - double step_size = init_step_size; - double next_step_size = step_size; - double min_step_size = m_min_step_size_ratio * step_size; - Vector current_pos = interface.get_const_coordinate(); - Vector new_pos; - do { - new_pos = current_pos + direction * step_size; - m_function_interface.store(new_pos); +public: +protected: +}; - step_size = next_step_size; - next_step_size /= 2; - } while (steps++ < m_max_steps && step_size > min_step_size && !check_state()); - if (steps == m_max_steps || step_size < min_step_size) { - return 0; - } else { - return step_size; - } - */ -} } // namespace wmtk::optimization diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 42ac22b334..0bbd9d497e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,6 +28,7 @@ set(TEST_SOURCES test_3d_operations.cpp test_multi_mesh.cpp test_variant_metaprogramming.cpp + test_autodiff.cpp tools/DEBUG_PointMesh.hpp diff --git a/tests/components/test_component_isotropic_remeshing.cpp b/tests/components/test_component_isotropic_remeshing.cpp index dfab7ad1a1..6d96cb88d7 100644 --- a/tests/components/test_component_isotropic_remeshing.cpp +++ b/tests/components/test_component_isotropic_remeshing.cpp @@ -49,6 +49,7 @@ TEST_CASE("smoothing_bunny", "[components][isotropic_remeshing][2D]") OperationSettings op_settings; op_settings.position = mesh.get_attribute_handle("position", PrimitiveType::Vertex); + op_settings.initialize_invariants(mesh); Scheduler scheduler(mesh); scheduler.add_operation_type("vertex_smooth", op_settings); @@ -166,7 +167,9 @@ TEST_CASE("tangential_smoothing", "[components][isotropic_remeshing][2D]") v4 = mesh.tuple_from_id(PrimitiveType::Vertex, 4); Eigen::Vector3d after_smooth = pos.vector_attribute(v4); - CHECK((after_smooth - Eigen::Vector3d{1, 0, p_init[2]}).squaredNorm() == 0); + Eigen::Vector3d target = Eigen::Vector3d{1, 0, p_init[2]}; + std::cout << after_smooth.transpose() << " == " << target.transpose() << std::endl; + CHECK((after_smooth - target).squaredNorm() == 0); } TEST_CASE("tangential_smoothing_boundary", "[components][isotropic_remeshing][2D]") @@ -181,6 +184,8 @@ TEST_CASE("tangential_smoothing_boundary", "[components][isotropic_remeshing][2D mesh.get_attribute_handle("position", PrimitiveType::Vertex); op_settings.smooth_settings.smooth_boundary = true; + op_settings.smooth_settings.initialize_invariants(mesh); + // offset interior vertex auto pos = mesh.create_accessor(op_settings.smooth_settings.position); Tuple v1 = mesh.tuple_from_id(PrimitiveType::Vertex, 1); @@ -550,6 +555,7 @@ TEST_CASE("swap_edge_for_valence", "[components][isotropic_remeshing][swap][2D]" { OperationSettings op_settings; op_settings.must_improve_valence = true; + // op_settings.initialize_invariants(mesh); const Tuple e = mesh.edge_tuple_between_v1_v2(6, 7, 5); EdgeSwap op(mesh, e, op_settings); const bool success = op(); diff --git a/tests/components/test_smoothing.cpp b/tests/components/test_smoothing.cpp index af7ae84d4b..e06ae604bb 100644 --- a/tests/components/test_smoothing.cpp +++ b/tests/components/test_smoothing.cpp @@ -1,8 +1,6 @@ #include #include -#include -#include -#include +#include #include #include #include "../tools/DEBUG_TriMesh.hpp" @@ -15,65 +13,73 @@ TEST_CASE("smoothing_Newton_Method") { DEBUG_TriMesh mesh = ten_triangles_with_position(2); OperationSettings op_settings; - op_settings.uv_position = mesh.get_attribute_handle("position", PrimitiveType::Vertex); + op_settings.coordinate_handle = + mesh.get_attribute_handle("position", PrimitiveType::Vertex); op_settings.smooth_boundary = false; op_settings.second_order = true; op_settings.line_search = false; - op_settings.energy = std::make_unique( - mesh, - mesh.get_attribute_handle("position", PrimitiveType::Vertex)); + op_settings.step_size = 0.1; + op_settings.energy = std::make_unique(mesh, op_settings.coordinate_handle); op_settings.initialize_invariants(mesh); + + + spdlog::info("HJELL?"); Scheduler scheduler(mesh); + const auto& factory = + scheduler.add_operation_type( + "optimize_vertices", + std::move(op_settings)); Tuple tuple = mesh.face_tuple_from_vids(2, 4, 5); - while (op_settings.energy->get_gradient(tuple).norm() > 1e-10) { - scheduler.add_operation_factory( - "tri_mesh_smooth_vertex_newton_method", - std::make_unique(op_settings)); - scheduler.run_operation_on_all( - PrimitiveType::Vertex, - "tri_mesh_smooth_vertex_newton_method"); + spdlog::warn("Initial valuenorm: {}", factory.settings().energy->get_one_ring_value(tuple)); + spdlog::warn( + "Initial gradient: norm: {}", + factory.settings().energy->get_one_ring_gradient(tuple).norm()); + while (factory.settings().energy->get_one_ring_gradient(tuple).norm() > 1e-10) { + scheduler.run_operation_on_all(PrimitiveType::Vertex, "optimize_vertices"); + REQUIRE(scheduler.number_of_successful_operations() > 0); tuple = mesh.face_tuple_from_vids(2, 4, 5); } - ConstAccessor pos = mesh.create_const_accessor(op_settings.uv_position); + ConstAccessor pos = mesh.create_const_accessor(op_settings.coordinate_handle); Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); Eigen::Vector2d uv1 = pos.const_vector_attribute(mesh.switch_vertex(tuple)); Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh.switch_vertex(mesh.switch_edge(tuple))); - REQUIRE((uv0 - uv1).norm() - (uv1 - uv2).norm() < 1e-6); - REQUIRE((uv0 - uv1).norm() - (uv0 - uv2).norm() < 1e-6); - REQUIRE((uv1 - uv2).norm() - (uv0 - uv2).norm() < 1e-6); + CHECK((uv0 - uv1).norm() - (uv1 - uv2).norm() < 1e-6); + CHECK((uv0 - uv1).norm() - (uv0 - uv2).norm() < 1e-6); + CHECK((uv1 - uv2).norm() - (uv0 - uv2).norm() < 1e-6); } TEST_CASE("smoothing_Newton_Method_line_search") { DEBUG_TriMesh mesh = ten_triangles_with_position(2); OperationSettings op_settings; - op_settings.uv_position = mesh.get_attribute_handle("position", PrimitiveType::Vertex); + op_settings.coordinate_handle = + mesh.get_attribute_handle("position", PrimitiveType::Vertex); op_settings.smooth_boundary = false; op_settings.second_order = true; op_settings.line_search = true; - op_settings.energy = std::make_unique( - mesh, - mesh.get_attribute_handle("position", PrimitiveType::Vertex)); + op_settings.energy = std::make_unique(mesh, op_settings.coordinate_handle); op_settings.initialize_invariants(mesh); Scheduler scheduler(mesh); + const auto& factory = + scheduler.add_operation_type( + "optimize_vertices", + std::move(op_settings)); Tuple tuple = mesh.face_tuple_from_vids(2, 4, 5); - while (op_settings.energy->get_gradient(tuple).norm() > 1e-10) { - scheduler.add_operation_factory( - "tri_mesh_smooth_vertex_newton_method", - std::make_unique(op_settings)); - scheduler.run_operation_on_all( - PrimitiveType::Vertex, - "tri_mesh_smooth_vertex_newton_method"); + spdlog::warn( + "Initial gradient: norm: {}", + factory.settings().energy->get_one_ring_gradient(tuple).norm()); + while (factory.settings().energy->get_one_ring_gradient(tuple).norm() > 1e-10) { + scheduler.run_operation_on_all(PrimitiveType::Vertex, "optimize_vertices"); tuple = mesh.face_tuple_from_vids(2, 4, 5); } - ConstAccessor pos = mesh.create_const_accessor(op_settings.uv_position); + ConstAccessor pos = mesh.create_const_accessor(op_settings.coordinate_handle); Eigen::Vector2d uv0 = pos.const_vector_attribute(tuple); Eigen::Vector2d uv1 = pos.const_vector_attribute(mesh.switch_vertex(tuple)); Eigen::Vector2d uv2 = pos.const_vector_attribute(mesh.switch_vertex(mesh.switch_edge(tuple))); - REQUIRE((uv0 - uv1).norm() - (uv1 - uv2).norm() < 1e-6); - REQUIRE((uv0 - uv1).norm() - (uv0 - uv2).norm() < 1e-6); - REQUIRE((uv1 - uv2).norm() - (uv0 - uv2).norm() < 1e-6); + CHECK((uv0 - uv1).norm() - (uv1 - uv2).norm() < 1e-6); + CHECK((uv0 - uv1).norm() - (uv0 - uv2).norm() < 1e-6); + CHECK((uv1 - uv2).norm() - (uv0 - uv2).norm() < 1e-6); } diff --git a/tests/test_autodiff.cpp b/tests/test_autodiff.cpp new file mode 100644 index 0000000000..8af524730a --- /dev/null +++ b/tests/test_autodiff.cpp @@ -0,0 +1,23 @@ + +#include +#include +#include +#include +#include + + +TEST_CASE("analytic_autodiff", "[autodiff]") +{ + auto raii = wmtk::function::utils::AutoDiffRAII(2); + REQUIRE(DiffScalarBase::getVariableCount() == 2); + Eigen::Vector2d x{2, 3}; + using DScalar = DScalar2; + auto xD = wmtk::function::utils::as_DScalar(x); + + auto v = xD.x() * xD.y() * xD.y(); + + CHECK(v.getValue() == 18); + + Eigen::Vector2d grad{9., 12.}; + CHECK(v.getGradient() == grad); +} From 9405d337f6b8ae05a198c8916b4d4284099c5ba4 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Mon, 16 Oct 2023 18:24:58 -0400 Subject: [PATCH 100/134] persimplex functions draft (not compiled) --- .../DifferentiablePerSimplexFunction.cpp | 31 +++++++++++++++++++ .../DifferentiablePerSimplexFunction.hpp | 24 ++++++++++++++ src/wmtk/function/PerSimplexFunction.cpp | 16 ++++++++++ src/wmtk/function/PerSimplexFunction.hpp | 27 ++++++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 src/wmtk/function/DifferentiablePerSimplexFunction.cpp create mode 100644 src/wmtk/function/DifferentiablePerSimplexFunction.hpp create mode 100644 src/wmtk/function/PerSimplexFunction.cpp create mode 100644 src/wmtk/function/PerSimplexFunction.hpp diff --git a/src/wmtk/function/DifferentiablePerSimplexFunction.cpp b/src/wmtk/function/DifferentiablePerSimplexFunction.cpp new file mode 100644 index 0000000000..b1431d28be --- /dev/null +++ b/src/wmtk/function/DifferentiablePerSimplexFunction.cpp @@ -0,0 +1,31 @@ +#include "DifferentiablePerSimplexFunction.hpp" + +namespace wmtk { +namespace function { +DifferentiablePerSimplexFunction::DifferentiablePerSimplexFunction( + const Mesh& mesh, + const Simplex::Type& simplex_type) + : PerSimplexFunction(mesh, simplex_type) +{} + +DifferentiablePerSimplexFunction::~DifferentiablePerSimplexFunction() = default; + +const MeshAttributeHandle& DifferentiablePerSimplexFunction::get_simplex_attribute_handle() + const +{ + return m_attribute_handle; +} + +void DifferentiablePerSimplexFunction::assert_function_type(const Simplex::type& s_type) const +{ + if (m_attribute_handle.get_attribute_type() != s_type) { + throw std::runtime_error("Differentiation of the DifferentiableFunction must be taken wrt " + "the attribute of the simplex type "); + } + if (get_simplex_type() > s_type || get_simplex_type() == s_type) { + throw std::runtime_error("The DifferentiableFunction must be defined on the cofaces of the " + "simplex type "); + } +} +} // namespace function +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/DifferentiablePerSimplexFunction.hpp b/src/wmtk/function/DifferentiablePerSimplexFunction.hpp new file mode 100644 index 0000000000..e9897bb2ae --- /dev/null +++ b/src/wmtk/function/DifferentiablePerSimplexFunction.hpp @@ -0,0 +1,24 @@ +#pragma once +#include +#include "PerSimplexFunction.hpp" +namespace wmtk { +namespace function { +class DifferentiablePerSimplexFunction : public PerSimplexFunction +{ +public: + DifferentiablePerSimplexFunction(const Mesh& mesh, const Simplex::Type& simplex_type); + virtual ~DifferentiablePerSimplexFunction(); + +public: + virtual Eigen::VectorXd get_gradient(const Simplex& s) const; + virtual Eigen::MatrixXd get_hessian(const Simplex& s) const; + + const MeshAttributeHandle& get_simplex_attribute_handle() const; + + void assert_function_type(const Simplex::type& s_type) const; + +private: + const MeshAttributeHandle m_attribute_handle; +}; +} // namespace function +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/PerSimplexFunction.cpp b/src/wmtk/function/PerSimplexFunction.cpp new file mode 100644 index 0000000000..b34bfed573 --- /dev/null +++ b/src/wmtk/function/PerSimplexFunction.cpp @@ -0,0 +1,16 @@ +#include "PerSimplexFunction.hpp" + +namespace wmtk { +namespace function { + +PerSimplexFunction::PerSimplexFunction(const Mesh& mesh, const Simplex::Type& simplex_type) + : m_mesh(mesh) + , m_simplex_type(simplex_type) +{} + +const Simplex::Type& PerSimplexFunction::get_simplex_type() const +{ + return m_simplex_type; +} +} // namespace function +}; // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/PerSimplexFunction.hpp b/src/wmtk/function/PerSimplexFunction.hpp new file mode 100644 index 0000000000..f69d9ef3cf --- /dev/null +++ b/src/wmtk/function/PerSimplexFunction.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include +#include +#include +namespace wmtk { +namespace function { +class PerSimplexFunction +{ +public: + PerSimplexFunction(const Mesh& mesh, const Simplex::Type& simplex_type); + virtual ~PerSimplexFunction(); + + const Mesh& mesh() const; + +public: + virtual double get_value(const Simplex& s) const = 0; + + const Simplex::Type& get_simplex_type() const; + +private: + const Mesh& m_mesh; + const Simplex::Type m_simplex_type; +}; +} // namespace function + +} // namespace wmtk \ No newline at end of file From 0067ccc3670207767b8957cf633787fb503632aa Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Mon, 16 Oct 2023 18:27:14 -0400 Subject: [PATCH 101/134] get mesh in persimplexfunction --- src/wmtk/function/PerSimplexFunction.cpp | 4 ++++ src/wmtk/function/PerSimplexFunction.hpp | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/wmtk/function/PerSimplexFunction.cpp b/src/wmtk/function/PerSimplexFunction.cpp index b34bfed573..a5ccd470cf 100644 --- a/src/wmtk/function/PerSimplexFunction.cpp +++ b/src/wmtk/function/PerSimplexFunction.cpp @@ -7,6 +7,10 @@ PerSimplexFunction::PerSimplexFunction(const Mesh& mesh, const Simplex::Type& si : m_mesh(mesh) , m_simplex_type(simplex_type) {} +const Mesh& mesh() const +{ + return m_mesh; +} const Simplex::Type& PerSimplexFunction::get_simplex_type() const { diff --git a/src/wmtk/function/PerSimplexFunction.hpp b/src/wmtk/function/PerSimplexFunction.hpp index f69d9ef3cf..e9965240f8 100644 --- a/src/wmtk/function/PerSimplexFunction.hpp +++ b/src/wmtk/function/PerSimplexFunction.hpp @@ -11,11 +11,9 @@ class PerSimplexFunction PerSimplexFunction(const Mesh& mesh, const Simplex::Type& simplex_type); virtual ~PerSimplexFunction(); - const Mesh& mesh() const; - public: + const Mesh& mesh() const; virtual double get_value(const Simplex& s) const = 0; - const Simplex::Type& get_simplex_type() const; private: From ff8f1682c8bae240e8460b5a85a93205596a87a3 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Thu, 19 Oct 2023 18:09:26 -0400 Subject: [PATCH 102/134] find the upper level cofaces of a simplex in TriMesh. Tests passed. Helper functions in SimplexCollection --- src/wmtk/simplex/SimplexCollection.cpp | 19 ++++++++ src/wmtk/simplex/SimplexCollection.hpp | 9 +++- src/wmtk/simplex/upper_level_cofaces.cpp | 40 ++++++++++++++++ src/wmtk/simplex/upper_level_cofaces.hpp | 12 +++++ tests/test_simplex_collection.cpp | 59 ++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 src/wmtk/simplex/upper_level_cofaces.cpp create mode 100644 src/wmtk/simplex/upper_level_cofaces.hpp diff --git a/src/wmtk/simplex/SimplexCollection.cpp b/src/wmtk/simplex/SimplexCollection.cpp index 17f2a8fc9c..9830fc178f 100644 --- a/src/wmtk/simplex/SimplexCollection.cpp +++ b/src/wmtk/simplex/SimplexCollection.cpp @@ -31,6 +31,19 @@ void SimplexCollection::add(const SimplexCollection& simplex_collection) m_simplices.insert(m_simplices.end(), s.begin(), s.end()); } +std::vector SimplexCollection::tuple_vector() const +{ + std::vector tuples; + tuples.reserve(m_simplices.size()); // giving the vector some (hopefully) resonable size + + // add simplices to the vector + for (const Simplex& s : m_simplices) { + tuples.emplace_back(s.tuple()); + } + + return tuples; +} + void SimplexCollection::sort_and_clean() { std::sort(m_simplices.begin(), m_simplices.end(), m_simplex_is_less); @@ -38,6 +51,12 @@ void SimplexCollection::sort_and_clean() m_simplices.erase(last, m_simplices.end()); } +void SimplexCollection::sort() +{ + std::sort(m_simplices.begin(), m_simplices.end(), m_simplex_is_less); +} + + bool SimplexCollection::contains(const Simplex& simplex) const { // TODO this is O(n) but can and should be done in O(log n) diff --git a/src/wmtk/simplex/SimplexCollection.hpp b/src/wmtk/simplex/SimplexCollection.hpp index 469ce1e739..12a9b49b03 100644 --- a/src/wmtk/simplex/SimplexCollection.hpp +++ b/src/wmtk/simplex/SimplexCollection.hpp @@ -34,11 +34,17 @@ class SimplexCollection void add(const Simplex& simplex); void add(const SimplexCollection& simplex_collection); - + /** + * @brief return the vector of tuples of the simplex collection. + * + * @return std::vector + */ + std::vector tuple_vector() const; /** * @brief Sort simplex vector and remove duplicates. */ void sort_and_clean(); + void sort(); /** * @brief Check if simplex is contained in collection. @@ -69,6 +75,7 @@ class SimplexCollection protected: const Mesh& m_mesh; std::vector m_simplices; + protected: internal::SimplexLessFunctor m_simplex_is_less; internal::SimplexEqualFunctor m_simplex_is_equal; diff --git a/src/wmtk/simplex/upper_level_cofaces.cpp b/src/wmtk/simplex/upper_level_cofaces.cpp new file mode 100644 index 0000000000..4196b6e9ae --- /dev/null +++ b/src/wmtk/simplex/upper_level_cofaces.cpp @@ -0,0 +1,40 @@ +#include "upper_level_cofaces.hpp" +#include +#include +#include +#include +#include +#include +#include "link.hpp" +#include "top_level_cofaces.hpp" +namespace wmtk::simplex { + +std::vector upper_level_cofaces_tuples( + const TriMesh& mesh, + const Simplex& my_simplex, + const PrimitiveType& cofaces_type) +{ + assert(my_simplex.primitive_type() < cofaces_type); + std::vector collection; + if (my_simplex.primitive_type() == PrimitiveType::Vertex && + (cofaces_type == PrimitiveType::Edge)) { + auto sc = link(mesh, my_simplex); + std::vector coface_edge_tuples; + for (const Simplex& edge : sc.simplex_vector(PrimitiveType::Edge)) { + coface_edge_tuples.emplace_back(mesh.switch_vertex(mesh.switch_edge(edge.tuple()))); + coface_edge_tuples.emplace_back( + mesh.switch_vertex(mesh.switch_edge(mesh.switch_vertex(edge.tuple())))); + } + SimplexCollection ec( + mesh, + utils::tuple_vector_to_homogeneous_simplex_vector( + coface_edge_tuples, + PrimitiveType::Edge)); + ec.sort_and_clean(); + collection = ec.tuple_vector(); + } else { + collection = top_level_cofaces_tuples(mesh, my_simplex); + } + return collection; +} +} // namespace wmtk::simplex \ No newline at end of file diff --git a/src/wmtk/simplex/upper_level_cofaces.hpp b/src/wmtk/simplex/upper_level_cofaces.hpp new file mode 100644 index 0000000000..31bff95e5b --- /dev/null +++ b/src/wmtk/simplex/upper_level_cofaces.hpp @@ -0,0 +1,12 @@ +#pragma once +#include +#include +#include +#include + +namespace wmtk::simplex { +std::vector upper_level_cofaces_tuples( + const TriMesh& mesh, + const Simplex& my_simplex, + const PrimitiveType& cofaces_type); +} \ No newline at end of file diff --git a/tests/test_simplex_collection.cpp b/tests/test_simplex_collection.cpp index af2c9a2b31..837ecfee45 100644 --- a/tests/test_simplex_collection.cpp +++ b/tests/test_simplex_collection.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include "tools/DEBUG_TriMesh.hpp" #include "tools/TriMesh_examples.hpp" @@ -855,3 +857,60 @@ TEST_CASE("simplex_link_iterable", "[simplex_collection][2D]") CHECK(m.simplices_are_equal(itrb_collection.simplex_vector()[i], coll.simplex_vector()[i])); } } + + +TEST_CASE("simplex_upper_level_cofaces", "[simplex_collection][2D]") +{ + tests::DEBUG_TriMesh m = tests::hex_plus_two(); + + SECTION("vertex_interior") + { + const Tuple t = m.edge_tuple_between_v1_v2(4, 5, 2); + const simplex::Simplex input = simplex::Simplex::vertex(t); + std::vector tc = upper_level_cofaces_tuples(m, input, PrimitiveType::Edge); + REQUIRE(tc.size() == 6); + + SimplexCollection sc( + m, + simplex::utils::tuple_vector_to_homogeneous_simplex_vector(tc, PrimitiveType::Face)); + sc.sort(); + const auto& cells = sc.simplex_vector(); + CHECK(m.id(m.switch_vertex(cells[0].tuple()), PrimitiveType::Vertex) == 3); + CHECK(m.id(m.switch_vertex(cells[1].tuple()), PrimitiveType::Vertex) == 0); + CHECK(m.id(m.switch_vertex(cells[2].tuple()), PrimitiveType::Vertex) == 1); + CHECK(m.id(m.switch_vertex(cells[3].tuple()), PrimitiveType::Vertex) == 5); + CHECK(m.id(m.switch_vertex(cells[4].tuple()), PrimitiveType::Vertex) == 7); + CHECK(m.id(m.switch_vertex(cells[5].tuple()), PrimitiveType::Vertex) == 8); + + // check the lower dimension coface is the same as input + CHECK(m.id(tc[0], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); + CHECK(m.id(tc[1], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); + CHECK(m.id(tc[2], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); + CHECK(m.id(tc[3], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); + CHECK(m.id(tc[4], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); + CHECK(m.id(tc[5], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); + } + + SECTION("vertex_boundary") + { + const Tuple t = m.edge_tuple_between_v1_v2(3, 4, 0); + const simplex::Simplex input = simplex::Simplex::vertex(t); + std::vector tc = upper_level_cofaces_tuples(m, input, PrimitiveType::Edge); + REQUIRE(tc.size() == 3); + SimplexCollection sc( + m, + simplex::utils::tuple_vector_to_homogeneous_simplex_vector(tc, PrimitiveType::Face)); + sc.sort(); + + const auto& cells = sc.simplex_vector(); + + // check the lower dimension coface is the same as input + CHECK(m.id(tc[0], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); + CHECK(m.id(tc[1], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); + CHECK(m.id(tc[2], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); + + CHECK(m.id(m.switch_vertex(cells[0].tuple()), PrimitiveType::Vertex) == 0); + CHECK(m.id(m.switch_vertex(cells[1].tuple()), PrimitiveType::Vertex) == 4); + CHECK(m.id(m.switch_vertex(cells[2].tuple()), PrimitiveType::Vertex) == 7); + } +} \ No newline at end of file From 5ecb6b27299722fc8299ae4609bc5e39429fdde5 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Fri, 20 Oct 2023 12:20:10 -0400 Subject: [PATCH 103/134] add public function for getting attribute primitive type in MeshAttrHandle --- src/wmtk/attribute/AttributeHandle.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wmtk/attribute/AttributeHandle.hpp b/src/wmtk/attribute/AttributeHandle.hpp index 1d6bfebe87..0938edfbaa 100644 --- a/src/wmtk/attribute/AttributeHandle.hpp +++ b/src/wmtk/attribute/AttributeHandle.hpp @@ -74,6 +74,7 @@ class MeshAttributeHandle m_primitive_type == o.m_primitive_type; } bool is_valid() const { return m_base_handle.is_valid(); } + PrimitiveType primitive_type() const { return m_primitive_type; } }; } // namespace attribute using AttributeHandle = attribute::AttributeHandle; From d591a8e0b311fa8c73f6e7ec40b7f18adebf3dd1 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Fri, 20 Oct 2023 12:21:08 -0400 Subject: [PATCH 104/134] upper level cofaces using general Mesh type --- src/wmtk/simplex/CMakeLists.txt | 2 ++ src/wmtk/simplex/upper_level_cofaces.cpp | 16 ++++++++++++++++ src/wmtk/simplex/upper_level_cofaces.hpp | 7 ++++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/wmtk/simplex/CMakeLists.txt b/src/wmtk/simplex/CMakeLists.txt index 7fc95f6a54..89efd42b72 100644 --- a/src/wmtk/simplex/CMakeLists.txt +++ b/src/wmtk/simplex/CMakeLists.txt @@ -22,6 +22,8 @@ set(SRC_FILES simplex_boundary.cpp simplex_boundary_iterable.hpp simplex_boundary_iterable.cpp + upper_level_cofaces.hpp + upper_level_cofaces.cpp internal/SimplexEqualFunctor.hpp internal/SimplexLessFunctor.hpp ) diff --git a/src/wmtk/simplex/upper_level_cofaces.cpp b/src/wmtk/simplex/upper_level_cofaces.cpp index 4196b6e9ae..62756069db 100644 --- a/src/wmtk/simplex/upper_level_cofaces.cpp +++ b/src/wmtk/simplex/upper_level_cofaces.cpp @@ -1,6 +1,8 @@ #include "upper_level_cofaces.hpp" #include #include +#include +#include #include #include #include @@ -9,6 +11,20 @@ #include "top_level_cofaces.hpp" namespace wmtk::simplex { +std::vector upper_level_cofaces_tuples( + const Mesh& mesh, + const Simplex& simplex, + const PrimitiveType& cofaces_type) +{ + switch (mesh.top_simplex_type()) { + case PrimitiveType::Face: + return upper_level_cofaces_tuples(static_cast(mesh), simplex, cofaces_type); + case PrimitiveType::Tetrahedron: + return upper_level_cofaces_tuples(static_cast(mesh), simplex, cofaces_type); + default: assert(false); throw "unknown mesh type in upper_level_cofaces_tuples"; + } +} + std::vector upper_level_cofaces_tuples( const TriMesh& mesh, const Simplex& my_simplex, diff --git a/src/wmtk/simplex/upper_level_cofaces.hpp b/src/wmtk/simplex/upper_level_cofaces.hpp index 31bff95e5b..9aaa81555c 100644 --- a/src/wmtk/simplex/upper_level_cofaces.hpp +++ b/src/wmtk/simplex/upper_level_cofaces.hpp @@ -9,4 +9,9 @@ std::vector upper_level_cofaces_tuples( const TriMesh& mesh, const Simplex& my_simplex, const PrimitiveType& cofaces_type); -} \ No newline at end of file + +std::vector upper_level_cofaces_tuples( + const Mesh& mesh, + const Simplex& my_simplex, + const PrimitiveType& cofaces_type); +} // namespace wmtk::simplex \ No newline at end of file From 0bf9576ecd2eb71cfa4dd3e31c90ceef80b38fa6 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Fri, 20 Oct 2023 12:21:51 -0400 Subject: [PATCH 105/134] define persimplexfunction --- src/wmtk/function/PerSimplexFunction.cpp | 6 +++--- src/wmtk/function/PerSimplexFunction.hpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wmtk/function/PerSimplexFunction.cpp b/src/wmtk/function/PerSimplexFunction.cpp index a5ccd470cf..648c6c3c2a 100644 --- a/src/wmtk/function/PerSimplexFunction.cpp +++ b/src/wmtk/function/PerSimplexFunction.cpp @@ -3,16 +3,16 @@ namespace wmtk { namespace function { -PerSimplexFunction::PerSimplexFunction(const Mesh& mesh, const Simplex::Type& simplex_type) +PerSimplexFunction::PerSimplexFunction(const Mesh& mesh, const PrimitiveType& simplex_type) : m_mesh(mesh) , m_simplex_type(simplex_type) {} -const Mesh& mesh() const +const Mesh& PerSimplexFunction::mesh() const { return m_mesh; } -const Simplex::Type& PerSimplexFunction::get_simplex_type() const +const PrimitiveType& PerSimplexFunction::get_simplex_type() const { return m_simplex_type; } diff --git a/src/wmtk/function/PerSimplexFunction.hpp b/src/wmtk/function/PerSimplexFunction.hpp index e9965240f8..8e13e55e24 100644 --- a/src/wmtk/function/PerSimplexFunction.hpp +++ b/src/wmtk/function/PerSimplexFunction.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include +#include #include #include namespace wmtk { @@ -8,17 +8,17 @@ namespace function { class PerSimplexFunction { public: - PerSimplexFunction(const Mesh& mesh, const Simplex::Type& simplex_type); + PerSimplexFunction(const Mesh& mesh, const PrimitiveType& simplex_type); virtual ~PerSimplexFunction(); public: const Mesh& mesh() const; virtual double get_value(const Simplex& s) const = 0; - const Simplex::Type& get_simplex_type() const; + const PrimitiveType& get_simplex_type() const; private: const Mesh& m_mesh; - const Simplex::Type m_simplex_type; + const PrimitiveType m_simplex_type; }; } // namespace function From d2a6819459a3f53fdd92724c9bec5c594a57536f Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Fri, 20 Oct 2023 12:22:49 -0400 Subject: [PATCH 106/134] Differentiable extension of the persimplexfunction --- .../DifferentiablePerSimplexFunction.cpp | 17 ++++++---- .../DifferentiablePerSimplexFunction.hpp | 32 +++++++++++++++---- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/wmtk/function/DifferentiablePerSimplexFunction.cpp b/src/wmtk/function/DifferentiablePerSimplexFunction.cpp index b1431d28be..327b7f0c71 100644 --- a/src/wmtk/function/DifferentiablePerSimplexFunction.cpp +++ b/src/wmtk/function/DifferentiablePerSimplexFunction.cpp @@ -4,27 +4,30 @@ namespace wmtk { namespace function { DifferentiablePerSimplexFunction::DifferentiablePerSimplexFunction( const Mesh& mesh, - const Simplex::Type& simplex_type) + const PrimitiveType& simplex_type, + const attribute::MeshAttributeHandle& variable_attribute_handle) : PerSimplexFunction(mesh, simplex_type) + , m_attribute_handle(variable_attribute_handle) {} DifferentiablePerSimplexFunction::~DifferentiablePerSimplexFunction() = default; -const MeshAttributeHandle& DifferentiablePerSimplexFunction::get_simplex_attribute_handle() +const MeshAttributeHandle& DifferentiablePerSimplexFunction::get_variable_attribute_handle() const { return m_attribute_handle; } -void DifferentiablePerSimplexFunction::assert_function_type(const Simplex::type& s_type) const +void DifferentiablePerSimplexFunction::assert_function_type(const PrimitiveType& s_type) const { - if (m_attribute_handle.get_attribute_type() != s_type) { + if (get_variable_attribute_handle().primitive_type() != s_type) { throw std::runtime_error("Differentiation of the DifferentiableFunction must be taken wrt " "the attribute of the simplex type "); } - if (get_simplex_type() > s_type || get_simplex_type() == s_type) { - throw std::runtime_error("The DifferentiableFunction must be defined on the cofaces of the " - "simplex type "); + if (get_simplex_type() < s_type) { + throw std::runtime_error( + "The DifferentiableFunction must be defined on the cofaces of higher dimension to the " + "simplex type "); } } } // namespace function diff --git a/src/wmtk/function/DifferentiablePerSimplexFunction.hpp b/src/wmtk/function/DifferentiablePerSimplexFunction.hpp index e9897bb2ae..e4681683a2 100644 --- a/src/wmtk/function/DifferentiablePerSimplexFunction.hpp +++ b/src/wmtk/function/DifferentiablePerSimplexFunction.hpp @@ -1,24 +1,44 @@ #pragma once +#include #include +#include #include "PerSimplexFunction.hpp" namespace wmtk { namespace function { class DifferentiablePerSimplexFunction : public PerSimplexFunction { public: - DifferentiablePerSimplexFunction(const Mesh& mesh, const Simplex::Type& simplex_type); + /** + * @brief Construct a new DifferentiablePerSimplexFunction object where the function is defined + * over simplices of simplex_type. And the differentiation is taken wrt the + * attribute_handle.primitive_type() + * + * @param mesh + * @param simplex_type + */ + DifferentiablePerSimplexFunction( + const Mesh& mesh, + const PrimitiveType& simplex_type, + const attribute::MeshAttributeHandle& variable_attribute_handle); virtual ~DifferentiablePerSimplexFunction(); public: - virtual Eigen::VectorXd get_gradient(const Simplex& s) const; - virtual Eigen::MatrixXd get_hessian(const Simplex& s) const; + virtual Eigen::VectorXd get_gradient(const Simplex& s) const = 0; + virtual Eigen::MatrixXd get_hessian(const Simplex& s) const = 0; - const MeshAttributeHandle& get_simplex_attribute_handle() const; + const attribute::MeshAttributeHandle& get_variable_attribute_handle() const; - void assert_function_type(const Simplex::type& s_type) const; + /** + * @brief the function should be defined on the simplex of type A and the differntiation is + * taken wrt simplex type B. The definition mandate (1) A should be coface with B, (2) the type + * of the attribute_handle should be of the same type as B. + * + * @param s_type simplex Type B as defined above + */ + void assert_function_type(const PrimitiveType& s_type) const; private: - const MeshAttributeHandle m_attribute_handle; + const attribute::MeshAttributeHandle m_attribute_handle; }; } // namespace function } // namespace wmtk \ No newline at end of file From 933ab8aeb3a2984533202f1ebb16d9a6a1c6ef12 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Fri, 20 Oct 2023 12:23:44 -0400 Subject: [PATCH 107/134] New function class and locally differnetiable function class using the persimplexfunction --- src/wmtk/function/Function.cpp | 14 ---- src/wmtk/function/Function.hpp | 4 +- .../LocallyDifferentiableFunction.cpp | 77 +++++++++++++++++++ .../LocallyDifferentiableFunction.hpp | 45 +++++++++++ 4 files changed, 123 insertions(+), 17 deletions(-) create mode 100644 src/wmtk/function/LocallyDifferentiableFunction.cpp create mode 100644 src/wmtk/function/LocallyDifferentiableFunction.hpp diff --git a/src/wmtk/function/Function.cpp b/src/wmtk/function/Function.cpp index a98701efa1..096bc83f7b 100644 --- a/src/wmtk/function/Function.cpp +++ b/src/wmtk/function/Function.cpp @@ -8,20 +8,6 @@ Function::Function(const Mesh& mesh) Function::~Function() = default; -double Function::get_one_ring_value(const Tuple& vertex) const -{ - auto simplices = - simplex::top_level_cofaces_tuples(mesh(), Simplex(PrimitiveType::Vertex, vertex)); - return get_value_sum(simplices); -} -double Function::get_value_sum(const std::vector& top_level_simplices) const -{ - double v = 0; - for (const Tuple& cell : top_level_simplices) { - v += get_value(cell); - } - return v; -} const Mesh& Function::mesh() const { diff --git a/src/wmtk/function/Function.hpp b/src/wmtk/function/Function.hpp index 58368728d1..b19bf8f0c7 100644 --- a/src/wmtk/function/Function.hpp +++ b/src/wmtk/function/Function.hpp @@ -14,12 +14,10 @@ class Function virtual ~Function(); const Mesh& mesh() const; - double get_one_ring_value(const Tuple& vertex) const; - double get_value_sum(const std::vector& top_level_simplices) const; public: // evaluate the function on the top level simplex of the tuple - virtual double get_value(const Tuple& top_level_simplex) const = 0; + virtual double get_value(const Tuple& tuple) const = 0; }; } // namespace function } // namespace wmtk diff --git a/src/wmtk/function/LocallyDifferentiableFunction.cpp b/src/wmtk/function/LocallyDifferentiableFunction.cpp new file mode 100644 index 0000000000..46710be52b --- /dev/null +++ b/src/wmtk/function/LocallyDifferentiableFunction.cpp @@ -0,0 +1,77 @@ +#include "LocallyDifferentiableFunction.hpp" +#include +#include +#include +namespace wmtk { +namespace function { +LocallyDifferentiableFunction::LocallyDifferentiableFunction( + const Mesh& mesh, + const std::unique_ptr& function) + : Function(mesh) + , m_function(std::move(function)) +{} + +LocallyDifferentiableFunction::~LocallyDifferentiableFunction() = default; + +long LocallyDifferentiableFunction::embedded_dimension() const +{ + return mesh().get_attribute_dimension(m_function->get_variable_attribute_handle()); +} + + +Eigen::VectorXd LocallyDifferentiableFunction::get_one_ring_gradient( + const Simplex& my_simplex, + const PrimitiveType& cofaces_type) const +{ + m_function->assert_function_type(my_simplex.primitive_type()); + std::vector coface_tuples = + simplex::upper_level_cofaces_tuples(mesh(), my_simplex, cofaces_type); + + return get_gradient_sum( + simplex::utils::tuple_vector_to_homogeneous_simplex_vector(coface_tuples, cofaces_type)); +} +Eigen::MatrixXd LocallyDifferentiableFunction::get_one_ring_hessian( + const Simplex& my_simplex, + const PrimitiveType& cofaces_type) const +{ + m_function->assert_function_type(my_simplex.primitive_type()); + std::vector coface_tuples = + simplex::upper_level_cofaces_tuples(mesh(), my_simplex, cofaces_type); + + return get_hessian_sum( + simplex::utils::tuple_vector_to_homogeneous_simplex_vector(coface_tuples, cofaces_type)); +} + +double LocallyDifferentiableFunction::get_value_sum( + const std::vector& coface_simplices) const +{ + double v = 0; + for (const Simplex& cell : coface_simplices) { + v += m_function->get_value(cell); + } + return v; +} + + +Eigen::VectorXd LocallyDifferentiableFunction::get_gradient_sum( + const std::vector& coface_simplices) const +{ + Eigen::VectorXd g = Eigen::VectorXd::Zero(embedded_dimension()); + for (const Simplex& cell : coface_simplices) { + g += m_function->get_gradient(cell); + } + return g; +} +Eigen::MatrixXd LocallyDifferentiableFunction::get_hessian_sum( + const std::vector& coface_simplices) const +{ + Eigen::MatrixXd h = Eigen::MatrixXd::Zero(embedded_dimension(), embedded_dimension()); + for (const Simplex& cell : coface_simplices) { + h += m_function->get_hessian(cell); + } + return h; +} + +} // namespace function + +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/LocallyDifferentiableFunction.hpp b/src/wmtk/function/LocallyDifferentiableFunction.hpp new file mode 100644 index 0000000000..fd418b5434 --- /dev/null +++ b/src/wmtk/function/LocallyDifferentiableFunction.hpp @@ -0,0 +1,45 @@ +#pragma once +#include +#include +#include "DifferentiablePerSimplexFunction.hpp" +#include "Function.hpp" +namespace wmtk { +namespace function { +class LocallyDifferentiableFunction : public Function +{ +public: + LocallyDifferentiableFunction( + const Mesh& mesh, + const std::unique_ptr& function); + virtual ~LocallyDifferentiableFunction(); + +public: + Eigen::VectorXd get_one_ring_gradient( + const Simplex& my_simplex, + const PrimitiveType& cofaces_type) const; + Eigen::MatrixXd get_one_ring_hessian( + const Simplex& my_simplex, + const PrimitiveType& cofaces_type) const; + + double get_value_sum(const std::vector& coface_simplices) const; + Eigen::VectorXd get_gradient_sum(const std::vector& coface_simplices) const; + Eigen::MatrixXd get_hessian_sum(const std::vector& coface_simplices) const; + + /** + * @brief Get the one ring simplices centered at vertex object. + * m_function wrt to vertex assums the tuple is the vertex that's differentiated wrt + *(thus can not use co-face, since the returned value of co-face are in arbitrary order) + * + * @param vertex_tuple + * @return std::vector& the simplices whose tuple is centered at the vertex and is + * defined over the same type as m_function + */ + std::vector& get_one_ring_simplices_centered_at_vertex( + const Tuple& vertex_tuple) const; + long embedded_dimension() const; + +private: + const std::unique_ptr& m_function; +}; +} // namespace function +} // namespace wmtk \ No newline at end of file From dcb51ddc72c26c775de7111f03822e05927a58e5 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Fri, 20 Oct 2023 12:24:06 -0400 Subject: [PATCH 108/134] cmake for differentiable function refactor --- src/wmtk/function/CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/wmtk/function/CMakeLists.txt b/src/wmtk/function/CMakeLists.txt index 657d16074a..2b932c40f7 100644 --- a/src/wmtk/function/CMakeLists.txt +++ b/src/wmtk/function/CMakeLists.txt @@ -4,6 +4,14 @@ set(SRC_FILES Function.cpp DifferentiableFunction.cpp DifferentiableFunction.hpp + + LocallyDifferentiableFunction.hpp + LocallyDifferentiableFunction.cpp + PerSimplexFunction.hpp + PerSimplexFunction.cpp + DifferentiablePerSimplexFunction.hpp + DifferentiablePerSimplexFunction.cpp + AMIPS.hpp AMIPS.cpp AMIPS2D.hpp From a6122d1cbb87013e50828b1b793c148428e40f7b Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Fri, 20 Oct 2023 12:24:30 -0400 Subject: [PATCH 109/134] comment functionevaluator in cmake out for compilation --- src/wmtk/function/utils/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wmtk/function/utils/CMakeLists.txt b/src/wmtk/function/utils/CMakeLists.txt index c5dcdc0036..991b647d38 100644 --- a/src/wmtk/function/utils/CMakeLists.txt +++ b/src/wmtk/function/utils/CMakeLists.txt @@ -9,8 +9,8 @@ set(SRC_FILES amips.hpp amips.cpp - FunctionEvaluator.hpp - FunctionEvaluator.cpp + # FunctionEvaluator.hpp + # FunctionEvaluator.cpp DifferentiableFunctionEvaluator.hpp DifferentiableFunctionEvaluator.cpp ) From 47807706fbcf5a578eeafb9e2a050571d9b33382 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 24 Oct 2023 15:06:33 -0400 Subject: [PATCH 110/134] AutodiffFunction takes input primitive type --- src/wmtk/function/AutodiffFunction.cpp | 17 +++++++++-------- src/wmtk/function/AutodiffFunction.hpp | 17 ++++++++++------- .../DifferentiablePerSimplexFunction.cpp | 6 ++++++ .../DifferentiablePerSimplexFunction.hpp | 1 + 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/wmtk/function/AutodiffFunction.cpp b/src/wmtk/function/AutodiffFunction.cpp index cc07a45e8c..316768619e 100644 --- a/src/wmtk/function/AutodiffFunction.cpp +++ b/src/wmtk/function/AutodiffFunction.cpp @@ -5,25 +5,26 @@ namespace wmtk::function { AutodiffFunction::AutodiffFunction( const Mesh& mesh, - const MeshAttributeHandle& vertex_attribute_handle) - : DifferentiableFunction(mesh, vertex_attribute_handle) + const PrimitiveType& simplex_type, + const MeshAttributeHandle& variable_attribute_handle) + : DifferentiablePerSimplexFunction(mesh, simplex_type, variable_attribute_handle) {} AutodiffFunction::~AutodiffFunction() = default; -double AutodiffFunction::get_value(const Tuple& tuple) const +double AutodiffFunction::get_value(const Simplex& simplex) const { auto scope = utils::AutoDiffRAII(embedded_dimension()); - return get_value_autodiff(tuple).getValue(); + return get_value_autodiff(simplex).getValue(); } -Eigen::VectorXd AutodiffFunction::get_gradient(const Tuple& tuple) const +Eigen::VectorXd AutodiffFunction::get_gradient(const Simplex& simplex) const { auto scope = utils::AutoDiffRAII(embedded_dimension()); - auto v = get_value_autodiff(tuple); + auto v = get_value_autodiff(simplex); return v.getGradient(); } -Eigen::MatrixXd AutodiffFunction::get_hessian(const Tuple& tuple) const +Eigen::MatrixXd AutodiffFunction::get_hessian(const Simplex& simplex) const { auto scope = utils::AutoDiffRAII(embedded_dimension()); - return get_value_autodiff(tuple).getHessian(); + return get_value_autodiff(simplex).getHessian(); } } // namespace wmtk::function diff --git a/src/wmtk/function/AutodiffFunction.hpp b/src/wmtk/function/AutodiffFunction.hpp index 6ac22f501e..7f1d5b8952 100644 --- a/src/wmtk/function/AutodiffFunction.hpp +++ b/src/wmtk/function/AutodiffFunction.hpp @@ -1,25 +1,28 @@ #pragma once #include -#include "DifferentiableFunction.hpp" +#include "DifferentiablePerSimplexFunction.hpp" namespace wmtk::function { -class AutodiffFunction : public DifferentiableFunction +class AutodiffFunction : public DifferentiablePerSimplexFunction { public: using DScalar = DScalar2, Eigen::Matrix>; using Scalar = typename DScalar::Scalar; static_assert( std::is_same_v); // MTAO: i'm leaving scalar here but is it ever not double? - AutodiffFunction(const Mesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); + AutodiffFunction( + const Mesh& mesh, + const PrimitiveType& simplex_type, + const attribute::MeshAttributeHandle& variable_attribute_handle); virtual ~AutodiffFunction(); public: - double get_value(const Tuple& tuple) const override; - Eigen::VectorXd get_gradient(const Tuple& tuple) const override; - Eigen::MatrixXd get_hessian(const Tuple& tuple) const override; + double get_value(const Simplex& simplex) const override; + Eigen::VectorXd get_gradient(const Simplex& simplex) const override; + Eigen::MatrixXd get_hessian(const Simplex& simplex) const override; protected: - virtual DScalar get_value_autodiff(const Tuple& tuple) const = 0; + virtual DScalar get_value_autodiff(const Simplex& simplex) const = 0; }; } // namespace wmtk::function diff --git a/src/wmtk/function/DifferentiablePerSimplexFunction.cpp b/src/wmtk/function/DifferentiablePerSimplexFunction.cpp index 327b7f0c71..66eb36f133 100644 --- a/src/wmtk/function/DifferentiablePerSimplexFunction.cpp +++ b/src/wmtk/function/DifferentiablePerSimplexFunction.cpp @@ -18,6 +18,12 @@ const MeshAttributeHandle& DifferentiablePerSimplexFunction::get_variabl return m_attribute_handle; } +long DifferentiablePerSimplexFunction::embedded_dimension() const +{ + return mesh().get_attribute_dimension(get_variable_attribute_handle()); +} + + void DifferentiablePerSimplexFunction::assert_function_type(const PrimitiveType& s_type) const { if (get_variable_attribute_handle().primitive_type() != s_type) { diff --git a/src/wmtk/function/DifferentiablePerSimplexFunction.hpp b/src/wmtk/function/DifferentiablePerSimplexFunction.hpp index e4681683a2..80a24643cd 100644 --- a/src/wmtk/function/DifferentiablePerSimplexFunction.hpp +++ b/src/wmtk/function/DifferentiablePerSimplexFunction.hpp @@ -36,6 +36,7 @@ class DifferentiablePerSimplexFunction : public PerSimplexFunction * @param s_type simplex Type B as defined above */ void assert_function_type(const PrimitiveType& s_type) const; + long embedded_dimension() const; private: const attribute::MeshAttributeHandle m_attribute_handle; From 8504b99c12a13e9500cf3a46442792c50800bfff Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 24 Oct 2023 15:07:10 -0400 Subject: [PATCH 111/134] add Sipmlex Type to AMIPS constructor --- src/wmtk/function/AMIPS.cpp | 2 +- src/wmtk/function/AMIPS2D.cpp | 9 +++++---- src/wmtk/function/AMIPS2D.hpp | 2 +- src/wmtk/function/AMIPS3D.cpp | 9 +++++---- src/wmtk/function/AMIPS3D.hpp | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/wmtk/function/AMIPS.cpp b/src/wmtk/function/AMIPS.cpp index acbc2ec9f2..92658755a0 100644 --- a/src/wmtk/function/AMIPS.cpp +++ b/src/wmtk/function/AMIPS.cpp @@ -3,7 +3,7 @@ namespace wmtk::function { AMIPS::AMIPS(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) - : AutodiffFunction(mesh, vertex_attribute_handle) + : AutodiffFunction(mesh, PrimitiveType::Face, vertex_attribute_handle) {} } // namespace wmtk::function diff --git a/src/wmtk/function/AMIPS2D.cpp b/src/wmtk/function/AMIPS2D.cpp index 1aaa6e4a66..63dacd383e 100644 --- a/src/wmtk/function/AMIPS2D.cpp +++ b/src/wmtk/function/AMIPS2D.cpp @@ -8,15 +8,16 @@ namespace wmtk::function { AMIPS2D::AMIPS2D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) : AMIPS(mesh, vertex_attribute_handle) { - assert(get_vertex_attribute_handle().is_valid()); + assert(get_variable_attribute_handle().is_valid()); // check the dimension of the position assert(embedded_dimension() == 2); } -auto AMIPS2D::get_value_autodiff(const Tuple& tuple) const -> DScalar +auto AMIPS2D::get_value_autodiff(const Simplex& simplex) const -> DScalar { - return function_eval(tuple); + assert(simplex.primitive_type() == PrimitiveType::Vertex); + return function_eval(simplex.tuple()); } template @@ -24,7 +25,7 @@ T AMIPS2D::function_eval(const Tuple& tuple) const { // get_autodiff_value sets the autodiff size if necessary // get the uv coordinates of the triangle - ConstAccessor pos = mesh().create_const_accessor(get_vertex_attribute_handle()); + ConstAccessor pos = mesh().create_const_accessor(get_variable_attribute_handle()); auto tuple_value = pos.const_vector_attribute(tuple); Vector2 uv0; diff --git a/src/wmtk/function/AMIPS2D.hpp b/src/wmtk/function/AMIPS2D.hpp index 022d816a2a..62778c41a3 100644 --- a/src/wmtk/function/AMIPS2D.hpp +++ b/src/wmtk/function/AMIPS2D.hpp @@ -7,7 +7,7 @@ class AMIPS2D : public AMIPS AMIPS2D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); protected: - DScalar get_value_autodiff(const Tuple& tuple) const override; + DScalar get_value_autodiff(const Simplex& simplex) const override; template diff --git a/src/wmtk/function/AMIPS3D.cpp b/src/wmtk/function/AMIPS3D.cpp index 1eef7151d5..7ef39b9c7e 100644 --- a/src/wmtk/function/AMIPS3D.cpp +++ b/src/wmtk/function/AMIPS3D.cpp @@ -8,15 +8,16 @@ namespace wmtk::function { AMIPS3D::AMIPS3D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) : AMIPS(mesh, vertex_attribute_handle) { - assert(get_vertex_attribute_handle().is_valid()); + assert(get_variable_attribute_handle().is_valid()); // check the dimension of the position assert(embedded_dimension() == 3); } -auto AMIPS3D::get_value_autodiff(const Tuple& tuple) const -> DScalar +auto AMIPS3D::get_value_autodiff(const Simplex& simplex) const -> DScalar { - return function_eval(tuple); + assert(simplex.primitive_type() == PrimitiveType::Vertex); + return function_eval(simplex.tuple()); } template @@ -24,7 +25,7 @@ T AMIPS3D::function_eval(const Tuple& tuple) const { // get_autodiff_value sets the autodiff size if necessary // get the pos coordinates of the triangle - ConstAccessor pos = mesh().create_const_accessor(get_vertex_attribute_handle()); + ConstAccessor pos = mesh().create_const_accessor(get_variable_attribute_handle()); auto tuple_value = pos.const_vector_attribute(tuple); Vector3 pos0; diff --git a/src/wmtk/function/AMIPS3D.hpp b/src/wmtk/function/AMIPS3D.hpp index 4d6e19b0c2..88d14b6347 100644 --- a/src/wmtk/function/AMIPS3D.hpp +++ b/src/wmtk/function/AMIPS3D.hpp @@ -8,7 +8,7 @@ class AMIPS3D : public AMIPS AMIPS3D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); protected: - DScalar get_value_autodiff(const Tuple& tuple) const override; + DScalar get_value_autodiff(const Simplex& simplex) const override; template T function_eval(const Tuple& tuple) const; From 4d5fcc2bd7d60e7cd61c5a7d34e1d1de86c450f9 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 24 Oct 2023 15:08:36 -0400 Subject: [PATCH 112/134] take out mesh from Function and locally differentiable function --- src/wmtk/function/Function.cpp | 14 ++++++------- src/wmtk/function/Function.hpp | 18 +++++++--------- .../LocallyDifferentiableFunction.cpp | 21 +++++++------------ .../LocallyDifferentiableFunction.hpp | 19 ++--------------- 4 files changed, 23 insertions(+), 49 deletions(-) diff --git a/src/wmtk/function/Function.cpp b/src/wmtk/function/Function.cpp index 096bc83f7b..f9bf52bd8a 100644 --- a/src/wmtk/function/Function.cpp +++ b/src/wmtk/function/Function.cpp @@ -1,16 +1,14 @@ #include "Function.hpp" #include -namespace wmtk::function { +namespace wmtk { +namespace function { -Function::Function(const Mesh& mesh) - : m_mesh(mesh) +Function::Function(std::unique_ptr&& function) + : m_function(std::move(function)) {} Function::~Function() = default; -const Mesh& Function::mesh() const -{ - return m_mesh; -} -} // namespace wmtk::function +} // namespace function +} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/Function.hpp b/src/wmtk/function/Function.hpp index b19bf8f0c7..37a8b45cd4 100644 --- a/src/wmtk/function/Function.hpp +++ b/src/wmtk/function/Function.hpp @@ -1,23 +1,21 @@ #pragma once -#include -#include +#include +#include +#include "PerSimplexFunction.hpp" namespace wmtk { namespace function { class Function { -private: - const Mesh& m_mesh; - - public: - Function(const Mesh& mesh); + Function(std::unique_ptr&& function); virtual ~Function(); - const Mesh& mesh() const; - public: // evaluate the function on the top level simplex of the tuple - virtual double get_value(const Tuple& tuple) const = 0; + virtual double get_value(const Simplex& simplex) const = 0; + +private: + std::unique_ptr m_function; }; } // namespace function } // namespace wmtk diff --git a/src/wmtk/function/LocallyDifferentiableFunction.cpp b/src/wmtk/function/LocallyDifferentiableFunction.cpp index 46710be52b..949830630a 100644 --- a/src/wmtk/function/LocallyDifferentiableFunction.cpp +++ b/src/wmtk/function/LocallyDifferentiableFunction.cpp @@ -5,27 +5,19 @@ namespace wmtk { namespace function { LocallyDifferentiableFunction::LocallyDifferentiableFunction( - const Mesh& mesh, - const std::unique_ptr& function) - : Function(mesh) - , m_function(std::move(function)) + std::unique_ptr&& function) + : Function(std::move(function)) {} LocallyDifferentiableFunction::~LocallyDifferentiableFunction() = default; -long LocallyDifferentiableFunction::embedded_dimension() const -{ - return mesh().get_attribute_dimension(m_function->get_variable_attribute_handle()); -} - - Eigen::VectorXd LocallyDifferentiableFunction::get_one_ring_gradient( const Simplex& my_simplex, const PrimitiveType& cofaces_type) const { m_function->assert_function_type(my_simplex.primitive_type()); std::vector coface_tuples = - simplex::upper_level_cofaces_tuples(mesh(), my_simplex, cofaces_type); + simplex::upper_level_cofaces_tuples(m_function->mesh(), my_simplex, cofaces_type); return get_gradient_sum( simplex::utils::tuple_vector_to_homogeneous_simplex_vector(coface_tuples, cofaces_type)); @@ -36,7 +28,7 @@ Eigen::MatrixXd LocallyDifferentiableFunction::get_one_ring_hessian( { m_function->assert_function_type(my_simplex.primitive_type()); std::vector coface_tuples = - simplex::upper_level_cofaces_tuples(mesh(), my_simplex, cofaces_type); + simplex::upper_level_cofaces_tuples(m_function->mesh(), my_simplex, cofaces_type); return get_hessian_sum( simplex::utils::tuple_vector_to_homogeneous_simplex_vector(coface_tuples, cofaces_type)); @@ -56,7 +48,7 @@ double LocallyDifferentiableFunction::get_value_sum( Eigen::VectorXd LocallyDifferentiableFunction::get_gradient_sum( const std::vector& coface_simplices) const { - Eigen::VectorXd g = Eigen::VectorXd::Zero(embedded_dimension()); + Eigen::VectorXd g = Eigen::VectorXd::Zero(m_function->embedded_dimension()); for (const Simplex& cell : coface_simplices) { g += m_function->get_gradient(cell); } @@ -65,7 +57,8 @@ Eigen::VectorXd LocallyDifferentiableFunction::get_gradient_sum( Eigen::MatrixXd LocallyDifferentiableFunction::get_hessian_sum( const std::vector& coface_simplices) const { - Eigen::MatrixXd h = Eigen::MatrixXd::Zero(embedded_dimension(), embedded_dimension()); + Eigen::MatrixXd h = + Eigen::MatrixXd::Zero(m_function->embedded_dimension(), m_function->embedded_dimension()); for (const Simplex& cell : coface_simplices) { h += m_function->get_hessian(cell); } diff --git a/src/wmtk/function/LocallyDifferentiableFunction.hpp b/src/wmtk/function/LocallyDifferentiableFunction.hpp index fd418b5434..ff037c1066 100644 --- a/src/wmtk/function/LocallyDifferentiableFunction.hpp +++ b/src/wmtk/function/LocallyDifferentiableFunction.hpp @@ -8,9 +8,7 @@ namespace function { class LocallyDifferentiableFunction : public Function { public: - LocallyDifferentiableFunction( - const Mesh& mesh, - const std::unique_ptr& function); + LocallyDifferentiableFunction(std::unique_ptr&& function); virtual ~LocallyDifferentiableFunction(); public: @@ -25,21 +23,8 @@ class LocallyDifferentiableFunction : public Function Eigen::VectorXd get_gradient_sum(const std::vector& coface_simplices) const; Eigen::MatrixXd get_hessian_sum(const std::vector& coface_simplices) const; - /** - * @brief Get the one ring simplices centered at vertex object. - * m_function wrt to vertex assums the tuple is the vertex that's differentiated wrt - *(thus can not use co-face, since the returned value of co-face are in arbitrary order) - * - * @param vertex_tuple - * @return std::vector& the simplices whose tuple is centered at the vertex and is - * defined over the same type as m_function - */ - std::vector& get_one_ring_simplices_centered_at_vertex( - const Tuple& vertex_tuple) const; - long embedded_dimension() const; - private: - const std::unique_ptr& m_function; + std::unique_ptr m_function; }; } // namespace function } // namespace wmtk \ No newline at end of file From 2e2f4d18d67b1b74411daba9d6aeda22b8d05840 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 24 Oct 2023 15:08:53 -0400 Subject: [PATCH 113/134] perSimplexFunction deconstructor --- src/wmtk/function/PerSimplexFunction.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/wmtk/function/PerSimplexFunction.cpp b/src/wmtk/function/PerSimplexFunction.cpp index 648c6c3c2a..24b822e786 100644 --- a/src/wmtk/function/PerSimplexFunction.cpp +++ b/src/wmtk/function/PerSimplexFunction.cpp @@ -7,6 +7,9 @@ PerSimplexFunction::PerSimplexFunction(const Mesh& mesh, const PrimitiveType& si : m_mesh(mesh) , m_simplex_type(simplex_type) {} + +PerSimplexFunction::~PerSimplexFunction() = default; + const Mesh& PerSimplexFunction::mesh() const { return m_mesh; From 48f9401fddded7efeb539b29566cacfe6e337cfb Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 24 Oct 2023 15:09:54 -0400 Subject: [PATCH 114/134] change member variable names add assertions to position amips 2d --- src/wmtk/function/PositionMapAMIPS2D.cpp | 6 ++++-- src/wmtk/function/PositionMapAMIPS2D.hpp | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/wmtk/function/PositionMapAMIPS2D.cpp b/src/wmtk/function/PositionMapAMIPS2D.cpp index 49a8ea8f50..a762132b08 100644 --- a/src/wmtk/function/PositionMapAMIPS2D.cpp +++ b/src/wmtk/function/PositionMapAMIPS2D.cpp @@ -24,12 +24,14 @@ PositionMapAMIPS2D::PositionMapAMIPS2D( {} -auto PositionMapAMIPS2D::get_value_autodiff(const Tuple& tuple) const -> DScalar +auto PositionMapAMIPS2D::get_value_autodiff(const Simplex& simplex) const -> DScalar { // get_autodiff_value sets the autodiff size if necessary // get the uv coordinates of the triangle - ConstAccessor pos = mesh().create_const_accessor(get_vertex_attribute_handle()); + ConstAccessor pos = mesh().create_const_accessor(get_variable_attribute_handle()); + assert(simplex.primitive_type() == PrimitiveType::Vertex); + const Tuple& tuple = simplex.tuple(); auto tuple_value = pos.const_vector_attribute(tuple); Vector2 uv0; diff --git a/src/wmtk/function/PositionMapAMIPS2D.hpp b/src/wmtk/function/PositionMapAMIPS2D.hpp index a9c4ca2ea0..b6595793c4 100644 --- a/src/wmtk/function/PositionMapAMIPS2D.hpp +++ b/src/wmtk/function/PositionMapAMIPS2D.hpp @@ -23,9 +23,8 @@ class PositionMapAMIPS2D : public AMIPS const double c); public: - DScalar get_value_autodiff(const Tuple& tuple) const override; + DScalar get_value_autodiff(const Simplex& simplex) const override; -protected: protected: utils::PositionMapEvaluator m_pos_evaluator; }; From a71ba52182cfae322ecceeb8dddd281ecf2aa31d Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 24 Oct 2023 15:10:48 -0400 Subject: [PATCH 115/134] add per simplex valence function, change previous valence function to Mesh Valence function --- src/wmtk/function/CMakeLists.txt | 6 +- src/wmtk/function/TriMeshValenceFunction.cpp | 51 ++--------------- src/wmtk/function/TriMeshValenceFunction.hpp | 10 +++- src/wmtk/function/ValenceEnergyPerEdge.cpp | 60 ++++++++++++++++++++ src/wmtk/function/ValenceEnergyPerEdge.hpp | 17 ++++++ 5 files changed, 91 insertions(+), 53 deletions(-) create mode 100644 src/wmtk/function/ValenceEnergyPerEdge.cpp create mode 100644 src/wmtk/function/ValenceEnergyPerEdge.hpp diff --git a/src/wmtk/function/CMakeLists.txt b/src/wmtk/function/CMakeLists.txt index 2b932c40f7..039c2de778 100644 --- a/src/wmtk/function/CMakeLists.txt +++ b/src/wmtk/function/CMakeLists.txt @@ -2,8 +2,6 @@ set(SRC_FILES Function.hpp Function.cpp - DifferentiableFunction.cpp - DifferentiableFunction.hpp LocallyDifferentiableFunction.hpp LocallyDifferentiableFunction.cpp @@ -20,9 +18,11 @@ set(SRC_FILES AMIPS3D.cpp PositionMapAMIPS2D.hpp PositionMapAMIPS2D.cpp - + ValenceEnergyPerEdge.hpp + ValenceEnergyPerEdge.cpp TriMeshValenceFunction.hpp TriMeshValenceFunction.cpp + AutodiffFunction.hpp AutodiffFunction.cpp ) diff --git a/src/wmtk/function/TriMeshValenceFunction.cpp b/src/wmtk/function/TriMeshValenceFunction.cpp index d1659e0ebb..494e2aa869 100644 --- a/src/wmtk/function/TriMeshValenceFunction.cpp +++ b/src/wmtk/function/TriMeshValenceFunction.cpp @@ -3,57 +3,14 @@ #include #include namespace wmtk::function { -TriMeshValenceFunction::TriMeshValenceFunction(const TriMesh& mesh) - : Function(mesh) +TriMeshValenceFunction::TriMeshValenceFunction(std::unique_ptr&& function) + : Function(std::move(function)) {} -double TriMeshValenceFunction::get_value(const Tuple& tuple) const +double TriMeshValenceFunction::get_value(const Simplex& simplex) const { - // assume tuple is not a boundary edge - const Tuple current_v = tuple; - const Tuple other_v = mesh().switch_vertex(tuple); - long val0 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), current_v).size()); - long val1 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), other_v).size()); - if (mesh().is_boundary_vertex(current_v)) { - val0 += 2; - } - if (mesh().is_boundary_vertex(other_v)) { - val1 += 2; - } - if (val0 < 4 || val1 < 4) { - return -1; - } - - // top_v - // / \ - // / \ - // current_v-----other_v - // \ / - // \ / - // bottom_v - const Tuple top_v = mesh().switch_vertex(mesh().switch_edge(tuple)); - const Tuple bottom_v = mesh().switch_vertex(mesh().switch_edge(mesh().switch_face(tuple))); - long val2 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), top_v).size()); - long val3 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), bottom_v).size()); - - if (mesh().is_boundary_vertex(top_v)) { - val2 += 2; - } - if (mesh().is_boundary_vertex(bottom_v)) { - val3 += 2; - } - // formula from: https://github.com/daniel-zint/hpmeshgen/blob/cdfb9163ed92523fcf41a127c8173097e935c0a3/src/HPMeshGen2/TriRemeshing.cpp#L315 - const long val_energy = std::max(std::abs(val0 - 6), std::abs(val1 - 6)) + - std::max(std::abs(val2 - 6), std::abs(val3 - 6)); - // const long val_after = std::max(std::abs(val0 - 7), std::abs(val1 - 7)) + - // std::max(std::abs(val2 - 5), std::abs(val3 - 5)); - - return static_cast(val_energy); + return m_function->get_value(simplex); } -const TriMesh& TriMeshValenceFunction::mesh() const -{ - return static_cast(Function::mesh()); -} } // namespace wmtk::function diff --git a/src/wmtk/function/TriMeshValenceFunction.hpp b/src/wmtk/function/TriMeshValenceFunction.hpp index 1526f32ccf..9c834b3cc1 100644 --- a/src/wmtk/function/TriMeshValenceFunction.hpp +++ b/src/wmtk/function/TriMeshValenceFunction.hpp @@ -1,16 +1,20 @@ +#pragma once #include "Function.hpp" +#include "ValenceEnergyPerEdge.hpp" namespace wmtk { -class TriMesh; namespace function { class TriMeshValenceFunction : public Function { public: - TriMeshValenceFunction(const TriMesh& mesh); - double get_value(const Tuple& tuple) const override; + TriMeshValenceFunction(std::unique_ptr&& function); + double get_value(const Simplex& simplex) const override; protected: const TriMesh& mesh() const; + +private: + std::unique_ptr m_function; }; } // namespace function } // namespace wmtk diff --git a/src/wmtk/function/ValenceEnergyPerEdge.cpp b/src/wmtk/function/ValenceEnergyPerEdge.cpp new file mode 100644 index 0000000000..64ed2d261a --- /dev/null +++ b/src/wmtk/function/ValenceEnergyPerEdge.cpp @@ -0,0 +1,60 @@ +#include "ValenceEnergyPerEdge.hpp" +#include +#include +#include +namespace wmtk::function { +ValenceEnergyPerEdge::ValenceEnergyPerEdge(const TriMesh& mesh) + : PerSimplexFunction(mesh, PrimitiveType::Face) +{} + +double ValenceEnergyPerEdge::get_value(const Simplex& simplex) const +{ + // assume tuple is not a boundary edge + const Tuple current_v = simplex.tuple(); + const Tuple other_v = mesh().switch_vertex(current_v); + long val0 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), current_v).size()); + long val1 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), other_v).size()); + if (mesh().is_boundary_vertex(current_v)) { + val0 += 2; + } + if (mesh().is_boundary_vertex(other_v)) { + val1 += 2; + } + if (val0 < 4 || val1 < 4) { + return -1; + } + + // top_v + // / \ + // / \ + // current_v-----other_v + // \ / + // \ / + // bottom_v + const Tuple top_v = mesh().switch_vertex(mesh().switch_edge(current_v)); + const Tuple bottom_v = mesh().switch_vertex(mesh().switch_edge(mesh().switch_face(current_v))); + long val2 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), top_v).size()); + long val3 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), bottom_v).size()); + + if (mesh().is_boundary_vertex(top_v)) { + val2 += 2; + } + if (mesh().is_boundary_vertex(bottom_v)) { + val3 += 2; + } + // formula from: https://github.com/daniel-zint/hpmeshgen/blob/cdfb9163ed92523fcf41a127c8173097e935c0a3/src/HPMeshGen2/TriRemeshing.cpp#L315 + const long val_energy = std::max(std::abs(val0 - 6), std::abs(val1 - 6)) + + std::max(std::abs(val2 - 6), std::abs(val3 - 6)); + // const long val_after = std::max(std::abs(val0 - 7), std::abs(val1 - 7)) + + // std::max(std::abs(val2 - 5), std::abs(val3 - 5)); + + return static_cast(val_energy); +} + +const TriMesh& ValenceEnergyPerEdge::mesh() const +{ + return static_cast(PerSimplexFunction::mesh()); +} + + +} // namespace wmtk::function diff --git a/src/wmtk/function/ValenceEnergyPerEdge.hpp b/src/wmtk/function/ValenceEnergyPerEdge.hpp new file mode 100644 index 0000000000..416b89027c --- /dev/null +++ b/src/wmtk/function/ValenceEnergyPerEdge.hpp @@ -0,0 +1,17 @@ +#pragma once +#include "PerSimplexFunction.hpp" + +namespace wmtk { +namespace function { + +class ValenceEnergyPerEdge : public PerSimplexFunction +{ +public: + ValenceEnergyPerEdge(const TriMesh& mesh); + double get_value(const Simplex& simplex) const override; + +protected: + const TriMesh& mesh() const; +}; +} // namespace function +} // namespace wmtk From be219db0641639c5f0923f6c42056c5a5892aa89 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Tue, 24 Oct 2023 15:11:01 -0400 Subject: [PATCH 116/134] chnage energy tests for compilation --- tests/function/test_2d_energy.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/tests/function/test_2d_energy.cpp b/tests/function/test_2d_energy.cpp index 3eb223c986..10e617cc5b 100644 --- a/tests/function/test_2d_energy.cpp +++ b/tests/function/test_2d_energy.cpp @@ -2,13 +2,18 @@ #include #include #include +#include +#include #include #include +#include +#include #include "../tools/DEBUG_TriMesh.hpp" #include "../tools/TriMesh_examples.hpp" using namespace wmtk; using namespace wmtk::function; using namespace wmtk::tests; +using namespace wmtk::simplex; TEST_CASE("energy_valence") { // 0---1---2 @@ -25,13 +30,14 @@ TEST_CASE("energy_valence") const TriMesh tri_mesh = static_cast(example_mesh); - TriMeshValenceFunction valence_energy(tri_mesh); + ValenceEnergyPerEdge valence_energy(tri_mesh); - REQUIRE(valence_energy.get_value(e1) == 2); - REQUIRE(valence_energy.get_value(e2) == 2); - REQUIRE(valence_energy.get_value(e3) == 2); - REQUIRE(valence_energy.get_value(e4) == 2); + + REQUIRE(valence_energy.get_value(Simplex(PrimitiveType::Edge, e1)) == 2); + REQUIRE(valence_energy.get_value(Simplex(PrimitiveType::Edge, e2)) == 2); + REQUIRE(valence_energy.get_value(Simplex(PrimitiveType::Edge, e3)) == 2); + REQUIRE(valence_energy.get_value(Simplex(PrimitiveType::Edge, e4)) == 2); } TEST_CASE("amips2d_values") @@ -47,7 +53,7 @@ TEST_CASE("amips2d_values") AMIPS2D amips2d(tri_mesh, uv_handle); - CHECK(amips2d.get_value(e1) == 2.0); + CHECK(amips2d.get_value(Simplex(PrimitiveType::Vertex, e1)) == 2.0); } SECTION("random_triangle") { @@ -60,7 +66,7 @@ TEST_CASE("amips2d_values") const TriMesh tri_mesh = static_cast(example_mesh); AMIPS2D amips2d(tri_mesh, uv_handle); - CHECK(amips2d.get_value(e1) >= 2.); + CHECK(amips2d.get_value(Simplex(PrimitiveType::Vertex, e1)) >= 2.); } } } @@ -84,7 +90,7 @@ TEST_CASE("PositionMapAMIPS_values") 0.0, 1.0); - CHECK(amips3d.get_value(e1) == 2.0); + CHECK(amips3d.get_value(Simplex(PrimitiveType::Vertex, e1)) == 2.0); } SECTION("random_triangle") { @@ -104,7 +110,7 @@ TEST_CASE("PositionMapAMIPS_values") 0.0, 1.0); - CHECK(amips3d.get_value(e1) >= 2.0); + CHECK(amips3d.get_value(Simplex(PrimitiveType::Vertex, e1)) >= 2.0); } } } From 669b591862415fbaddc170d9abd68c6ca82e06e9 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 25 Oct 2023 18:10:17 -0400 Subject: [PATCH 117/134] restricting get_gradient and hessian to only the differnetiableFunctionEvaluator --- src/wmtk/function/utils/CMakeLists.txt | 4 +- .../utils/DifferentiableFunctionEvaluator.cpp | 64 ++++++++++++++++--- .../utils/DifferentiableFunctionEvaluator.hpp | 29 +++++++-- src/wmtk/function/utils/FunctionEvaluator.cpp | 23 ++----- src/wmtk/function/utils/FunctionEvaluator.hpp | 28 ++++---- 5 files changed, 102 insertions(+), 46 deletions(-) diff --git a/src/wmtk/function/utils/CMakeLists.txt b/src/wmtk/function/utils/CMakeLists.txt index 991b647d38..c5dcdc0036 100644 --- a/src/wmtk/function/utils/CMakeLists.txt +++ b/src/wmtk/function/utils/CMakeLists.txt @@ -9,8 +9,8 @@ set(SRC_FILES amips.hpp amips.cpp - # FunctionEvaluator.hpp - # FunctionEvaluator.cpp + FunctionEvaluator.hpp + FunctionEvaluator.cpp DifferentiableFunctionEvaluator.hpp DifferentiableFunctionEvaluator.cpp ) diff --git a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp index 880cc59fc7..c839e3817f 100644 --- a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp +++ b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp @@ -1,34 +1,78 @@ #include "DifferentiableFunctionEvaluator.hpp" -namespace wmtk::function::utils { +#include +#include +#include + +namespace wmtk::function::utils { DifferentiableFunctionEvaluator::DifferentiableFunctionEvaluator( - const function::DifferentiableFunction& function, + const function::LocallyDifferentiableFunction& function, Accessor& accessor, - const Tuple& tuple) - : FunctionEvaluator(function, accessor, tuple) -{} + const Simplex& simplex) + : FunctionEvaluator(function, accessor, simplex) +{ + m_upper_level_cofaces = compute_upper_level_cofaces(); +} + +auto DifferentiableFunctionEvaluator::function() const + -> const function::LocallyDifferentiableFunction& +{ + return static_cast(FunctionEvaluator::function()); +} + +auto DifferentiableFunctionEvaluator::get_value(double v) -> double +{ + store(v); + return get_value(); +} -auto DifferentiableFunctionEvaluator::function() const -> const function::DifferentiableFunction& +auto DifferentiableFunctionEvaluator::get_value() const -> double { - return static_cast(FunctionEvaluator::function()); + return function().get_value_sum( + wmtk::simplex::utils::tuple_vector_to_homogeneous_simplex_vector( + upper_level_cofaces(), + function_simplex_type())); } auto DifferentiableFunctionEvaluator::get_gradient() const -> Vector { - return function().get_gradient_sum(tuple(), top_level_cofaces()); + return function().get_gradient_sum( + wmtk::simplex::utils::tuple_vector_to_homogeneous_simplex_vector( + upper_level_cofaces(), + function_simplex_type())); } + auto DifferentiableFunctionEvaluator::get_hessian() const -> Matrix { - return function().get_hessian_sum(tuple(), top_level_cofaces()); + return function().get_hessian_sum( + wmtk::simplex::utils::tuple_vector_to_homogeneous_simplex_vector( + upper_level_cofaces(), + function_simplex_type())); } + auto DifferentiableFunctionEvaluator::get_gradient(double v) -> Vector { store(v); return get_gradient(); } + auto DifferentiableFunctionEvaluator::get_hessian(double v) -> Matrix { store(v); return get_hessian(); } -} // namespace wmtk::function::utils + +const std::vector& DifferentiableFunctionEvaluator::upper_level_cofaces() const +{ + return m_upper_level_cofaces; +} +std::vector DifferentiableFunctionEvaluator::compute_upper_level_cofaces() const +{ + return simplex::upper_level_cofaces_tuples(mesh(), simplex(), function_simplex_type()); +} + +std::vector DifferentiableFunctionEvaluator::compute_top_level_cofaces() const +{ + return simplex::top_level_cofaces_tuples(mesh(), simplex()); +} +} // namespace wmtk::function::utils \ No newline at end of file diff --git a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp index db4e4dcebe..b82afdcc2b 100644 --- a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp +++ b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "FunctionEvaluator.hpp" namespace wmtk::function::utils { @@ -18,26 +18,43 @@ class DifferentiableFunctionEvaluator : public FunctionEvaluator { public: DifferentiableFunctionEvaluator( - const function::DifferentiableFunction& function, + const function::LocallyDifferentiableFunction& function, Accessor& accessor, - const Tuple& tuple); + const Simplex& simplex); using Vector = Eigen::VectorXd; using Matrix = Eigen::MatrixXd; - + double get_value() const; Vector get_gradient() const; Matrix get_hessian() const; + + template + double get_value(const Eigen::MatrixBase& v); template Vector get_gradient(const Eigen::MatrixBase& v); template Matrix get_hessian(const Eigen::MatrixBase& v); + double get_value(double v); Vector get_gradient(double v); Matrix get_hessian(double v); - const function::DifferentiableFunction& function() const; -}; + const function::LocallyDifferentiableFunction& function() const; + + const std::vector& upper_level_cofaces() const; +private: + // cache the top simplices + std::vector m_upper_level_cofaces; + std::vector compute_upper_level_cofaces() const; + std::vector compute_top_level_cofaces() const; +}; +template +double DifferentiableFunctionEvaluator::get_value(const Eigen::MatrixBase& v) +{ + store(v); + return get_value(); +} template auto DifferentiableFunctionEvaluator::get_gradient(const Eigen::MatrixBase& v) -> Vector diff --git a/src/wmtk/function/utils/FunctionEvaluator.cpp b/src/wmtk/function/utils/FunctionEvaluator.cpp index ed7e952316..c375a8ef73 100644 --- a/src/wmtk/function/utils/FunctionEvaluator.cpp +++ b/src/wmtk/function/utils/FunctionEvaluator.cpp @@ -1,41 +1,30 @@ #include "FunctionEvaluator.hpp" #include +#include namespace wmtk::function::utils { FunctionEvaluator::FunctionEvaluator( const function::Function& function, Accessor& accessor, - const Tuple& tuple) + const Simplex& simplex) : m_function(function) , m_accessor(accessor) - , m_tuple(tuple) -{ - m_top_level_cofaces = compute_top_level_cofaces(); -} - + , m_simplex(simplex) +{} void FunctionEvaluator::store(double v) { - m_accessor.scalar_attribute(m_tuple) = v; + m_accessor.scalar_attribute(tuple()) = v; } double FunctionEvaluator::get_value() const { - return m_function.get_value_sum(m_top_level_cofaces); + return m_function.get_value(m_simplex); } auto FunctionEvaluator::get_value(double v) -> double { store(v); return get_value(); } - -const std::vector& FunctionEvaluator::top_level_cofaces() const -{ - return m_top_level_cofaces; -} -std::vector FunctionEvaluator::compute_top_level_cofaces() const -{ - return simplex::top_level_cofaces_tuples(mesh(), Simplex(PrimitiveType::Vertex, tuple())); -} } // namespace wmtk::function::utils diff --git a/src/wmtk/function/utils/FunctionEvaluator.hpp b/src/wmtk/function/utils/FunctionEvaluator.hpp index d9cd6b9e93..4a9fd4513b 100644 --- a/src/wmtk/function/utils/FunctionEvaluator.hpp +++ b/src/wmtk/function/utils/FunctionEvaluator.hpp @@ -2,9 +2,11 @@ #include #include #include +#include #include #include #include +#include namespace wmtk::function::utils { @@ -16,11 +18,14 @@ namespace wmtk::function::utils { class FunctionEvaluator { public: - FunctionEvaluator(const Function& function, Accessor& accessor, const Tuple& tuple); + FunctionEvaluator(const Function& function, Accessor& accessor, const Simplex& simplex); - auto get_coordinate() { return m_accessor.vector_attribute(m_tuple); } - auto get_const_coordinate() const { return m_accessor.const_vector_attribute(m_tuple); } + auto get_coordinate() { return m_accessor.vector_attribute(m_simplex.tuple()); } + auto get_const_coordinate() const + { + return m_accessor.const_vector_attribute(m_simplex.tuple()); + } void store(double v); template @@ -36,30 +41,31 @@ class FunctionEvaluator double get_value(double v); - const Tuple& tuple() const { return m_tuple; } + const Tuple& tuple() const { return m_simplex.tuple(); } + const Simplex& simplex() const { return m_simplex; } Mesh& mesh() { return m_accessor.mesh(); } const Mesh& mesh() const { return m_accessor.mesh(); } Accessor& accessor() { return m_accessor; } const Function& function() const { return m_function; } + const PrimitiveType& function_simplex_type() const + { + return m_function.get_function()->get_function_simplex_type(); + } - const std::vector& top_level_cofaces() const; + const PrimitiveType& my_simplex_type() const { return m_simplex.primitive_type(); } private: const Function& m_function; Accessor& m_accessor; - const Tuple& m_tuple; - - // cache the top simplices - std::vector m_top_level_cofaces; - std::vector compute_top_level_cofaces() const; + const Simplex& m_simplex; }; template void FunctionEvaluator::store(const Eigen::MatrixBase& v) { - m_accessor.vector_attribute(m_tuple) = v; + m_accessor.vector_attribute(tuple()) = v; } template double FunctionEvaluator::get_value(const Eigen::MatrixBase& v) From d690f0c925684963bf8ef309529c05a835b6930a Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 25 Oct 2023 18:11:40 -0400 Subject: [PATCH 118/134] add simplex type in constructor for PerSImplexFunction. Check type validity of PerSimplexFunction at construction time --- .../LocallyDifferentiableFunction.cpp | 52 ++++++++++++------- .../LocallyDifferentiableFunction.hpp | 17 +++--- src/wmtk/function/PerSimplexFunction.cpp | 6 +-- src/wmtk/function/PerSimplexFunction.hpp | 4 +- 4 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/wmtk/function/LocallyDifferentiableFunction.cpp b/src/wmtk/function/LocallyDifferentiableFunction.cpp index 949830630a..c89de31184 100644 --- a/src/wmtk/function/LocallyDifferentiableFunction.cpp +++ b/src/wmtk/function/LocallyDifferentiableFunction.cpp @@ -5,33 +5,47 @@ namespace wmtk { namespace function { LocallyDifferentiableFunction::LocallyDifferentiableFunction( - std::unique_ptr&& function) - : Function(std::move(function)) -{} + std::shared_ptr&& function, + const PrimitiveType& my_simplex_type) + : Function(function) + , m_my_simplex_type(my_simplex_type) +{ + m_function->assert_function_type(m_my_simplex_type); +} LocallyDifferentiableFunction::~LocallyDifferentiableFunction() = default; -Eigen::VectorXd LocallyDifferentiableFunction::get_one_ring_gradient( - const Simplex& my_simplex, - const PrimitiveType& cofaces_type) const +Eigen::VectorXd LocallyDifferentiableFunction::get_local_gradient(const Tuple& my_tuple) const +{ + return get_local_gradient(Simplex(m_my_simplex_type, my_tuple)); +} + +Eigen::MatrixXd LocallyDifferentiableFunction::get_local_hessian(const Tuple& my_tuple) const +{ + return get_local_hessian(Simplex(m_my_simplex_type, my_tuple)); +} + +Eigen::VectorXd LocallyDifferentiableFunction::get_local_gradient(const Simplex& my_simplex) const { - m_function->assert_function_type(my_simplex.primitive_type()); - std::vector coface_tuples = - simplex::upper_level_cofaces_tuples(m_function->mesh(), my_simplex, cofaces_type); + std::vector coface_tuples = simplex::upper_level_cofaces_tuples( + m_function->mesh(), + my_simplex, + m_function->get_function_simplex_type()); - return get_gradient_sum( - simplex::utils::tuple_vector_to_homogeneous_simplex_vector(coface_tuples, cofaces_type)); + return get_gradient_sum(simplex::utils::tuple_vector_to_homogeneous_simplex_vector( + coface_tuples, + m_function->get_function_simplex_type())); } -Eigen::MatrixXd LocallyDifferentiableFunction::get_one_ring_hessian( - const Simplex& my_simplex, - const PrimitiveType& cofaces_type) const +Eigen::MatrixXd LocallyDifferentiableFunction::get_local_hessian(const Simplex& my_simplex) const { - m_function->assert_function_type(my_simplex.primitive_type()); - std::vector coface_tuples = - simplex::upper_level_cofaces_tuples(m_function->mesh(), my_simplex, cofaces_type); + std::vector coface_tuples = simplex::upper_level_cofaces_tuples( + m_function->mesh(), + my_simplex, + m_function->get_function_simplex_type()); - return get_hessian_sum( - simplex::utils::tuple_vector_to_homogeneous_simplex_vector(coface_tuples, cofaces_type)); + return get_hessian_sum(simplex::utils::tuple_vector_to_homogeneous_simplex_vector( + coface_tuples, + m_function->get_function_simplex_type())); } double LocallyDifferentiableFunction::get_value_sum( diff --git a/src/wmtk/function/LocallyDifferentiableFunction.hpp b/src/wmtk/function/LocallyDifferentiableFunction.hpp index ff037c1066..e9cf40a2a8 100644 --- a/src/wmtk/function/LocallyDifferentiableFunction.hpp +++ b/src/wmtk/function/LocallyDifferentiableFunction.hpp @@ -8,23 +8,24 @@ namespace function { class LocallyDifferentiableFunction : public Function { public: - LocallyDifferentiableFunction(std::unique_ptr&& function); + LocallyDifferentiableFunction( + std::shared_ptr&& function, + const PrimitiveType& my_simplex_type); virtual ~LocallyDifferentiableFunction(); public: - Eigen::VectorXd get_one_ring_gradient( - const Simplex& my_simplex, - const PrimitiveType& cofaces_type) const; - Eigen::MatrixXd get_one_ring_hessian( - const Simplex& my_simplex, - const PrimitiveType& cofaces_type) const; + Eigen::VectorXd get_local_gradient(const Simplex& my_simplex) const; + Eigen::MatrixXd get_local_hessian(const Simplex& my_simplex) const; + Eigen::VectorXd get_local_gradient(const Tuple& my_tuple) const; + Eigen::MatrixXd get_local_hessian(const Tuple& my_tuple) const; double get_value_sum(const std::vector& coface_simplices) const; Eigen::VectorXd get_gradient_sum(const std::vector& coface_simplices) const; Eigen::MatrixXd get_hessian_sum(const std::vector& coface_simplices) const; private: - std::unique_ptr m_function; + std::shared_ptr m_function; + const PrimitiveType m_my_simplex_type; }; } // namespace function } // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/PerSimplexFunction.cpp b/src/wmtk/function/PerSimplexFunction.cpp index 24b822e786..f5fb579d1f 100644 --- a/src/wmtk/function/PerSimplexFunction.cpp +++ b/src/wmtk/function/PerSimplexFunction.cpp @@ -5,7 +5,7 @@ namespace function { PerSimplexFunction::PerSimplexFunction(const Mesh& mesh, const PrimitiveType& simplex_type) : m_mesh(mesh) - , m_simplex_type(simplex_type) + , m_function_simplex_type(simplex_type) {} PerSimplexFunction::~PerSimplexFunction() = default; @@ -15,9 +15,9 @@ const Mesh& PerSimplexFunction::mesh() const return m_mesh; } -const PrimitiveType& PerSimplexFunction::get_simplex_type() const +const PrimitiveType& PerSimplexFunction::get_function_simplex_type() const { - return m_simplex_type; + return m_function_simplex_type; } } // namespace function }; // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/PerSimplexFunction.hpp b/src/wmtk/function/PerSimplexFunction.hpp index 8e13e55e24..5e55272bd6 100644 --- a/src/wmtk/function/PerSimplexFunction.hpp +++ b/src/wmtk/function/PerSimplexFunction.hpp @@ -14,11 +14,11 @@ class PerSimplexFunction public: const Mesh& mesh() const; virtual double get_value(const Simplex& s) const = 0; - const PrimitiveType& get_simplex_type() const; + const PrimitiveType& get_function_simplex_type() const; private: const Mesh& m_mesh; - const PrimitiveType m_simplex_type; + const PrimitiveType m_function_simplex_type; }; } // namespace function From fef47da7c9e5a48606803534b501d8f2a1dca35c Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 25 Oct 2023 18:12:10 -0400 Subject: [PATCH 119/134] returning per simplex function in Function --- src/wmtk/function/Function.cpp | 9 ++++++--- src/wmtk/function/Function.hpp | 5 +++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/wmtk/function/Function.cpp b/src/wmtk/function/Function.cpp index f9bf52bd8a..35d7e75462 100644 --- a/src/wmtk/function/Function.cpp +++ b/src/wmtk/function/Function.cpp @@ -3,12 +3,15 @@ namespace wmtk { namespace function { -Function::Function(std::unique_ptr&& function) - : m_function(std::move(function)) +Function::Function(std::shared_ptr&& function) + : m_function(function) {} Function::~Function() = default; - +std::shared_ptr Function::get_function() const +{ + return m_function; +} } // namespace function } // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/Function.hpp b/src/wmtk/function/Function.hpp index 37a8b45cd4..df8c079853 100644 --- a/src/wmtk/function/Function.hpp +++ b/src/wmtk/function/Function.hpp @@ -7,15 +7,16 @@ namespace function { class Function { public: - Function(std::unique_ptr&& function); + Function(std::shared_ptr&& function); virtual ~Function(); public: // evaluate the function on the top level simplex of the tuple virtual double get_value(const Simplex& simplex) const = 0; + std::shared_ptr get_function() const; private: - std::unique_ptr m_function; + std::shared_ptr m_function; }; } // namespace function } // namespace wmtk From 2d09286ea5bd628d773836c41540a54426763af1 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 25 Oct 2023 18:12:55 -0400 Subject: [PATCH 120/134] change naming get_function_type and get_local_gradient --- src/wmtk/function/DifferentiablePerSimplexFunction.cpp | 2 +- tests/components/test_smoothing.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wmtk/function/DifferentiablePerSimplexFunction.cpp b/src/wmtk/function/DifferentiablePerSimplexFunction.cpp index 66eb36f133..a48795ed42 100644 --- a/src/wmtk/function/DifferentiablePerSimplexFunction.cpp +++ b/src/wmtk/function/DifferentiablePerSimplexFunction.cpp @@ -30,7 +30,7 @@ void DifferentiablePerSimplexFunction::assert_function_type(const PrimitiveType& throw std::runtime_error("Differentiation of the DifferentiableFunction must be taken wrt " "the attribute of the simplex type "); } - if (get_simplex_type() < s_type) { + if (get_function_simplex_type() > s_type) { throw std::runtime_error( "The DifferentiableFunction must be defined on the cofaces of higher dimension to the " "simplex type "); diff --git a/tests/components/test_smoothing.cpp b/tests/components/test_smoothing.cpp index e06ae604bb..03609c009e 100644 --- a/tests/components/test_smoothing.cpp +++ b/tests/components/test_smoothing.cpp @@ -33,8 +33,8 @@ TEST_CASE("smoothing_Newton_Method") spdlog::warn("Initial valuenorm: {}", factory.settings().energy->get_one_ring_value(tuple)); spdlog::warn( "Initial gradient: norm: {}", - factory.settings().energy->get_one_ring_gradient(tuple).norm()); - while (factory.settings().energy->get_one_ring_gradient(tuple).norm() > 1e-10) { + factory.settings().energy->get_local_gradient(tuple).norm()); + while (factory.settings().energy->get_local_gradient(tuple).norm() > 1e-10) { scheduler.run_operation_on_all(PrimitiveType::Vertex, "optimize_vertices"); REQUIRE(scheduler.number_of_successful_operations() > 0); tuple = mesh.face_tuple_from_vids(2, 4, 5); From 54c1b63a9b08ee9707173614d2a93c6f7bf26ecc Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 25 Oct 2023 18:13:50 -0400 Subject: [PATCH 121/134] Taking DifferentiableEvaluator in base smoothing --- .../tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp | 4 ++-- .../tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp index ed6d6a567b..a7ec176213 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.cpp @@ -1,6 +1,7 @@ #include "VertexSmoothUsingDifferentiableEnergy.hpp" #include #include +#include namespace wmtk::operations { void OperationSettings::initialize_invariants( @@ -32,8 +33,7 @@ VertexSmoothUsingDifferentiableEnergy::get_function_evaluator(Accessor& return function::utils::DifferentiableFunctionEvaluator( *m_settings.energy, accessor, - input_tuple() - ); + simplex::Simplex(PrimitiveType::Vertex, input_tuple())); } diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp index 5150924d5d..d96b32e3e0 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp @@ -1,4 +1,6 @@ #pragma once +#include +#include #include #include #include "VertexAttributesUpdateBase.hpp" @@ -18,7 +20,7 @@ template <> struct OperationSettings { OperationSettings base_settings; - std::unique_ptr energy; + std::unique_ptr energy; // coordinate for teh attribute used to evaluate the energy MeshAttributeHandle coordinate_handle; bool smooth_boundary = false; From 0f7d28042996a5ffb166e4e4094646b501fa4f5b Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 25 Oct 2023 18:14:29 -0400 Subject: [PATCH 122/134] compilation in smoothing gradient descent --- .../tri_mesh/internal/VertexSmoothGradientDescent.cpp | 2 +- .../tri_mesh/internal/VertexSmoothNewtonMethod.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp index e680087658..9bc41499fb 100644 --- a/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp @@ -36,7 +36,7 @@ bool VertexSmoothGradientDescent::execute() } std::vector VertexSmoothGradientDescent::priority() const { - double gradnorm = m_settings.energy->get_one_ring_gradient(input_tuple()).norm(); + double gradnorm = m_settings.energy->get_local_gradient(input_tuple()).norm(); std::vector r; r.emplace_back(-gradnorm); return r; diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp index 387a4eab80..55ad628fbb 100644 --- a/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp @@ -6,8 +6,7 @@ VertexSmoothNewtonMethod::VertexSmoothNewtonMethod( const Tuple& t, const OperationSettings& settings) : VertexSmoothUsingDifferentiableEnergy(m, t, settings) -{ -} +{} std::string VertexSmoothNewtonMethod::name() const { return "tri_mesh_vertex_smooth_newton_method"; @@ -54,7 +53,7 @@ bool VertexSmoothNewtonMethod::execute() } std::vector VertexSmoothNewtonMethod::priority() const { - double gradnorm = m_settings.energy->get_one_ring_gradient(input_tuple()).norm(); + double gradnorm = m_settings.energy->get_local_gradient(input_tuple()).norm(); std::vector r; r.emplace_back(-gradnorm); return r; From cbd1db0a3407b94b029473cc1d3ab0eb89bc42d4 Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 25 Oct 2023 18:14:51 -0400 Subject: [PATCH 123/134] change top level to upper level in LineSearch class --- src/wmtk/optimization/LineSearch.cpp | 9 +++++---- src/wmtk/optimization/LineSearch.hpp | 9 ++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/wmtk/optimization/LineSearch.cpp b/src/wmtk/optimization/LineSearch.cpp index 5f20bd4f8e..7936ca8926 100644 --- a/src/wmtk/optimization/LineSearch.cpp +++ b/src/wmtk/optimization/LineSearch.cpp @@ -1,23 +1,24 @@ #include "LineSearch.hpp" + namespace wmtk::optimization { LineSearch::LineSearch( - function::utils::FunctionEvaluator& interface, + function::utils::DifferentiableFunctionEvaluator& interface, const InvariantCollection& invariants) : m_interface(interface) , m_invariants(invariants) {} -const std::vector& LineSearch::top_level_cofaces() const +const std::vector& LineSearch::upper_level_cofaces() const { - return m_interface.top_level_cofaces(); + return m_interface.upper_level_cofaces(); } bool LineSearch::check_state() const { PrimitiveType top_type = m_interface.mesh().top_simplex_type(); bool before_pass = m_invariants.before(m_interface.tuple()); - bool after_pass = m_invariants.after(top_type, top_level_cofaces()); + bool after_pass = m_invariants.after(top_type, upper_level_cofaces()); return before_pass && after_pass; } double LineSearch::run(const Eigen::VectorXd& direction, double step_size) diff --git a/src/wmtk/optimization/LineSearch.hpp b/src/wmtk/optimization/LineSearch.hpp index 60551cb028..5ae817a281 100644 --- a/src/wmtk/optimization/LineSearch.hpp +++ b/src/wmtk/optimization/LineSearch.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include @@ -12,7 +12,7 @@ class LineSearch public: using InvariantCollection = wmtk::InvariantCollection; LineSearch( - function::utils::FunctionEvaluator& interface, + function::utils::DifferentiableFunctionEvaluator& interface, const InvariantCollection& invariants); using Vector = Eigen::VectorXd; @@ -28,14 +28,14 @@ class LineSearch } protected: - function::utils::FunctionEvaluator& m_interface; + function::utils::DifferentiableFunctionEvaluator& m_interface; const InvariantCollection& m_invariants; bool m_create_scope = true; long m_max_steps = 10; double m_min_step_size_ratio = 1e-6; - const std::vector& top_level_cofaces() const; + const std::vector& upper_level_cofaces() const; // TODO: formally define what checking the state means // we currently make sure that we pass before on the input tuple and after on all top level @@ -47,4 +47,3 @@ class LineSearch }; } // namespace wmtk::optimization - From fb26313d6b59741ed30d06b13587cb8fdf726d5d Mon Sep 17 00:00:00 2001 From: yunfanzhou Date: Wed, 25 Oct 2023 18:25:40 -0400 Subject: [PATCH 124/134] warning. add upper_level_cofaces to cmake --- src/wmtk/function/utils/FunctionEvaluator.hpp | 6 +++++- src/wmtk/simplex/CMakeLists.txt | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/wmtk/function/utils/FunctionEvaluator.hpp b/src/wmtk/function/utils/FunctionEvaluator.hpp index 4a9fd4513b..3fa257a9fc 100644 --- a/src/wmtk/function/utils/FunctionEvaluator.hpp +++ b/src/wmtk/function/utils/FunctionEvaluator.hpp @@ -53,7 +53,11 @@ class FunctionEvaluator return m_function.get_function()->get_function_simplex_type(); } - const PrimitiveType& my_simplex_type() const { return m_simplex.primitive_type(); } + const PrimitiveType& my_simplex_type() const + { + PrimitiveType type = m_simplex.primitive_type(); + return type; + } private: const Function& m_function; diff --git a/src/wmtk/simplex/CMakeLists.txt b/src/wmtk/simplex/CMakeLists.txt index 6f73501a0d..73c1613265 100644 --- a/src/wmtk/simplex/CMakeLists.txt +++ b/src/wmtk/simplex/CMakeLists.txt @@ -11,6 +11,10 @@ set(SRC_FILES top_level_cofaces.cpp top_level_cofaces_iterable.hpp top_level_cofaces_iterable.cpp + + upper_level_cofaces.hpp + upper_level_cofaces.cpp + link.hpp link.cpp link_iterable.hpp From 7cfb406549847f48faa8766b2c1c92cd23e62f60 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 28 Oct 2023 14:30:50 -0400 Subject: [PATCH 125/134] updated basic function interface to have ocnsistent naming --- src/wmtk/function/AMIPS2D.cpp | 9 +- src/wmtk/function/AMIPS2D.hpp | 2 +- src/wmtk/function/AMIPS3D.cpp | 9 +- src/wmtk/function/AMIPS3D.hpp | 2 +- src/wmtk/function/AutodiffFunction.cpp | 21 +++-- src/wmtk/function/AutodiffFunction.hpp | 17 ++-- src/wmtk/function/CMakeLists.txt | 14 ++-- src/wmtk/function/DifferentiableFunction.cpp | 45 +--------- src/wmtk/function/DifferentiableFunction.hpp | 36 ++------ .../DifferentiablePerSimplexFunction.cpp | 40 --------- .../DifferentiablePerSimplexFunction.hpp | 45 ---------- src/wmtk/function/Function.cpp | 15 +--- src/wmtk/function/Function.hpp | 24 +++--- .../function/LocalDifferentiableFunction.cpp | 57 +++++++++++++ .../function/LocalDifferentiableFunction.hpp | 36 ++++++++ src/wmtk/function/LocalFunction.cpp | 49 +++++++++++ src/wmtk/function/LocalFunction.hpp | 37 ++++++++ .../LocallyDifferentiableFunction.cpp | 84 ------------------- .../LocallyDifferentiableFunction.hpp | 31 ------- .../PerSimplexDifferentiableFunction.cpp | 73 ++++++++++++++++ .../PerSimplexDifferentiableFunction.hpp | 50 +++++++++++ src/wmtk/function/PerSimplexFunction.cpp | 36 ++++++-- src/wmtk/function/PerSimplexFunction.hpp | 25 ++++-- src/wmtk/function/PositionMapAMIPS2D.cpp | 6 +- src/wmtk/function/PositionMapAMIPS2D.hpp | 2 +- src/wmtk/function/TriMeshValenceFunction.cpp | 16 ---- src/wmtk/function/TriMeshValenceFunction.hpp | 20 ----- src/wmtk/function/ValenceEnergyPerEdge.cpp | 32 +++---- src/wmtk/function/ValenceEnergyPerEdge.hpp | 9 +- .../utils/DifferentiableFunctionEvaluator.cpp | 77 +++++++++-------- .../utils/DifferentiableFunctionEvaluator.hpp | 23 ++--- src/wmtk/function/utils/FunctionEvaluator.hpp | 6 +- 32 files changed, 484 insertions(+), 464 deletions(-) delete mode 100644 src/wmtk/function/DifferentiablePerSimplexFunction.cpp delete mode 100644 src/wmtk/function/DifferentiablePerSimplexFunction.hpp create mode 100644 src/wmtk/function/LocalDifferentiableFunction.cpp create mode 100644 src/wmtk/function/LocalDifferentiableFunction.hpp create mode 100644 src/wmtk/function/LocalFunction.cpp create mode 100644 src/wmtk/function/LocalFunction.hpp delete mode 100644 src/wmtk/function/LocallyDifferentiableFunction.cpp delete mode 100644 src/wmtk/function/LocallyDifferentiableFunction.hpp create mode 100644 src/wmtk/function/PerSimplexDifferentiableFunction.cpp create mode 100644 src/wmtk/function/PerSimplexDifferentiableFunction.hpp delete mode 100644 src/wmtk/function/TriMeshValenceFunction.cpp delete mode 100644 src/wmtk/function/TriMeshValenceFunction.hpp diff --git a/src/wmtk/function/AMIPS2D.cpp b/src/wmtk/function/AMIPS2D.cpp index 63dacd383e..35dbcdf3e5 100644 --- a/src/wmtk/function/AMIPS2D.cpp +++ b/src/wmtk/function/AMIPS2D.cpp @@ -8,16 +8,15 @@ namespace wmtk::function { AMIPS2D::AMIPS2D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) : AMIPS(mesh, vertex_attribute_handle) { - assert(get_variable_attribute_handle().is_valid()); + assert(get_coordinate_attribute_handle().is_valid()); // check the dimension of the position assert(embedded_dimension() == 2); } -auto AMIPS2D::get_value_autodiff(const Simplex& simplex) const -> DScalar +auto AMIPS2D::get_value_autodiff(const Tuple& simplex) const -> DScalar { - assert(simplex.primitive_type() == PrimitiveType::Vertex); - return function_eval(simplex.tuple()); + return function_eval(simplex); } template @@ -25,7 +24,7 @@ T AMIPS2D::function_eval(const Tuple& tuple) const { // get_autodiff_value sets the autodiff size if necessary // get the uv coordinates of the triangle - ConstAccessor pos = mesh().create_const_accessor(get_variable_attribute_handle()); + ConstAccessor pos = mesh().create_const_accessor(get_coordinate_attribute_handle()); auto tuple_value = pos.const_vector_attribute(tuple); Vector2 uv0; diff --git a/src/wmtk/function/AMIPS2D.hpp b/src/wmtk/function/AMIPS2D.hpp index 62778c41a3..ae161d3938 100644 --- a/src/wmtk/function/AMIPS2D.hpp +++ b/src/wmtk/function/AMIPS2D.hpp @@ -7,7 +7,7 @@ class AMIPS2D : public AMIPS AMIPS2D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); protected: - DScalar get_value_autodiff(const Simplex& simplex) const override; + DScalar get_value_autodiff(const Tuple& simplex) const override; template diff --git a/src/wmtk/function/AMIPS3D.cpp b/src/wmtk/function/AMIPS3D.cpp index 7ef39b9c7e..7426371e62 100644 --- a/src/wmtk/function/AMIPS3D.cpp +++ b/src/wmtk/function/AMIPS3D.cpp @@ -8,16 +8,15 @@ namespace wmtk::function { AMIPS3D::AMIPS3D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle) : AMIPS(mesh, vertex_attribute_handle) { - assert(get_variable_attribute_handle().is_valid()); + assert(get_coordinate_attribute_handle().is_valid()); // check the dimension of the position assert(embedded_dimension() == 3); } -auto AMIPS3D::get_value_autodiff(const Simplex& simplex) const -> DScalar +auto AMIPS3D::get_value_autodiff(const Tuple& simplex) const -> DScalar { - assert(simplex.primitive_type() == PrimitiveType::Vertex); - return function_eval(simplex.tuple()); + return function_eval(simplex); } template @@ -25,7 +24,7 @@ T AMIPS3D::function_eval(const Tuple& tuple) const { // get_autodiff_value sets the autodiff size if necessary // get the pos coordinates of the triangle - ConstAccessor pos = mesh().create_const_accessor(get_variable_attribute_handle()); + ConstAccessor pos = mesh().create_const_accessor(get_coordinate_attribute_handle()); auto tuple_value = pos.const_vector_attribute(tuple); Vector3 pos0; diff --git a/src/wmtk/function/AMIPS3D.hpp b/src/wmtk/function/AMIPS3D.hpp index 88d14b6347..93f87285ef 100644 --- a/src/wmtk/function/AMIPS3D.hpp +++ b/src/wmtk/function/AMIPS3D.hpp @@ -8,7 +8,7 @@ class AMIPS3D : public AMIPS AMIPS3D(const TriMesh& mesh, const MeshAttributeHandle& vertex_attribute_handle); protected: - DScalar get_value_autodiff(const Simplex& simplex) const override; + DScalar get_value_autodiff(const Tuple& simplex) const override; template T function_eval(const Tuple& tuple) const; diff --git a/src/wmtk/function/AutodiffFunction.cpp b/src/wmtk/function/AutodiffFunction.cpp index 316768619e..ab5048191f 100644 --- a/src/wmtk/function/AutodiffFunction.cpp +++ b/src/wmtk/function/AutodiffFunction.cpp @@ -7,24 +7,33 @@ AutodiffFunction::AutodiffFunction( const Mesh& mesh, const PrimitiveType& simplex_type, const MeshAttributeHandle& variable_attribute_handle) - : DifferentiablePerSimplexFunction(mesh, simplex_type, variable_attribute_handle) + : PerSimplexDifferentiableFunction(mesh, simplex_type, variable_attribute_handle) {} AutodiffFunction::~AutodiffFunction() = default; -double AutodiffFunction::get_value(const Simplex& simplex) const + +auto AutodiffFunction::get_value_autodiff(const Simplex& simplex) const -> DScalar +{ + assert(simplex.primitive_type() == get_simplex_type()); + return get_value_autodiff(simplex.tuple()); +} +double AutodiffFunction::get_value(const Tuple& simplex) const { auto scope = utils::AutoDiffRAII(embedded_dimension()); - return get_value_autodiff(simplex).getValue(); + auto v = get_value_autodiff(simplex); + return v.getValue(); } -Eigen::VectorXd AutodiffFunction::get_gradient(const Simplex& simplex) const + +Eigen::VectorXd AutodiffFunction::get_gradient(const Tuple& simplex) const { auto scope = utils::AutoDiffRAII(embedded_dimension()); auto v = get_value_autodiff(simplex); return v.getGradient(); } -Eigen::MatrixXd AutodiffFunction::get_hessian(const Simplex& simplex) const +Eigen::MatrixXd AutodiffFunction::get_hessian(const Tuple& simplex) const { auto scope = utils::AutoDiffRAII(embedded_dimension()); - return get_value_autodiff(simplex).getHessian(); + auto v = get_value_autodiff(simplex); + return v.getHessian(); } } // namespace wmtk::function diff --git a/src/wmtk/function/AutodiffFunction.hpp b/src/wmtk/function/AutodiffFunction.hpp index 7f1d5b8952..34dd8916df 100644 --- a/src/wmtk/function/AutodiffFunction.hpp +++ b/src/wmtk/function/AutodiffFunction.hpp @@ -1,9 +1,9 @@ #pragma once #include -#include "DifferentiablePerSimplexFunction.hpp" +#include "PerSimplexDifferentiableFunction.hpp" namespace wmtk::function { -class AutodiffFunction : public DifferentiablePerSimplexFunction +class AutodiffFunction : public PerSimplexDifferentiableFunction { public: using DScalar = DScalar2, Eigen::Matrix>; @@ -18,11 +18,16 @@ class AutodiffFunction : public DifferentiablePerSimplexFunction virtual ~AutodiffFunction(); public: - double get_value(const Simplex& simplex) const override; - Eigen::VectorXd get_gradient(const Simplex& simplex) const override; - Eigen::MatrixXd get_hessian(const Simplex& simplex) const override; + using PerSimplexFunction::get_value; + using PerSimplexDifferentiableFunction::get_hessian; + using PerSimplexDifferentiableFunction::get_gradient; + double get_value(const Tuple& tuple) const final override; + Eigen::VectorXd get_gradient(const Tuple& tuple) const final override; + Eigen::MatrixXd get_hessian(const Tuple& tuple) const final override; + protected: - virtual DScalar get_value_autodiff(const Simplex& simplex) const = 0; + virtual DScalar get_value_autodiff(const Tuple& simplex) const = 0; + DScalar get_value_autodiff(const Simplex& simplex) const; }; } // namespace wmtk::function diff --git a/src/wmtk/function/CMakeLists.txt b/src/wmtk/function/CMakeLists.txt index 039c2de778..f2be8f59c6 100644 --- a/src/wmtk/function/CMakeLists.txt +++ b/src/wmtk/function/CMakeLists.txt @@ -1,14 +1,14 @@ set(SRC_FILES - Function.hpp - Function.cpp - LocallyDifferentiableFunction.hpp - LocallyDifferentiableFunction.cpp + LocalFunction.hpp + LocalFunction.cpp + LocalDifferentiableFunction.hpp + LocalDifferentiableFunction.cpp PerSimplexFunction.hpp PerSimplexFunction.cpp - DifferentiablePerSimplexFunction.hpp - DifferentiablePerSimplexFunction.cpp + PerSimplexDifferentiableFunction.hpp + PerSimplexDifferentiableFunction.cpp AMIPS.hpp AMIPS.cpp @@ -20,8 +20,6 @@ set(SRC_FILES PositionMapAMIPS2D.cpp ValenceEnergyPerEdge.hpp ValenceEnergyPerEdge.cpp - TriMeshValenceFunction.hpp - TriMeshValenceFunction.cpp AutodiffFunction.hpp AutodiffFunction.cpp diff --git a/src/wmtk/function/DifferentiableFunction.cpp b/src/wmtk/function/DifferentiableFunction.cpp index 9ae9b4907f..bbac81a300 100644 --- a/src/wmtk/function/DifferentiableFunction.cpp +++ b/src/wmtk/function/DifferentiableFunction.cpp @@ -3,53 +3,14 @@ namespace wmtk::function { DifferentiableFunction::DifferentiableFunction( - const Mesh& mesh, - const MeshAttributeHandle& vertex_attribute_handle) - : Function(mesh) - , m_coordinate_vertex_attribute_handle(vertex_attribute_handle) + const MeshAttributeHandle& attribute_handle) + : m_attribute_handle(attribute_handle) {} -Eigen::VectorXd DifferentiableFunction::get_one_ring_gradient(const Tuple& vertex) const -{ - auto simplices = - simplex::top_level_cofaces_tuples(mesh(), Simplex(PrimitiveType::Vertex, vertex)); - return get_gradient_sum(vertex, simplices); -} -Eigen::MatrixXd DifferentiableFunction::get_one_ring_hessian(const Tuple& vertex) const -{ - auto simplices = - simplex::top_level_cofaces_tuples(mesh(), Simplex(PrimitiveType::Vertex, vertex)); - return get_hessian_sum(vertex, simplices); -} -Eigen::VectorXd DifferentiableFunction::get_gradient_sum( - const Tuple& vertex, - const std::vector& top_level_simplices) const -{ - Eigen::VectorXd v = Eigen::VectorXd::Zero(embedded_dimension()); - for (const Tuple& cell : top_level_simplices) { - v += get_gradient(cell); - } - return v; -} -Eigen::MatrixXd DifferentiableFunction::get_hessian_sum( - const Tuple& vertex, - const std::vector& top_level_simplices) const -{ - Eigen::MatrixXd v = Eigen::MatrixXd::Zero(embedded_dimension(), embedded_dimension()); - for (const Tuple& cell : top_level_simplices) { - v += get_hessian(cell); - } - return v; -} - -const MeshAttributeHandle& DifferentiableFunction::get_vertex_attribute_handle() const -{ - return get_coordinate_attribute_handle(); -} const MeshAttributeHandle& DifferentiableFunction::get_coordinate_attribute_handle() const { - return m_coordinate_vertex_attribute_handle; + return m_coordinate_attribute_handle; } long DifferentiableFunction::embedded_dimension() const { diff --git a/src/wmtk/function/DifferentiableFunction.hpp b/src/wmtk/function/DifferentiableFunction.hpp index 4adadbf04d..b01ae34f7e 100644 --- a/src/wmtk/function/DifferentiableFunction.hpp +++ b/src/wmtk/function/DifferentiableFunction.hpp @@ -1,44 +1,22 @@ #pragma once +#include #include #include #include "Function.hpp" namespace wmtk::function { -class DifferentiableFunction : public Function +class DifferentiableFunction : public virtual Function { public: + // evaluates the gradient of the tuple - // @param The mesh representing the domain the function is differentiated over - // @param The mesh's attribute that the function is differentiated against - DifferentiableFunction( - const Mesh& mesh, - const MeshAttributeHandle& coordinate_vertex_attribute_handle); + virtual Eigen::VectorXd get_gradient(const simplex::Simplex& tuple) const = 0; - virtual ~DifferentiableFunction() = default; - - - Eigen::VectorXd get_one_ring_gradient(const Tuple& vertex) const; - Eigen::MatrixXd get_one_ring_hessian(const Tuple& vertex) const; - - Eigen::VectorXd get_gradient_sum(const Tuple& tuple, const std::vector& top_level_simplices) const; - Eigen::MatrixXd get_hessian_sum(const Tuple& tuple, const std::vector& top_level_simplices) const; - - - const MeshAttributeHandle& get_coordinate_attribute_handle() const; + // TODO: should differentiable function be required to be twice differentiable? + virtual Eigen::MatrixXd get_hessian(const simplex::Simplex& tuple) const = 0; - // alias for the coordinate_attribute_handle - const MeshAttributeHandle& get_vertex_attribute_handle() const; - // The dimension that this differentiable function lies in - // (this is the dimension of the vector space the vertex attribute lies in) long embedded_dimension() const; + virtual MeshAttributeHandle get_coordinate_attribute_handle() const = 0; -protected: - virtual Eigen::VectorXd get_gradient(const Tuple& top_level_simplex) const = 0; - - // TODO: should differentiable function be required to be twice differentiable? - virtual Eigen::MatrixXd get_hessian(const Tuple& top_level_simplex) const = 0; - -private: - const MeshAttributeHandle m_coordinate_vertex_attribute_handle; }; } // namespace wmtk::function diff --git a/src/wmtk/function/DifferentiablePerSimplexFunction.cpp b/src/wmtk/function/DifferentiablePerSimplexFunction.cpp deleted file mode 100644 index a48795ed42..0000000000 --- a/src/wmtk/function/DifferentiablePerSimplexFunction.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "DifferentiablePerSimplexFunction.hpp" - -namespace wmtk { -namespace function { -DifferentiablePerSimplexFunction::DifferentiablePerSimplexFunction( - const Mesh& mesh, - const PrimitiveType& simplex_type, - const attribute::MeshAttributeHandle& variable_attribute_handle) - : PerSimplexFunction(mesh, simplex_type) - , m_attribute_handle(variable_attribute_handle) -{} - -DifferentiablePerSimplexFunction::~DifferentiablePerSimplexFunction() = default; - -const MeshAttributeHandle& DifferentiablePerSimplexFunction::get_variable_attribute_handle() - const -{ - return m_attribute_handle; -} - -long DifferentiablePerSimplexFunction::embedded_dimension() const -{ - return mesh().get_attribute_dimension(get_variable_attribute_handle()); -} - - -void DifferentiablePerSimplexFunction::assert_function_type(const PrimitiveType& s_type) const -{ - if (get_variable_attribute_handle().primitive_type() != s_type) { - throw std::runtime_error("Differentiation of the DifferentiableFunction must be taken wrt " - "the attribute of the simplex type "); - } - if (get_function_simplex_type() > s_type) { - throw std::runtime_error( - "The DifferentiableFunction must be defined on the cofaces of higher dimension to the " - "simplex type "); - } -} -} // namespace function -} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/DifferentiablePerSimplexFunction.hpp b/src/wmtk/function/DifferentiablePerSimplexFunction.hpp deleted file mode 100644 index 80a24643cd..0000000000 --- a/src/wmtk/function/DifferentiablePerSimplexFunction.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#include -#include -#include -#include "PerSimplexFunction.hpp" -namespace wmtk { -namespace function { -class DifferentiablePerSimplexFunction : public PerSimplexFunction -{ -public: - /** - * @brief Construct a new DifferentiablePerSimplexFunction object where the function is defined - * over simplices of simplex_type. And the differentiation is taken wrt the - * attribute_handle.primitive_type() - * - * @param mesh - * @param simplex_type - */ - DifferentiablePerSimplexFunction( - const Mesh& mesh, - const PrimitiveType& simplex_type, - const attribute::MeshAttributeHandle& variable_attribute_handle); - virtual ~DifferentiablePerSimplexFunction(); - -public: - virtual Eigen::VectorXd get_gradient(const Simplex& s) const = 0; - virtual Eigen::MatrixXd get_hessian(const Simplex& s) const = 0; - - const attribute::MeshAttributeHandle& get_variable_attribute_handle() const; - - /** - * @brief the function should be defined on the simplex of type A and the differntiation is - * taken wrt simplex type B. The definition mandate (1) A should be coface with B, (2) the type - * of the attribute_handle should be of the same type as B. - * - * @param s_type simplex Type B as defined above - */ - void assert_function_type(const PrimitiveType& s_type) const; - long embedded_dimension() const; - -private: - const attribute::MeshAttributeHandle m_attribute_handle; -}; -} // namespace function -} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/Function.cpp b/src/wmtk/function/Function.cpp index 35d7e75462..ff6b12811f 100644 --- a/src/wmtk/function/Function.cpp +++ b/src/wmtk/function/Function.cpp @@ -1,17 +1,8 @@ #include "Function.hpp" #include -namespace wmtk { -namespace function { +namespace wmtk::function { -Function::Function(std::shared_ptr&& function) - : m_function(function) -{} +// Function::Function() {} Function::~Function() = default; - -std::shared_ptr Function::get_function() const -{ - return m_function; -} -} // namespace function -} // namespace wmtk \ No newline at end of file +} // namespace wmtk::function diff --git a/src/wmtk/function/Function.hpp b/src/wmtk/function/Function.hpp index df8c079853..71d65ee4f1 100644 --- a/src/wmtk/function/Function.hpp +++ b/src/wmtk/function/Function.hpp @@ -1,22 +1,20 @@ #pragma once #include -#include -#include "PerSimplexFunction.hpp" +#include namespace wmtk { -namespace function { +class Mesh; +namespace simplex { +class Simplex; +} + +} // namespace wmtk +namespace wmtk::function { class Function { public: - Function(std::shared_ptr&& function); virtual ~Function(); - -public: // evaluate the function on the top level simplex of the tuple - virtual double get_value(const Simplex& simplex) const = 0; - std::shared_ptr get_function() const; - -private: - std::shared_ptr m_function; + virtual double get_value(const simplex::Simplex& simplex) const = 0; + virtual const Mesh& mesh() const = 0; }; -} // namespace function -} // namespace wmtk +} // namespace wmtk::function diff --git a/src/wmtk/function/LocalDifferentiableFunction.cpp b/src/wmtk/function/LocalDifferentiableFunction.cpp new file mode 100644 index 0000000000..8c5235e8fe --- /dev/null +++ b/src/wmtk/function/LocalDifferentiableFunction.cpp @@ -0,0 +1,57 @@ +#include "LocalDifferentiableFunction.hpp" +#include +#include +#include +#include "PerSimplexDifferentiableFunction.hpp" +namespace wmtk::function { +LocalDifferentiableFunction::LocalDifferentiableFunction( + std::shared_ptr function, + const PrimitiveType& simplex_type) + : LocalFunction(std::move(function)) + , m_simplex_type(simplex_type) +{ +} + +LocalDifferentiableFunction::~LocalDifferentiableFunction() = default; + +Eigen::VectorXd LocalDifferentiableFunction::get_gradient(const Tuple& tuple) const +{ + return get_gradient(Simplex(m_simplex_type, tuple)); +} + +Eigen::MatrixXd LocalDifferentiableFunction::get_hessian(const Tuple& tuple) const +{ + return get_hessian(Simplex(m_simplex_type, tuple)); +} + +Eigen::VectorXd LocalDifferentiableFunction::get_gradient(const Simplex& simplex) const +{ + return per_simplex_function().get_gradient_sum(get_local_neighborhood_tuples(simplex)); +} +Eigen::MatrixXd LocalDifferentiableFunction::get_hessian(const Simplex& simplex) const +{ + + return per_simplex_function().get_hessian_sum(get_local_neighborhood_tuples(simplex)); +} + +const PerSimplexDifferentiableFunction& LocalDifferentiableFunction::per_simplex_function() const +{ + //return m_function; + return static_cast( + LocalFunction::per_simplex_function()); +} +std::shared_ptr +LocalDifferentiableFunction::per_simplex_function_ptr() const +{ + return std::static_pointer_cast( + LocalFunction::per_simplex_function_ptr()); +} + + + +MeshAttributeHandle +LocalDifferentiableFunction::get_coordinate_attribute_handle() const +{ + return per_simplex_function().get_coordinate_attribute_handle(); +} +} // namespace wmtk::function diff --git a/src/wmtk/function/LocalDifferentiableFunction.hpp b/src/wmtk/function/LocalDifferentiableFunction.hpp new file mode 100644 index 0000000000..bdb2eee851 --- /dev/null +++ b/src/wmtk/function/LocalDifferentiableFunction.hpp @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include "DifferentiableFunction.hpp" +#include "LocalFunction.hpp" +namespace wmtk::function { +class PerSimplexDifferentiableFunction; +; +class LocalDifferentiableFunction : public LocalFunction, public DifferentiableFunction +{ +public: + LocalDifferentiableFunction( + std::shared_ptr function, + const PrimitiveType& simplex_type); + virtual ~LocalDifferentiableFunction(); + +public: + //Eigen::VectorXd get_local_gradient(const Simplex& simplex) const override; + //Eigen::MatrixXd get_local_hessian(const Simplex& simplex) const override; + //Eigen::VectorXd get_local_gradient(const Tuple& tuple) const; + //Eigen::MatrixXd get_local_hessian(const Tuple& tuple) const; + + Eigen::VectorXd get_gradient(const Simplex& simplex) const override; + Eigen::MatrixXd get_hessian(const Simplex& simplex) const override; + Eigen::VectorXd get_gradient(const Tuple& tuple) const; + Eigen::MatrixXd get_hessian(const Tuple& tuple) const; + + const PerSimplexDifferentiableFunction& per_simplex_function() const; + std::shared_ptr per_simplex_function_ptr() const; + + attribute::MeshAttributeHandle get_coordinate_attribute_handle() const final override; + +private: + const PrimitiveType m_simplex_type; +}; +} // namespace wmtk::function diff --git a/src/wmtk/function/LocalFunction.cpp b/src/wmtk/function/LocalFunction.cpp new file mode 100644 index 0000000000..d7c26e362d --- /dev/null +++ b/src/wmtk/function/LocalFunction.cpp @@ -0,0 +1,49 @@ + +#include "LocalFunction.hpp" +#include +namespace wmtk::function { + +LocalFunction::LocalFunction(std::shared_ptr function) + : m_function(std::move(function)) +{} + +LocalFunction::~LocalFunction() = default; + +const PerSimplexFunction& LocalFunction::per_simplex_function() const +{ + return *per_simplex_function_ptr(); +} + +std::shared_ptr LocalFunction::per_simplex_function_ptr() const +{ + return m_function; +} + +const Mesh& LocalFunction::mesh() const +{ + return per_simplex_function().mesh(); +} +std::vector LocalFunction::get_local_neighborhood_tuples(const Simplex& simplex) const +{ + return wmtk::simplex::upper_level_cofaces_tuples( + m_function->mesh(), + my_simplex, + per_simplex_function().get_simplex_type()); +} + +double LocalFunction::get_value(const Simplex& simplex) const +{ + return per_simplex_function().get_value_sum(get_local_neighborhood_tuples(simplex)); +} + +PrimitiveType LocalFunction::get_simplex_type() const +{ + return per_simplex_function().get_simplex_type(); +} + +double LocalFunction::get_value(const Tuple& simplex) const +{ + return get_value(Simplex(get_simplex_type(), simplex)); +} + +} // namespace wmtk::function diff --git a/src/wmtk/function/LocalFunction.hpp b/src/wmtk/function/LocalFunction.hpp new file mode 100644 index 0000000000..a3798ad32d --- /dev/null +++ b/src/wmtk/function/LocalFunction.hpp @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include "PerSimplexFunction.hpp" +namespace wmtk::function { + +// a function that invokes a function on a local neighborhood of an input simplex +// Typically we will want to compute something like the gradient of a function defined on the triangles/tetrahedra with respect to a vertex and the choice of basis functions means we only need to compute a one-ring neighborhood. +// This class lets us select a per-simplex function (tet or tri) and evaluate that function in a +// (one-ring) neighborhood of an input simplex(vertex). This class will allow for us to evaluate how +// a function changes, which might be useful fo ra line-search, but the real purpose is to define an +// interface for a differentiable variant that enables the use of gradient descent. + +class LocalFunction : public virtual Function +{ +public: + LocalFunction(std::shared_ptr function); + virtual ~LocalFunction(); + +public: + // evaluate the function on the top level simplex of the tuple + double get_value(const Simplex& simplex) const override; + const Mesh& mesh() const final override; + double get_value(const Tuple& simplex) const; + const PerSimplexFunction& per_simplex_function() const; + std::shared_ptr per_simplex_function_ptr() const; + + PrimitiveType get_simplex_type() const; + +protected: + std::vector get_local_neighborhood_tuples(const Simplex& simplex) const; + + +private: + std::shared_ptr m_function; +}; +} // namespace wmtk::function diff --git a/src/wmtk/function/LocallyDifferentiableFunction.cpp b/src/wmtk/function/LocallyDifferentiableFunction.cpp deleted file mode 100644 index c89de31184..0000000000 --- a/src/wmtk/function/LocallyDifferentiableFunction.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "LocallyDifferentiableFunction.hpp" -#include -#include -#include -namespace wmtk { -namespace function { -LocallyDifferentiableFunction::LocallyDifferentiableFunction( - std::shared_ptr&& function, - const PrimitiveType& my_simplex_type) - : Function(function) - , m_my_simplex_type(my_simplex_type) -{ - m_function->assert_function_type(m_my_simplex_type); -} - -LocallyDifferentiableFunction::~LocallyDifferentiableFunction() = default; - -Eigen::VectorXd LocallyDifferentiableFunction::get_local_gradient(const Tuple& my_tuple) const -{ - return get_local_gradient(Simplex(m_my_simplex_type, my_tuple)); -} - -Eigen::MatrixXd LocallyDifferentiableFunction::get_local_hessian(const Tuple& my_tuple) const -{ - return get_local_hessian(Simplex(m_my_simplex_type, my_tuple)); -} - -Eigen::VectorXd LocallyDifferentiableFunction::get_local_gradient(const Simplex& my_simplex) const -{ - std::vector coface_tuples = simplex::upper_level_cofaces_tuples( - m_function->mesh(), - my_simplex, - m_function->get_function_simplex_type()); - - return get_gradient_sum(simplex::utils::tuple_vector_to_homogeneous_simplex_vector( - coface_tuples, - m_function->get_function_simplex_type())); -} -Eigen::MatrixXd LocallyDifferentiableFunction::get_local_hessian(const Simplex& my_simplex) const -{ - std::vector coface_tuples = simplex::upper_level_cofaces_tuples( - m_function->mesh(), - my_simplex, - m_function->get_function_simplex_type()); - - return get_hessian_sum(simplex::utils::tuple_vector_to_homogeneous_simplex_vector( - coface_tuples, - m_function->get_function_simplex_type())); -} - -double LocallyDifferentiableFunction::get_value_sum( - const std::vector& coface_simplices) const -{ - double v = 0; - for (const Simplex& cell : coface_simplices) { - v += m_function->get_value(cell); - } - return v; -} - - -Eigen::VectorXd LocallyDifferentiableFunction::get_gradient_sum( - const std::vector& coface_simplices) const -{ - Eigen::VectorXd g = Eigen::VectorXd::Zero(m_function->embedded_dimension()); - for (const Simplex& cell : coface_simplices) { - g += m_function->get_gradient(cell); - } - return g; -} -Eigen::MatrixXd LocallyDifferentiableFunction::get_hessian_sum( - const std::vector& coface_simplices) const -{ - Eigen::MatrixXd h = - Eigen::MatrixXd::Zero(m_function->embedded_dimension(), m_function->embedded_dimension()); - for (const Simplex& cell : coface_simplices) { - h += m_function->get_hessian(cell); - } - return h; -} - -} // namespace function - -} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/LocallyDifferentiableFunction.hpp b/src/wmtk/function/LocallyDifferentiableFunction.hpp deleted file mode 100644 index e9cf40a2a8..0000000000 --- a/src/wmtk/function/LocallyDifferentiableFunction.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include -#include -#include "DifferentiablePerSimplexFunction.hpp" -#include "Function.hpp" -namespace wmtk { -namespace function { -class LocallyDifferentiableFunction : public Function -{ -public: - LocallyDifferentiableFunction( - std::shared_ptr&& function, - const PrimitiveType& my_simplex_type); - virtual ~LocallyDifferentiableFunction(); - -public: - Eigen::VectorXd get_local_gradient(const Simplex& my_simplex) const; - Eigen::MatrixXd get_local_hessian(const Simplex& my_simplex) const; - Eigen::VectorXd get_local_gradient(const Tuple& my_tuple) const; - Eigen::MatrixXd get_local_hessian(const Tuple& my_tuple) const; - - double get_value_sum(const std::vector& coface_simplices) const; - Eigen::VectorXd get_gradient_sum(const std::vector& coface_simplices) const; - Eigen::MatrixXd get_hessian_sum(const std::vector& coface_simplices) const; - -private: - std::shared_ptr m_function; - const PrimitiveType m_my_simplex_type; -}; -} // namespace function -} // namespace wmtk \ No newline at end of file diff --git a/src/wmtk/function/PerSimplexDifferentiableFunction.cpp b/src/wmtk/function/PerSimplexDifferentiableFunction.cpp new file mode 100644 index 0000000000..b9b22903a8 --- /dev/null +++ b/src/wmtk/function/PerSimplexDifferentiableFunction.cpp @@ -0,0 +1,73 @@ +#include "PerSimplexDifferentiableFunction.hpp" + +namespace wmtk::function { +PerSimplexDifferentiableFunction::PerSimplexDifferentiableFunction( + const Mesh& mesh, + PrimitiveType simplex_type, + const attribute::MeshAttributeHandle& variable_attribute_handle) + : PerSimplexFunction(mesh, simplex_type) + , m_coordinate_attribute_handle(variable_attribute_handle) +{} + +PerSimplexDifferentiableFunction::~PerSimplexDifferentiableFunction() = default; + +MeshAttributeHandle +PerSimplexDifferentiableFunction::get_coordinate_attribute_handle() const +{ + return m_coordinate_attribute_handle; +} + +long PerSimplexDifferentiableFunction::embedded_dimension() const +{ + return DifferentiableFunction::embedded_dimension(); +} + +Eigen::VectorXd PerSimplexDifferentiableFunction::get_gradient(const Simplex& s) const +{ + assert(get_simplex_type() == s.primitive_type()); + return get_gradient(s.tuple()); +} +Eigen::MatrixXd PerSimplexDifferentiableFunction::get_hessian(const Simplex& s) const +{ + assert(get_simplex_type() == s.primitive_type()); + return get_hessian(s.tuple()); +} + +Eigen::VectorXd PerSimplexDifferentiableFunction::get_gradient_sum( + const std::vector& simplices) const +{ + Eigen::VectorXd g = Eigen::VectorXd::Zero(embedded_dimension()); + for (const Simplex& cell : simplices) { + g += get_gradient(cell); + } + return g; +} +Eigen::MatrixXd PerSimplexDifferentiableFunction::get_hessian_sum( + const std::vector& simplices) const +{ + Eigen::MatrixXd h = Eigen::MatrixXd::Zero(embedded_dimension(), embedded_dimension()); + for (const Simplex& cell : simplices) { + h += get_hessian(cell); + } + return h; +} + +Eigen::VectorXd PerSimplexDifferentiableFunction::get_gradient_sum( + const std::vector& tuples) const +{ + Eigen::VectorXd g = Eigen::VectorXd::Zero(embedded_dimension()); + for (const Tuple& t : tuples) { + g += get_gradient(t); + } + return g; +} +Eigen::MatrixXd PerSimplexDifferentiableFunction::get_hessian_sum( + const std::vector& tuples) const +{ + Eigen::MatrixXd h = Eigen::MatrixXd::Zero(embedded_dimension(), embedded_dimension()); + for (const Tuple& t : tuples) { + h += get_hessian(t); + } + return h; +} +} // namespace wmtk::function diff --git a/src/wmtk/function/PerSimplexDifferentiableFunction.hpp b/src/wmtk/function/PerSimplexDifferentiableFunction.hpp new file mode 100644 index 0000000000..00e0845627 --- /dev/null +++ b/src/wmtk/function/PerSimplexDifferentiableFunction.hpp @@ -0,0 +1,50 @@ +#pragma once +#include +#include +#include +#include "DifferentiableFunction.hpp" +#include "PerSimplexFunction.hpp" +namespace wmtk { +namespace function { +class PerSimplexDifferentiableFunction : public PerSimplexFunction, DifferentiableFunction +{ +public: + /** + * @brief Construct a new PerSimplexDifferentiableFunction object where the function is defined + * over simplices of simplex_type. And the differentiation is taken wrt the + * attribute_handle.primitive_type() + * + * @param mesh + * @param simplex_type + * @param attribute_handle, the attribute that differentiation is with respect to + */ + PerSimplexDifferentiableFunction( + const Mesh& mesh, + PrimitiveType simplex_type, + const attribute::MeshAttributeHandle& attribute_handle); + virtual ~PerSimplexDifferentiableFunction(); + +public: + virtual Eigen::VectorXd get_gradient(const Tuple& s) const = 0; + virtual Eigen::MatrixXd get_hessian(const Tuple& s) const = 0; + Eigen::VectorXd get_gradient(const Simplex& s) const final override; + Eigen::MatrixXd get_hessian(const Simplex& s) const final override; + + attribute::MeshAttributeHandle get_coordinate_attribute_handle() const final override; + + + long embedded_dimension() const; + + // computes the sum over a set of simplices - assumes each simplex has the same dimension as the + // function's simplex type + Eigen::VectorXd get_gradient_sum(const std::vector& simplices) const; + Eigen::MatrixXd get_hessian_sum(const std::vector& simplices) const; + + Eigen::VectorXd get_gradient_sum(const std::vector& simplices) const; + Eigen::MatrixXd get_hessian_sum(const std::vector& simplices) const; + +private: + const MeshAttributeHandle m_coordinate_attribute_handle; +}; +} // namespace function +} // namespace wmtk diff --git a/src/wmtk/function/PerSimplexFunction.cpp b/src/wmtk/function/PerSimplexFunction.cpp index f5fb579d1f..0500ae698c 100644 --- a/src/wmtk/function/PerSimplexFunction.cpp +++ b/src/wmtk/function/PerSimplexFunction.cpp @@ -1,11 +1,10 @@ #include "PerSimplexFunction.hpp" -namespace wmtk { -namespace function { +namespace wmtk::function { PerSimplexFunction::PerSimplexFunction(const Mesh& mesh, const PrimitiveType& simplex_type) : m_mesh(mesh) - , m_function_simplex_type(simplex_type) + , m_simplex_type(simplex_type) {} PerSimplexFunction::~PerSimplexFunction() = default; @@ -15,9 +14,32 @@ const Mesh& PerSimplexFunction::mesh() const return m_mesh; } -const PrimitiveType& PerSimplexFunction::get_function_simplex_type() const +PrimitiveType PerSimplexFunction::get_simplex_type() const { - return m_function_simplex_type; + return m_simplex_type; } -} // namespace function -}; // namespace wmtk \ No newline at end of file + + +double PerSimplexFunction::get_value(const Simplex& s) const +{ + assert(get_simplex_type() == s.primitive_type()); + return get_value(s.tuple()); +} +double PerSimplexFunction::get_value_sum(const std::vector& simplices) const +{ + double v = 0; + for (const Simplex& cell : simplices) { + v += get_value(cell); + } + return v; +} +double PerSimplexFunction::get_value_sum(const std::vector& tuples) const +{ + double v = 0; + const PrimitiveType pt = get_simplex_type(); + for (const Tuple& tuple : tuples) { + v += get_value(tuple); + } + return v; +} +}; // namespace wmtk::function diff --git a/src/wmtk/function/PerSimplexFunction.hpp b/src/wmtk/function/PerSimplexFunction.hpp index 5e55272bd6..406d55bfea 100644 --- a/src/wmtk/function/PerSimplexFunction.hpp +++ b/src/wmtk/function/PerSimplexFunction.hpp @@ -3,23 +3,30 @@ #include #include #include -namespace wmtk { -namespace function { -class PerSimplexFunction +#include "Function.hpp" +namespace wmtk::function { +class PerSimplexFunction : public virtual Function { public: PerSimplexFunction(const Mesh& mesh, const PrimitiveType& simplex_type); virtual ~PerSimplexFunction(); public: - const Mesh& mesh() const; - virtual double get_value(const Simplex& s) const = 0; - const PrimitiveType& get_function_simplex_type() const; + using Function::get_value; + const Mesh& mesh() const final override; + virtual double get_value(const Tuple& s) const = 0; + double get_value(const simplex::Simplex& s) const final override; + // the type of simplex that this function operates on + PrimitiveType get_simplex_type() const; + + // helper because in many cases we want to compute the value of multiple simplices at once + double get_value_sum(const std::vector& simplices) const; + // assumes that the underlying simplices are all of the same as get_simplex_type() + double get_value_sum(const std::vector& tuples) const; private: const Mesh& m_mesh; - const PrimitiveType m_function_simplex_type; + const PrimitiveType m_simplex_type; }; -} // namespace function -} // namespace wmtk \ No newline at end of file +} // namespace wmtk::function diff --git a/src/wmtk/function/PositionMapAMIPS2D.cpp b/src/wmtk/function/PositionMapAMIPS2D.cpp index a762132b08..1723daf823 100644 --- a/src/wmtk/function/PositionMapAMIPS2D.cpp +++ b/src/wmtk/function/PositionMapAMIPS2D.cpp @@ -24,14 +24,14 @@ PositionMapAMIPS2D::PositionMapAMIPS2D( {} -auto PositionMapAMIPS2D::get_value_autodiff(const Simplex& simplex) const -> DScalar +auto PositionMapAMIPS2D::get_value_autodiff(const Tuple& simplex) const -> DScalar { // get_autodiff_value sets the autodiff size if necessary // get the uv coordinates of the triangle - ConstAccessor pos = mesh().create_const_accessor(get_variable_attribute_handle()); + ConstAccessor pos = mesh().create_const_accessor(get_coordinate_attribute_handle()); assert(simplex.primitive_type() == PrimitiveType::Vertex); - const Tuple& tuple = simplex.tuple(); + const Tuple& tuple = simplex; auto tuple_value = pos.const_vector_attribute(tuple); Vector2 uv0; diff --git a/src/wmtk/function/PositionMapAMIPS2D.hpp b/src/wmtk/function/PositionMapAMIPS2D.hpp index b6595793c4..6a6e838643 100644 --- a/src/wmtk/function/PositionMapAMIPS2D.hpp +++ b/src/wmtk/function/PositionMapAMIPS2D.hpp @@ -23,7 +23,7 @@ class PositionMapAMIPS2D : public AMIPS const double c); public: - DScalar get_value_autodiff(const Simplex& simplex) const override; + DScalar get_value_autodiff(const Tuple& simplex) const override; protected: utils::PositionMapEvaluator m_pos_evaluator; diff --git a/src/wmtk/function/TriMeshValenceFunction.cpp b/src/wmtk/function/TriMeshValenceFunction.cpp deleted file mode 100644 index 494e2aa869..0000000000 --- a/src/wmtk/function/TriMeshValenceFunction.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "TriMeshValenceFunction.hpp" -#include -#include -#include -namespace wmtk::function { -TriMeshValenceFunction::TriMeshValenceFunction(std::unique_ptr&& function) - : Function(std::move(function)) -{} - -double TriMeshValenceFunction::get_value(const Simplex& simplex) const -{ - return m_function->get_value(simplex); -} - - -} // namespace wmtk::function diff --git a/src/wmtk/function/TriMeshValenceFunction.hpp b/src/wmtk/function/TriMeshValenceFunction.hpp deleted file mode 100644 index 9c834b3cc1..0000000000 --- a/src/wmtk/function/TriMeshValenceFunction.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "Function.hpp" -#include "ValenceEnergyPerEdge.hpp" -namespace wmtk { -namespace function { - -class TriMeshValenceFunction : public Function -{ -public: - TriMeshValenceFunction(std::unique_ptr&& function); - double get_value(const Simplex& simplex) const override; - -protected: - const TriMesh& mesh() const; - -private: - std::unique_ptr m_function; -}; -} // namespace function -} // namespace wmtk diff --git a/src/wmtk/function/ValenceEnergyPerEdge.cpp b/src/wmtk/function/ValenceEnergyPerEdge.cpp index 64ed2d261a..9768627bb1 100644 --- a/src/wmtk/function/ValenceEnergyPerEdge.cpp +++ b/src/wmtk/function/ValenceEnergyPerEdge.cpp @@ -7,39 +7,41 @@ ValenceEnergyPerEdge::ValenceEnergyPerEdge(const TriMesh& mesh) : PerSimplexFunction(mesh, PrimitiveType::Face) {} -double ValenceEnergyPerEdge::get_value(const Simplex& simplex) const +double ValenceEnergyPerEdge::get_value(const Tuple& tuple) const { // assume tuple is not a boundary edge - const Tuple current_v = simplex.tuple(); - const Tuple other_v = mesh().switch_vertex(current_v); - long val0 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), current_v).size()); - long val1 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), other_v).size()); - if (mesh().is_boundary_vertex(current_v)) { + const Tuple& current_v = tuple; + const Tuple other_v = tri_mesh().switch_vertex(current_v); + long val0 = static_cast(SimplicialComplex::vertex_one_ring(tri_mesh(), current_v).size()); + long val1 = static_cast(SimplicialComplex::vertex_one_ring(tri_mesh(), other_v).size()); + if (tri_mesh().is_boundary_vertex(current_v)) { val0 += 2; } - if (mesh().is_boundary_vertex(other_v)) { + if (tri_mesh().is_boundary_vertex(other_v)) { val1 += 2; } if (val0 < 4 || val1 < 4) { return -1; } - // top_v + /* top_v // / \ // / \ // current_v-----other_v // \ / // \ / // bottom_v - const Tuple top_v = mesh().switch_vertex(mesh().switch_edge(current_v)); - const Tuple bottom_v = mesh().switch_vertex(mesh().switch_edge(mesh().switch_face(current_v))); - long val2 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), top_v).size()); - long val3 = static_cast(SimplicialComplex::vertex_one_ring(mesh(), bottom_v).size()); + */ + const Tuple top_v = tri_mesh().switch_vertex(tri_mesh().switch_edge(current_v)); + const Tuple bottom_v = + tri_mesh().switch_vertex(tri_mesh().switch_edge(tri_mesh().switch_face(current_v))); + long val2 = static_cast(SimplicialComplex::vertex_one_ring(tri_mesh(), top_v).size()); + long val3 = static_cast(SimplicialComplex::vertex_one_ring(tri_mesh(), bottom_v).size()); - if (mesh().is_boundary_vertex(top_v)) { + if (tri_mesh().is_boundary_vertex(top_v)) { val2 += 2; } - if (mesh().is_boundary_vertex(bottom_v)) { + if (tri_mesh().is_boundary_vertex(bottom_v)) { val3 += 2; } // formula from: https://github.com/daniel-zint/hpmeshgen/blob/cdfb9163ed92523fcf41a127c8173097e935c0a3/src/HPMeshGen2/TriRemeshing.cpp#L315 @@ -51,7 +53,7 @@ double ValenceEnergyPerEdge::get_value(const Simplex& simplex) const return static_cast(val_energy); } -const TriMesh& ValenceEnergyPerEdge::mesh() const +const TriMesh& ValenceEnergyPerEdge::tri_mesh() const { return static_cast(PerSimplexFunction::mesh()); } diff --git a/src/wmtk/function/ValenceEnergyPerEdge.hpp b/src/wmtk/function/ValenceEnergyPerEdge.hpp index 416b89027c..d3e464c203 100644 --- a/src/wmtk/function/ValenceEnergyPerEdge.hpp +++ b/src/wmtk/function/ValenceEnergyPerEdge.hpp @@ -1,17 +1,16 @@ #pragma once #include "PerSimplexFunction.hpp" -namespace wmtk { -namespace function { +namespace wmtk::function { class ValenceEnergyPerEdge : public PerSimplexFunction { public: ValenceEnergyPerEdge(const TriMesh& mesh); - double get_value(const Simplex& simplex) const override; + double get_value(const Tuple& simplex) const override; + using PerSimplexFunction::get_value; protected: - const TriMesh& mesh() const; + const TriMesh& tri_mesh() const; }; -} // namespace function } // namespace wmtk diff --git a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp index c839e3817f..dec1e2be1c 100644 --- a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp +++ b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp @@ -6,48 +6,47 @@ namespace wmtk::function::utils { DifferentiableFunctionEvaluator::DifferentiableFunctionEvaluator( - const function::LocallyDifferentiableFunction& function, + const function::DifferentiableFunction& function, Accessor& accessor, const Simplex& simplex) : FunctionEvaluator(function, accessor, simplex) + , m_function(function) { - m_upper_level_cofaces = compute_upper_level_cofaces(); + // m_upper_level_cofaces = compute_upper_level_cofaces(); } -auto DifferentiableFunctionEvaluator::function() const - -> const function::LocallyDifferentiableFunction& +auto DifferentiableFunctionEvaluator::function() const -> const function::DifferentiableFunction& { - return static_cast(FunctionEvaluator::function()); + return m_function; + // return static_cast(FunctionEvaluator::function()); } -auto DifferentiableFunctionEvaluator::get_value(double v) -> double -{ - store(v); - return get_value(); -} -auto DifferentiableFunctionEvaluator::get_value() const -> double -{ - return function().get_value_sum( - wmtk::simplex::utils::tuple_vector_to_homogeneous_simplex_vector( - upper_level_cofaces(), - function_simplex_type())); -} +// auto DifferentiableFunctionEvaluator::get_value() const -> double +//{ +// return get_value(simplex()); +// // return function().get_value_sum( +// // wmtk::simplex::utils::tuple_vector_to_homogeneous_simplex_vector( +// // upper_level_cofaces(), +// // function_simplex_type())); +//} auto DifferentiableFunctionEvaluator::get_gradient() const -> Vector { - return function().get_gradient_sum( - wmtk::simplex::utils::tuple_vector_to_homogeneous_simplex_vector( - upper_level_cofaces(), - function_simplex_type())); + return function().get_hessian(simplex()); + // return function().get_gradient_sum( + // wmtk::simplex::utils::tuple_vector_to_homogeneous_simplex_vector( + // upper_level_cofaces(), + // function_simplex_type())); } auto DifferentiableFunctionEvaluator::get_hessian() const -> Matrix { - return function().get_hessian_sum( - wmtk::simplex::utils::tuple_vector_to_homogeneous_simplex_vector( - upper_level_cofaces(), - function_simplex_type())); + return function().get_hessian(simplex()); + // return function().get_hessian_sum( + // wmtk::simplex::utils::tuple_vector_to_homogeneous_simplex_vector( + // upper_level_cofaces(), + // function_simplex_type())); } auto DifferentiableFunctionEvaluator::get_gradient(double v) -> Vector @@ -62,17 +61,17 @@ auto DifferentiableFunctionEvaluator::get_hessian(double v) -> Matrix return get_hessian(); } -const std::vector& DifferentiableFunctionEvaluator::upper_level_cofaces() const -{ - return m_upper_level_cofaces; -} -std::vector DifferentiableFunctionEvaluator::compute_upper_level_cofaces() const -{ - return simplex::upper_level_cofaces_tuples(mesh(), simplex(), function_simplex_type()); -} - -std::vector DifferentiableFunctionEvaluator::compute_top_level_cofaces() const -{ - return simplex::top_level_cofaces_tuples(mesh(), simplex()); -} -} // namespace wmtk::function::utils \ No newline at end of file +// const std::vector& DifferentiableFunctionEvaluator::upper_level_cofaces() const +//{ +// return m_upper_level_cofaces; +// } +// std::vector DifferentiableFunctionEvaluator::compute_upper_level_cofaces() const +//{ +// return simplex::upper_level_cofaces_tuples(mesh(), simplex(), function_simplex_type()); +// } +// +// std::vector DifferentiableFunctionEvaluator::compute_top_level_cofaces() const +//{ +// return simplex::top_level_cofaces_tuples(mesh(), simplex()); +// } +} // namespace wmtk::function::utils diff --git a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp index b82afdcc2b..ce64bf34c6 100644 --- a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp +++ b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "FunctionEvaluator.hpp" namespace wmtk::function::utils { @@ -18,43 +18,34 @@ class DifferentiableFunctionEvaluator : public FunctionEvaluator { public: DifferentiableFunctionEvaluator( - const function::LocallyDifferentiableFunction& function, + const function::DifferentiableFunction& function, Accessor& accessor, const Simplex& simplex); using Vector = Eigen::VectorXd; using Matrix = Eigen::MatrixXd; - double get_value() const; Vector get_gradient() const; Matrix get_hessian() const; - template - double get_value(const Eigen::MatrixBase& v); template Vector get_gradient(const Eigen::MatrixBase& v); template Matrix get_hessian(const Eigen::MatrixBase& v); - double get_value(double v); Vector get_gradient(double v); Matrix get_hessian(double v); - const function::LocallyDifferentiableFunction& function() const; + const function::DifferentiableFunction& function() const; const std::vector& upper_level_cofaces() const; private: // cache the top simplices - std::vector m_upper_level_cofaces; - std::vector compute_upper_level_cofaces() const; - std::vector compute_top_level_cofaces() const; + const function::DifferentiableFunction& m_function; + // std::vector m_upper_level_cofaces; + // std::vector compute_upper_level_cofaces() const; + // std::vector compute_top_level_cofaces() const; }; -template -double DifferentiableFunctionEvaluator::get_value(const Eigen::MatrixBase& v) -{ - store(v); - return get_value(); -} template auto DifferentiableFunctionEvaluator::get_gradient(const Eigen::MatrixBase& v) -> Vector diff --git a/src/wmtk/function/utils/FunctionEvaluator.hpp b/src/wmtk/function/utils/FunctionEvaluator.hpp index 3fa257a9fc..1cdd88b07f 100644 --- a/src/wmtk/function/utils/FunctionEvaluator.hpp +++ b/src/wmtk/function/utils/FunctionEvaluator.hpp @@ -48,12 +48,8 @@ class FunctionEvaluator Accessor& accessor() { return m_accessor; } const Function& function() const { return m_function; } - const PrimitiveType& function_simplex_type() const - { - return m_function.get_function()->get_function_simplex_type(); - } - const PrimitiveType& my_simplex_type() const + PrimitiveType my_simplex_type() const { PrimitiveType type = m_simplex.primitive_type(); return type; From 7cdcf39b87c2c0188910881fb14fc939efe24231 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 28 Oct 2023 14:32:50 -0400 Subject: [PATCH 126/134] renaming upper_level_cofaces to cofaces_single_dimension --- src/wmtk/simplex/CMakeLists.txt | 5 ++--- ...cofaces.cpp => cofaces_single_dimension.cpp} | 17 ++++++++++------- ...cofaces.hpp => cofaces_single_dimension.hpp} | 4 ++-- 3 files changed, 14 insertions(+), 12 deletions(-) rename src/wmtk/simplex/{upper_level_cofaces.cpp => cofaces_single_dimension.cpp} (74%) rename src/wmtk/simplex/{upper_level_cofaces.hpp => cofaces_single_dimension.hpp} (77%) diff --git a/src/wmtk/simplex/CMakeLists.txt b/src/wmtk/simplex/CMakeLists.txt index 73c1613265..1b43004ec1 100644 --- a/src/wmtk/simplex/CMakeLists.txt +++ b/src/wmtk/simplex/CMakeLists.txt @@ -12,8 +12,8 @@ set(SRC_FILES top_level_cofaces_iterable.hpp top_level_cofaces_iterable.cpp - upper_level_cofaces.hpp - upper_level_cofaces.cpp + cofaces_single_dimension.hpp + cofaces_single_dimension.cpp link.hpp link.cpp @@ -24,7 +24,6 @@ set(SRC_FILES open_star_iterable.hpp open_star_iterable.cpp - upper_level_cofaces.hpp faces.hpp faces.cpp diff --git a/src/wmtk/simplex/upper_level_cofaces.cpp b/src/wmtk/simplex/cofaces_single_dimension.cpp similarity index 74% rename from src/wmtk/simplex/upper_level_cofaces.cpp rename to src/wmtk/simplex/cofaces_single_dimension.cpp index 62756069db..8fa6f84981 100644 --- a/src/wmtk/simplex/upper_level_cofaces.cpp +++ b/src/wmtk/simplex/cofaces_single_dimension.cpp @@ -1,4 +1,4 @@ -#include "upper_level_cofaces.hpp" +#include "cofaces_single_dimension.hpp" #include #include #include @@ -11,21 +11,24 @@ #include "top_level_cofaces.hpp" namespace wmtk::simplex { -std::vector upper_level_cofaces_tuples( +std::vector cofaces_single_dimension_tuples( const Mesh& mesh, const Simplex& simplex, const PrimitiveType& cofaces_type) { switch (mesh.top_simplex_type()) { case PrimitiveType::Face: - return upper_level_cofaces_tuples(static_cast(mesh), simplex, cofaces_type); + return cofaces_single_dimension_tuples(static_cast(mesh), simplex, cofaces_type); case PrimitiveType::Tetrahedron: - return upper_level_cofaces_tuples(static_cast(mesh), simplex, cofaces_type); - default: assert(false); throw "unknown mesh type in upper_level_cofaces_tuples"; + return cofaces_single_dimension_tuples(static_cast(mesh), simplex, cofaces_type); + case PrimitiveType::Edge: + case PrimitiveType::Vertex: + default: throw std::runtime_error("unknown mesh type in cofaces_single_dimension_tuples"); } + return {}; } -std::vector upper_level_cofaces_tuples( +std::vector cofaces_single_dimension_tuples( const TriMesh& mesh, const Simplex& my_simplex, const PrimitiveType& cofaces_type) @@ -53,4 +56,4 @@ std::vector upper_level_cofaces_tuples( } return collection; } -} // namespace wmtk::simplex \ No newline at end of file +} // namespace wmtk::simplex diff --git a/src/wmtk/simplex/upper_level_cofaces.hpp b/src/wmtk/simplex/cofaces_single_dimension.hpp similarity index 77% rename from src/wmtk/simplex/upper_level_cofaces.hpp rename to src/wmtk/simplex/cofaces_single_dimension.hpp index 9aaa81555c..208ff2ea21 100644 --- a/src/wmtk/simplex/upper_level_cofaces.hpp +++ b/src/wmtk/simplex/cofaces_single_dimension.hpp @@ -5,12 +5,12 @@ #include namespace wmtk::simplex { -std::vector upper_level_cofaces_tuples( +std::vector cofaces_single_dimension_tuples( const TriMesh& mesh, const Simplex& my_simplex, const PrimitiveType& cofaces_type); -std::vector upper_level_cofaces_tuples( +std::vector cofaces_single_dimension_tuples( const Mesh& mesh, const Simplex& my_simplex, const PrimitiveType& cofaces_type); From 18887b5c41a4d8d4aa5db241b88ce9d2be278ca6 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 28 Oct 2023 14:34:18 -0400 Subject: [PATCH 127/134] adding comment to coface_single_dimension and removing infinite loop --- src/wmtk/simplex/cofaces_single_dimension.cpp | 6 +++--- src/wmtk/simplex/cofaces_single_dimension.hpp | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/wmtk/simplex/cofaces_single_dimension.cpp b/src/wmtk/simplex/cofaces_single_dimension.cpp index 8fa6f84981..b68c186d81 100644 --- a/src/wmtk/simplex/cofaces_single_dimension.cpp +++ b/src/wmtk/simplex/cofaces_single_dimension.cpp @@ -14,13 +14,13 @@ namespace wmtk::simplex { std::vector cofaces_single_dimension_tuples( const Mesh& mesh, const Simplex& simplex, - const PrimitiveType& cofaces_type) + PrimitiveType cofaces_type) { switch (mesh.top_simplex_type()) { case PrimitiveType::Face: return cofaces_single_dimension_tuples(static_cast(mesh), simplex, cofaces_type); case PrimitiveType::Tetrahedron: - return cofaces_single_dimension_tuples(static_cast(mesh), simplex, cofaces_type); + //return cofaces_single_dimension_tuples(static_cast(mesh), simplex, cofaces_type); case PrimitiveType::Edge: case PrimitiveType::Vertex: default: throw std::runtime_error("unknown mesh type in cofaces_single_dimension_tuples"); @@ -31,7 +31,7 @@ std::vector cofaces_single_dimension_tuples( std::vector cofaces_single_dimension_tuples( const TriMesh& mesh, const Simplex& my_simplex, - const PrimitiveType& cofaces_type) + PrimitiveType cofaces_type) { assert(my_simplex.primitive_type() < cofaces_type); std::vector collection; diff --git a/src/wmtk/simplex/cofaces_single_dimension.hpp b/src/wmtk/simplex/cofaces_single_dimension.hpp index 208ff2ea21..2b6e54ce8f 100644 --- a/src/wmtk/simplex/cofaces_single_dimension.hpp +++ b/src/wmtk/simplex/cofaces_single_dimension.hpp @@ -5,13 +5,16 @@ #include namespace wmtk::simplex { + + // Returns the cofaces of a provided simplex, but only providing the cofaces in the provided coface type + std::vector cofaces_single_dimension_tuples( const TriMesh& mesh, const Simplex& my_simplex, - const PrimitiveType& cofaces_type); + PrimitiveType cofaces_type); std::vector cofaces_single_dimension_tuples( const Mesh& mesh, const Simplex& my_simplex, - const PrimitiveType& cofaces_type); -} // namespace wmtk::simplex \ No newline at end of file + PrimitiveType cofaces_type); +} // namespace wmtk::simplex From cde20d861f1e046084789e665489d76e47dc8f31 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 28 Oct 2023 14:34:56 -0400 Subject: [PATCH 128/134] commiting forgotten function interafce updates --- .../function/LocalDifferentiableFunction.cpp | 2 +- src/wmtk/function/LocalFunction.cpp | 2 +- .../utils/DifferentiableFunctionEvaluator.cpp | 18 +++++++++--------- .../utils/DifferentiableFunctionEvaluator.hpp | 6 +++--- src/wmtk/function/utils/FunctionEvaluator.cpp | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/wmtk/function/LocalDifferentiableFunction.cpp b/src/wmtk/function/LocalDifferentiableFunction.cpp index 8c5235e8fe..2cebd3a62f 100644 --- a/src/wmtk/function/LocalDifferentiableFunction.cpp +++ b/src/wmtk/function/LocalDifferentiableFunction.cpp @@ -1,6 +1,6 @@ #include "LocalDifferentiableFunction.hpp" #include -#include +#include #include #include "PerSimplexDifferentiableFunction.hpp" namespace wmtk::function { diff --git a/src/wmtk/function/LocalFunction.cpp b/src/wmtk/function/LocalFunction.cpp index d7c26e362d..d8dc01aa4f 100644 --- a/src/wmtk/function/LocalFunction.cpp +++ b/src/wmtk/function/LocalFunction.cpp @@ -25,7 +25,7 @@ const Mesh& LocalFunction::mesh() const } std::vector LocalFunction::get_local_neighborhood_tuples(const Simplex& simplex) const { - return wmtk::simplex::upper_level_cofaces_tuples( + return wmtk::simplex::cofaces_single_dimension_tuples( m_function->mesh(), my_simplex, per_simplex_function().get_simplex_type()); diff --git a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp index dec1e2be1c..5a9ad41a7a 100644 --- a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp +++ b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.cpp @@ -1,6 +1,6 @@ #include "DifferentiableFunctionEvaluator.hpp" #include -#include +#include #include @@ -12,7 +12,7 @@ DifferentiableFunctionEvaluator::DifferentiableFunctionEvaluator( : FunctionEvaluator(function, accessor, simplex) , m_function(function) { - // m_upper_level_cofaces = compute_upper_level_cofaces(); + // m_cofaces_single_dimension = compute_cofaces_single_dimension(); } auto DifferentiableFunctionEvaluator::function() const -> const function::DifferentiableFunction& @@ -27,7 +27,7 @@ auto DifferentiableFunctionEvaluator::function() const -> const function::Differ // return get_value(simplex()); // // return function().get_value_sum( // // wmtk::simplex::utils::tuple_vector_to_homogeneous_simplex_vector( -// // upper_level_cofaces(), +// // cofaces_single_dimension(), // // function_simplex_type())); //} @@ -36,7 +36,7 @@ auto DifferentiableFunctionEvaluator::get_gradient() const -> Vector return function().get_hessian(simplex()); // return function().get_gradient_sum( // wmtk::simplex::utils::tuple_vector_to_homogeneous_simplex_vector( - // upper_level_cofaces(), + // cofaces_single_dimension(), // function_simplex_type())); } @@ -45,7 +45,7 @@ auto DifferentiableFunctionEvaluator::get_hessian() const -> Matrix return function().get_hessian(simplex()); // return function().get_hessian_sum( // wmtk::simplex::utils::tuple_vector_to_homogeneous_simplex_vector( - // upper_level_cofaces(), + // cofaces_single_dimension(), // function_simplex_type())); } @@ -61,13 +61,13 @@ auto DifferentiableFunctionEvaluator::get_hessian(double v) -> Matrix return get_hessian(); } -// const std::vector& DifferentiableFunctionEvaluator::upper_level_cofaces() const +// const std::vector& DifferentiableFunctionEvaluator::cofaces_single_dimension() const //{ -// return m_upper_level_cofaces; +// return m_cofaces_single_dimension; // } -// std::vector DifferentiableFunctionEvaluator::compute_upper_level_cofaces() const +// std::vector DifferentiableFunctionEvaluator::compute_cofaces_single_dimension() const //{ -// return simplex::upper_level_cofaces_tuples(mesh(), simplex(), function_simplex_type()); +// return simplex::cofaces_single_dimension_tuples(mesh(), simplex(), function_simplex_type()); // } // // std::vector DifferentiableFunctionEvaluator::compute_top_level_cofaces() const diff --git a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp index ce64bf34c6..780ec49f81 100644 --- a/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp +++ b/src/wmtk/function/utils/DifferentiableFunctionEvaluator.hpp @@ -37,13 +37,13 @@ class DifferentiableFunctionEvaluator : public FunctionEvaluator Matrix get_hessian(double v); const function::DifferentiableFunction& function() const; - const std::vector& upper_level_cofaces() const; + const std::vector& cofaces_single_dimension() const; private: // cache the top simplices const function::DifferentiableFunction& m_function; - // std::vector m_upper_level_cofaces; - // std::vector compute_upper_level_cofaces() const; + // std::vector m_cofaces_single_dimension; + // std::vector compute_cofaces_single_dimension() const; // std::vector compute_top_level_cofaces() const; }; diff --git a/src/wmtk/function/utils/FunctionEvaluator.cpp b/src/wmtk/function/utils/FunctionEvaluator.cpp index c375a8ef73..6144407f25 100644 --- a/src/wmtk/function/utils/FunctionEvaluator.cpp +++ b/src/wmtk/function/utils/FunctionEvaluator.cpp @@ -1,6 +1,6 @@ #include "FunctionEvaluator.hpp" #include -#include +#include namespace wmtk::function::utils { FunctionEvaluator::FunctionEvaluator( From c780fe93d1f5269ec6366894046b142878ee09d2 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 28 Oct 2023 21:34:36 -0400 Subject: [PATCH 129/134] adding primitive range function to conveniently get ranges of simplices --- src/wmtk/utils/CMakeLists.txt | 3 + src/wmtk/utils/primitive_range.cpp | 64 +++++++++++++++++ src/wmtk/utils/primitive_range.hpp | 12 ++++ tests/CMakeLists.txt | 1 + tests/test_primitive.cpp | 109 +++++++++++++++++++++++++++++ 5 files changed, 189 insertions(+) create mode 100644 src/wmtk/utils/primitive_range.cpp create mode 100644 src/wmtk/utils/primitive_range.hpp create mode 100644 tests/test_primitive.cpp diff --git a/src/wmtk/utils/CMakeLists.txt b/src/wmtk/utils/CMakeLists.txt index 0008bcde63..7c04dbe0cb 100644 --- a/src/wmtk/utils/CMakeLists.txt +++ b/src/wmtk/utils/CMakeLists.txt @@ -32,6 +32,9 @@ set(SRC_FILES metaprogramming/ReferenceWrapperVariant.hpp metaprogramming/as_variant.hpp metaprogramming/unwrap_ref.hpp + + primitive_range.hpp + primitive_range.cpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/utils/primitive_range.cpp b/src/wmtk/utils/primitive_range.cpp new file mode 100644 index 0000000000..7b2c6d34af --- /dev/null +++ b/src/wmtk/utils/primitive_range.cpp @@ -0,0 +1,64 @@ +#include "primitive_range.hpp" +namespace wmtk::utils { +std::vector primitive_range(PrimitiveType pt0, PrimitiveType pt1) +{ + std::vector r; + switch (pt0) { + case PrimitiveType::Vertex: + r.emplace_back(PrimitiveType::Vertex); + if (pt1 == r.back()) { + break; + } + [[fallthrough]]; + case PrimitiveType::Edge: + r.emplace_back(PrimitiveType::Edge); + if (pt1 == r.back()) { + break; + } + [[fallthrough]]; + case PrimitiveType::Face: + r.emplace_back(PrimitiveType::Face); + if (pt1 == r.back()) { + break; + } + [[fallthrough]]; + case PrimitiveType::Tetrahedron: + r.emplace_back(PrimitiveType::Tetrahedron); + if (pt1 == r.back()) { + break; + } + [[fallthrough]]; + case PrimitiveType::HalfEdge: + default: break; + } + return r; +} +std::vector primitive_above(PrimitiveType pt) +{ + std::vector r; + + switch (pt) { + case PrimitiveType::Vertex: r.emplace_back(PrimitiveType::Vertex); [[fallthrough]]; + case PrimitiveType::Edge: r.emplace_back(PrimitiveType::Edge); [[fallthrough]]; + case PrimitiveType::Face: r.emplace_back(PrimitiveType::Face); [[fallthrough]]; + case PrimitiveType::Tetrahedron: r.emplace_back(PrimitiveType::Tetrahedron); [[fallthrough]]; + case PrimitiveType::HalfEdge: + default: break; + } + return r; +} +std::vector primitive_below(PrimitiveType pt) +{ + std::vector r; + + switch (pt) { + case PrimitiveType::Tetrahedron: r.emplace_back(PrimitiveType::Tetrahedron); [[fallthrough]]; + case PrimitiveType::Face: r.emplace_back(PrimitiveType::Face); [[fallthrough]]; + case PrimitiveType::Edge: r.emplace_back(PrimitiveType::Edge); [[fallthrough]]; + case PrimitiveType::Vertex: r.emplace_back(PrimitiveType::Vertex); [[fallthrough]]; + case PrimitiveType::HalfEdge: + default: break; + } + return r; +} +} // namespace wmtk::utils diff --git a/src/wmtk/utils/primitive_range.hpp b/src/wmtk/utils/primitive_range.hpp new file mode 100644 index 0000000000..ac07f6e6b7 --- /dev/null +++ b/src/wmtk/utils/primitive_range.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +namespace wmtk::utils { +// returns a vector of primitives including the endpoitns of the range +std::vector primitive_range(PrimitiveType pt0, PrimitiveType pt1); +// returns a vector of primitives including the endpoint +std::vector primitive_above(PrimitiveType pt0); +// returns a vector of primitives including the endpoint +std::vector primitive_below(PrimitiveType pt1); +} // namespace wmtk::utils diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 49fbbb06ac..4af9d758c1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -33,6 +33,7 @@ set(TEST_SOURCES test_autodiff.cpp test_tuple_metaprogramming.cpp test_local_switch_search.cpp + test_primitive.cpp tools/all_valid_local_tuples.hpp diff --git a/tests/test_primitive.cpp b/tests/test_primitive.cpp new file mode 100644 index 0000000000..eb464f0c47 --- /dev/null +++ b/tests/test_primitive.cpp @@ -0,0 +1,109 @@ +#include +#include +#include + +using namespace wmtk; +TEST_CASE("primitive_range", "[primitive]") +{ + for (PrimitiveType pt : + {PrimitiveType::Vertex, + PrimitiveType::Edge, + PrimitiveType::Face, + PrimitiveType::Tetrahedron}) { + { + auto a = wmtk::utils::primitive_range(pt, PrimitiveType::Tetrahedron); + auto b = wmtk::utils::primitive_above(pt); + CHECK(a == b); + } + { + auto a = wmtk::utils::primitive_range(PrimitiveType::Vertex, pt); + auto b = wmtk::utils::primitive_below(pt); + std::reverse(b.begin(), b.end()); + CHECK(a == b); + } + } + // 1,1 + // 1,2 + // 2,2 + { + auto a = wmtk::utils::primitive_range(PrimitiveType::Edge, PrimitiveType::Edge); + std::vector b{PrimitiveType::Edge}; + CHECK(a == b); + } + { + auto a = wmtk::utils::primitive_range(PrimitiveType::Face, PrimitiveType::Face); + std::vector b{PrimitiveType::Face}; + CHECK(a == b); + } + { + auto a = wmtk::utils::primitive_range(PrimitiveType::Edge, PrimitiveType::Face); + std::vector b{PrimitiveType::Edge, PrimitiveType::Face}; + CHECK(a == b); + } +} +TEST_CASE("primitive_above", "[primitive]") +{ + { + auto a = wmtk::utils::primitive_above(PrimitiveType::Tetrahedron); + std::vector b{PrimitiveType::Tetrahedron}; + CHECK(a == b); + } + { + auto a = wmtk::utils::primitive_above(PrimitiveType::Face); + std::vector b{PrimitiveType::Face, PrimitiveType::Tetrahedron}; + CHECK(a == b); + } + { + auto a = wmtk::utils::primitive_above(PrimitiveType::Edge); + std::vector b{ + PrimitiveType::Edge, + PrimitiveType::Face, + PrimitiveType::Tetrahedron, + }; + CHECK(a == b); + } + { + auto a = wmtk::utils::primitive_above(PrimitiveType::Vertex); + std::vector b{ + PrimitiveType::Vertex, + PrimitiveType::Edge, + PrimitiveType::Face, + PrimitiveType::Tetrahedron}; + CHECK(a == b); + } +} +TEST_CASE("primitive_below", "[primitive]") +{ + { + auto a = wmtk::utils::primitive_below(PrimitiveType::Tetrahedron); + std::vector b{ + PrimitiveType::Tetrahedron, + PrimitiveType::Face, + PrimitiveType::Edge, + PrimitiveType::Vertex, + }; + CHECK(a == b); + } + { + auto a = wmtk::utils::primitive_below(PrimitiveType::Face); + std::vector b{ + PrimitiveType::Face, + PrimitiveType::Edge, + PrimitiveType::Vertex, + }; + CHECK(a == b); + } + { + auto a = wmtk::utils::primitive_below(PrimitiveType::Edge); + std::vector b{ + PrimitiveType::Edge, + PrimitiveType::Vertex, + }; + CHECK(a == b); + } + { + auto a = wmtk::utils::primitive_below(PrimitiveType::Vertex); + std::vector b{PrimitiveType::Vertex}; + CHECK(a == b); + } +} From fc20f3a0a251a86528d8304e759d3ffc8f9c8d86 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 28 Oct 2023 21:34:46 -0400 Subject: [PATCH 130/134] updates to functions, line search might be broken --- src/wmtk/function/CMakeLists.txt | 4 ++++ src/wmtk/function/DifferentiableFunction.cpp | 22 +++++++++---------- src/wmtk/function/Function.cpp | 1 - src/wmtk/function/LocalFunction.cpp | 5 +++-- .../VertexSmoothUsingDifferentiableEnergy.hpp | 4 ++-- .../internal/VertexSmoothGradientDescent.cpp | 2 +- .../internal/VertexSmoothNewtonMethod.cpp | 2 +- src/wmtk/optimization/LineSearch.cpp | 8 ++++--- src/wmtk/optimization/LineSearch.hpp | 2 +- tests/function/test_2d_energy.cpp | 1 - tests/test_2d_operations.cpp | 8 +++---- tests/test_simplex_collection.cpp | 8 +++---- 12 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/wmtk/function/CMakeLists.txt b/src/wmtk/function/CMakeLists.txt index f2be8f59c6..ca44244947 100644 --- a/src/wmtk/function/CMakeLists.txt +++ b/src/wmtk/function/CMakeLists.txt @@ -1,5 +1,9 @@ set(SRC_FILES + Function.cpp + Function.hpp + DifferentiableFunction.cpp + DifferentiableFunction.hpp LocalFunction.hpp LocalFunction.cpp diff --git a/src/wmtk/function/DifferentiableFunction.cpp b/src/wmtk/function/DifferentiableFunction.cpp index bbac81a300..a3da334250 100644 --- a/src/wmtk/function/DifferentiableFunction.cpp +++ b/src/wmtk/function/DifferentiableFunction.cpp @@ -1,17 +1,17 @@ #include "DifferentiableFunction.hpp" -#include +#include namespace wmtk::function { -DifferentiableFunction::DifferentiableFunction( - const MeshAttributeHandle& attribute_handle) - : m_attribute_handle(attribute_handle) -{} - - -const MeshAttributeHandle& DifferentiableFunction::get_coordinate_attribute_handle() const -{ - return m_coordinate_attribute_handle; -} +//DifferentiableFunction::DifferentiableFunction( +// const MeshAttributeHandle& attribute_handle) +// : m_attribute_handle(attribute_handle) +//{} +// +// +//const MeshAttributeHandle& DifferentiableFunction::get_coordinate_attribute_handle() const +//{ +// return m_coordinate_attribute_handle; +//} long DifferentiableFunction::embedded_dimension() const { return mesh().get_attribute_dimension(get_coordinate_attribute_handle()); diff --git a/src/wmtk/function/Function.cpp b/src/wmtk/function/Function.cpp index ff6b12811f..12473a4152 100644 --- a/src/wmtk/function/Function.cpp +++ b/src/wmtk/function/Function.cpp @@ -1,5 +1,4 @@ #include "Function.hpp" -#include namespace wmtk::function { // Function::Function() {} diff --git a/src/wmtk/function/LocalFunction.cpp b/src/wmtk/function/LocalFunction.cpp index d8dc01aa4f..e951e8fa8e 100644 --- a/src/wmtk/function/LocalFunction.cpp +++ b/src/wmtk/function/LocalFunction.cpp @@ -1,6 +1,7 @@ #include "LocalFunction.hpp" -#include +#include + namespace wmtk::function { LocalFunction::LocalFunction(std::shared_ptr function) @@ -27,7 +28,7 @@ std::vector LocalFunction::get_local_neighborhood_tuples(const Simplex& s { return wmtk::simplex::cofaces_single_dimension_tuples( m_function->mesh(), - my_simplex, + simplex, per_simplex_function().get_simplex_type()); } diff --git a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp index d96b32e3e0..e3fd948b36 100644 --- a/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp +++ b/src/wmtk/operations/tri_mesh/VertexSmoothUsingDifferentiableEnergy.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include +#include #include #include #include "VertexAttributesUpdateBase.hpp" @@ -20,7 +20,7 @@ template <> struct OperationSettings { OperationSettings base_settings; - std::unique_ptr energy; + std::unique_ptr energy; // coordinate for teh attribute used to evaluate the energy MeshAttributeHandle coordinate_handle; bool smooth_boundary = false; diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp index 9bc41499fb..122e532ad7 100644 --- a/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothGradientDescent.cpp @@ -36,7 +36,7 @@ bool VertexSmoothGradientDescent::execute() } std::vector VertexSmoothGradientDescent::priority() const { - double gradnorm = m_settings.energy->get_local_gradient(input_tuple()).norm(); + double gradnorm = m_settings.energy->get_gradient(input_tuple()).norm(); std::vector r; r.emplace_back(-gradnorm); return r; diff --git a/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp index 55ad628fbb..ff9fac2cef 100644 --- a/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp +++ b/src/wmtk/operations/tri_mesh/internal/VertexSmoothNewtonMethod.cpp @@ -53,7 +53,7 @@ bool VertexSmoothNewtonMethod::execute() } std::vector VertexSmoothNewtonMethod::priority() const { - double gradnorm = m_settings.energy->get_local_gradient(input_tuple()).norm(); + double gradnorm = m_settings.energy->get_gradient(input_tuple()).norm(); std::vector r; r.emplace_back(-gradnorm); return r; diff --git a/src/wmtk/optimization/LineSearch.cpp b/src/wmtk/optimization/LineSearch.cpp index 7936ca8926..dc619a3635 100644 --- a/src/wmtk/optimization/LineSearch.cpp +++ b/src/wmtk/optimization/LineSearch.cpp @@ -9,16 +9,18 @@ LineSearch::LineSearch( , m_invariants(invariants) {} -const std::vector& LineSearch::upper_level_cofaces() const +std::vector LineSearch::modified_simplices(PrimitiveType pt) const { - return m_interface.upper_level_cofaces(); + return {}; + //return m_interface.upper_level_cofaces(); } bool LineSearch::check_state() const { PrimitiveType top_type = m_interface.mesh().top_simplex_type(); bool before_pass = m_invariants.before(m_interface.tuple()); - bool after_pass = m_invariants.after(top_type, upper_level_cofaces()); + bool after_pass = true; + //bool after_pass = m_invariants.after(top_type, modified_simplices()); return before_pass && after_pass; } double LineSearch::run(const Eigen::VectorXd& direction, double step_size) diff --git a/src/wmtk/optimization/LineSearch.hpp b/src/wmtk/optimization/LineSearch.hpp index 5ae817a281..8237fd9799 100644 --- a/src/wmtk/optimization/LineSearch.hpp +++ b/src/wmtk/optimization/LineSearch.hpp @@ -35,7 +35,7 @@ class LineSearch long m_max_steps = 10; double m_min_step_size_ratio = 1e-6; - const std::vector& upper_level_cofaces() const; + std::vector modified_simplices(PrimitiveType pt) const; // TODO: formally define what checking the state means // we currently make sure that we pass before on the input tuple and after on all top level diff --git a/tests/function/test_2d_energy.cpp b/tests/function/test_2d_energy.cpp index 10e617cc5b..cccda58092 100644 --- a/tests/function/test_2d_energy.cpp +++ b/tests/function/test_2d_energy.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include "../tools/DEBUG_TriMesh.hpp" diff --git a/tests/test_2d_operations.cpp b/tests/test_2d_operations.cpp index 5c423fc7c6..0d58999057 100644 --- a/tests/test_2d_operations.cpp +++ b/tests/test_2d_operations.cpp @@ -1318,7 +1318,7 @@ TEST_CASE("split_face", "[operations][split][2D]") // V.row(0) << 0, 0, 0; // V.row(1) << 1, 0, 0; // V.row(2) << 0.5, 0.866, 0; - DEBUG_TriMesh m = single_triangle_with_position(); + DEBUG_TriMesh m = single_equilateral_triangle(3); Tuple f = m.edge_tuple_between_v1_v2(1, 2, 0); OperationSettings settings; settings.initialize_invariants(m); @@ -1387,7 +1387,7 @@ TEST_CASE("split_face", "[operations][split][2D]") // V.row(0) << 0, 0, 0; // V.row(1) << 1, 0, 0; // V.row(2) << 0.5, 0.866, 0; - DEBUG_TriMesh m = single_triangle_with_position(); + DEBUG_TriMesh m = single_equilateral_triangle(3); Tuple f = m.edge_tuple_between_v1_v2(1, 2, 0); MeshAttributeHandle pos_handle = m.get_attribute_handle("position", PV); MeshAttributeHandle todo_handle = m.register_attribute("todo_face", PF, 1); @@ -1422,7 +1422,7 @@ TEST_CASE("split_face", "[operations][split][2D]") } SECTION("should fail with todo tag 0") { - DEBUG_TriMesh m = single_triangle_with_position(); + DEBUG_TriMesh m = single_equilateral_triangle(3); Tuple f = m.edge_tuple_between_v1_v2(1, 2, 0); MeshAttributeHandle pos_handle = m.get_attribute_handle("position", PV); MeshAttributeHandle todo_handle = m.register_attribute("todo_face", PF, 1); @@ -1585,4 +1585,4 @@ TEST_CASE("split_edge_operation_with_tag", "[operations][split][2D]") } CHECK(success_num == 2); } -} \ No newline at end of file +} diff --git a/tests/test_simplex_collection.cpp b/tests/test_simplex_collection.cpp index 64e17c946e..997861ed10 100644 --- a/tests/test_simplex_collection.cpp +++ b/tests/test_simplex_collection.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include "tools/DEBUG_TriMesh.hpp" #include "tools/TriMesh_examples.hpp" @@ -859,7 +859,7 @@ TEST_CASE("simplex_link_iterable", "[simplex_collection][2D]") } -TEST_CASE("simplex_upper_level_cofaces", "[simplex_collection][2D]") +TEST_CASE("simplex_cofaces_single_dimension", "[simplex_collection][2D]") { tests::DEBUG_TriMesh m = tests::hex_plus_two(); @@ -867,7 +867,7 @@ TEST_CASE("simplex_upper_level_cofaces", "[simplex_collection][2D]") { const Tuple t = m.edge_tuple_between_v1_v2(4, 5, 2); const simplex::Simplex input = simplex::Simplex::vertex(t); - std::vector tc = upper_level_cofaces_tuples(m, input, PrimitiveType::Edge); + std::vector tc = cofaces_single_dimension_tuples(m, input, PrimitiveType::Edge); REQUIRE(tc.size() == 6); SimplexCollection sc( @@ -895,7 +895,7 @@ TEST_CASE("simplex_upper_level_cofaces", "[simplex_collection][2D]") { const Tuple t = m.edge_tuple_between_v1_v2(3, 4, 0); const simplex::Simplex input = simplex::Simplex::vertex(t); - std::vector tc = upper_level_cofaces_tuples(m, input, PrimitiveType::Edge); + std::vector tc = cofaces_single_dimension_tuples(m, input, PrimitiveType::Edge); REQUIRE(tc.size() == 3); SimplexCollection sc( m, From 95d56bb564e29a00673cce4a4594405dfb6bd665 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 28 Oct 2023 21:41:36 -0400 Subject: [PATCH 131/134] updating line search to check higher level simplices for invariants --- src/wmtk/optimization/LineSearch.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/wmtk/optimization/LineSearch.cpp b/src/wmtk/optimization/LineSearch.cpp index dc619a3635..18dab8ce90 100644 --- a/src/wmtk/optimization/LineSearch.cpp +++ b/src/wmtk/optimization/LineSearch.cpp @@ -1,4 +1,6 @@ #include "LineSearch.hpp" +#include +#include namespace wmtk::optimization { @@ -11,8 +13,11 @@ LineSearch::LineSearch( std::vector LineSearch::modified_simplices(PrimitiveType pt) const { - return {}; - //return m_interface.upper_level_cofaces(); + return wmtk::simplex::cofaces_single_dimension_tuples( + m_interface.mesh(), + m_interface.simplex(), + pt); + // return m_interface.upper_level_cofaces(); } bool LineSearch::check_state() const @@ -20,7 +25,9 @@ bool LineSearch::check_state() const PrimitiveType top_type = m_interface.mesh().top_simplex_type(); bool before_pass = m_invariants.before(m_interface.tuple()); bool after_pass = true; - //bool after_pass = m_invariants.after(top_type, modified_simplices()); + for (const PrimitiveType pt : wmtk::utils::primitive_below(top_type)) { + after_pass |= m_invariants.after(pt, modified_simplices(pt)); + } return before_pass && after_pass; } double LineSearch::run(const Eigen::VectorXd& direction, double step_size) From 18089d66ecd9cc4629de519dc840b0049395b6a7 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 28 Oct 2023 22:18:49 -0400 Subject: [PATCH 132/134] removing unecessary assert --- src/wmtk/function/PositionMapAMIPS2D.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wmtk/function/PositionMapAMIPS2D.cpp b/src/wmtk/function/PositionMapAMIPS2D.cpp index 1723daf823..0b04fd2483 100644 --- a/src/wmtk/function/PositionMapAMIPS2D.cpp +++ b/src/wmtk/function/PositionMapAMIPS2D.cpp @@ -30,7 +30,6 @@ auto PositionMapAMIPS2D::get_value_autodiff(const Tuple& simplex) const -> DScal // get the uv coordinates of the triangle ConstAccessor pos = mesh().create_const_accessor(get_coordinate_attribute_handle()); - assert(simplex.primitive_type() == PrimitiveType::Vertex); const Tuple& tuple = simplex; auto tuple_value = pos.const_vector_attribute(tuple); From b193996a47f866195567dee44930cf0b62cf3c21 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 28 Oct 2023 22:33:23 -0400 Subject: [PATCH 133/134] fixing the simplex types for some functions/tests --- src/wmtk/function/ValenceEnergyPerEdge.cpp | 2 +- tests/function/test_2d_energy.cpp | 8 ++++---- tests/test_io.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/wmtk/function/ValenceEnergyPerEdge.cpp b/src/wmtk/function/ValenceEnergyPerEdge.cpp index 9768627bb1..1f5553d634 100644 --- a/src/wmtk/function/ValenceEnergyPerEdge.cpp +++ b/src/wmtk/function/ValenceEnergyPerEdge.cpp @@ -4,7 +4,7 @@ #include namespace wmtk::function { ValenceEnergyPerEdge::ValenceEnergyPerEdge(const TriMesh& mesh) - : PerSimplexFunction(mesh, PrimitiveType::Face) + : PerSimplexFunction(mesh, PrimitiveType::Edge) {} double ValenceEnergyPerEdge::get_value(const Tuple& tuple) const diff --git a/tests/function/test_2d_energy.cpp b/tests/function/test_2d_energy.cpp index cccda58092..4c0ac259a1 100644 --- a/tests/function/test_2d_energy.cpp +++ b/tests/function/test_2d_energy.cpp @@ -52,7 +52,7 @@ TEST_CASE("amips2d_values") AMIPS2D amips2d(tri_mesh, uv_handle); - CHECK(amips2d.get_value(Simplex(PrimitiveType::Vertex, e1)) == 2.0); + CHECK(amips2d.get_value(Simplex(PrimitiveType::Face, e1)) == 2.0); } SECTION("random_triangle") { @@ -65,7 +65,7 @@ TEST_CASE("amips2d_values") const TriMesh tri_mesh = static_cast(example_mesh); AMIPS2D amips2d(tri_mesh, uv_handle); - CHECK(amips2d.get_value(Simplex(PrimitiveType::Vertex, e1)) >= 2.); + CHECK(amips2d.get_value(Simplex(PrimitiveType::Face, e1)) >= 2.); } } } @@ -89,7 +89,7 @@ TEST_CASE("PositionMapAMIPS_values") 0.0, 1.0); - CHECK(amips3d.get_value(Simplex(PrimitiveType::Vertex, e1)) == 2.0); + CHECK(amips3d.get_value(Simplex(PrimitiveType::Face, e1)) == 2.0); } SECTION("random_triangle") { @@ -109,7 +109,7 @@ TEST_CASE("PositionMapAMIPS_values") 0.0, 1.0); - CHECK(amips3d.get_value(Simplex(PrimitiveType::Vertex, e1)) >= 2.0); + CHECK(amips3d.get_value(Simplex(PrimitiveType::Face, e1)) >= 2.0); } } } diff --git a/tests/test_io.cpp b/tests/test_io.cpp index 0b59fad1f4..9582f89e6a 100644 --- a/tests/test_io.cpp +++ b/tests/test_io.cpp @@ -99,7 +99,7 @@ TEST_CASE("paraview_3d", "[io]") TEST_CASE("attribute_after_split", "[io]") { - DEBUG_TriMesh m = single_triangle_with_position(); + DEBUG_TriMesh m = single_equilateral_triangle(); wmtk::MeshAttributeHandle attribute_handle = m.register_attribute(std::string("test_attribute"), PE, 1); wmtk::MeshAttributeHandle pos_handle = From e875bb8a8f3e9e0676f738ce14befdc3df141126 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sun, 29 Oct 2023 11:50:28 -0400 Subject: [PATCH 134/134] making cofaces single dimension tests less order dependent --- tests/test_simplex_collection.cpp | 48 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/tests/test_simplex_collection.cpp b/tests/test_simplex_collection.cpp index 997861ed10..0787434c65 100644 --- a/tests/test_simplex_collection.cpp +++ b/tests/test_simplex_collection.cpp @@ -5,15 +5,15 @@ #include #include #include +#include +#include +#include #include #include #include #include -#include -#include #include #include -#include #include #include "tools/DEBUG_TriMesh.hpp" #include "tools/TriMesh_examples.hpp" @@ -539,9 +539,7 @@ TEST_CASE("simplex_closed_star", "[simplex_collection][2D]") for (size_t i = 7; i < 19; ++i) { const Simplex& e = simplices[i]; const Tuple center = m.switch_vertex(m.next_edge(e.tuple())); - CHECK( - (faces(m, e).contains(v) || - m.simplices_are_equal(v, Simplex::vertex(center)))); + CHECK((faces(m, e).contains(v) || m.simplices_are_equal(v, Simplex::vertex(center)))); } CHECK(m.id(simplices[19]) == 0); @@ -572,9 +570,7 @@ TEST_CASE("simplex_closed_star", "[simplex_collection][2D]") for (size_t i = 4; i < 9; ++i) { const Simplex& e = simplices[i]; const Tuple center = m.switch_vertex(m.next_edge(e.tuple())); - CHECK( - (faces(m, e).contains(v) || - m.simplices_are_equal(v, Simplex::vertex(center)))); + CHECK((faces(m, e).contains(v) || m.simplices_are_equal(v, Simplex::vertex(center)))); } CHECK(m.id(simplices[9]) == 0); @@ -875,20 +871,22 @@ TEST_CASE("simplex_cofaces_single_dimension", "[simplex_collection][2D]") simplex::utils::tuple_vector_to_homogeneous_simplex_vector(tc, PrimitiveType::Face)); sc.sort(); const auto& cells = sc.simplex_vector(); - CHECK(m.id(m.switch_vertex(cells[0].tuple()), PrimitiveType::Vertex) == 3); - CHECK(m.id(m.switch_vertex(cells[1].tuple()), PrimitiveType::Vertex) == 0); - CHECK(m.id(m.switch_vertex(cells[2].tuple()), PrimitiveType::Vertex) == 1); - CHECK(m.id(m.switch_vertex(cells[3].tuple()), PrimitiveType::Vertex) == 5); - CHECK(m.id(m.switch_vertex(cells[4].tuple()), PrimitiveType::Vertex) == 7); - CHECK(m.id(m.switch_vertex(cells[5].tuple()), PrimitiveType::Vertex) == 8); + std::set target_vids({0, 3, 1, 5, 7, 8}); + std::set vids; + std::transform( + cells.begin(), + cells.end(), + std::inserter(vids, vids.end()), + [&](const Simplex& s) { + return m.id(m.switch_vertex(s.tuple()), PrimitiveType::Vertex); + }); + + CHECK(target_vids == vids); // check the lower dimension coface is the same as input - CHECK(m.id(tc[0], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); - CHECK(m.id(tc[1], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); - CHECK(m.id(tc[2], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); - CHECK(m.id(tc[3], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); - CHECK(m.id(tc[4], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); - CHECK(m.id(tc[5], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); + for (const Tuple& tup : tc) { + CHECK(m.id(tup, PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); + } } SECTION("vertex_boundary") @@ -905,12 +903,12 @@ TEST_CASE("simplex_cofaces_single_dimension", "[simplex_collection][2D]") const auto& cells = sc.simplex_vector(); // check the lower dimension coface is the same as input - CHECK(m.id(tc[0], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); - CHECK(m.id(tc[1], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); - CHECK(m.id(tc[2], PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); + for (const Tuple& tup : tc) { + CHECK(m.id(tup, PrimitiveType::Vertex) == m.id(t, PrimitiveType::Vertex)); + } CHECK(m.id(m.switch_vertex(cells[0].tuple()), PrimitiveType::Vertex) == 0); CHECK(m.id(m.switch_vertex(cells[1].tuple()), PrimitiveType::Vertex) == 4); CHECK(m.id(m.switch_vertex(cells[2].tuple()), PrimitiveType::Vertex) == 7); } -} \ No newline at end of file +}