From e6fe102574537476d3f76df0f15d497b51e97a8f Mon Sep 17 00:00:00 2001 From: iasonmanolas Date: Thu, 8 Apr 2021 21:03:23 +0300 Subject: [PATCH] Refactoring, linear model, renaming --- CMakeLists.txt | 32 +- beam.hpp | 31 +- beamformfinder.cpp | 1803 --------------- csvfile.hpp | 1 + drmsimulationmodel.cpp | 1977 +++++++++++++++++ beamformfinder.hpp => drmsimulationmodel.hpp | 17 +- edgemesh.cpp | 128 +- edgemesh.hpp | 40 +- linearsimulationmodel.hpp | 313 +++ mesh.hpp | 9 +- optimizationstructs.hpp | 5 - patternIO.cpp | 4 +- polymesh.hpp | 233 +- reducedmodeloptimizer_structs.hpp | 528 +++++ ...lationresult.hpp => simulation_structs.hpp | 105 +- simulationhistoryplotter.hpp | 4 +- elementalmesh.cpp => simulationmesh.cpp | 197 +- elementalmesh.hpp => simulationmesh.hpp | 10 +- simulationresult.hpp.orig | 397 ---- topologyenumerator.cpp | 637 ++++++ topologyenumerator.hpp | 5 +- trianglepatterngeometry.cpp | 375 +--- trianglepatterngeometry.hpp | 254 ++- utilities.hpp | 1 + vcgtrimesh.cpp | 187 +- vcgtrimesh.hpp | 29 +- 26 files changed, 4432 insertions(+), 2890 deletions(-) delete mode 100755 beamformfinder.cpp create mode 100755 drmsimulationmodel.cpp rename beamformfinder.hpp => drmsimulationmodel.hpp (96%) create mode 100644 linearsimulationmodel.hpp delete mode 100755 optimizationstructs.hpp create mode 100644 reducedmodeloptimizer_structs.hpp rename simulationresult.hpp => simulation_structs.hpp (78%) rename elementalmesh.cpp => simulationmesh.cpp (68%) rename elementalmesh.hpp => simulationmesh.hpp (96%) delete mode 100755 simulationresult.hpp.orig mode change 100644 => 100755 topologyenumerator.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5aefe84..6b5e1b4 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,11 +10,18 @@ else() set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1") endif() +set(EXTERNAL_DEPS_DIR "/home/iason/Coding/build/external dependencies") +if(NOT EXISTS ${EXTERNAL_DEPS_DIR}) + set(EXTERNAL_DEPS_DIR ${CMAKE_CURRENT_BINARY_DIR}) +endif() +##Create directory for the external libraries +file(MAKE_DIRECTORY ${EXTERNAL_DEPS_DIR}) + ##vcglib devel branch download_project(PROJ vcglib_devel GIT_REPOSITORY https://github.com/IasonManolas/vcglib.git GIT_TAG devel - PREFIX ${CMAKE_CURRENT_LIST_DIR}/build/external/ + PREFIX ${EXTERNAL_DEPS_DIR} ${UPDATE_DISCONNECTED_IF_AVAILABLE} ) @@ -22,11 +29,21 @@ download_project(PROJ vcglib_devel download_project(PROJ MATPLOTPLUSPLUS GIT_REPOSITORY https://github.com/alandefreitas/matplotplusplus GIT_TAG master - PREFIX ${CMAKE_CURRENT_LIST_DIR}/build/external/ + PREFIX ${EXTERNAL_DEPS_DIR} ${UPDATE_DISCONNECTED_IF_AVAILABLE} ) add_subdirectory(${MATPLOTPLUSPLUS_SOURCE_DIR} ${MATPLOTPLUSPLUS_BINARY_DIR}) +##threed-beam-fea +download_project(PROJ threed-beam-fea + GIT_REPOSITORY https://github.com/IasonManolas/threed-beam-fea.git + GIT_TAG master + PREFIX ${EXTERNAL_DEPS_DIR} + ${UPDATE_DISCONNECTED_IF_AVAILABLE} +) +add_subdirectory(${threed-beam-fea_SOURCE_DIR} ${threed-beam-fea_BINARY_DIR}) + + ###Eigen 3 NOTE: Eigen is required on the system the code is ran find_package(Eigen3 3.3 REQUIRED) @@ -34,15 +51,18 @@ find_package(Eigen3 3.3 REQUIRED) file(GLOB MySourcesFiles ${CMAKE_CURRENT_LIST_DIR}/*.hpp ${CMAKE_CURRENT_LIST_DIR}/*.cpp) add_library(${PROJECT_NAME} ${MySourcesFiles} ${vcglib_devel_SOURCE_DIR}/wrap/ply/plylib.cpp) -target_link_libraries(${PROJECT_NAME} Eigen3::Eigen matplot) -target_link_directories(MySources PUBLIC ${CMAKE_CURRENT_LIST_DIR}/boost_graph/libs) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/boost_graph - PRIVATE ${vcglib_devel_SOURCE_DIR} + PUBLIC ${vcglib_devel_SOURCE_DIR} + PUBLIC ${threed-beam-fea_SOURCE_DIR} PUBLIC ${MySourcesFiles} ) if(${USE_POLYSCOPE}) -target_link_libraries(${PROJECT_NAME} polyscope glad) + find_package(OpenMP REQUIRED) + target_link_libraries(${PROJECT_NAME} Eigen3::Eigen matplot polyscope glad ThreedBeamFEA OpenMP::OpenMP_CXX) +else() + target_link_libraries(${PROJECT_NAME} -static Eigen3::Eigen matplot ThreedBeamFEA) endif() +target_link_directories(MySources PUBLIC ${CMAKE_CURRENT_LIST_DIR}/boost_graph/libs) diff --git a/beam.hpp b/beam.hpp index dc485a4..cb23c66 100755 --- a/beam.hpp +++ b/beam.hpp @@ -35,21 +35,22 @@ struct CylindricalBeamDimensions { CylindricalBeamDimensions() : od(0.03), id(0.026) {} }; -struct ElementMaterial { - double poissonsRatio; // NOTE: if I make this double the unit - // tests produced different results and thus fail - double youngsModulus; - ElementMaterial(const double &poissonsRatio, const double &youngsModulus) - : poissonsRatio(poissonsRatio), youngsModulus(youngsModulus) { - assert(poissonsRatio <= 0.5 && poissonsRatio >= -1); - } - ElementMaterial() : poissonsRatio(0.3), youngsModulus(200) {} - std::string toString() const { - return std::string("Material:") + std::string("\nPoisson's ratio=") + - std::to_string(poissonsRatio) + - std::string("\nYoung's Modulus(GPa)=") + - std::to_string(youngsModulus / 1e9); - } +struct ElementMaterial +{ + double poissonsRatio; + double youngsModulus; + ElementMaterial(const double &poissonsRatio, const double &youngsModulus) + : poissonsRatio(poissonsRatio), youngsModulus(youngsModulus) + { + assert(poissonsRatio <= 0.5 && poissonsRatio >= -1); + } + ElementMaterial() : poissonsRatio(0.3), youngsModulus(200 * 1e9) {} + std::string toString() const + { + return std::string("Material:") + std::string("\nPoisson's ratio=") + + std::to_string(poissonsRatio) + std::string("\nYoung's Modulus(GPa)=") + + std::to_string(youngsModulus / 1e9); + } }; #endif // BEAM_HPP diff --git a/beamformfinder.cpp b/beamformfinder.cpp deleted file mode 100755 index 4000ffc..0000000 --- a/beamformfinder.cpp +++ /dev/null @@ -1,1803 +0,0 @@ -#include "beamformfinder.hpp" -#include "simulationhistoryplotter.hpp" -#include -#include -#include -#include - -void FormFinder::runUnitTests() { - const std::filesystem::path groundOfTruthFolder{ - "/home/iason/Coding/Libraries/MySources/formFinder_unitTestFiles"}; - - FormFinder formFinder; - - // First example of the paper - VCGEdgeMesh beam; - // const size_t spanGridSize = 11; - // mesh.createSpanGrid(spanGridSize); - beam.load(std::filesystem::path(groundOfTruthFolder) - .append("simpleBeam.ply") - .string()); - std::unordered_map> fixedVertices; - fixedVertices[0] = std::unordered_set{0, 1, 2, 3}; - fixedVertices[beam.VN() - 1] = std::unordered_set{1, 2}; - std::unordered_map nodalForces{ - {beam.VN() / 2, Vector6d({0, 1000, 1000, 0, 0, 0})}}; - // Forced displacements - std::unordered_map nodalForcedDisplacements; - nodalForcedDisplacements.insert({beam.VN() - 1, Eigen::Vector3d(-0.2, 0, 0)}); - - SimulationJob beamSimulationJob{ - std::make_shared(beam), "First paper example", - // SimulationJob::constructFixedVerticesSpanGrid(spanGridSize, - // mesh.VN()), - fixedVertices, nodalForces, nodalForcedDisplacements}; - beamSimulationJob.pMesh->setBeamMaterial(0.3, 200 * 1e9); - assert(CrossSectionType::name == CylindricalBeamDimensions::name); - - beamSimulationJob.pMesh->setBeamCrossSection(CrossSectionType{0.03, 0.026}); - Settings settings; - settings.Dtini = 0.1; - settings.xi = 0.9969; - settings.maxDRMIterations = 0; - settings.totalResidualForcesNormThreshold = 1; - // settings.shouldDraw = true; - settings.beVerbose = true; - SimulationResults simpleBeam_simulationResults = formFinder.executeSimulation( - std::make_shared(beamSimulationJob), settings); - simpleBeam_simulationResults.save(); - const std::string simpleBeamGroundOfTruthBinaryFilename = - std::filesystem::path(groundOfTruthFolder) - .append("SimpleBeam_displacements.eigenBin") - .string(); - assert(std::filesystem::exists( - std::filesystem::path(simpleBeamGroundOfTruthBinaryFilename))); - Eigen::MatrixXd simpleBeam_groundOfTruthDisplacements; - Eigen::read_binary(simpleBeamGroundOfTruthBinaryFilename, - simpleBeam_groundOfTruthDisplacements); - if (!simpleBeam_simulationResults.isEqual( - simpleBeam_groundOfTruthDisplacements)) { - std::cerr << "Error:Beam simulation produces wrong results." << std::endl; - // return; - } - - // Second example of the paper - VCGEdgeMesh shortSpanGrid; - // const size_t spanGridSize = 11; - // mesh.createSpanGrid(spanGridSize); - shortSpanGrid.load(std::filesystem::path(groundOfTruthFolder) - .append("shortSpanGridshell.ply") - .string()); - - fixedVertices.clear(); - //// Corner nodes - fixedVertices[0] = std::unordered_set{2}; - fixedVertices[4] = std::unordered_set{2}; - fixedVertices[16] = std::unordered_set{2}; - fixedVertices[20] = std::unordered_set{2}; - //// Center node - fixedVertices[10] = std::unordered_set{0, 1, 3, 4, 5}; - - nodalForcedDisplacements.clear(); - nodalForcedDisplacements.insert({{0, Eigen::Vector3d(0.1, 0.1, 0)}, - {4, Eigen::Vector3d(-0.1, 0.1, 0)}, - {16, Eigen::Vector3d(0.1, -0.1, 0)}, - {20, Eigen::Vector3d(-0.1, -0.1, 0)}}); - - SimulationJob shortSpanGridshellSimulationJob{ - std::make_shared(shortSpanGrid), - "Short span gridshell", - fixedVertices, - {}, - nodalForcedDisplacements}; - shortSpanGridshellSimulationJob.pMesh->setBeamMaterial(0.3, 200 * 1e9); - assert(typeid(CrossSectionType) == typeid(CylindricalBeamDimensions)); - shortSpanGridshellSimulationJob.pMesh->setBeamCrossSection( - CrossSectionType{0.03, 0.026}); - SimulationResults shortSpanGridshellSimulationResults = - formFinder.executeSimulation( - std::make_shared(shortSpanGridshellSimulationJob), - settings); - shortSpanGridshellSimulationResults.save(); - - const std::string groundOfTruthBinaryFilename = - std::filesystem::path(groundOfTruthFolder) - .append("ShortSpanGridshell_displacements.eigenBin") - .string(); - assert(std::filesystem::exists( - std::filesystem::path(groundOfTruthBinaryFilename))); - Eigen::MatrixXd shortSpanGridshell_groundOfTruthDisplacements; - Eigen::read_binary(groundOfTruthBinaryFilename, - shortSpanGridshell_groundOfTruthDisplacements); - // shortSpanGridshellSimulationResults.registerForDrawing( - // shortSpanGridshellSimulationJob); - // polyscope::show(); - assert(shortSpanGridshellSimulationResults.isEqual( - shortSpanGridshell_groundOfTruthDisplacements)); - if (!shortSpanGridshellSimulationResults.isEqual( - shortSpanGridshell_groundOfTruthDisplacements)) { - std::cerr << "Error:Short span simulation produces wrong results." - << std::endl; - return; - } - - // Third example of the paper - VCGEdgeMesh longSpanGrid; - longSpanGrid.load(std::filesystem::path(groundOfTruthFolder) - .append("longSpanGridshell.ply") - .string()); - const size_t spanGridSize = 11; - - fixedVertices.clear(); - for (size_t vi = 0; vi < spanGridSize - 1; vi++) { - fixedVertices[vi] = {0, 2}; - } - for (size_t vi = longSpanGrid.VN() - 1 - (spanGridSize - 2); - vi < longSpanGrid.VN(); vi++) { - fixedVertices[vi] = {0, 2}; - } - for (size_t vi = spanGridSize - 1; - vi < longSpanGrid.VN() - 1 - (spanGridSize - 2) - spanGridSize + 1; - vi += spanGridSize + 1) { - fixedVertices[vi] = {1, 2}; - fixedVertices[vi + spanGridSize] = {1, 2}; - } - - nodalForcedDisplacements.clear(); - const size_t horizontalOffset = std::floor((spanGridSize - 2) / 2); - nodalForcedDisplacements.insert( - {{horizontalOffset, Eigen::Vector3d(0, 0.3, 0)}, - {horizontalOffset + 1, Eigen::Vector3d(0, 0.3, 0)}, - {spanGridSize * (spanGridSize + 1) - 2 + horizontalOffset, - Eigen::Vector3d(0, -0.3, 0)}, - {spanGridSize * (spanGridSize + 1) - 2 + horizontalOffset + 1, - Eigen::Vector3d(0, -0.3, 0)}, - {std::floor(spanGridSize / 2) * (spanGridSize + 1) - 2, - Eigen::Vector3d(0.3, 0, 0)}, - {(std::floor(spanGridSize / 2) + 1) * (spanGridSize + 1) - 2, - Eigen::Vector3d(0.3, 0, 0)}, - {std::floor(spanGridSize / 2) * (spanGridSize + 1) - 2 + spanGridSize, - Eigen::Vector3d(-0.3, 0, 0)}, - {(std::floor(spanGridSize / 2) + 1) * (spanGridSize + 1) - 2 + - spanGridSize, - Eigen::Vector3d(-0.3, 0, 0)}}); - - SimulationJob longSpanGridshell_simulationJob{ - std::make_shared(longSpanGrid), - "long span gridshell", - fixedVertices, - {}, - nodalForcedDisplacements}; - longSpanGridshell_simulationJob.pMesh->setBeamMaterial(0.3, 200 * 1e9); - if (typeid(CrossSectionType) != typeid(CylindricalBeamDimensions)) { - std::cerr << "A cylindrical cross section is expected for running the " - "paper examples." - << std::endl; - } - longSpanGridshell_simulationJob.pMesh->setBeamCrossSection( - CrossSectionType{0.03, 0.026}); - SimulationResults longSpanGridshell_simulationResults = - formFinder.executeSimulation( - std::make_shared(longSpanGridshell_simulationJob), - settings); - longSpanGridshell_simulationResults.save(); - - const std::string longSpan_groundOfTruthBinaryFilename = - std::filesystem::path(groundOfTruthFolder) - .append("LongSpanGridshell_displacements.eigenBin") - .string(); - assert(std::filesystem::exists( - std::filesystem::path(longSpan_groundOfTruthBinaryFilename))); - Eigen::MatrixXd longSpanGridshell_groundOfTruthDisplacements; - Eigen::read_binary(longSpan_groundOfTruthBinaryFilename, - longSpanGridshell_groundOfTruthDisplacements); - assert(longSpanGridshell_simulationResults.isEqual( - longSpanGridshell_groundOfTruthDisplacements)); - if (!longSpanGridshell_simulationResults.isEqual( - longSpanGridshell_groundOfTruthDisplacements)) { - std::cerr << "Error:Long span simulation produces wrong results." - << std::endl; - return; - } - - std::cout << "Form finder unit tests succesufully passed." << std::endl; - - // polyscope::show(); -} - -void FormFinder::reset() { - mCurrentSimulationStep = 0; - history.clear(); - constrainedVertices.clear(); - rigidSupports.clear(); - pMesh.reset(); - plotYValues.clear(); - plotHandle.reset(); - checkedForMaximumMoment = false; - mSettings.shouldUseTranslationalKineticEnergyThreshold = false; - externalMomentsNorm = 0; - mSettings.drawingStep = 1; - Dt = mSettings.Dtini; - numOfDampings = 0; -} - -VectorType FormFinder::computeDisplacementDifferenceDerivative( - const EdgeType &e, const DifferentiateWithRespectTo &dui) const { - VectorType displacementDiffDeriv(0, 0, 0); - const DoFType &dofi = dui.dofi; - const bool differentiateWithRespectToANonEdgeNode = - e.cV(0) != &dui.v && e.cV(1) != &dui.v; - if (differentiateWithRespectToANonEdgeNode || dofi > 2) { - return displacementDiffDeriv; - } - - if (e.cV(0) == &dui.v) { - displacementDiffDeriv[dofi] = -1; - } else if (e.cV(1) == &dui.v) { - displacementDiffDeriv[dofi] = 1; - } - - return displacementDiffDeriv; -} - -VectorType FormFinder::computeDerivativeOfNormal( - const VertexType &v, const DifferentiateWithRespectTo &dui) const { - const size_t vi = pMesh->getIndex(v); - VectorType normalDerivative(0, 0, 0); - if (&dui.v != &v || - (dui.dofi == 0 || dui.dofi == 1 || dui.dofi == 2 || dui.dofi == 5)) { - return normalDerivative; - } - const VectorType &n = v.cN(); - const double &nx = n[0], ny = n[1]; - const double nxnyMagnitude = std::pow(nx, 2) + std::pow(ny, 2); - - if (dui.dofi == 3) { - if (nxnyMagnitude + 1e-5 >= 1) { - const double normalDerivativeX = - 1 / sqrt(nxnyMagnitude) - - std::pow(nx, 2) / std::pow(nxnyMagnitude, 1.5); - const double normalDerivativeY = -nx * ny / std::pow(nxnyMagnitude, 1.5); - const double normalDerivativeZ = 0; - normalDerivative = - VectorType(normalDerivativeX, normalDerivativeY, normalDerivativeZ); - } else { - const double normalDerivativeX = 1; - const double normalDerivativeY = 0; - const double normalDerivativeZ = -nx / std::sqrt(1 - nxnyMagnitude); - normalDerivative = - VectorType(normalDerivativeX, normalDerivativeY, normalDerivativeZ); - } - } else if (dui.dofi == 4) { - if (nxnyMagnitude + 1e-5 >= 1) { - const double normalDerivativeX = -nx * ny / std::pow(nxnyMagnitude, 1.5); - const double normalDerivativeY = - 1 / sqrt(nxnyMagnitude) - - std::pow(ny, 2) / std::pow(nxnyMagnitude, 1.5); - const double normalDerivativeZ = 0; - normalDerivative = - VectorType(normalDerivativeX, normalDerivativeY, normalDerivativeZ); - } else { - const double normalDerivativeX = 0; - const double normalDerivativeY = 1; - const double normalDerivativeZ = -ny / std::sqrt(1 - nxnyMagnitude); - normalDerivative = - VectorType(normalDerivativeX, normalDerivativeY, normalDerivativeZ); - } - } - - const bool normalDerivativeIsFinite = std::isfinite(normalDerivative[0]) && - std::isfinite(normalDerivative[1]) && - std::isfinite(normalDerivative[2]); - assert(normalDerivativeIsFinite); - const bool shouldBreak = - mCurrentSimulationStep == 118 && vi == 1 && dui.dofi == 3; - - return normalDerivative; -} - -double FormFinder::computeDerivativeElementLength( - const EdgeType &e, const DifferentiateWithRespectTo &dui) const { - if (e.cV(0) != &dui.v && e.cV(1) != &dui.v) { - return 0; - } - - const VectorType &X_j = e.cP(0); - const VectorType &X_jplus1 = e.cP(1); - const VectorType positionVectorDiff = X_jplus1 - X_j; - const VectorType displacementDiffDeriv = - computeDisplacementDifferenceDerivative(e, dui); - const double edgeLength = pMesh->elements[e].length; - const double L_kDeriv = - positionVectorDiff * displacementDiffDeriv / edgeLength; - return L_kDeriv; -} - -double FormFinder::computeDerivativeOfNorm(const VectorType &x, - const VectorType &derivativeOfX) { - return x.dot(derivativeOfX) / x.Norm(); -} - -VectorType FormFinder::computeDerivativeOfCrossProduct( - const VectorType &a, const VectorType &derivativeOfA, const VectorType &b, - const VectorType &derivativeOfB) { - const auto firstTerm = Cross(derivativeOfA, b); - const auto secondTerm = Cross(a, derivativeOfB); - return firstTerm + secondTerm; -} - -VectorType -FormFinder::computeDerivativeOfR(const EdgeType &e, - const DifferentiateWithRespectTo &dui) const { - - const VertexType &v_j = *e.cV(0); - const VertexType &v_jplus1 = *e.cV(1); - const VectorType normal_j = v_j.cN(); - const VectorType normal_jplus1 = v_jplus1.cN(); - const VectorType derivativeOfNormal_j = - &v_j == &dui.v && dui.dofi > 2 - ? pMesh->nodes[v_j].derivativeOfNormal[dui.dofi] - : VectorType(0, 0, 0); - const VectorType derivativeOfNormal_jplus1 = - &v_jplus1 == &dui.v && dui.dofi > 2 - ? pMesh->nodes[v_jplus1].derivativeOfNormal[dui.dofi] - : VectorType(0, 0, 0); - - const VectorType derivativeOfSumOfNormals = - derivativeOfNormal_j + derivativeOfNormal_jplus1; - const VectorType sumOfNormals = normal_j + normal_jplus1; - const double normOfSumOfNormals = sumOfNormals.Norm(); - const double derivativeOfNormOfSumOfNormals = - computeDerivativeOfNorm(sumOfNormals, derivativeOfSumOfNormals); - - const VectorType derivativeOfR_firstTerm = -sumOfNormals * - derivativeOfNormOfSumOfNormals / - std::pow(normOfSumOfNormals, 2); - const VectorType derivativeOfR_secondTerm = - derivativeOfSumOfNormals / normOfSumOfNormals; - const VectorType derivativeOfR = - derivativeOfR_firstTerm + derivativeOfR_secondTerm; - - assert(std::isfinite(derivativeOfR[0]) && std::isfinite(derivativeOfR[1]) && - std::isfinite(derivativeOfR[2])); - - return derivativeOfR; -} - -VectorType -FormFinder::computeDerivativeT1(const EdgeType &e, - const DifferentiateWithRespectTo &dui) const { - - const VectorType &X_j = e.cP(0); - const VectorType &X_jplus1 = e.cP(1); - const VectorType edgeVector = X_jplus1 - X_j; - const double L_kDerivative = computeDerivativeElementLength(e, dui); - const double edgeLength = pMesh->elements[e].length; - const VectorType firstTerm = - -edgeVector * L_kDerivative / std::pow(edgeLength, 2); - const VectorType secondTerm = - computeDisplacementDifferenceDerivative(e, dui) / edgeLength; - const VectorType t1Derivative = firstTerm + secondTerm; - - return t1Derivative; -} - -VectorType -FormFinder::computeDerivativeT2(const EdgeType &e, - const DifferentiateWithRespectTo &dui) const { - - const DoFType dofi = dui.dofi; - - const VertexType &v_j = *e.cV(0); - const size_t vi_j = pMesh->getIndex(v_j); - const VertexType &v_jplus1 = *e.cV(1); - const size_t vi_jplus1 = pMesh->getIndex(v_jplus1); - - const VectorType r = (v_j.cN() + v_jplus1.cN()).Normalize(); - const VectorType derivativeR_j = - dofi > 2 && &v_j == &dui.v ? pMesh->elements[e].derivativeR_j[dui.dofi] - : VectorType(0, 0, 0); - const VectorType derivativeR_jplus1 = - dofi > 2 && &v_jplus1 == &dui.v - ? pMesh->elements[e].derivativeR_jplus1[dui.dofi] - : VectorType(0, 0, 0); - const VectorType derivativeR = derivativeR_j + derivativeR_jplus1; - - const VectorType &t1 = pMesh->elements[e].frame.t1; - const VectorType derivativeT1_j = - dofi < 3 && &v_j == &dui.v ? pMesh->elements[e].derivativeT1_j[dui.dofi] - : VectorType(0, 0, 0); - const VectorType derivativeT1_jplus1 = - dofi < 3 && &v_jplus1 == &dui.v - ? pMesh->elements[e].derivativeT1_jplus1[dui.dofi] - : VectorType(0, 0, 0); - const VectorType derivativeT1 = derivativeT1_j + derivativeT1_jplus1; - - const VectorType derivativeOfRCrossT1 = - computeDerivativeOfCrossProduct(r, derivativeR, t1, derivativeT1); - const VectorType rCrossT1 = Cross(r, t1); - const double normOfRCrossT1 = rCrossT1.Norm(); - const double derivativeNormRCrossT1 = - computeDerivativeOfNorm(rCrossT1, derivativeOfRCrossT1); - - const VectorType t2Deriv_firstTerm = - -(rCrossT1 * derivativeNormRCrossT1) / std::pow(normOfRCrossT1, 2); - const VectorType t2Deriv_secondTerm = derivativeOfRCrossT1 / normOfRCrossT1; - const VectorType t2Deriv = t2Deriv_firstTerm + t2Deriv_secondTerm; - - const double t2DerivNorm = t2Deriv.Norm(); - assert(std::isfinite(t2DerivNorm)); - const bool shouldBreak = mCurrentSimulationStep == 118 && - (vi_jplus1 == 1 || vi_j == 1) && dofi == 3; - return t2Deriv; -} - -VectorType -FormFinder::computeDerivativeT3(const EdgeType &e, - const DifferentiateWithRespectTo &dui) const { - const Element &element = pMesh->elements[e]; - const VectorType &t1 = element.frame.t1; - const VectorType &t2 = element.frame.t2; - const VectorType t1CrossT2 = Cross(t1, t2); - const VertexType &v_j = *e.cV(0); - const size_t vi_j = pMesh->getIndex(v_j); - const VertexType &v_jplus1 = *e.cV(1); - const size_t vi_jplus1 = pMesh->getIndex(v_jplus1); - const VectorType derivativeT1_j = - dui.dofi < 3 && &v_j == &dui.v - ? pMesh->elements[e].derivativeT1_j[dui.dofi] - : VectorType(0, 0, 0); - const VectorType derivativeT1_jplus1 = - dui.dofi < 3 && &v_jplus1 == &dui.v - ? pMesh->elements[e].derivativeT1_jplus1[dui.dofi] - : VectorType(0, 0, 0); - const VectorType derivativeT1 = derivativeT1_j + derivativeT1_jplus1; - - // const VectorType derivativeOfT2 = computeDerivativeT2(e, dui); - // const VectorType derivativeT2_j = - // &v_j == &dui.v - // ? mesh->elements[e].derivativeT2_j[dui.dofi] - // : VectorType(0, 0, 0); - // const VectorType derivativeT2_jplus1 = - // &v_jplus1 == &dui.v - // ? mesh->elements[e].derivativeT2_jplus1[dui.dofi] - // : VectorType(0, 0, 0); - VectorType derivativeT2(0, 0, 0); - if (&v_j == &dui.v) { - derivativeT2 = pMesh->elements[e].derivativeT2_j[dui.dofi]; - } else if (&v_jplus1 == &dui.v) { - derivativeT2 = pMesh->elements[e].derivativeT2_jplus1[dui.dofi]; - } - - const VectorType derivativeT1CrossT2 = - computeDerivativeOfCrossProduct(t1, derivativeT1, t2, derivativeT2); - const double derivativeOfNormT1CrossT2 = - computeDerivativeOfNorm(t1CrossT2, derivativeT1CrossT2); - const double normT1CrossT2 = t1CrossT2.Norm(); - - const VectorType t3Deriv_firstTerm = - -(t1CrossT2 * derivativeOfNormT1CrossT2) / std::pow(normT1CrossT2, 2); - const VectorType t3Deriv_secondTerm = derivativeT1CrossT2 / normT1CrossT2; - const VectorType t3Deriv = t3Deriv_firstTerm + t3Deriv_secondTerm; - - assert(std::isfinite(t3Deriv[0]) && std::isfinite(t3Deriv[1]) && - std::isfinite(t3Deriv[2])); - return t3Deriv; -} - -double FormFinder::computeDerivativeTheta1(const EdgeType &e, - const VertexIndex &evi, - const VertexIndex &dwrt_evi, - const DoFType &dwrt_dofi) const { - const VertexType &v = *e.cV(evi); - const size_t vi = pMesh->getIndex(v); - const Element &element = pMesh->elements[e]; - const VectorType derivativeT1 = element.derivativeT1[dwrt_evi][dwrt_dofi]; - const VectorType derivativeT3 = element.derivativeT3[dwrt_evi][dwrt_dofi]; - const VectorType nDerivative = - evi != dwrt_evi ? VectorType(0, 0, 0) - : pMesh->nodes[v].derivativeOfNormal[dwrt_dofi]; - const VectorType n = v.cN(); - const VectorType &t1 = element.frame.t1; - const VectorType &t3 = element.frame.t3; - const double theta1Derivative = - derivativeT1 * Cross(t3, n) + - t1 * (Cross(derivativeT3, n) + Cross(t3, nDerivative)); - const bool shouldBreak = - mCurrentSimulationStep == 118 && vi == 1 && dwrt_dofi == 3; - - return theta1Derivative; -} - -double FormFinder::computeDerivativeTheta2(const EdgeType &e, - const VertexIndex &evi, - const VertexIndex &dwrt_evi, - const DoFType &dwrt_dofi) const { - const VertexType &v = *e.cV(evi); - - const Element &element = pMesh->elements[e]; - const VectorType derivativeT2 = element.derivativeT2[dwrt_evi][dwrt_dofi]; - const VectorType derivativeT3 = element.derivativeT3[dwrt_evi][dwrt_dofi]; - - const VectorType n = v.cN(); - const VectorType &t2 = element.frame.t2; - const VectorType &t3 = element.frame.t3; - const VectorType nDerivative = - dwrt_evi == evi ? pMesh->nodes[v].derivativeOfNormal[dwrt_dofi] - : VectorType(0, 0, 0); - const double theta2Derivative = - derivativeT2 * Cross(t3, n) + - t2 * (Cross(derivativeT3, n) + Cross(t3, nDerivative)); - - return theta2Derivative; -} - -double FormFinder::computeTheta3(const EdgeType &e, const VertexType &v) { - const VertexIndex &vi = pMesh->nodes[v].vi; - // assert(e.cV(0) == &v || e.cV(1) == &v); - - const Element &elem = pMesh->elements[e]; - const EdgeIndex &ei = elem.ei; - const Element::LocalFrame &ef = elem.frame; - const VectorType &t1 = ef.t1; - const VectorType &n = v.cN(); - const Node &node = pMesh->nodes[v]; - const double &nR = node.nR; // TODO: This makes the function non-const. - // Should be refactored in the future - - double theta3; - if (&e == node.referenceElement) { - // Use nR as theta3 only for the first star edge - return nR; - } - // assert(node.alphaAngles.contains(mesh->getIndex(e))); - double alphaAngle = node.alphaAngles.find(elem.ei)->second; - const EdgeType &refElem = *node.referenceElement; - const double rotationAngle = nR + alphaAngle; - - // const VectorType &t1_k = computeT1Vector(refElem); - const VectorType &t1_k = pMesh->elements[refElem].frame.t1; - const VectorType f1 = (t1_k - (n * (t1_k * n))).Normalize(); - vcg::Matrix44 rotationMatrix; - rotationMatrix.SetRotateRad(rotationAngle, n); - const double cosRotationAngle = cos(rotationAngle); - const double sinRotationAngle = sin(rotationAngle); - const VectorType f2 = - (f1 * cosRotationAngle + Cross(n, f1) * sinRotationAngle).Normalize(); - const VectorType &t1Current = t1; - const VectorType f3 = Cross(t1Current, f2); - - Element &element = pMesh->elements[e]; - // Save for computing theta3Derivative - if (&v == e.cV(0)) { - element.f1_j = f1; - element.f2_j = f2; - element.f3_j = f3; - element.cosRotationAngle_j = cosRotationAngle; - element.sinRotationAngle_j = sinRotationAngle; - } else { - element.f1_jplus1 = f1; - element.f2_jplus1 = f2; - element.f3_jplus1 = f3; - element.cosRotationAngle_jplus1 = cosRotationAngle; - element.sinRotationAngle_jplus1 = sinRotationAngle; - } - theta3 = f3.dot(n); - - return theta3; -} - -double FormFinder::computeDerivativeTheta3( - const EdgeType &e, const VertexType &v, - const DifferentiateWithRespectTo &dui) const { - const Node &node = pMesh->nodes[v]; - const VertexIndex &vi = pMesh->nodes[v].vi; - const bool isRigidSupport = rigidSupports.contains(vi); - if (&e == node.referenceElement && !isRigidSupport) { - if (dui.dofi == DoF::Nr && &dui.v == &v) { - return 1; - } else { - return 0; - } - } - // assert(e.cV(0) == &v || e.cV(1) == &v); - - const Element &element = pMesh->elements[e]; - const Element::LocalFrame &ef = element.frame; - const VectorType &t1 = ef.t1; - const VectorType &n = v.cN(); - const DoFType &dofi = dui.dofi; - const VertexPointer &vp_j = e.cV(0); - const VertexPointer &vp_jplus1 = e.cV(1); - - double derivativeTheta3_dofi = 0; - if (isRigidSupport) { - const VectorType &t1Initial = - computeT1Vector(pMesh->nodes[vp_j].initialLocation, - pMesh->nodes[vp_jplus1].initialLocation); - VectorType g1 = Cross(t1, t1Initial); - - const VectorType derivativeInitialT1_dofi(0, 0, 0); - const VectorType derivativeT1_j = dui.dofi < 3 && vp_j == &dui.v - ? element.derivativeT1_j[dui.dofi] - : VectorType(0, 0, 0); - const VectorType derivativeT1_jplus1 = - dui.dofi < 3 && vp_jplus1 == &dui.v - ? element.derivativeT1_jplus1[dui.dofi] - : VectorType(0, 0, 0); - const VectorType derivativeT1_dofi = derivativeT1_j + derivativeT1_jplus1; - // VectorType derivativeT1_dofi(0, 0, 0); - // if (dui.dofi < 3) { - // if (&v_j == &dui.v) { - // derivativeT1_dofi = mesh->elements[e].derivativeT1_j[dui.dofi]; - // } else if (&v_jplus1 == &dui.v) { - // derivativeT1_dofi = - // mesh->elements[e].derivativeT1_jplus1[dui.dofi]; - // } - // } - - const VectorType derivativeG1_firstTerm = - Cross(derivativeT1_dofi, t1Initial); - const VectorType derivativeG1_secondTerm = - Cross(t1, derivativeInitialT1_dofi); - const VectorType derivativeG1 = - derivativeG1_firstTerm + derivativeG1_secondTerm; - const VectorType derivativeNormal = &v == &dui.v && dui.dofi > 2 - ? node.derivativeOfNormal[dui.dofi] - : VectorType(0, 0, 0); - derivativeTheta3_dofi = derivativeG1 * n + g1 * derivativeNormal; - return derivativeTheta3_dofi; - } - EdgeType &refElem = *node.referenceElement; - const VectorType &t1_k = pMesh->elements[refElem].frame.t1; - VectorType f1, f2, f3; - double cosRotationAngle, sinRotationAngle; - if (e.cV(0) == &v) { - f1 = element.f1_j; - cosRotationAngle = element.cosRotationAngle_j; - sinRotationAngle = element.sinRotationAngle_j; - f2 = element.f2_j; - f3 = element.f3_j; - } else { - f1 = element.f1_jplus1; - cosRotationAngle = element.cosRotationAngle_jplus1; - sinRotationAngle = element.sinRotationAngle_jplus1; - f2 = element.f2_jplus1; - f3 = element.f3_jplus1; - } - const VectorType &t1_kplus1 = t1; - const VectorType f1Normalized = f1 / f1.Norm(); - - VectorType derivativeF1(0, 0, 0); - VectorType derivativeF2(0, 0, 0); - VectorType derivativeF3(0, 0, 0); - if (dui.dofi < 3) { - - const VectorType derivativeT1_kplus1_j = - vp_j == &dui.v ? element.derivativeT1_j[dui.dofi] : VectorType(0, 0, 0); - const VectorType derivativeT1_kplus1_jplus1 = - vp_jplus1 == &dui.v ? element.derivativeT1_jplus1[dui.dofi] - : VectorType(0, 0, 0); - const VectorType derivativeT1_kplus1 = - derivativeT1_kplus1_j + derivativeT1_kplus1_jplus1; - - const VectorType derivativeT1_k_j = - refElem.cV(0) == &dui.v - ? pMesh->elements[refElem].derivativeT1_j[dui.dofi] - : VectorType(0, 0, 0); - const VectorType derivativeT1_k_jplus1 = - refElem.cV(1) == &dui.v - ? pMesh->elements[refElem].derivativeT1_jplus1[dui.dofi] - : VectorType(0, 0, 0); - const VectorType derivativeT1_k = derivativeT1_k_j + derivativeT1_k_jplus1; - - derivativeF1 = derivativeT1_k - (n * (derivativeT1_k * n)); - - const double f1Norm = f1.Norm(); - const VectorType derivativeF1Normalized = - -f1 * (f1 * derivativeF1) / (f1Norm * f1Norm * f1Norm) + - derivativeF1 / f1Norm; - - derivativeF2 = derivativeF1Normalized * cosRotationAngle + - Cross(n, derivativeF1Normalized) * sinRotationAngle; - const VectorType derivativeF3_firstTerm = Cross(derivativeT1_kplus1, f2); - const VectorType derivativeF3_secondTerm = Cross(t1_kplus1, derivativeF2); - derivativeF3 = derivativeF3_firstTerm + derivativeF3_secondTerm; - derivativeTheta3_dofi = derivativeF3 * n; - - } else if (dui.dofi == DoF::Nr && &dui.v == &v) { - derivativeF2 = f1Normalized * (-sinRotationAngle) + - Cross(n, f1Normalized) * cosRotationAngle; - derivativeF3 = Cross(t1_kplus1, derivativeF2); - derivativeTheta3_dofi = derivativeF3 * n; - } else { // 2vert) { - const Node &node = pMesh->nodes[v]; - if (!node.force.hasExternalForce()) { - continue; - } - const auto nodeDisplacement = v.cP() - node.initialLocation; - const SimulationMesh::CoordType externalForce( - node.force.external[0], node.force.external[1], node.force.external[2]); - totalExternalPotentialEnergy += externalForce.dot(nodeDisplacement); - } - - double totalInternalPotentialEnergy = 0; - for (const SimulationMesh::EdgeType &e : pMesh->edge) { - - const Element &element = pMesh->elements[e]; - const EdgeIndex ei = pMesh->getIndex(e); - const double e_k = element.length - element.initialLength; - const double axialPE = pow(e_k, 2) * element.material.youngsModulus * - element.A / (2 * element.initialLength); - const double theta1Diff = element.rotationalDisplacements_jplus1.theta1 - - element.rotationalDisplacements_j.theta1; - const double torsionalPE = element.G * element.J * pow(theta1Diff, 2) / - (2 * element.initialLength); - const double &theta2_j = element.rotationalDisplacements_j.theta2; - const double &theta2_jplus1 = element.rotationalDisplacements_jplus1.theta2; - const double firstBendingPE_inBracketsTerm = 4 * pow(theta2_j, 2) + - 4 * theta2_j * theta2_jplus1 + - 4 * pow(theta2_jplus1, 2); - const double firstBendingPE = firstBendingPE_inBracketsTerm * - element.material.youngsModulus * element.I2 / - (2 * element.initialLength); - const double &theta3_j = element.rotationalDisplacements_j.theta3; - const double &theta3_jplus1 = element.rotationalDisplacements_jplus1.theta3; - const double secondBendingPE_inBracketsTerm = 4 * pow(theta3_j, 2) + - 4 * theta3_j * theta3_jplus1 + - 4 * pow(theta3_jplus1, 2); - const double secondBendingPE = secondBendingPE_inBracketsTerm * 2 * - element.material.youngsModulus * element.I3 / - element.initialLength; - - totalInternalPotentialEnergy += - axialPE + torsionalPE + firstBendingPE + secondBendingPE; - } - return totalInternalPotentialEnergy - totalExternalPotentialEnergy; -} - -void FormFinder::updateResidualForcesOnTheFly( - const std::unordered_map> - &fixedVertices) { - - // std::vector internalForcesParallel(mesh->vert.size()); - - std::vector>> - internalForcesContributionsFromEachEdge( - pMesh->EN(), - std::vector>(4, {-1, Vector6d()})); - // omp_lock_t writelock; - // omp_init_lock(&writelock); - //#pragma omp parallel for schedule(static) num_threads(8) - for (int ei = 0; ei < pMesh->EN(); ei++) { - const EdgeType &e = pMesh->edge[ei]; - const SimulationMesh::VertexType &ev_j = *e.cV(0); - const SimulationMesh::VertexType &ev_jplus1 = *e.cV(1); - const Element &element = pMesh->elements[e]; - const Element::LocalFrame &ef = element.frame; - const VectorType t3CrossN_j = Cross(ef.t3, ev_j.cN()); - const VectorType t3CrossN_jplus1 = Cross(ef.t3, ev_jplus1.cN()); - const double theta1_j = ef.t1.dot(t3CrossN_j); - const double theta1_jplus1 = ef.t1.dot(t3CrossN_jplus1); - const double theta2_j = ef.t2.dot(t3CrossN_j); - const double theta2_jplus1 = ef.t2.dot(t3CrossN_jplus1); - const double theta3_j = computeTheta3(e, ev_j); - const double theta3_jplus1 = computeTheta3(e, ev_jplus1); - // element.rotationalDisplacements_j.theta1 = theta1_j; - // element.rotationalDisplacements_jplus1.theta1 = theta1_jplus1; - // element.rotationalDisplacements_j.theta2 = theta2_j; - // element.rotationalDisplacements_jplus1.theta2 = theta2_jplus1; - // element.rotationalDisplacements_j.theta3 = theta3_j; - // element.rotationalDisplacements_jplus1.theta3 = theta3_jplus1; - std::vector> - internalForcesContributionFromThisEdge(4, {-1, Vector6d()}); - for (VertexIndex evi = 0; evi < 2; evi++) { - const SimulationMesh::VertexType &ev = *e.cV(evi); - const Node &edgeNode = pMesh->nodes[ev]; - internalForcesContributionFromThisEdge[evi].first = edgeNode.vi; - - const VertexPointer &rev_j = edgeNode.referenceElement->cV(0); - const VertexPointer &rev_jplus1 = edgeNode.referenceElement->cV(1); - // refElemOtherVertex can be precomputed - const VertexPointer &refElemOtherVertex = - rev_j == &ev ? rev_jplus1 : rev_j; - const Node &refElemOtherVertexNode = pMesh->nodes[refElemOtherVertex]; - if (edgeNode.referenceElement != &e) { - internalForcesContributionFromThisEdge[evi + 2].first = - refElemOtherVertexNode.vi; - } - const size_t vi = edgeNode.vi; - // #pragma omp parallel for schedule(static) num_threads(6) - for (DoFType dofi = DoF::Ux; dofi < DoF::NumDoF; dofi++) { - const bool isDofConstrainedFor_ev = - fixedVertices.contains(edgeNode.vi) && - fixedVertices.at(edgeNode.vi).contains(dofi); - if (!isDofConstrainedFor_ev) { - DifferentiateWithRespectTo dui{ev, dofi}; - // Axial force computation - const double e_k = element.length - element.initialLength; - const double e_kDeriv = computeDerivativeElementLength(e, dui); - const double axialForce_dofi = - e_kDeriv * e_k * element.rigidity.axial; - - // Torsional force computation - const double theta1_j_deriv = - computeDerivativeTheta1(e, 0, evi, dofi); - const double theta1_jplus1_deriv = - computeDerivativeTheta1(e, 1, evi, dofi); - const double theta1Diff = theta1_jplus1 - theta1_j; - const double theta1DiffDerivative = - theta1_jplus1_deriv - theta1_j_deriv; - const double torsionalForce_dofi = - theta1DiffDerivative * theta1Diff * element.rigidity.torsional; - - // First bending force computation - ////theta2_j derivative - const double theta2_j_deriv = - computeDerivativeTheta2(e, 0, evi, dofi); - ////theta2_jplus1 derivative - const double theta2_jplus1_deriv = - computeDerivativeTheta2(e, 1, evi, dofi); - ////1st in bracket term - const double firstBendingForce_inBracketsTerm_0 = - theta2_j_deriv * 2 * theta2_j; - ////2nd in bracket term - const double firstBendingForce_inBracketsTerm_1 = - theta2_jplus1_deriv * theta2_j; - ////3rd in bracket term - const double firstBendingForce_inBracketsTerm_2 = - theta2_j_deriv * theta2_jplus1; - ////4th in bracket term - const double firstBendingForce_inBracketsTerm_3 = - 2 * theta2_jplus1_deriv * theta2_jplus1; - // 3rd term computation - const double firstBendingForce_inBracketsTerm = - firstBendingForce_inBracketsTerm_0 + - firstBendingForce_inBracketsTerm_1 + - firstBendingForce_inBracketsTerm_2 + - firstBendingForce_inBracketsTerm_3; - const double firstBendingForce_dofi = - firstBendingForce_inBracketsTerm * element.rigidity.firstBending; - - // Second bending force computation - ////theta2_j derivative - const double theta3_j_deriv = computeDerivativeTheta3(e, ev_j, dui); - ////theta2_jplus1 derivative - const double theta3_jplus1_deriv = - computeDerivativeTheta3(e, ev_jplus1, dui); - ////1st in bracket term - const double secondBendingForce_inBracketsTerm_0 = - theta3_j_deriv * 2 * theta3_j; - ////2nd in bracket term - const double secondBendingForce_inBracketsTerm_1 = - theta3_jplus1_deriv * theta3_j; - ////3rd in bracket term - const double secondBendingForce_inBracketsTerm_2 = - theta3_j_deriv * theta3_jplus1; - ////4th in bracket term - const double secondBendingForce_inBracketsTerm_3 = - 2 * theta3_jplus1_deriv * theta3_jplus1; - // 3rd term computation - const double secondBendingForce_inBracketsTerm = - secondBendingForce_inBracketsTerm_0 + - secondBendingForce_inBracketsTerm_1 + - secondBendingForce_inBracketsTerm_2 + - secondBendingForce_inBracketsTerm_3; - double secondBendingForce_dofi = secondBendingForce_inBracketsTerm * - element.rigidity.secondBending; - const bool shouldBreak = - mCurrentSimulationStep == 118 && edgeNode.vi == 1 && dofi == 3; - internalForcesContributionFromThisEdge[evi].second[dofi] = - axialForce_dofi + firstBendingForce_dofi + - secondBendingForce_dofi + torsionalForce_dofi; - } - if (edgeNode.referenceElement != &e) { - const bool isDofConstrainedFor_refElemOtherVertex = - fixedVertices.contains(refElemOtherVertexNode.vi) && - fixedVertices.at(refElemOtherVertexNode.vi).contains(dofi); - if (!isDofConstrainedFor_refElemOtherVertex) { - DifferentiateWithRespectTo dui{*refElemOtherVertex, dofi}; - ////theta3_j derivative - const double theta3_j_deriv = computeDerivativeTheta3(e, ev_j, dui); - ////theta3_jplus1 derivative - const double theta3_jplus1_deriv = - computeDerivativeTheta3(e, ev_jplus1, dui); - ////1st in bracket term - const double secondBendingForce_inBracketsTerm_0 = - theta3_j_deriv * 2 * theta3_j; - ////2nd in bracket term - const double secondBendingForce_inBracketsTerm_1 = - theta3_jplus1_deriv * theta3_j; - ////3rd in bracket term - const double secondBendingForce_inBracketsTerm_2 = - theta3_j_deriv * theta3_jplus1; - ////4th in bracket term - const double secondBendingForce_inBracketsTerm_3 = - theta3_jplus1_deriv * 2 * theta3_jplus1; - - // 4th term computation - const double secondBendingForce_inBracketsTerm = - secondBendingForce_inBracketsTerm_0 + - secondBendingForce_inBracketsTerm_1 + - secondBendingForce_inBracketsTerm_2 + - secondBendingForce_inBracketsTerm_3; - const double secondBendingForce_dofi = - secondBendingForce_inBracketsTerm * - element.rigidity.secondBending; - internalForcesContributionFromThisEdge[evi + 2].second[dofi] = - secondBendingForce_dofi; - } - } - } - } - internalForcesContributionsFromEachEdge[ei] = - internalForcesContributionFromThisEdge; - } - - //#pragma omp parallel for schedule(static) num_threads(8) - - for (size_t vi = 0; vi < pMesh->VN(); vi++) { - Node::Forces &force = pMesh->nodes[vi].force; - force.residual = force.external; - force.internal = 0; - } - double totalResidualForcesNorm = 0; - for (size_t ei = 0; ei < pMesh->EN(); ei++) { - for (int i = 0; i < 4; i++) { - std::pair internalForcePair = - internalForcesContributionsFromEachEdge[ei][i]; - int vi = internalForcePair.first; - if (i > 1 && vi == -1) { - continue; - } - Node::Forces &force = pMesh->nodes[vi].force; - force.internal = force.internal + internalForcePair.second; - force.residual = force.residual + (internalForcePair.second * -1); - } - } - Vector6d sumOfResidualForces(0); - for (size_t vi = 0; vi < pMesh->VN(); vi++) { - Node::Forces &force = pMesh->nodes[vi].force; - const Vector6d &nodeResidualForce = force.residual; - // sumOfResidualForces = sumOfResidualForces + nodeResidualForce; - const double residualForceNorm = nodeResidualForce.norm(); - const bool shouldTrigger = std::isnan(residualForceNorm); - totalResidualForcesNorm += residualForceNorm; - } - pMesh->previousTotalResidualForcesNorm = pMesh->totalResidualForcesNorm; - pMesh->totalResidualForcesNorm = totalResidualForcesNorm; - // mesh->totalResidualForcesNorm = sumOfResidualForces.norm(); - - // plotYValues.push_back(totalResidualForcesNorm); - // auto xPlot = matplot::linspace(0, plotYValues.size(), plotYValues.size()); - // plotHandle = matplot::scatter(xPlot, plotYValues); -} - -void FormFinder::updateNodalExternalForces( - const std::unordered_map &nodalForces, - const std::unordered_map> - &fixedVertices) { - - externalMomentsNorm = 0; - for (const std::pair &nodalForce : nodalForces) { - const VertexIndex nodeIndex = nodalForce.first; - const bool isNodeConstrained = fixedVertices.contains(nodeIndex); - Node &node = pMesh->nodes[nodeIndex]; - Vector6d nodalExternalForce(0); - for (DoFType dofi = 0; dofi < 6; dofi++) { - const bool isDofConstrained = - isNodeConstrained && fixedVertices.at(nodeIndex).contains(dofi); - if (isDofConstrained) { - continue; - } - nodalExternalForce[dofi] = nodalForce.second[dofi]; - } - externalMomentsNorm += std::sqrt(pow(nodalExternalForce[3], 2) + - pow(nodalExternalForce[4], 2)); - - /* - * The external moments are given as a rotation around an axis. - * In this implementation we model moments as rotation of the normal vector - * and because of that we need to transform the moments. - */ - - if (externalMomentsNorm != 0) { - VectorType momentVector( - nodalExternalForce[3], nodalExternalForce[4], - nodalExternalForce[5]); // rotation around this vector - VectorType transformedVector = momentVector ^ VectorType(0, 0, 1); - nodalExternalForce[3] = transformedVector[0]; - nodalExternalForce[4] = transformedVector[1]; - nodalExternalForce[5] = transformedVector[5]; - } - - node.force.external = nodalExternalForce; - } -} - -void FormFinder::updateResidualForces() { - - pMesh->totalResidualForcesNorm = 0; - for (const VertexType &v : pMesh->vert) { - Node &node = pMesh->nodes[v]; - node.force.residual = node.force.external - node.force.internal; - const double residualForceNorm = (node.force.residual).norm(); - pMesh->totalResidualForcesNorm += residualForceNorm; - } -} - -void FormFinder::computeRigidSupports() { - - for (const VertexType &v : pMesh->vert) { - const VertexIndex vi = pMesh->nodes[v].vi; - const bool isVertexConstrained = constrainedVertices.contains(vi); - if (isVertexConstrained) { - auto constrainedDoFType = constrainedVertices.at(vi); - const bool hasAllDoFTypeConstrained = - constrainedDoFType.contains(DoF::Ux) && - constrainedDoFType.contains(DoF::Uy) && - constrainedDoFType.contains(DoF::Uz) && - constrainedDoFType.contains(DoF::Nx) && - constrainedDoFType.contains(DoF::Ny) && - constrainedDoFType.contains(DoF::Nr); - if (hasAllDoFTypeConstrained) { - rigidSupports.insert(vi); - } - } - } -} - -void FormFinder::updateNormalDerivatives() { - for (const VertexType &v : pMesh->vert) { - for (DoFType dofi = DoF::Nx; dofi < DoF::NumDoF; dofi++) { - DifferentiateWithRespectTo dui{v, dofi}; - pMesh->nodes[v].derivativeOfNormal[dofi] = - computeDerivativeOfNormal(v, dui); - } - } -} - -void FormFinder::updateT1Derivatives() { - for (const EdgeType &e : pMesh->edge) { - for (DoFType dofi = DoF::Ux; dofi < DoF::Nx; dofi++) { - DifferentiateWithRespectTo dui_v0{*e.cV(0), dofi}; - Element &element = pMesh->elements[e]; - element.derivativeT1_j[dofi] = computeDerivativeT1(e, dui_v0); - DifferentiateWithRespectTo dui_v1{*e.cV(1), dofi}; - element.derivativeT1_jplus1[dofi] = computeDerivativeT1(e, dui_v1); - - element.derivativeT1[0][dofi] = element.derivativeT1_j[dofi]; - element.derivativeT1[1][dofi] = element.derivativeT1_jplus1[dofi]; - } - } -} - -void FormFinder::updateT2Derivatives() { - for (const EdgeType &e : pMesh->edge) { - for (DoFType dofi = DoF::Ux; dofi < DoF::NumDoF; dofi++) { - DifferentiateWithRespectTo dui_v0{*e.cV(0), dofi}; - pMesh->elements[e].derivativeT2_j[dofi] = computeDerivativeT2(e, dui_v0); - DifferentiateWithRespectTo dui_v1{*e.cV(1), dofi}; - pMesh->elements[e].derivativeT2_jplus1[dofi] = - computeDerivativeT2(e, dui_v1); - - Element &element = pMesh->elements[e]; - element.derivativeT2[0][dofi] = element.derivativeT2_j[dofi]; - element.derivativeT2[1][dofi] = element.derivativeT2_jplus1[dofi]; - } - } -} - -void FormFinder::updateT3Derivatives() { - for (const EdgeType &e : pMesh->edge) { - for (DoFType dofi = DoF::Ux; dofi < DoF::NumDoF; dofi++) { - DifferentiateWithRespectTo dui_v0{*e.cV(0), dofi}; - Element &element = pMesh->elements[e]; - element.derivativeT3_j[dofi] = computeDerivativeT3(e, dui_v0); - DifferentiateWithRespectTo dui_v1{*e.cV(1), dofi}; - element.derivativeT3_jplus1[dofi] = computeDerivativeT3(e, dui_v1); - - element.derivativeT3[0][dofi] = element.derivativeT3_j[dofi]; - element.derivativeT3[1][dofi] = element.derivativeT3_jplus1[dofi]; - } - } -} - -void FormFinder::updateRDerivatives() { - for (const EdgeType &e : pMesh->edge) { - for (DoFType dofi = DoF::Nx; dofi < DoF::NumDoF; dofi++) { - DifferentiateWithRespectTo dui_v0{*e.cV(0), dofi}; - pMesh->elements[e].derivativeR_j[dofi] = computeDerivativeOfR(e, dui_v0); - DifferentiateWithRespectTo dui_v1{*e.cV(1), dofi}; - pMesh->elements[e].derivativeR_jplus1[dofi] = - computeDerivativeOfR(e, dui_v1); - } - } -} - -void FormFinder::updateElementalLengths() { pMesh->updateElementalLengths(); } - -FormFinder::FormFinder() {} - -void FormFinder::updateNodalMasses() { - const double gamma = 0.8; - for (VertexType &v : pMesh->vert) { - const size_t vi = pMesh->getIndex(v); - double translationalSumSk = 0; - double rotationalSumSk_I2 = 0; - double rotationalSumSk_I3 = 0; - double rotationalSumSk_J = 0; - for (const EdgePointer &ep : pMesh->nodes[v].incidentElements) { - const size_t ei = pMesh->getIndex(ep); - const Element &elem = pMesh->elements[ep]; - const double SkTranslational = - elem.material.youngsModulus * elem.A / elem.length; - translationalSumSk += SkTranslational; - const double lengthToThe3 = std::pow(elem.length, 3); - const long double SkRotational_I2 = - elem.material.youngsModulus * elem.I2 / - lengthToThe3; // TODO: I2->t2,I3->t3,t1->polar inertia - const long double SkRotational_I3 = - elem.material.youngsModulus * elem.I3 / - lengthToThe3; // TODO: I2->t2,I3->t3,t1->polar inertia - const long double SkRotational_J = - elem.material.youngsModulus * elem.J / - lengthToThe3; // TODO: I2->t2,I3->t3,t1->polar inertia - rotationalSumSk_I2 += SkRotational_I2; - rotationalSumSk_I3 += SkRotational_I3; - rotationalSumSk_J += SkRotational_J; - assert(rotationalSumSk_I2 != 0); - assert(rotationalSumSk_I3 != 0); - assert(rotationalSumSk_J != 0); - } - pMesh->nodes[v].mass.translational = - gamma * pow(mSettings.Dtini, 2) * 2 * translationalSumSk; - pMesh->nodes[v].mass.rotationalI2 = - gamma * pow(mSettings.Dtini, 2) * 8 * rotationalSumSk_I2; - pMesh->nodes[v].mass.rotationalI3 = - gamma * pow(mSettings.Dtini, 2) * 8 * rotationalSumSk_I3; - pMesh->nodes[v].mass.rotationalJ = - gamma * pow(mSettings.Dtini, 2) * 8 * rotationalSumSk_J; - - assert(std::pow(mSettings.Dtini, 2.0) * translationalSumSk / - pMesh->nodes[v].mass.translational < - 2); - assert(std::pow(mSettings.Dtini, 2.0) * rotationalSumSk_I2 / - pMesh->nodes[v].mass.rotationalI2 < - 2); - assert(std::pow(mSettings.Dtini, 2.0) * rotationalSumSk_I3 / - pMesh->nodes[v].mass.rotationalI3 < - 2); - assert(std::pow(mSettings.Dtini, 2.0) * rotationalSumSk_J / - pMesh->nodes[v].mass.rotationalJ < - 2); - } -} - -void FormFinder::updateNodalAccelerations() { - for (VertexType &v : pMesh->vert) { - Node &node = pMesh->nodes[v]; - const VertexIndex vi = pMesh->getIndex(v); - for (DoFType dofi = DoF::Ux; dofi < DoF::NumDoF; dofi++) { - if (dofi == DoF::Ux || dofi == DoF::Uy || dofi == DoF::Uz) { - node.acceleration[dofi] = - node.force.residual[dofi] / node.mass.translational; - } else if (dofi == DoF::Nx) { - node.acceleration[dofi] = - node.force.residual[dofi] / node.mass.rotationalJ; - } else if (dofi == DoF::Ny) { - node.acceleration[dofi] = - node.force.residual[dofi] / node.mass.rotationalI3; - } else if (dofi == DoF::Nr) { - node.acceleration[dofi] = - node.force.residual[dofi] / node.mass.rotationalI2; - } - } - } -} - -void FormFinder::updateNodalVelocities() { - for (VertexType &v : pMesh->vert) { - const VertexIndex vi = pMesh->getIndex(v); - Node &node = pMesh->nodes[v]; - node.velocity = node.velocity + node.acceleration * Dt; - } - updateKineticEnergy(); -} - -void FormFinder::updateNodalDisplacements() { - for (VertexType &v : pMesh->vert) { - Node &node = pMesh->nodes[v]; - node.displacements = node.displacements + node.velocity * Dt; - // if (mSettings.beVerbose) { - // std::cout << "Node " << node.vi << ":" << endl; - // std::cout << node.displacements[0] << " " << node.displacements[0] - // << " " - // << node.displacements[1] << " " << node.displacements[2] - // << " " - // << node.displacements[3] << " " << node.displacements[4] - // << " " - // << node.displacements[5] << std::endl; - // } - } -} - -void FormFinder::updateNodePosition( - VertexType &v, - const std::unordered_map> - &fixedVertices) { - Node &node = pMesh->nodes[v]; - const VertexIndex &vi = pMesh->nodes[v].vi; - - VectorType displacementVector(0, 0, 0); - if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(0)) { - displacementVector += VectorType(node.displacements[0], 0, 0); - } - if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(1)) { - displacementVector += VectorType(0, node.displacements[1], 0); - } - if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(2)) { - displacementVector += VectorType(0, 0, node.displacements[2]); - } - v.P() = node.initialLocation + displacementVector; - if (shouldApplyInitialDistortion && mCurrentSimulationStep < 40) { - VectorType desiredInitialDisplacement(0, 0, 0.01); - v.P() = v.P() + desiredInitialDisplacement; - } -} - -void FormFinder::updateNodeNormal( - VertexType &v, - const std::unordered_map> - &fixedVertices) { - - Node &node = pMesh->nodes[v]; - const VertexIndex &vi = node.vi; - // if (vi == 1) { - // std::cout << "PRE:" << mesh->vert[1].N()[0] << " " << - // mesh->vert[1].N()[1] - // << " " << mesh->vert[1].N()[2] << std::endl; - // } - VectorType normalDisplacementVector(0, 0, 0); - if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(3)) { - normalDisplacementVector += VectorType(node.displacements[3], 0, 0); - } - if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(4)) { - normalDisplacementVector += VectorType(0, node.displacements[4], 0); - } - const double nxnyMagnitudePre = std::pow(v.N()[0], 2) + std::pow(v.N()[1], 2); - v.N() = node.initialNormal + normalDisplacementVector; - const double &nx = v.N()[0], ny = v.N()[1], nz = v.N()[2]; - const double nxnyMagnitude = std::pow(nx, 2) + std::pow(ny, 2); - VectorType n = v.N(); - const bool shouldBreak = mCurrentSimulationStep == 118 && vi == 3; - if (nxnyMagnitude > 1) { - VectorType newNormal(nx / std::sqrt(nxnyMagnitude), - ny / std::sqrt(nxnyMagnitude), 0); - v.N() = newNormal; - - /*If an external moment caused the normal to lay on the xy plane this means - * that in order to disable its effect a greater internal force is needed - * than what is possible (the constraint on the z of the normals imposes a - * constraint on the maximum internal force). Because of that the - * totalResidualForcesNorm can't drop below the magnitude of external moment - * applied on vertex vi. In order to allow termination of the simulation - * when the described phenomenon happens we allow the termination of the - * algorithm if the kinetic energy of the system drops below the set - * threshold. - * */ - const bool viHasMoments = - node.force.external[3] != 0 || node.force.external[4] != 0; - if (!checkedForMaximumMoment && viHasMoments) { - mSettings.shouldUseTranslationalKineticEnergyThreshold = true; - if (mSettings.beVerbose) { - std::cout - << "Maximum moment reached.The Kinetic energy of the system will " - "be used as a convergence criterion" - << std::endl; - } - checkedForMaximumMoment = true; - } - - } else { - const double nzSquared = 1.0 - nxnyMagnitude; - const double nz = std::sqrt(nzSquared); - VectorType newNormal(nx, ny, nz); - v.N() = newNormal; - } - if (!rigidSupports.contains(vi)) { - node.nR = node.displacements[5]; - } else { - const EdgePointer &refElem = node.referenceElement; - const VectorType &refT1 = pMesh->elements[refElem].frame.t1; - - const VectorType &t1Initial = - computeT1Vector(pMesh->nodes[refElem->cV(0)].initialLocation, - pMesh->nodes[refElem->cV(1)].initialLocation); - VectorType g1 = Cross(refT1, t1Initial); - node.nR = g1.dot(v.cN()); - } - // if (vi == 1) { - // std::cout << "AFTER:" << mesh->vert[1].N()[0] << " " << - // mesh->vert[1].N()[1] - // << " " << mesh->vert[1].N()[2] << std::endl; - // } -} - -void FormFinder::applyDisplacements( - const std::unordered_map> - &fixedVertices) { - for (VertexType &v : pMesh->vert) { - updateNodePosition(v, fixedVertices); - updateNodeNormal(v, fixedVertices); - } - updateElementalFrames(); - if (mSettings.shouldDraw) { - pMesh->updateEigenEdgeAndVertices(); - } -} - -void FormFinder::updateElementalFrames() { - for (const EdgeType &e : pMesh->edge) { - const VectorType elementNormal = - (e.cV(0)->cN() + e.cV(1)->cN()).Normalize(); - pMesh->elements[e].frame = - computeElementFrame(e.cP(0), e.cP(1), elementNormal); - } -} - -void FormFinder::applyForcedDisplacements( - const std::unordered_map - nodalForcedDisplacements) { - for (const std::pair - vertexIndexDisplacementPair : nodalForcedDisplacements) { - const VertexIndex vi = vertexIndexDisplacementPair.first; - const Eigen::Vector3d vertexDisplacement = - vertexIndexDisplacementPair.second; - Node &node = pMesh->nodes[vi]; - VectorType displacementVector(vertexDisplacement(0), vertexDisplacement(1), - vertexDisplacement(2)); - pMesh->vert[vi].P() = node.initialLocation + displacementVector; - node.displacements = Vector6d( - {vertexDisplacement(0), vertexDisplacement(1), vertexDisplacement(2), - node.displacements[3], node.displacements[4], node.displacements[5]}); - } - - if (mSettings.shouldDraw) { - pMesh->updateEigenEdgeAndVertices(); - } -} - -void FormFinder::applyForcedNormals( - const std::unordered_map nodalForcedRotations) { - for (const std::pair vertexIndexDesiredNormalPair : - nodalForcedRotations) { - const VertexIndex vi = vertexIndexDesiredNormalPair.first; - - Node &node = pMesh->nodes[vi]; - pMesh->vert[vi].N() = vertexIndexDesiredNormalPair.second; - node.displacements = Vector6d( - {node.displacements[0], node.displacements[1], node.displacements[2], - vertexIndexDesiredNormalPair.second[0] - node.initialNormal[0], - vertexIndexDesiredNormalPair.second[1] - node.initialNormal[1], - node.displacements[5]}); - } -} - -// NOTE: Is this correct? Should the kinetic energy be computed like that? -void FormFinder::updateKineticEnergy() { - pMesh->previousTotalKineticEnergy = pMesh->currentTotalKineticEnergy; - pMesh->currentTotalKineticEnergy = 0; - pMesh->currentTotalTranslationalKineticEnergy = 0; - for (const VertexType &v : pMesh->vert) { - Node &node = pMesh->nodes[v]; - node.kineticEnergy = 0; - - const double translationalVelocityNorm = std::sqrt( - std::pow(node.velocity[0], 2) + std::pow(node.velocity[1], 2) + - std::pow(node.velocity[2], 2)); - const double nodeTranslationalKineticEnergy = - 0.5 * node.mass.translational * pow(translationalVelocityNorm, 2); - - const double nodeRotationalKineticEnergy = - 0.5 * (node.mass.rotationalJ * pow(node.velocity[3], 2) + - +node.mass.rotationalI3 * pow(node.velocity[4], 2) + - +node.mass.rotationalI2 * pow(node.velocity[5], 2)); - - node.kineticEnergy += - nodeTranslationalKineticEnergy /*+ nodeRotationalKineticEnergy*/; - assert(node.kineticEnergy < 1e15); - - pMesh->currentTotalKineticEnergy += node.kineticEnergy; - pMesh->currentTotalTranslationalKineticEnergy += - nodeTranslationalKineticEnergy; - } - // assert(mesh->currentTotalKineticEnergy < 100000000000000); -} - -void FormFinder::resetVelocities() { - for (const VertexType &v : pMesh->vert) { - pMesh->nodes[v].velocity = 0; - // mesh->nodes[v].force.residual * 0.5 * Dt / - // mesh->nodes[v].mass; // NOTE: Do I reset the previous - // velocity? - // reset - // current to 0 or this? - } - updateKineticEnergy(); -} - -void FormFinder::updatePositionsOnTheFly( - const std::unordered_map> - &fixedVertices) { - const double gamma = 0.8; - for (VertexType &v : pMesh->vert) { - double translationalSumSk = 0; - double rotationalSumSk_I2 = 0; - double rotationalSumSk_I3 = 0; - double rotationalSumSk_J = 0; - for (const EdgePointer &ep : pMesh->nodes[v].incidentElements) { - const Element &elem = pMesh->elements[ep]; - const double SkTranslational = - elem.material.youngsModulus * elem.A / elem.length; - translationalSumSk += SkTranslational; - const double lengthToThe3 = std::pow(elem.length, 3); - const double SkRotational_I2 = - elem.material.youngsModulus * elem.I2 / - lengthToThe3; // TODO: I2->t2,I3->t3,t1->polar inertia - const double SkRotational_I3 = - elem.material.youngsModulus * elem.I3 / - lengthToThe3; // TODO: I2->t2,I3->t3,t1->polar inertia - const double SkRotational_J = - elem.material.youngsModulus * elem.J / - lengthToThe3; // TODO: I2->t2,I3->t3,t1->polar inertia - rotationalSumSk_I2 += SkRotational_I2; - rotationalSumSk_I3 += SkRotational_I3; - rotationalSumSk_J += SkRotational_J; - // assert(rotationalSumSk_I2 != 0); - // assert(rotationalSumSk_I3 != 0); - // assert(rotationalSumSk_J != 0); - } - pMesh->nodes[v].mass.translational = - gamma * pow(mSettings.Dtini, 2) * 2 * translationalSumSk; - pMesh->nodes[v].mass.rotationalI2 = - gamma * pow(mSettings.Dtini, 2) * 8 * rotationalSumSk_I2; - pMesh->nodes[v].mass.rotationalI3 = - gamma * pow(mSettings.Dtini, 2) * 8 * rotationalSumSk_I3; - pMesh->nodes[v].mass.rotationalJ = - gamma * pow(mSettings.Dtini, 2) * 8 * rotationalSumSk_J; - - // assert(std::pow(mSettings.Dtini, 2.0) * translationalSumSk / - // mesh->nodes[v].translationalMass < - // 2); - // assert(std::pow(mSettings.Dtini, 2.0) * rotationalSumSk_I2 / - // mesh->nodes[v].rotationalMass_I2 < - // 2); - // assert(std::pow(mSettings.Dtini, 2.0) * rotationalSumSk_I3 / - // mesh->nodes[v].rotationalMass_I3 < - // 2); - // assert(std::pow(mSettings.Dtini, 2.0) * rotationalSumSk_J / - // mesh->nodes[v].rotationalMass_J < - // 2); - } - - for (VertexType &v : pMesh->vert) { - Node &node = pMesh->nodes[v]; - for (DoFType dofi = DoF::Ux; dofi < DoF::NumDoF; dofi++) { - if (dofi == DoF::Ux || dofi == DoF::Uy || dofi == DoF::Uz) { - node.acceleration[dofi] = - node.force.residual[dofi] / node.mass.translational; - } else if (dofi == DoF::Nx) { - node.acceleration[dofi] = - node.force.residual[dofi] / node.mass.rotationalJ; - } else if (dofi == DoF::Ny) { - node.acceleration[dofi] = - node.force.residual[dofi] / node.mass.rotationalI3; - } else if (dofi == DoF::Nr) { - node.acceleration[dofi] = - node.force.residual[dofi] / node.mass.rotationalI2; - } - } - } - - for (VertexType &v : pMesh->vert) { - Node &node = pMesh->nodes[v]; - node.velocity = node.velocity + node.acceleration * Dt; - } - updateKineticEnergy(); - - for (VertexType &v : pMesh->vert) { - Node &node = pMesh->nodes[v]; - node.displacements = node.displacements + node.velocity * Dt; - } - - for (VertexType &v : pMesh->vert) { - updateNodePosition(v, fixedVertices); - Node &node = pMesh->nodes[v]; - const VertexIndex &vi = node.vi; - VectorType normalDisplacementVector(0, 0, 0); - if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(3)) { - normalDisplacementVector += VectorType(node.displacements[3], 0, 0); - } - if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(4)) { - normalDisplacementVector += VectorType(0, node.displacements[4], 0); - } - v.N() = node.initialNormal + normalDisplacementVector; - const double &nx = v.N()[0], ny = v.N()[1]; - const double nxnyMagnitude = std::pow(nx, 2) + std::pow(ny, 2); - if (nxnyMagnitude > 1) { - v.N() = VectorType(nx / std::sqrt(nxnyMagnitude), - ny / std::sqrt(nxnyMagnitude), 0); - } else { - const double nzSquared = 1.0 - nxnyMagnitude; - const double nz = std::sqrt(nzSquared); - VectorType newNormal(nx, ny, nz); - v.N() = newNormal; - } - if (!rigidSupports.contains(vi)) { - node.nR = node.displacements[5]; - } else { - } - } - updateElementalFrames(); -} - -SimulationResults -FormFinder::computeResults(const std::shared_ptr &pJob) { - std::vector displacements(pMesh->VN()); - for (size_t vi = 0; vi < pMesh->VN(); vi++) { - displacements[vi] = pMesh->nodes[vi].displacements; - } - history.numberOfSteps = mCurrentSimulationStep; - - return SimulationResults{true, pJob, history, displacements}; -} - -void FormFinder::printCurrentState() const { - std::cout << "Simulation steps executed:" << mCurrentSimulationStep - << std::endl; - std::cout << "Residual forces norm: " << pMesh->totalResidualForcesNorm - << std::endl; - std::cout << "Kinetic energy:" << pMesh->currentTotalKineticEnergy - << std::endl; -} - -void FormFinder::printDebugInfo() const { - std::cout << pMesh->elements[0].rigidity.toString() << std::endl; - std::cout << "Number of dampings:" << numOfDampings << endl; - printCurrentState(); -} - -SimulationResults -FormFinder::executeSimulation(const std::shared_ptr &pJob, - const Settings &settings) { - assert(pJob->pMesh.operator bool()); - auto t1 = std::chrono::high_resolution_clock::now(); - reset(); - mSettings = settings; - - // if (!pJob->nodalExternalForces.empty()) { - // double externalForcesNorm = 0; - // for (const auto &externalForce : pJob->nodalExternalForces) { - // externalForcesNorm += externalForce.second.norm(); - // } - // mSettings.totalResidualForcesNormThreshold = externalForcesNorm * 1e-2; - // } - - constrainedVertices = pJob->constrainedVertices; - if (!pJob->nodalForcedDisplacements.empty()) { - for (std::pair viDisplPair : - pJob->nodalForcedDisplacements) { - const VertexIndex vi = viDisplPair.first; - constrainedVertices[vi].insert({0, 1, 2}); - } - } - // if (!pJob->nodalForcedNormals.empty()) { - // for (std::pair viNormalPair : - // pJob->nodalForcedDisplacements) { - // assert(viNormalPair3second[2] >= 0); - // } - // } - - pMesh.reset(); - pMesh = std::make_unique(*pJob->pMesh); - if (mSettings.beVerbose) { - std::cout << "Executing simulation for mesh with:" << pMesh->VN() - << " nodes and " << pMesh->EN() << " elements." << std::endl; - } - computeRigidSupports(); - for (auto fixedVertex : pJob->constrainedVertices) { - assert(fixedVertex.first < pMesh->VN()); - } - - updateElementalFrames(); - updateNodalMasses(); - if (!pJob->nodalForcedDisplacements.empty() && - pJob->nodalExternalForces.empty()) { - shouldApplyInitialDistortion = true; - } - updateNodalExternalForces(pJob->nodalExternalForces, constrainedVertices); - while (settings.maxDRMIterations == 0 || - mCurrentSimulationStep < settings.maxDRMIterations) { - // while (true) { - updateNormalDerivatives(); - updateT1Derivatives(); - updateRDerivatives(); - updateT2Derivatives(); - updateT3Derivatives(); - updateResidualForcesOnTheFly(constrainedVertices); - - // TODO: write parallel function for updating positions - // TODO: Make a single function out of updateResidualForcesOnTheFly - // updatePositionsOnTheFly - // updatePositionsOnTheFly(constrainedVertices); - updateNodalMasses(); - updateNodalAccelerations(); - updateNodalVelocities(); - updateNodalDisplacements(); - applyDisplacements(constrainedVertices); - if (!pJob->nodalForcedDisplacements.empty()) { - applyForcedDisplacements( - - pJob->nodalForcedDisplacements); - } - // if (!pJob->nodalForcedNormals.empty()) { - // applyForcedNormals(pJob->nodalForcedNormals); - // } - updateElementalLengths(); - pMesh->previousTotalPotentialEnergykN = - pMesh->currentTotalPotentialEnergykN; - pMesh->currentTotalPotentialEnergykN = computeTotalPotentialEnergy() / 1000; - - if (mCurrentSimulationStep != 0) { - history.stepPulse(*pMesh); - } - - if (std::isnan(pMesh->currentTotalKineticEnergy)) { - if (mSettings.beVerbose) { - std::cout << "Infinite kinetic energy detected.Exiting.." << std::endl; - } - break; - } - - if (mSettings.shouldDraw && - mCurrentSimulationStep % mSettings.drawingStep == 0) /* && -currentSimulationStep > maxDRMIterations*/ - { - // std::string saveTo = std::filesystem::current_path() - // .append("Debugging_files") - // .append("Screenshots") - // .string(); - // draw(saveTo); - // yValues = history.kineticEnergy; - // plotHandle = matplot::scatter(xPlot, yValues); - // label = "Log of Kinetic energy"; - // plotHandle->legend_string(label); - - // shouldUseKineticEnergyThreshold = true; - } - if (mSettings.shouldCreatePlots && mCurrentSimulationStep % 10 == 0) { - printCurrentState(); - SimulationResultsReporter::createPlot( - "Number of Steps", "Log of Kinetic energy", history.kineticEnergy); - } - // t = t + Dt; - mCurrentSimulationStep++; - // std::cout << "Kinetic energy:" << mesh.currentTotalKineticEnergy - // << std::endl; - // std::cout << "Residual forces norm:" << mesh.totalResidualForcesNorm - // << std::endl; - // Kinetic energy convergence - if ((mSettings.shouldUseTranslationalKineticEnergyThreshold || - mCurrentSimulationStep > 100000) && - pMesh->currentTotalTranslationalKineticEnergy < - mSettings.totalTranslationalKineticEnergyThreshold) { - if (mSettings.beVerbose) { - std::cout << "Simulation converged." << std::endl; - printCurrentState(); - std::cout << "Total potential:" << pMesh->currentTotalPotentialEnergykN - << " kNm" << std::endl; - std::cout << "Warning: The kinetic energy of the system was " - " used as a convergence criterion" - << std::endl; - } - break; - } - // Residual forces norm convergence - if (pMesh->previousTotalKineticEnergy > pMesh->currentTotalKineticEnergy - /*|| -mesh->previousTotalPotentialEnergykN > -mesh->currentTotalPotentialEnergykN*/ - /*|| mesh->currentTotalPotentialEnergykN < minPotentialEnergy*/) { - if (pMesh->totalResidualForcesNorm < - mSettings.totalResidualForcesNormThreshold) { - if (mSettings.beVerbose) { - std::cout << "Simulation converged." << std::endl; - printCurrentState(); - std::cout << "Total potential:" - << pMesh->currentTotalPotentialEnergykN << " kNm" - << std::endl; - } - break; - // } - } - // for (VertexType &v : mesh->vert) { - // Node &node = mesh->nodes[v]; - // node.displacements = node.displacements - node.velocity * Dt; - // } - // applyDisplacements(constrainedVertices); - // if (!pJob->nodalForcedDisplacements.empty()) { - // applyForcedDisplacements( - - // pJob->nodalForcedDisplacements); - // } - // updateElementalLengths(); - resetVelocities(); - Dt = Dt * mSettings.xi; - ++numOfDampings; - } - - if (mSettings.debugMessages) { - printDebugInfo(); - } - } - - SimulationResults results = computeResults(pJob); - - if (mCurrentSimulationStep == settings.maxDRMIterations && - mCurrentSimulationStep != 0) { - std::cout << "Did not reach equilibrium before reaching the maximum number " - "of DRM steps (" - << settings.maxDRMIterations << "). Breaking simulation" - << std::endl; - results.converged = false; - } else if (std::isnan(pMesh->currentTotalKineticEnergy)) { - results.converged = false; - - } else if (mSettings.beVerbose) { - auto t2 = std::chrono::high_resolution_clock::now(); - double simulationDuration = - std::chrono::duration_cast(t2 - t1).count(); - simulationDuration /= 1000; - std::cout << "Simulation converged after " << simulationDuration << "s" - << std::endl; - std::cout << "Time/(node*iteration) " - << simulationDuration / - (static_cast(mCurrentSimulationStep) * pMesh->VN()) - << "s" << std::endl; - std::cout << "Number of dampings:" << numOfDampings << endl; - } - // mesh.printVertexCoordinates(mesh.VN() / 2); - if (results.converged) { - if (mSettings.shouldCreatePlots) { - SimulationResultsReporter reporter; - reporter.reportResults({results}, "Results", pJob->pMesh->getLabel()); - } - } - return results; -} \ No newline at end of file diff --git a/csvfile.hpp b/csvfile.hpp index 303829f..de28e8c 100755 --- a/csvfile.hpp +++ b/csvfile.hpp @@ -1,5 +1,6 @@ #ifndef CSVFILE_HPP #define CSVFILE_HPP +#include #include #include #include diff --git a/drmsimulationmodel.cpp b/drmsimulationmodel.cpp new file mode 100755 index 0000000..83f49e0 --- /dev/null +++ b/drmsimulationmodel.cpp @@ -0,0 +1,1977 @@ +#include "drmsimulationmodel.hpp" +#include "simulationhistoryplotter.hpp" +#include +#include +#include +#include +#include + +void DRMSimulationModel::runUnitTests() +{ + const std::filesystem::path groundOfTruthFolder{ + "/home/iason/Coding/Libraries/MySources/formFinder_unitTestFiles"}; + + DRMSimulationModel formFinder; + + // First example of the paper + VCGEdgeMesh beam; + // const size_t spanGridSize = 11; + // mesh.createSpanGrid(spanGridSize); + beam.load(std::filesystem::path(groundOfTruthFolder).append("simpleBeam.ply").string()); + std::unordered_map> fixedVertices; + fixedVertices[0] = std::unordered_set{0, 1, 2, 3}; + fixedVertices[beam.VN() - 1] = std::unordered_set{1, 2}; + std::unordered_map nodalForces{ + {beam.VN() / 2, Vector6d({0, 1000, 1000, 0, 0, 0})}}; + // Forced displacements + std::unordered_map nodalForcedDisplacements; + nodalForcedDisplacements.insert({beam.VN() - 1, Eigen::Vector3d(-0.2, 0, 0)}); + + SimulationJob beamSimulationJob{std::make_shared(beam), + "First paper example", + // SimulationJob::constructFixedVerticesSpanGrid(spanGridSize, + // mesh.VN()), + fixedVertices, + nodalForces, + nodalForcedDisplacements}; + beamSimulationJob.pMesh->setBeamMaterial(0.3, 200 * 1e9); + assert(CrossSectionType::name == CylindricalBeamDimensions::name); + + beamSimulationJob.pMesh->setBeamCrossSection(CrossSectionType{0.03, 0.026}); + Settings settings; + settings.Dtini = 0.1; + settings.xi = 0.9969; + settings.maxDRMIterations = 0; + settings.totalResidualForcesNormThreshold = 1; + // settings.shouldDraw = true; + settings.beVerbose = true; + SimulationResults simpleBeam_simulationResults + = formFinder.executeSimulation(std::make_shared(beamSimulationJob), settings); + simpleBeam_simulationResults.save(); + const std::string simpleBeamGroundOfTruthBinaryFilename + = std::filesystem::path(groundOfTruthFolder) + .append("SimpleBeam_displacements.eigenBin") + .string(); + assert(std::filesystem::exists(std::filesystem::path(simpleBeamGroundOfTruthBinaryFilename))); + Eigen::MatrixXd simpleBeam_groundOfTruthDisplacements; + Eigen::read_binary(simpleBeamGroundOfTruthBinaryFilename, simpleBeam_groundOfTruthDisplacements); + if (!simpleBeam_simulationResults.isEqual(simpleBeam_groundOfTruthDisplacements)) { + std::cerr << "Error:Beam simulation produces wrong results." << std::endl; + // return; + } + + // Second example of the paper + VCGEdgeMesh shortSpanGrid; + // const size_t spanGridSize = 11; + // mesh.createSpanGrid(spanGridSize); + shortSpanGrid.load( + std::filesystem::path(groundOfTruthFolder).append("shortSpanGridshell.ply").string()); + + fixedVertices.clear(); + //// Corner nodes + fixedVertices[0] = std::unordered_set{2}; + fixedVertices[4] = std::unordered_set{2}; + fixedVertices[16] = std::unordered_set{2}; + fixedVertices[20] = std::unordered_set{2}; + //// Center node + fixedVertices[10] = std::unordered_set{0, 1, 3, 4, 5}; + + nodalForcedDisplacements.clear(); + nodalForcedDisplacements.insert({{0, Eigen::Vector3d(0.1, 0.1, 0)}, + {4, Eigen::Vector3d(-0.1, 0.1, 0)}, + {16, Eigen::Vector3d(0.1, -0.1, 0)}, + {20, Eigen::Vector3d(-0.1, -0.1, 0)}}); + + SimulationJob shortSpanGridshellSimulationJob{std::make_shared(shortSpanGrid), + "Short span gridshell", + fixedVertices, + {}, + nodalForcedDisplacements}; + shortSpanGridshellSimulationJob.pMesh->setBeamMaterial(0.3, 200 * 1e9); + assert(typeid(CrossSectionType) == typeid(CylindricalBeamDimensions)); + shortSpanGridshellSimulationJob.pMesh->setBeamCrossSection(CrossSectionType{0.03, 0.026}); + SimulationResults shortSpanGridshellSimulationResults + = formFinder.executeSimulation(std::make_shared( + shortSpanGridshellSimulationJob), + settings); + shortSpanGridshellSimulationResults.save(); + + const std::string groundOfTruthBinaryFilename + = std::filesystem::path(groundOfTruthFolder) + .append("ShortSpanGridshell_displacements.eigenBin") + .string(); + assert(std::filesystem::exists(std::filesystem::path(groundOfTruthBinaryFilename))); + Eigen::MatrixXd shortSpanGridshell_groundOfTruthDisplacements; + Eigen::read_binary(groundOfTruthBinaryFilename, shortSpanGridshell_groundOfTruthDisplacements); + // shortSpanGridshellSimulationResults.registerForDrawing( + // shortSpanGridshellSimulationJob); + // polyscope::show(); + assert( + shortSpanGridshellSimulationResults.isEqual(shortSpanGridshell_groundOfTruthDisplacements)); + if (!shortSpanGridshellSimulationResults.isEqual( + shortSpanGridshell_groundOfTruthDisplacements)) { + std::cerr << "Error:Short span simulation produces wrong results." << std::endl; + return; + } + + // Third example of the paper + VCGEdgeMesh longSpanGrid; + longSpanGrid.load( + std::filesystem::path(groundOfTruthFolder).append("longSpanGridshell.ply").string()); + const size_t spanGridSize = 11; + + fixedVertices.clear(); + for (size_t vi = 0; vi < spanGridSize - 1; vi++) { + fixedVertices[vi] = {0, 2}; + } + for (size_t vi = longSpanGrid.VN() - 1 - (spanGridSize - 2); vi < longSpanGrid.VN(); vi++) { + fixedVertices[vi] = {0, 2}; + } + for (size_t vi = spanGridSize - 1; + vi < longSpanGrid.VN() - 1 - (spanGridSize - 2) - spanGridSize + 1; + vi += spanGridSize + 1) { + fixedVertices[vi] = {1, 2}; + fixedVertices[vi + spanGridSize] = {1, 2}; + } + + nodalForcedDisplacements.clear(); + const size_t horizontalOffset = std::floor((spanGridSize - 2) / 2); + nodalForcedDisplacements.insert( + {{horizontalOffset, Eigen::Vector3d(0, 0.3, 0)}, + {horizontalOffset + 1, Eigen::Vector3d(0, 0.3, 0)}, + {spanGridSize * (spanGridSize + 1) - 2 + horizontalOffset, Eigen::Vector3d(0, -0.3, 0)}, + {spanGridSize * (spanGridSize + 1) - 2 + horizontalOffset + 1, Eigen::Vector3d(0, -0.3, 0)}, + {std::floor(spanGridSize / 2) * (spanGridSize + 1) - 2, Eigen::Vector3d(0.3, 0, 0)}, + {(std::floor(spanGridSize / 2) + 1) * (spanGridSize + 1) - 2, Eigen::Vector3d(0.3, 0, 0)}, + {std::floor(spanGridSize / 2) * (spanGridSize + 1) - 2 + spanGridSize, + Eigen::Vector3d(-0.3, 0, 0)}, + {(std::floor(spanGridSize / 2) + 1) * (spanGridSize + 1) - 2 + spanGridSize, + Eigen::Vector3d(-0.3, 0, 0)}}); + + SimulationJob longSpanGridshell_simulationJob{std::make_shared(longSpanGrid), + "long span gridshell", + fixedVertices, + {}, + nodalForcedDisplacements}; + longSpanGridshell_simulationJob.pMesh->setBeamMaterial(0.3, 200 * 1e9); + if (typeid(CrossSectionType) != typeid(CylindricalBeamDimensions)) { + std::cerr << "A cylindrical cross section is expected for running the " + "paper examples." + << std::endl; + } + longSpanGridshell_simulationJob.pMesh->setBeamCrossSection(CrossSectionType{0.03, 0.026}); + SimulationResults longSpanGridshell_simulationResults + = formFinder.executeSimulation(std::make_shared( + longSpanGridshell_simulationJob), + settings); + longSpanGridshell_simulationResults.save(); + + const std::string longSpan_groundOfTruthBinaryFilename + = std::filesystem::path(groundOfTruthFolder) + .append("LongSpanGridshell_displacements.eigenBin") + .string(); + assert(std::filesystem::exists(std::filesystem::path(longSpan_groundOfTruthBinaryFilename))); + Eigen::MatrixXd longSpanGridshell_groundOfTruthDisplacements; + Eigen::read_binary(longSpan_groundOfTruthBinaryFilename, + longSpanGridshell_groundOfTruthDisplacements); + assert( + longSpanGridshell_simulationResults.isEqual(longSpanGridshell_groundOfTruthDisplacements)); + if (!longSpanGridshell_simulationResults.isEqual(longSpanGridshell_groundOfTruthDisplacements)) { + std::cerr << "Error:Long span simulation produces wrong results." << std::endl; + return; + } + + std::cout << "Form finder unit tests succesufully passed." << std::endl; + + // polyscope::show(); +} + +void DRMSimulationModel::reset() +{ + mCurrentSimulationStep = 0; + history.clear(); + constrainedVertices.clear(); + rigidSupports.clear(); + pMesh.reset(); + plotYValues.clear(); + plotHandle.reset(); + checkedForMaximumMoment = false; + mSettings.shouldUseTranslationalKineticEnergyThreshold = false; + externalMomentsNorm = 0; + mSettings.drawingStep = 1; + Dt = mSettings.Dtini; + numOfDampings = 0; +} + +VectorType DRMSimulationModel::computeDisplacementDifferenceDerivative( + const EdgeType &e, const DifferentiateWithRespectTo &dui) const +{ + VectorType displacementDiffDeriv(0, 0, 0); + const DoFType &dofi = dui.dofi; + const bool differentiateWithRespectToANonEdgeNode = e.cV(0) != &dui.v && e.cV(1) != &dui.v; + if (differentiateWithRespectToANonEdgeNode || dofi > 2) { + return displacementDiffDeriv; + } + + if (e.cV(0) == &dui.v) { + displacementDiffDeriv[dofi] = -1; + } else if (e.cV(1) == &dui.v) { + displacementDiffDeriv[dofi] = 1; + } + + return displacementDiffDeriv; +} + +VectorType DRMSimulationModel::computeDerivativeOfNormal(const VertexType &v, + const DifferentiateWithRespectTo &dui) const +{ + const size_t vi = pMesh->getIndex(v); + VectorType normalDerivative(0, 0, 0); + if (&dui.v != &v || (dui.dofi == 0 || dui.dofi == 1 || dui.dofi == 2 || dui.dofi == 5)) { + return normalDerivative; + } + const VectorType &n = v.cN(); + const double &nx = n[0], ny = n[1]; + const double nxnyMagnitude = std::pow(nx, 2) + std::pow(ny, 2); + + if (dui.dofi == 3) { + if (nxnyMagnitude + 1e-5 >= 1) { + const double normalDerivativeX = 1 / sqrt(nxnyMagnitude) + - std::pow(nx, 2) / std::pow(nxnyMagnitude, 1.5); + const double normalDerivativeY = -nx * ny / std::pow(nxnyMagnitude, 1.5); + const double normalDerivativeZ = 0; + normalDerivative = VectorType(normalDerivativeX, normalDerivativeY, normalDerivativeZ); + } else { + const double normalDerivativeX = 1; + const double normalDerivativeY = 0; + const double normalDerivativeZ = -nx / std::sqrt(1 - nxnyMagnitude); + normalDerivative = VectorType(normalDerivativeX, normalDerivativeY, normalDerivativeZ); + } + } else if (dui.dofi == 4) { + if (nxnyMagnitude + 1e-5 >= 1) { + const double normalDerivativeX = -nx * ny / std::pow(nxnyMagnitude, 1.5); + const double normalDerivativeY = 1 / sqrt(nxnyMagnitude) + - std::pow(ny, 2) / std::pow(nxnyMagnitude, 1.5); + const double normalDerivativeZ = 0; + normalDerivative = VectorType(normalDerivativeX, normalDerivativeY, normalDerivativeZ); + } else { + const double normalDerivativeX = 0; + const double normalDerivativeY = 1; + const double normalDerivativeZ = -ny / std::sqrt(1 - nxnyMagnitude); + normalDerivative = VectorType(normalDerivativeX, normalDerivativeY, normalDerivativeZ); + } + } + + const bool normalDerivativeIsFinite = std::isfinite(normalDerivative[0]) + && std::isfinite(normalDerivative[1]) + && std::isfinite(normalDerivative[2]); + assert(normalDerivativeIsFinite); + const bool shouldBreak = mCurrentSimulationStep == 118 && vi == 1 && dui.dofi == 3; + + return normalDerivative; +} + +double DRMSimulationModel::computeDerivativeElementLength( + const EdgeType &e, const DifferentiateWithRespectTo &dui) const +{ + if (e.cV(0) != &dui.v && e.cV(1) != &dui.v) { + return 0; + } + + const VectorType &X_j = e.cP(0); + const VectorType &X_jplus1 = e.cP(1); + const VectorType positionVectorDiff = X_jplus1 - X_j; + const VectorType displacementDiffDeriv = computeDisplacementDifferenceDerivative(e, dui); + const double edgeLength = pMesh->elements[e].length; + const double L_kDeriv = positionVectorDiff * displacementDiffDeriv / edgeLength; + return L_kDeriv; +} + +double DRMSimulationModel::computeDerivativeOfNorm(const VectorType &x, + const VectorType &derivativeOfX) +{ + return x.dot(derivativeOfX) / x.Norm(); +} + +VectorType DRMSimulationModel::computeDerivativeOfCrossProduct(const VectorType &a, + const VectorType &derivativeOfA, + const VectorType &b, + const VectorType &derivativeOfB) +{ + const auto firstTerm = Cross(derivativeOfA, b); + const auto secondTerm = Cross(a, derivativeOfB); + return firstTerm + secondTerm; +} + +VectorType DRMSimulationModel::computeDerivativeOfR(const EdgeType &e, + const DifferentiateWithRespectTo &dui) const +{ + const VertexType &v_j = *e.cV(0); + const VertexType &v_jplus1 = *e.cV(1); + const VectorType normal_j = v_j.cN(); + const VectorType normal_jplus1 = v_jplus1.cN(); + const VectorType derivativeOfNormal_j = &v_j == &dui.v && dui.dofi > 2 + ? pMesh->nodes[v_j].derivativeOfNormal[dui.dofi] + : VectorType(0, 0, 0); + const VectorType derivativeOfNormal_jplus1 + = &v_jplus1 == &dui.v && dui.dofi > 2 ? pMesh->nodes[v_jplus1].derivativeOfNormal[dui.dofi] + : VectorType(0, 0, 0); + + const VectorType derivativeOfSumOfNormals = derivativeOfNormal_j + derivativeOfNormal_jplus1; + const VectorType sumOfNormals = normal_j + normal_jplus1; + const double normOfSumOfNormals = sumOfNormals.Norm(); + const double derivativeOfNormOfSumOfNormals = computeDerivativeOfNorm(sumOfNormals, + derivativeOfSumOfNormals); + + const VectorType derivativeOfR_firstTerm = -sumOfNormals * derivativeOfNormOfSumOfNormals + / std::pow(normOfSumOfNormals, 2); + const VectorType derivativeOfR_secondTerm = derivativeOfSumOfNormals / normOfSumOfNormals; + const VectorType derivativeOfR = derivativeOfR_firstTerm + derivativeOfR_secondTerm; + + assert(std::isfinite(derivativeOfR[0]) && std::isfinite(derivativeOfR[1]) + && std::isfinite(derivativeOfR[2])); + + return derivativeOfR; +} + +VectorType DRMSimulationModel::computeDerivativeT1(const EdgeType &e, + const DifferentiateWithRespectTo &dui) const +{ + const VectorType &X_j = e.cP(0); + const VectorType &X_jplus1 = e.cP(1); + const VectorType edgeVector = X_jplus1 - X_j; + const double L_kDerivative = computeDerivativeElementLength(e, dui); + const double edgeLength = pMesh->elements[e].length; + const VectorType firstTerm = -edgeVector * L_kDerivative / std::pow(edgeLength, 2); + const VectorType secondTerm = computeDisplacementDifferenceDerivative(e, dui) / edgeLength; + const VectorType t1Derivative = firstTerm + secondTerm; + + return t1Derivative; +} + +VectorType DRMSimulationModel::computeDerivativeT2(const EdgeType &e, + const DifferentiateWithRespectTo &dui) const +{ + const DoFType dofi = dui.dofi; + + const VertexType &v_j = *e.cV(0); + const size_t vi_j = pMesh->getIndex(v_j); + const VertexType &v_jplus1 = *e.cV(1); + const size_t vi_jplus1 = pMesh->getIndex(v_jplus1); + + const VectorType r = (v_j.cN() + v_jplus1.cN()).Normalize(); + const VectorType derivativeR_j = dofi > 2 && &v_j == &dui.v + ? pMesh->elements[e].derivativeR_j[dui.dofi] + : VectorType(0, 0, 0); + const VectorType derivativeR_jplus1 = dofi > 2 && &v_jplus1 == &dui.v + ? pMesh->elements[e].derivativeR_jplus1[dui.dofi] + : VectorType(0, 0, 0); + const VectorType derivativeR = derivativeR_j + derivativeR_jplus1; + + const VectorType &t1 = pMesh->elements[e].frame.t1; + const VectorType derivativeT1_j = dofi < 3 && &v_j == &dui.v + ? pMesh->elements[e].derivativeT1_j[dui.dofi] + : VectorType(0, 0, 0); + const VectorType derivativeT1_jplus1 = dofi < 3 && &v_jplus1 == &dui.v + ? pMesh->elements[e].derivativeT1_jplus1[dui.dofi] + : VectorType(0, 0, 0); + const VectorType derivativeT1 = derivativeT1_j + derivativeT1_jplus1; + + const VectorType derivativeOfRCrossT1 = computeDerivativeOfCrossProduct(r, + derivativeR, + t1, + derivativeT1); + const VectorType rCrossT1 = Cross(r, t1); + const double normOfRCrossT1 = rCrossT1.Norm(); + const double derivativeNormRCrossT1 = computeDerivativeOfNorm(rCrossT1, derivativeOfRCrossT1); + + const VectorType t2Deriv_firstTerm = -(rCrossT1 * derivativeNormRCrossT1) + / std::pow(normOfRCrossT1, 2); + const VectorType t2Deriv_secondTerm = derivativeOfRCrossT1 / normOfRCrossT1; + const VectorType t2Deriv = t2Deriv_firstTerm + t2Deriv_secondTerm; + + const double t2DerivNorm = t2Deriv.Norm(); + assert(std::isfinite(t2DerivNorm)); + const bool shouldBreak = mCurrentSimulationStep == 118 && (vi_jplus1 == 1 || vi_j == 1) + && dofi == 3; + return t2Deriv; +} + +VectorType DRMSimulationModel::computeDerivativeT3(const EdgeType &e, + const DifferentiateWithRespectTo &dui) const +{ + const Element &element = pMesh->elements[e]; + const VectorType &t1 = element.frame.t1; + const VectorType &t2 = element.frame.t2; + const VectorType t1CrossT2 = Cross(t1, t2); + const VertexType &v_j = *e.cV(0); + const size_t vi_j = pMesh->getIndex(v_j); + const VertexType &v_jplus1 = *e.cV(1); + const size_t vi_jplus1 = pMesh->getIndex(v_jplus1); + const VectorType derivativeT1_j = dui.dofi < 3 && &v_j == &dui.v + ? pMesh->elements[e].derivativeT1_j[dui.dofi] + : VectorType(0, 0, 0); + const VectorType derivativeT1_jplus1 = dui.dofi < 3 && &v_jplus1 == &dui.v + ? pMesh->elements[e].derivativeT1_jplus1[dui.dofi] + : VectorType(0, 0, 0); + const VectorType derivativeT1 = derivativeT1_j + derivativeT1_jplus1; + + // const VectorType derivativeOfT2 = computeDerivativeT2(e, dui); + // const VectorType derivativeT2_j = + // &v_j == &dui.v + // ? mesh->elements[e].derivativeT2_j[dui.dofi] + // : VectorType(0, 0, 0); + // const VectorType derivativeT2_jplus1 = + // &v_jplus1 == &dui.v + // ? mesh->elements[e].derivativeT2_jplus1[dui.dofi] + // : VectorType(0, 0, 0); + VectorType derivativeT2(0, 0, 0); + if (&v_j == &dui.v) { + derivativeT2 = pMesh->elements[e].derivativeT2_j[dui.dofi]; + } else if (&v_jplus1 == &dui.v) { + derivativeT2 = pMesh->elements[e].derivativeT2_jplus1[dui.dofi]; + } + + const VectorType derivativeT1CrossT2 = computeDerivativeOfCrossProduct(t1, + derivativeT1, + t2, + derivativeT2); + const double derivativeOfNormT1CrossT2 = computeDerivativeOfNorm(t1CrossT2, derivativeT1CrossT2); + const double normT1CrossT2 = t1CrossT2.Norm(); + + const VectorType t3Deriv_firstTerm = -(t1CrossT2 * derivativeOfNormT1CrossT2) + / std::pow(normT1CrossT2, 2); + const VectorType t3Deriv_secondTerm = derivativeT1CrossT2 / normT1CrossT2; + const VectorType t3Deriv = t3Deriv_firstTerm + t3Deriv_secondTerm; + + assert(std::isfinite(t3Deriv[0]) && std::isfinite(t3Deriv[1]) && std::isfinite(t3Deriv[2])); + return t3Deriv; +} + +double DRMSimulationModel::computeDerivativeTheta1(const EdgeType &e, + const VertexIndex &evi, + const VertexIndex &dwrt_evi, + const DoFType &dwrt_dofi) const +{ + const VertexType &v = *e.cV(evi); + const size_t vi = pMesh->getIndex(v); + const Element &element = pMesh->elements[e]; + const VectorType derivativeT1 = element.derivativeT1[dwrt_evi][dwrt_dofi]; + const VectorType derivativeT3 = element.derivativeT3[dwrt_evi][dwrt_dofi]; + const VectorType nDerivative = evi != dwrt_evi ? VectorType(0, 0, 0) + : pMesh->nodes[v].derivativeOfNormal[dwrt_dofi]; + const VectorType n = v.cN(); + const VectorType &t1 = element.frame.t1; + const VectorType &t3 = element.frame.t3; + const double theta1Derivative = derivativeT1 * Cross(t3, n) + + t1 * (Cross(derivativeT3, n) + Cross(t3, nDerivative)); + const bool shouldBreak = mCurrentSimulationStep == 118 && vi == 1 && dwrt_dofi == 3; + + return theta1Derivative; +} + +double DRMSimulationModel::computeDerivativeTheta2(const EdgeType &e, + const VertexIndex &evi, + const VertexIndex &dwrt_evi, + const DoFType &dwrt_dofi) const +{ + const VertexType &v = *e.cV(evi); + + const Element &element = pMesh->elements[e]; + const VectorType derivativeT2 = element.derivativeT2[dwrt_evi][dwrt_dofi]; + const VectorType derivativeT3 = element.derivativeT3[dwrt_evi][dwrt_dofi]; + + const VectorType n = v.cN(); + const VectorType &t2 = element.frame.t2; + const VectorType &t3 = element.frame.t3; + const VectorType nDerivative = dwrt_evi == evi ? pMesh->nodes[v].derivativeOfNormal[dwrt_dofi] + : VectorType(0, 0, 0); + const double theta2Derivative = derivativeT2 * Cross(t3, n) + + t2 * (Cross(derivativeT3, n) + Cross(t3, nDerivative)); + + return theta2Derivative; +} + +double DRMSimulationModel::computeTheta3(const EdgeType &e, const VertexType &v) +{ + const VertexIndex &vi = pMesh->nodes[v].vi; + // assert(e.cV(0) == &v || e.cV(1) == &v); + + const Element &elem = pMesh->elements[e]; + const EdgeIndex &ei = elem.ei; + const Element::LocalFrame &ef = elem.frame; + const VectorType &t1 = ef.t1; + const VectorType &n = v.cN(); + const Node &node = pMesh->nodes[v]; + const double &nR = node.nR; // TODO: This makes the function non-const. + // Should be refactored in the future + + double theta3; + if (&e == node.referenceElement) { + // Use nR as theta3 only for the first star edge + return nR; + } + std::vector incidentElementsIndices(node.incidentElements.size()); + for (int iei = 0; iei < incidentElementsIndices.size(); iei++) { + incidentElementsIndices[iei] = pMesh->getIndex(node.incidentElements[iei]); + } + assert(pMesh->getIndex(e) == ei); + assert(node.alphaAngles.contains(ei)); + const double alphaAngle = node.alphaAngles.at(elem.ei); + const EdgeType &refElem = *node.referenceElement; + const double rotationAngle = nR + alphaAngle; + + // const VectorType &t1_k = computeT1Vector(refElem); + const VectorType &t1_k = pMesh->elements[refElem].frame.t1; + const VectorType f1 = (t1_k - (n * (t1_k * n))).Normalize(); + vcg::Matrix44 rotationMatrix; + rotationMatrix.SetRotateRad(rotationAngle, n); + const double cosRotationAngle = cos(rotationAngle); + const double sinRotationAngle = sin(rotationAngle); + const VectorType f2 = (f1 * cosRotationAngle + Cross(n, f1) * sinRotationAngle).Normalize(); + const VectorType &t1Current = t1; + const VectorType f3 = Cross(t1Current, f2); + + Element &element = pMesh->elements[e]; + // Save for computing theta3Derivative + if (&v == e.cV(0)) { + element.f1_j = f1; + element.f2_j = f2; + element.f3_j = f3; + element.cosRotationAngle_j = cosRotationAngle; + element.sinRotationAngle_j = sinRotationAngle; + } else { + element.f1_jplus1 = f1; + element.f2_jplus1 = f2; + element.f3_jplus1 = f3; + element.cosRotationAngle_jplus1 = cosRotationAngle; + element.sinRotationAngle_jplus1 = sinRotationAngle; + } + theta3 = f3.dot(n); + + return theta3; +} + +double DRMSimulationModel::computeDerivativeTheta3(const EdgeType &e, + const VertexType &v, + const DifferentiateWithRespectTo &dui) const +{ + const Node &node = pMesh->nodes[v]; + const VertexIndex &vi = pMesh->nodes[v].vi; + const bool isRigidSupport = rigidSupports.contains(vi); + if (&e == node.referenceElement && !isRigidSupport) { + if (dui.dofi == DoF::Nr && &dui.v == &v) { + return 1; + } else { + return 0; + } + } + // assert(e.cV(0) == &v || e.cV(1) == &v); + + const Element &element = pMesh->elements[e]; + const Element::LocalFrame &ef = element.frame; + const VectorType &t1 = ef.t1; + const VectorType &n = v.cN(); + const DoFType &dofi = dui.dofi; + const VertexPointer &vp_j = e.cV(0); + const VertexPointer &vp_jplus1 = e.cV(1); + + double derivativeTheta3_dofi = 0; + if (isRigidSupport) { + const VectorType &t1Initial = computeT1Vector(pMesh->nodes[vp_j].initialLocation, + pMesh->nodes[vp_jplus1].initialLocation); + VectorType g1 = Cross(t1, t1Initial); + + const VectorType derivativeInitialT1_dofi(0, 0, 0); + const VectorType derivativeT1_j = dui.dofi < 3 && vp_j == &dui.v + ? element.derivativeT1_j[dui.dofi] + : VectorType(0, 0, 0); + const VectorType derivativeT1_jplus1 = dui.dofi < 3 && vp_jplus1 == &dui.v + ? element.derivativeT1_jplus1[dui.dofi] + : VectorType(0, 0, 0); + const VectorType derivativeT1_dofi = derivativeT1_j + derivativeT1_jplus1; + // VectorType derivativeT1_dofi(0, 0, 0); + // if (dui.dofi < 3) { + // if (&v_j == &dui.v) { + // derivativeT1_dofi = mesh->elements[e].derivativeT1_j[dui.dofi]; + // } else if (&v_jplus1 == &dui.v) { + // derivativeT1_dofi = + // mesh->elements[e].derivativeT1_jplus1[dui.dofi]; + // } + // } + + const VectorType derivativeG1_firstTerm = Cross(derivativeT1_dofi, t1Initial); + const VectorType derivativeG1_secondTerm = Cross(t1, derivativeInitialT1_dofi); + const VectorType derivativeG1 = derivativeG1_firstTerm + derivativeG1_secondTerm; + const VectorType derivativeNormal = &v == &dui.v && dui.dofi > 2 + ? node.derivativeOfNormal[dui.dofi] + : VectorType(0, 0, 0); + derivativeTheta3_dofi = derivativeG1 * n + g1 * derivativeNormal; + return derivativeTheta3_dofi; + } + EdgeType &refElem = *node.referenceElement; + const VectorType &t1_k = pMesh->elements[refElem].frame.t1; + VectorType f1, f2, f3; + double cosRotationAngle, sinRotationAngle; + if (e.cV(0) == &v) { + f1 = element.f1_j; + cosRotationAngle = element.cosRotationAngle_j; + sinRotationAngle = element.sinRotationAngle_j; + f2 = element.f2_j; + f3 = element.f3_j; + } else { + f1 = element.f1_jplus1; + cosRotationAngle = element.cosRotationAngle_jplus1; + sinRotationAngle = element.sinRotationAngle_jplus1; + f2 = element.f2_jplus1; + f3 = element.f3_jplus1; + } + const VectorType &t1_kplus1 = t1; + const VectorType f1Normalized = f1 / f1.Norm(); + + VectorType derivativeF1(0, 0, 0); + VectorType derivativeF2(0, 0, 0); + VectorType derivativeF3(0, 0, 0); + if (dui.dofi < 3) { + const VectorType derivativeT1_kplus1_j = vp_j == &dui.v ? element.derivativeT1_j[dui.dofi] + : VectorType(0, 0, 0); + const VectorType derivativeT1_kplus1_jplus1 = vp_jplus1 == &dui.v + ? element.derivativeT1_jplus1[dui.dofi] + : VectorType(0, 0, 0); + const VectorType derivativeT1_kplus1 = derivativeT1_kplus1_j + derivativeT1_kplus1_jplus1; + + const VectorType derivativeT1_k_j = refElem.cV(0) == &dui.v + ? pMesh->elements[refElem].derivativeT1_j[dui.dofi] + : VectorType(0, 0, 0); + const VectorType derivativeT1_k_jplus1 + = refElem.cV(1) == &dui.v ? pMesh->elements[refElem].derivativeT1_jplus1[dui.dofi] + : VectorType(0, 0, 0); + const VectorType derivativeT1_k = derivativeT1_k_j + derivativeT1_k_jplus1; + + derivativeF1 = derivativeT1_k - (n * (derivativeT1_k * n)); + + const double f1Norm = f1.Norm(); + const VectorType derivativeF1Normalized = -f1 * (f1 * derivativeF1) + / (f1Norm * f1Norm * f1Norm) + + derivativeF1 / f1Norm; + + derivativeF2 = derivativeF1Normalized * cosRotationAngle + + Cross(n, derivativeF1Normalized) * sinRotationAngle; + const VectorType derivativeF3_firstTerm = Cross(derivativeT1_kplus1, f2); + const VectorType derivativeF3_secondTerm = Cross(t1_kplus1, derivativeF2); + derivativeF3 = derivativeF3_firstTerm + derivativeF3_secondTerm; + derivativeTheta3_dofi = derivativeF3 * n; + + } else if (dui.dofi == DoF::Nr && &dui.v == &v) { + derivativeF2 = f1Normalized * (-sinRotationAngle) + + Cross(n, f1Normalized) * cosRotationAngle; + derivativeF3 = Cross(t1_kplus1, derivativeF2); + derivativeTheta3_dofi = derivativeF3 * n; + } else { // 2edge) { + const Element &element = pMesh->elements[e]; + const SimulationMesh::VertexType &ev_j = *e.cV(0); + const SimulationMesh::VertexType &ev_jplus1 = *e.cV(1); + const Element::LocalFrame &ef = element.frame; + const VectorType t3CrossN_j = Cross(ef.t3, ev_j.cN()); + const VectorType t3CrossN_jplus1 = Cross(ef.t3, ev_jplus1.cN()); + const double theta1_j = ef.t1.dot(t3CrossN_j); + const double theta1_jplus1 = ef.t1.dot(t3CrossN_jplus1); + const double theta2_j = ef.t2.dot(t3CrossN_j); + const double theta2_jplus1 = ef.t2.dot(t3CrossN_jplus1); + const double theta3_j = computeTheta3(e, ev_j); + const double theta3_jplus1 = computeTheta3(e, ev_jplus1); + + const EdgeIndex ei = pMesh->getIndex(e); + const double e_k = element.length - element.initialLength; + const double axialPE = pow(e_k, 2) * element.material.youngsModulus * element.A + / (2 * element.initialLength); + const double theta1Diff = theta1_jplus1 - theta1_j; + const double torsionalPE = element.G * element.J * pow(theta1Diff, 2) + / (2 * element.initialLength); + const double firstBendingPE_inBracketsTerm = 4 * pow(theta2_j, 2) + + 4 * theta2_j * theta2_jplus1 + + 4 * pow(theta2_jplus1, 2); + const double firstBendingPE = firstBendingPE_inBracketsTerm * element.material.youngsModulus + * element.I2 / (2 * element.initialLength); + const double secondBendingPE_inBracketsTerm = 4 * pow(theta3_j, 2) + + 4 * theta3_j * theta3_jplus1 + + 4 * pow(theta3_jplus1, 2); + const double secondBendingPE = secondBendingPE_inBracketsTerm + * element.material.youngsModulus * element.I3 + / (2 * element.initialLength); + + totalInternalPotentialEnergy += axialPE + torsionalPE + firstBendingPE + secondBendingPE; + int i = 0; + i++; + } + + return totalInternalPotentialEnergy; +} + +double DRMSimulationModel::computeTotalPotentialEnergy() +{ + double totalExternalPotentialEnergy = 0; + for (const SimulationMesh::VertexType &v : pMesh->vert) { + const Node &node = pMesh->nodes[v]; + if (!node.force.hasExternalForce()) { + continue; + } + const auto nodeDisplacement = v.cP() - node.initialLocation; + const SimulationMesh::CoordType externalForce(node.force.external[0], + node.force.external[1], + node.force.external[2]); + totalExternalPotentialEnergy += externalForce.dot(nodeDisplacement); + } + + const double totalInternalPotentialEnergy = computeTotalInternalPotentialEnergy(); + + return totalInternalPotentialEnergy - totalExternalPotentialEnergy; +} + +void DRMSimulationModel::updateResidualForcesOnTheFly( + const std::unordered_map> &fixedVertices) +{ + // std::vector internalForcesParallel(mesh->vert.size()); + + std::vector>> internalForcesContributionsFromEachEdge( + pMesh->EN(), std::vector>(4, {-1, Vector6d()})); + // omp_lock_t writelock; + // omp_init_lock(&writelock); + //#pragma omp parallel for schedule(static) num_threads(8) + for (int ei = 0; ei < pMesh->EN(); ei++) { + const EdgeType &e = pMesh->edge[ei]; + const SimulationMesh::VertexType &ev_j = *e.cV(0); + const SimulationMesh::VertexType &ev_jplus1 = *e.cV(1); + const Element &element = pMesh->elements[e]; + const Element::LocalFrame &ef = element.frame; + const VectorType t3CrossN_j = Cross(ef.t3, ev_j.cN()); + const VectorType t3CrossN_jplus1 = Cross(ef.t3, ev_jplus1.cN()); + const double theta1_j = ef.t1.dot(t3CrossN_j); + const double theta1_jplus1 = ef.t1.dot(t3CrossN_jplus1); + const double theta2_j = ef.t2.dot(t3CrossN_j); + const double theta2_jplus1 = ef.t2.dot(t3CrossN_jplus1); + const double theta3_j = computeTheta3(e, ev_j); + const double theta3_jplus1 = computeTheta3(e, ev_jplus1); + // element.rotationalDisplacements_j.theta1 = theta1_j; + // element.rotationalDisplacements_jplus1.theta1 = theta1_jplus1; + // element.rotationalDisplacements_j.theta2 = theta2_j; + // element.rotationalDisplacements_jplus1.theta2 = theta2_jplus1; + // element.rotationalDisplacements_j.theta3 = theta3_j; + // element.rotationalDisplacements_jplus1.theta3 = theta3_jplus1; + std::vector> internalForcesContributionFromThisEdge(4, + {-1, + Vector6d()}); + for (VertexIndex evi = 0; evi < 2; evi++) { + const SimulationMesh::VertexType &ev = *e.cV(evi); + const Node &edgeNode = pMesh->nodes[ev]; + internalForcesContributionFromThisEdge[evi].first = edgeNode.vi; + + const VertexPointer &rev_j = edgeNode.referenceElement->cV(0); + const VertexPointer &rev_jplus1 = edgeNode.referenceElement->cV(1); + // refElemOtherVertex can be precomputed + const VertexPointer &refElemOtherVertex = rev_j == &ev ? rev_jplus1 : rev_j; + const Node &refElemOtherVertexNode = pMesh->nodes[refElemOtherVertex]; + if (edgeNode.referenceElement != &e) { + internalForcesContributionFromThisEdge[evi + 2].first = refElemOtherVertexNode.vi; + } + const size_t vi = edgeNode.vi; + // #pragma omp parallel for schedule(static) num_threads(6) + for (DoFType dofi = DoF::Ux; dofi < DoF::NumDoF; dofi++) { + const bool isDofConstrainedFor_ev = fixedVertices.contains(edgeNode.vi) + && fixedVertices.at(edgeNode.vi).contains(dofi); + const bool shouldBreak = edgeNode.vi == 0 && dofi == 5; + if (!isDofConstrainedFor_ev) { + DifferentiateWithRespectTo dui{ev, dofi}; + // Axial force computation + const double e_k = element.length - element.initialLength; + const double e_kDeriv = computeDerivativeElementLength(e, dui); + const double axialForce_dofi = e_kDeriv * e_k * element.rigidity.axial; + + // Torsional force computation + const double theta1_j_deriv = computeDerivativeTheta1(e, 0, evi, dofi); + const double theta1_jplus1_deriv = computeDerivativeTheta1(e, 1, evi, dofi); + const double theta1Diff = theta1_jplus1 - theta1_j; + const double theta1DiffDerivative = theta1_jplus1_deriv - theta1_j_deriv; + const double torsionalForce_dofi = theta1DiffDerivative * theta1Diff + * element.rigidity.torsional; + + // First bending force computation + ////theta2_j derivative + const double theta2_j_deriv = computeDerivativeTheta2(e, 0, evi, dofi); + ////theta2_jplus1 derivative + const double theta2_jplus1_deriv = computeDerivativeTheta2(e, 1, evi, dofi); + ////1st in bracket term + const double firstBendingForce_inBracketsTerm_0 = theta2_j_deriv * 2 * theta2_j; + ////2nd in bracket term + const double firstBendingForce_inBracketsTerm_1 = theta2_jplus1_deriv + * theta2_j; + ////3rd in bracket term + const double firstBendingForce_inBracketsTerm_2 = theta2_j_deriv + * theta2_jplus1; + ////4th in bracket term + const double firstBendingForce_inBracketsTerm_3 = 2 * theta2_jplus1_deriv + * theta2_jplus1; + // 3rd term computation + const double firstBendingForce_inBracketsTerm + = firstBendingForce_inBracketsTerm_0 + firstBendingForce_inBracketsTerm_1 + + firstBendingForce_inBracketsTerm_2 + firstBendingForce_inBracketsTerm_3; + const double firstBendingForce_dofi = firstBendingForce_inBracketsTerm + * element.rigidity.firstBending; + + // Second bending force computation + ////theta2_j derivative + const double theta3_j_deriv = computeDerivativeTheta3(e, ev_j, dui); + ////theta2_jplus1 derivative + const double theta3_jplus1_deriv = computeDerivativeTheta3(e, ev_jplus1, dui); + ////1st in bracket term + const double secondBendingForce_inBracketsTerm_0 = theta3_j_deriv * 2 + * theta3_j; + ////2nd in bracket term + const double secondBendingForce_inBracketsTerm_1 = theta3_jplus1_deriv + * theta3_j; + ////3rd in bracket term + const double secondBendingForce_inBracketsTerm_2 = theta3_j_deriv + * theta3_jplus1; + ////4th in bracket term + const double secondBendingForce_inBracketsTerm_3 = 2 * theta3_jplus1_deriv + * theta3_jplus1; + // 3rd term computation + const double secondBendingForce_inBracketsTerm + = secondBendingForce_inBracketsTerm_0 + secondBendingForce_inBracketsTerm_1 + + secondBendingForce_inBracketsTerm_2 + + secondBendingForce_inBracketsTerm_3; + double secondBendingForce_dofi = secondBendingForce_inBracketsTerm + * element.rigidity.secondBending; + internalForcesContributionFromThisEdge[evi].second[dofi] + = axialForce_dofi + firstBendingForce_dofi + secondBendingForce_dofi + + torsionalForce_dofi; + } + if (edgeNode.referenceElement != &e) { + const bool isDofConstrainedFor_refElemOtherVertex + = fixedVertices.contains(refElemOtherVertexNode.vi) + && fixedVertices.at(refElemOtherVertexNode.vi).contains(dofi); + if (!isDofConstrainedFor_refElemOtherVertex) { + DifferentiateWithRespectTo dui{*refElemOtherVertex, dofi}; + ////theta3_j derivative + const double theta3_j_deriv = computeDerivativeTheta3(e, ev_j, dui); + ////theta3_jplus1 derivative + const double theta3_jplus1_deriv = computeDerivativeTheta3(e, + ev_jplus1, + dui); + ////1st in bracket term + const double secondBendingForce_inBracketsTerm_0 = theta3_j_deriv * 2 + * theta3_j; + ////2nd in bracket term + const double secondBendingForce_inBracketsTerm_1 = theta3_jplus1_deriv + * theta3_j; + ////3rd in bracket term + const double secondBendingForce_inBracketsTerm_2 = theta3_j_deriv + * theta3_jplus1; + ////4th in bracket term + const double secondBendingForce_inBracketsTerm_3 = theta3_jplus1_deriv * 2 + * theta3_jplus1; + + // 4th term computation + const double secondBendingForce_inBracketsTerm + = secondBendingForce_inBracketsTerm_0 + + secondBendingForce_inBracketsTerm_1 + + secondBendingForce_inBracketsTerm_2 + + secondBendingForce_inBracketsTerm_3; + const double secondBendingForce_dofi = secondBendingForce_inBracketsTerm + * element.rigidity.secondBending; + internalForcesContributionFromThisEdge[evi + 2].second[dofi] + = secondBendingForce_dofi; + } + } + } + } + internalForcesContributionsFromEachEdge[ei] = internalForcesContributionFromThisEdge; + } + + //#pragma omp parallel for schedule(static) num_threads(8) + + for (size_t vi = 0; vi < pMesh->VN(); vi++) { + Node::Forces &force = pMesh->nodes[vi].force; + // Vector6d ext = force.external; + // if (mCurrentSimulationStep <= 100) { + // ext[3] = 0; + // ext[4] = 0; + // } + force.residual = force.external; + force.internal = 0; + } + double totalResidualForcesNorm = 0; + for (size_t ei = 0; ei < pMesh->EN(); ei++) { + for (int i = 0; i < 4; i++) { + std::pair internalForcePair + = internalForcesContributionsFromEachEdge[ei][i]; + int vi = internalForcePair.first; + if (i > 1 && vi == -1) { + continue; + } + Node::Forces &force = pMesh->nodes[vi].force; + force.internal = force.internal + internalForcePair.second; + force.residual = force.residual + (internalForcePair.second * -1); + } + } + Vector6d sumOfResidualForces(0); + for (size_t vi = 0; vi < pMesh->VN(); vi++) { + Node::Forces &force = pMesh->nodes[vi].force; + const Vector6d &nodeResidualForce = force.residual; + // sumOfResidualForces = sumOfResidualForces + nodeResidualForce; + const double residualForceNorm = nodeResidualForce.norm(); + const bool shouldTrigger = std::isnan(residualForceNorm); + totalResidualForcesNorm += residualForceNorm; + } + pMesh->previousTotalResidualForcesNorm = pMesh->totalResidualForcesNorm; + pMesh->totalResidualForcesNorm = totalResidualForcesNorm; + // mesh->totalResidualForcesNorm = sumOfResidualForces.norm(); + + // plotYValues.push_back(totalResidualForcesNorm); + // auto xPlot = matplot::linspace(0, plotYValues.size(), plotYValues.size()); + // plotHandle = matplot::scatter(xPlot, plotYValues); +} + +void DRMSimulationModel::updateNodalExternalForces( + const std::unordered_map &nodalForces, + const std::unordered_map> &fixedVertices) +{ + externalMomentsNorm = 0; + for (const std::pair &nodalForce : nodalForces) { + const VertexIndex nodeIndex = nodalForce.first; + const bool isNodeConstrained = fixedVertices.contains(nodeIndex); + Node &node = pMesh->nodes[nodeIndex]; + Vector6d nodalExternalForce(0); + for (DoFType dofi = 0; dofi < 6; dofi++) { + const bool isDofConstrained = isNodeConstrained + && fixedVertices.at(nodeIndex).contains(dofi); + if (isDofConstrained) { + continue; + } + nodalExternalForce[dofi] = nodalForce.second[dofi]; + } + externalMomentsNorm += std::sqrt(pow(nodalExternalForce[3], 2) + + pow(nodalExternalForce[4], 2) + + pow(nodalExternalForce[5], 2)); + + /* + * The external moments are given as a rotation around an axis. + * In this implementation we model moments as rotation of the normal vector + * and because of that we need to transform the moments. + */ + + if (externalMomentsNorm != 0) { + VectorType momentAxis(nodalExternalForce[3], + nodalExternalForce[4], + nodalExternalForce[5]); // rotation around this vector + VectorType transformedVector = vcg::RotationMatrix(VectorType(0, 0, 1), + vcg::math::ToRad(-90.0)) + * momentAxis; + nodalExternalForce[3] = transformedVector[0]; + nodalExternalForce[4] = transformedVector[1]; + nodalExternalForce[5] = transformedVector[2]; + // node.nR = transformedVector[2]; + } + + node.force.external = nodalExternalForce; + } +} + +void DRMSimulationModel::updateResidualForces() +{ + pMesh->totalResidualForcesNorm = 0; + for (const VertexType &v : pMesh->vert) { + Node &node = pMesh->nodes[v]; + node.force.residual = node.force.external - node.force.internal; + const double residualForceNorm = (node.force.residual).norm(); + pMesh->totalResidualForcesNorm += residualForceNorm; + } +} + +void DRMSimulationModel::computeRigidSupports() +{ + for (const VertexType &v : pMesh->vert) { + const VertexIndex vi = pMesh->nodes[v].vi; + const bool isVertexConstrained = constrainedVertices.contains(vi); + if (isVertexConstrained) { + auto constrainedDoFType = constrainedVertices.at(vi); + const bool hasAllDoFTypeConstrained = constrainedDoFType.contains(DoF::Ux) + && constrainedDoFType.contains(DoF::Uy) + && constrainedDoFType.contains(DoF::Uz) + && constrainedDoFType.contains(DoF::Nx) + && constrainedDoFType.contains(DoF::Ny) + && constrainedDoFType.contains(DoF::Nr); + if (hasAllDoFTypeConstrained) { + rigidSupports.insert(vi); + } + } + } +} + +void DRMSimulationModel::updateNormalDerivatives() +{ + for (const VertexType &v : pMesh->vert) { + for (DoFType dofi = DoF::Nx; dofi < DoF::NumDoF; dofi++) { + DifferentiateWithRespectTo dui{v, dofi}; + pMesh->nodes[v].derivativeOfNormal[dofi] = computeDerivativeOfNormal(v, dui); + } + } +} + +void DRMSimulationModel::updateT1Derivatives() +{ + for (const EdgeType &e : pMesh->edge) { + for (DoFType dofi = DoF::Ux; dofi < DoF::Nx; dofi++) { + DifferentiateWithRespectTo dui_v0{*e.cV(0), dofi}; + Element &element = pMesh->elements[e]; + element.derivativeT1_j[dofi] = computeDerivativeT1(e, dui_v0); + DifferentiateWithRespectTo dui_v1{*e.cV(1), dofi}; + element.derivativeT1_jplus1[dofi] = computeDerivativeT1(e, dui_v1); + + element.derivativeT1[0][dofi] = element.derivativeT1_j[dofi]; + element.derivativeT1[1][dofi] = element.derivativeT1_jplus1[dofi]; + } + } +} + +void DRMSimulationModel::updateT2Derivatives() +{ + for (const EdgeType &e : pMesh->edge) { + for (DoFType dofi = DoF::Ux; dofi < DoF::NumDoF; dofi++) { + DifferentiateWithRespectTo dui_v0{*e.cV(0), dofi}; + pMesh->elements[e].derivativeT2_j[dofi] = computeDerivativeT2(e, dui_v0); + DifferentiateWithRespectTo dui_v1{*e.cV(1), dofi}; + pMesh->elements[e].derivativeT2_jplus1[dofi] = computeDerivativeT2(e, dui_v1); + + Element &element = pMesh->elements[e]; + element.derivativeT2[0][dofi] = element.derivativeT2_j[dofi]; + element.derivativeT2[1][dofi] = element.derivativeT2_jplus1[dofi]; + } + } +} + +void DRMSimulationModel::updateT3Derivatives() +{ + for (const EdgeType &e : pMesh->edge) { + for (DoFType dofi = DoF::Ux; dofi < DoF::NumDoF; dofi++) { + DifferentiateWithRespectTo dui_v0{*e.cV(0), dofi}; + Element &element = pMesh->elements[e]; + element.derivativeT3_j[dofi] = computeDerivativeT3(e, dui_v0); + DifferentiateWithRespectTo dui_v1{*e.cV(1), dofi}; + element.derivativeT3_jplus1[dofi] = computeDerivativeT3(e, dui_v1); + + element.derivativeT3[0][dofi] = element.derivativeT3_j[dofi]; + element.derivativeT3[1][dofi] = element.derivativeT3_jplus1[dofi]; + } + } +} + +void DRMSimulationModel::updateRDerivatives() +{ + for (const EdgeType &e : pMesh->edge) { + for (DoFType dofi = DoF::Nx; dofi < DoF::NumDoF; dofi++) { + DifferentiateWithRespectTo dui_v0{*e.cV(0), dofi}; + pMesh->elements[e].derivativeR_j[dofi] = computeDerivativeOfR(e, dui_v0); + DifferentiateWithRespectTo dui_v1{*e.cV(1), dofi}; + pMesh->elements[e].derivativeR_jplus1[dofi] = computeDerivativeOfR(e, dui_v1); + } + } +} + +void DRMSimulationModel::updateElementalLengths() +{ + pMesh->updateElementalLengths(); +} + +DRMSimulationModel::DRMSimulationModel() {} + +void DRMSimulationModel::updateNodalMasses() +{ + const double gamma = 0.8; + for (VertexType &v : pMesh->vert) { + const size_t vi = pMesh->getIndex(v); + double translationalSumSk = 0; + double rotationalSumSk_I2 = 0; + double rotationalSumSk_I3 = 0; + double rotationalSumSk_J = 0; + for (const EdgePointer &ep : pMesh->nodes[v].incidentElements) { + const size_t ei = pMesh->getIndex(ep); + const Element &elem = pMesh->elements[ep]; + const double SkTranslational = elem.material.youngsModulus * elem.A / elem.length; + translationalSumSk += SkTranslational; + const double lengthToThe3 = std::pow(elem.length, 3); + const long double SkRotational_I2 + = elem.material.youngsModulus * elem.I2 + / lengthToThe3; // TODO: I2->t2,I3->t3,t1->polar inertia + const long double SkRotational_I3 + = elem.material.youngsModulus * elem.I3 + / lengthToThe3; // TODO: I2->t2,I3->t3,t1->polar inertia + const long double SkRotational_J = elem.material.youngsModulus * elem.J + / lengthToThe3; // TODO: I2->t2,I3->t3,t1->polar inertia + rotationalSumSk_I2 += SkRotational_I2; + rotationalSumSk_I3 += SkRotational_I3; + rotationalSumSk_J += SkRotational_J; + assert(rotationalSumSk_I2 != 0); + assert(rotationalSumSk_I3 != 0); + assert(rotationalSumSk_J != 0); + } + pMesh->nodes[v].mass.translational = gamma * pow(mSettings.Dtini, 2) * 2 + * translationalSumSk; + pMesh->nodes[v].mass.rotationalI2 = gamma * pow(mSettings.Dtini, 2) * 8 + * rotationalSumSk_I2; + pMesh->nodes[v].mass.rotationalI3 = gamma * pow(mSettings.Dtini, 2) * 8 + * rotationalSumSk_I3; + pMesh->nodes[v].mass.rotationalJ = gamma * pow(mSettings.Dtini, 2) * 8 * rotationalSumSk_J; + + assert(std::pow(mSettings.Dtini, 2.0) * translationalSumSk + / pMesh->nodes[v].mass.translational + < 2); + assert(std::pow(mSettings.Dtini, 2.0) * rotationalSumSk_I2 + / pMesh->nodes[v].mass.rotationalI2 + < 2); + assert(std::pow(mSettings.Dtini, 2.0) * rotationalSumSk_I3 + / pMesh->nodes[v].mass.rotationalI3 + < 2); + assert(std::pow(mSettings.Dtini, 2.0) * rotationalSumSk_J / pMesh->nodes[v].mass.rotationalJ + < 2); + } +} + +void DRMSimulationModel::updateNodalAccelerations() +{ + for (VertexType &v : pMesh->vert) { + Node &node = pMesh->nodes[v]; + const VertexIndex vi = pMesh->getIndex(v); + for (DoFType dofi = DoF::Ux; dofi < DoF::NumDoF; dofi++) { + if (dofi == DoF::Ux || dofi == DoF::Uy || dofi == DoF::Uz) { + node.acceleration[dofi] = node.force.residual[dofi] / node.mass.translational; + } else if (dofi == DoF::Nx) { + node.acceleration[dofi] = node.force.residual[dofi] / node.mass.rotationalJ; + } else if (dofi == DoF::Ny) { + node.acceleration[dofi] = node.force.residual[dofi] / node.mass.rotationalI3; + } else if (dofi == DoF::Nr) { + node.acceleration[dofi] = node.force.residual[dofi] / node.mass.rotationalI2; + } + } + } +} + +void DRMSimulationModel::updateNodalVelocities() +{ + for (VertexType &v : pMesh->vert) { + const VertexIndex vi = pMesh->getIndex(v); + Node &node = pMesh->nodes[v]; + node.velocity = node.velocity + node.acceleration * Dt; + } + updateKineticEnergy(); +} + +void DRMSimulationModel::updateNodalDisplacements() +{ + for (VertexType &v : pMesh->vert) { + Node &node = pMesh->nodes[v]; + node.displacements = node.displacements + node.velocity * Dt; + // if (mSettings.beVerbose) { + // std::cout << "Node " << node.vi << ":" << endl; + // std::cout << node.displacements[0] << " " << node.displacements[0] + // << " " + // << node.displacements[1] << " " << node.displacements[2] + // << " " + // << node.displacements[3] << " " << node.displacements[4] + // << " " + // << node.displacements[5] << std::endl; + // } + } +} + +void DRMSimulationModel::updateNodePosition( + VertexType &v, const std::unordered_map> &fixedVertices) +{ + Node &node = pMesh->nodes[v]; + const VertexIndex &vi = pMesh->nodes[v].vi; + + VectorType displacementVector(0, 0, 0); + if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(0)) { + displacementVector += VectorType(node.displacements[0], 0, 0); + } + if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(1)) { + displacementVector += VectorType(0, node.displacements[1], 0); + } + if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(2)) { + displacementVector += VectorType(0, 0, node.displacements[2]); + } + v.P() = node.initialLocation + displacementVector; + if (shouldApplyInitialDistortion && mCurrentSimulationStep < 100) { + //TODO:The initial displacement should depend on the model and should only be applied if the forced displacements applied are out of plane + VectorType desiredInitialDisplacement(0.0015, 0.0015, 0.0015); + v.P() = v.P() + desiredInitialDisplacement; + } +} + +void DRMSimulationModel::updateNodeNormal( + VertexType &v, const std::unordered_map> &fixedVertices) +{ + Node &node = pMesh->nodes[v]; + const VertexIndex &vi = node.vi; + // if (vi == 1) { + // std::cout << "PRE:" << mesh->vert[1].N()[0] << " " << + // mesh->vert[1].N()[1] + // << " " << mesh->vert[1].N()[2] << std::endl; + // } + VectorType normalDisplacementVector(0, 0, 0); + if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(3)) { + normalDisplacementVector += VectorType(node.displacements[3], 0, 0); + } + if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(4)) { + normalDisplacementVector += VectorType(0, node.displacements[4], 0); + } + const double nxnyMagnitudePre = std::pow(v.N()[0], 2) + std::pow(v.N()[1], 2); + v.N() = node.initialNormal + normalDisplacementVector; + const double &nx = v.N()[0], ny = v.N()[1], nz = v.N()[2]; + const double nxnyMagnitude = std::pow(nx, 2) + std::pow(ny, 2); + VectorType n = v.N(); + const bool shouldBreak = mCurrentSimulationStep == 118 && vi == 3; + if (nxnyMagnitude > 1) { + VectorType newNormal(nx / std::sqrt(nxnyMagnitude), ny / std::sqrt(nxnyMagnitude), 0); + v.N() = newNormal; + + /*If an external moment caused the normal to lay on the xy plane this means + * that in order to disable its effect a greater internal force is needed + * than what is possible (the constraint on the z of the normals imposes a + * constraint on the maximum internal force). Because of that the + * totalResidualForcesNorm can't drop below the magnitude of external moment + * applied on vertex vi. In order to allow termination of the simulation + * when the described phenomenon happens we allow the termination of the + * algorithm if the kinetic energy of the system drops below the set + * threshold. + * */ + const bool viHasMoments = node.force.external[3] != 0 || node.force.external[4] != 0; + if (!checkedForMaximumMoment && viHasMoments) { + mSettings.shouldUseTranslationalKineticEnergyThreshold = true; + if (mSettings.beVerbose) { + std::cout << "Maximum moment reached.The Kinetic energy of the system will " + "be used as a convergence criterion" + << std::endl; + } + checkedForMaximumMoment = true; + } + + } else { + const double nzSquared = 1.0 - nxnyMagnitude; + const double nz = std::sqrt(nzSquared); + VectorType newNormal(nx, ny, nz); + v.N() = newNormal; + } + + // if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(DoF::Nr)) { + if (!rigidSupports.contains(vi)) { + node.nR = node.displacements[5]; + } else { + const EdgePointer &refElem = node.referenceElement; + const VectorType &refT1 = pMesh->elements[refElem].frame.t1; + + const VectorType &t1Initial = computeT1Vector(pMesh->nodes[refElem->cV(0)].initialLocation, + pMesh->nodes[refElem->cV(1)].initialLocation); + VectorType g1 = Cross(refT1, t1Initial); + node.nR = g1.dot(v.cN()); + int i = 0; + i++; + } + // if (vi == 1) { + // std::cout << "AFTER:" << mesh->vert[1].N()[0] << " " << + // mesh->vert[1].N()[1] + // << " " << mesh->vert[1].N()[2] << std::endl; + // } +} + +void DRMSimulationModel::applyDisplacements( + const std::unordered_map> &fixedVertices) +{ + for (VertexType &v : pMesh->vert) { + updateNodePosition(v, fixedVertices); + updateNodeNormal(v, fixedVertices); + } + updateElementalFrames(); + if (mSettings.shouldDraw) { + pMesh->updateEigenEdgeAndVertices(); + } +} + +void DRMSimulationModel::updateElementalFrames() +{ + for (const EdgeType &e : pMesh->edge) { + const VectorType elementNormal = (e.cV(0)->cN() + e.cV(1)->cN()).Normalize(); + pMesh->elements[e].frame = computeElementFrame(e.cP(0), e.cP(1), elementNormal); + } +} + +void DRMSimulationModel::applyForcedDisplacements( + const std::unordered_map nodalForcedDisplacements) +{ + const int gradualDisplacementSteps = 500; + for (const std::pair vertexIndexDisplacementPair : + nodalForcedDisplacements) { + const VertexIndex vi = vertexIndexDisplacementPair.first; + const Eigen::Vector3d vertexDisplacement = vertexIndexDisplacementPair.second; + Node &node = pMesh->nodes[vi]; + VectorType displacementVector(vertexDisplacement(0), + vertexDisplacement(1), + vertexDisplacement(2)); + if (mCurrentSimulationStep < gradualDisplacementSteps) { + displacementVector *= mCurrentSimulationStep + / static_cast(gradualDisplacementSteps); + } + pMesh->vert[vi].P() = node.initialLocation + displacementVector; + node.displacements = Vector6d({displacementVector[0], + displacementVector[1], + displacementVector[2], + node.displacements[3], + node.displacements[4], + node.displacements[5]}); + } + + if (mSettings.shouldDraw) { + pMesh->updateEigenEdgeAndVertices(); + } +} + +void DRMSimulationModel::applyForcedNormals( + const std::unordered_map nodalForcedRotations) +{ + for (const std::pair vertexIndexDesiredNormalPair : + nodalForcedRotations) { + const VertexIndex vi = vertexIndexDesiredNormalPair.first; + + Node &node = pMesh->nodes[vi]; + pMesh->vert[vi].N() = vertexIndexDesiredNormalPair.second; + node.displacements = Vector6d( + {node.displacements[0], + node.displacements[1], + node.displacements[2], + vertexIndexDesiredNormalPair.second[0] - node.initialNormal[0], + vertexIndexDesiredNormalPair.second[1] - node.initialNormal[1], + node.displacements[5]}); + } +} + +// NOTE: Is this correct? Should the kinetic energy be computed like that? +void DRMSimulationModel::updateKineticEnergy() +{ + pMesh->previousTotalKineticEnergy = pMesh->currentTotalKineticEnergy; + pMesh->currentTotalKineticEnergy = 0; + pMesh->currentTotalTranslationalKineticEnergy = 0; + for (const VertexType &v : pMesh->vert) { + Node &node = pMesh->nodes[v]; + node.kineticEnergy = 0; + + const double translationalVelocityNorm = std::sqrt(std::pow(node.velocity[0], 2) + + std::pow(node.velocity[1], 2) + + std::pow(node.velocity[2], 2)); + const double nodeTranslationalKineticEnergy = 0.5 * node.mass.translational + * pow(translationalVelocityNorm, 2); + + const double nodeRotationalKineticEnergy + = 0.5 + * (node.mass.rotationalJ * pow(node.velocity[3], 2) + + +node.mass.rotationalI3 * pow(node.velocity[4], 2) + + +node.mass.rotationalI2 * pow(node.velocity[5], 2)); + + node.kineticEnergy += nodeTranslationalKineticEnergy /*+ nodeRotationalKineticEnergy*/; + assert(node.kineticEnergy < 1e15); + + pMesh->currentTotalKineticEnergy += node.kineticEnergy; + pMesh->currentTotalTranslationalKineticEnergy += nodeTranslationalKineticEnergy; + } + // assert(mesh->currentTotalKineticEnergy < 100000000000000); +} + +void DRMSimulationModel::resetVelocities() +{ + for (const VertexType &v : pMesh->vert) { + pMesh->nodes[v].velocity = 0; + // mesh->nodes[v].force.residual * 0.5 * Dt / + // mesh->nodes[v].mass; // NOTE: Do I reset the previous + // velocity? + // reset + // current to 0 or this? + } + updateKineticEnergy(); +} + +void DRMSimulationModel::updatePositionsOnTheFly( + const std::unordered_map> &fixedVertices) +{ + const double gamma = 0.8; + for (VertexType &v : pMesh->vert) { + double translationalSumSk = 0; + double rotationalSumSk_I2 = 0; + double rotationalSumSk_I3 = 0; + double rotationalSumSk_J = 0; + for (const EdgePointer &ep : pMesh->nodes[v].incidentElements) { + const Element &elem = pMesh->elements[ep]; + const double SkTranslational = elem.material.youngsModulus * elem.A / elem.length; + translationalSumSk += SkTranslational; + const double lengthToThe3 = std::pow(elem.length, 3); + const double SkRotational_I2 = elem.material.youngsModulus * elem.I2 + / lengthToThe3; // TODO: I2->t2,I3->t3,t1->polar inertia + const double SkRotational_I3 = elem.material.youngsModulus * elem.I3 + / lengthToThe3; // TODO: I2->t2,I3->t3,t1->polar inertia + const double SkRotational_J = elem.material.youngsModulus * elem.J + / lengthToThe3; // TODO: I2->t2,I3->t3,t1->polar inertia + rotationalSumSk_I2 += SkRotational_I2; + rotationalSumSk_I3 += SkRotational_I3; + rotationalSumSk_J += SkRotational_J; + // assert(rotationalSumSk_I2 != 0); + // assert(rotationalSumSk_I3 != 0); + // assert(rotationalSumSk_J != 0); + } + pMesh->nodes[v].mass.translational = gamma * pow(mSettings.Dtini, 2) * 2 + * translationalSumSk; + pMesh->nodes[v].mass.rotationalI2 = gamma * pow(mSettings.Dtini, 2) * 8 + * rotationalSumSk_I2; + pMesh->nodes[v].mass.rotationalI3 = gamma * pow(mSettings.Dtini, 2) * 8 + * rotationalSumSk_I3; + pMesh->nodes[v].mass.rotationalJ = gamma * pow(mSettings.Dtini, 2) * 8 * rotationalSumSk_J; + + // assert(std::pow(mSettings.Dtini, 2.0) * translationalSumSk / + // mesh->nodes[v].translationalMass < + // 2); + // assert(std::pow(mSettings.Dtini, 2.0) * rotationalSumSk_I2 / + // mesh->nodes[v].rotationalMass_I2 < + // 2); + // assert(std::pow(mSettings.Dtini, 2.0) * rotationalSumSk_I3 / + // mesh->nodes[v].rotationalMass_I3 < + // 2); + // assert(std::pow(mSettings.Dtini, 2.0) * rotationalSumSk_J / + // mesh->nodes[v].rotationalMass_J < + // 2); + } + + for (VertexType &v : pMesh->vert) { + Node &node = pMesh->nodes[v]; + for (DoFType dofi = DoF::Ux; dofi < DoF::NumDoF; dofi++) { + if (dofi == DoF::Ux || dofi == DoF::Uy || dofi == DoF::Uz) { + node.acceleration[dofi] = node.force.residual[dofi] / node.mass.translational; + } else if (dofi == DoF::Nx) { + node.acceleration[dofi] = node.force.residual[dofi] / node.mass.rotationalJ; + } else if (dofi == DoF::Ny) { + node.acceleration[dofi] = node.force.residual[dofi] / node.mass.rotationalI3; + } else if (dofi == DoF::Nr) { + node.acceleration[dofi] = node.force.residual[dofi] / node.mass.rotationalI2; + } + } + } + + for (VertexType &v : pMesh->vert) { + Node &node = pMesh->nodes[v]; + node.velocity = node.velocity + node.acceleration * Dt; + } + updateKineticEnergy(); + + for (VertexType &v : pMesh->vert) { + Node &node = pMesh->nodes[v]; + node.displacements = node.displacements + node.velocity * Dt; + } + + for (VertexType &v : pMesh->vert) { + updateNodePosition(v, fixedVertices); + Node &node = pMesh->nodes[v]; + const VertexIndex &vi = node.vi; + VectorType normalDisplacementVector(0, 0, 0); + if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(3)) { + normalDisplacementVector += VectorType(node.displacements[3], 0, 0); + } + if (!fixedVertices.contains(vi) || !fixedVertices.at(vi).contains(4)) { + normalDisplacementVector += VectorType(0, node.displacements[4], 0); + } + v.N() = node.initialNormal + normalDisplacementVector; + const double &nx = v.N()[0], ny = v.N()[1]; + const double nxnyMagnitude = std::pow(nx, 2) + std::pow(ny, 2); + if (nxnyMagnitude > 1) { + v.N() = VectorType(nx / std::sqrt(nxnyMagnitude), ny / std::sqrt(nxnyMagnitude), 0); + } else { + const double nzSquared = 1.0 - nxnyMagnitude; + const double nz = std::sqrt(nzSquared); + VectorType newNormal(nx, ny, nz); + v.N() = newNormal; + } + if (!rigidSupports.contains(vi)) { + node.nR = node.displacements[5]; + } else { + } + } + updateElementalFrames(); +} + +SimulationResults DRMSimulationModel::computeResults(const std::shared_ptr &pJob) +{ + std::vector displacements(pMesh->VN()); + for (size_t vi = 0; vi < pMesh->VN(); vi++) { + displacements[vi] = pMesh->nodes[vi].displacements; + } + history.numberOfSteps = mCurrentSimulationStep; + + return SimulationResults{true, pJob, history, displacements}; +} + +void DRMSimulationModel::printCurrentState() const +{ + std::cout << "Simulation steps executed:" << mCurrentSimulationStep << std::endl; + std::cout << "Residual forces norm: " << pMesh->totalResidualForcesNorm << std::endl; + std::cout << "Kinetic energy:" << pMesh->currentTotalKineticEnergy << std::endl; +} + +void DRMSimulationModel::printDebugInfo() const +{ + std::cout << pMesh->elements[0].rigidity.toString() << std::endl; + std::cout << "Number of dampings:" << numOfDampings << endl; + printCurrentState(); +} + +#ifdef POLYSCOPE_DEFINED +void DRMSimulationModel::draw(const std::string &screenshotsFolder) +{ + // update positions + // polyscope::getCurveNetwork("Undeformed edge mesh")->setEnabled(false); + polyscope::CurveNetwork *meshPolyscopeHandle = polyscope::getCurveNetwork(meshPolyscopeLabel); + meshPolyscopeHandle->updateNodePositions(pMesh->getEigenVertices()); + + // Vertex quantities + std::vector kineticEnergies(pMesh->VN()); + std::vector> nodeNormals(pMesh->VN()); + std::vector> internalForces(pMesh->VN()); + std::vector> externalForces(pMesh->VN()); + std::vector> externalMoments(pMesh->VN()); + std::vector internalForcesNorm(pMesh->VN()); + std::vector> internalAxialForces(pMesh->VN()); + std::vector> internalFirstBendingTranslationForces( + pMesh->VN()); + std::vector> internalFirstBendingRotationForces( + pMesh->VN()); + std::vector> internalSecondBendingTranslationForces( + pMesh->VN()); + std::vector> internalSecondBendingRotationForces( + pMesh->VN()); + std::vector nRs(pMesh->VN()); + std::vector theta2(pMesh->VN()); + std::vector theta3(pMesh->VN()); + std::vector> residualForces(pMesh->VN()); + std::vector residualForcesNorm(pMesh->VN()); + std::vector accelerationX(pMesh->VN()); + for (const VertexType &v : pMesh->vert) { + kineticEnergies[pMesh->getIndex(v)] = pMesh->nodes[v].kineticEnergy; + const VectorType n = v.cN(); + nodeNormals[pMesh->getIndex(v)] = {n[0], n[1], n[2]}; + // per node internal forces + const Vector6d nodeforce = pMesh->nodes[v].force.internal * (-1); + internalForces[pMesh->getIndex(v)] = {nodeforce[0], nodeforce[1], + nodeforce[2]}; + internalForcesNorm[pMesh->getIndex(v)] = nodeforce.norm(); + // External force + const Vector6d nodeExternalForce = pMesh->nodes[v].force.external; + externalForces[pMesh->getIndex(v)] = { + nodeExternalForce[0], nodeExternalForce[1], nodeExternalForce[2]}; + externalMoments[pMesh->getIndex(v)] = {nodeExternalForce[3], + nodeExternalForce[4], 0}; + internalAxialForces[pMesh->getIndex(v)] = {nodeforce[0], nodeforce[1], + nodeforce[2]}; + const Node &node = pMesh->nodes[v]; + const Vector6d nodeForceFirst = node.force.internalFirstBending * (-1); + internalFirstBendingTranslationForces[pMesh->getIndex(v)] = { + nodeForceFirst[0], nodeForceFirst[1], nodeForceFirst[2]}; + internalFirstBendingRotationForces[pMesh->getIndex(v)] = { + nodeForceFirst[3], nodeForceFirst[4], 0}; + + const Vector6d nodeForceSecond = node.force.internalSecondBending * (-1); + internalSecondBendingTranslationForces[pMesh->getIndex(v)] = { + nodeForceSecond[0], nodeForceSecond[1], nodeForceSecond[2]}; + internalSecondBendingRotationForces[pMesh->getIndex(v)] = { + nodeForceSecond[3], nodeForceSecond[4], 0}; + nRs[pMesh->getIndex(v)] = node.nR; + const Vector6d nodeResidualForce = pMesh->nodes[v].force.residual; + residualForces[pMesh->getIndex(v)] = { + nodeResidualForce[0], nodeResidualForce[1], nodeResidualForce[2]}; + residualForcesNorm[pMesh->getIndex(v)] = nodeResidualForce.norm(); + accelerationX[pMesh->getIndex(v)] = pMesh->nodes[v].acceleration[0]; + } + meshPolyscopeHandle->addNodeScalarQuantity("Kinetic Energy", kineticEnergies)->setEnabled(false); + meshPolyscopeHandle->addNodeVectorQuantity("Node normals", nodeNormals)->setEnabled(true); + meshPolyscopeHandle->addNodeVectorQuantity("Internal force", internalForces)->setEnabled(false); + meshPolyscopeHandle->addNodeVectorQuantity("External force", externalForces)->setEnabled(false); + meshPolyscopeHandle->addNodeVectorQuantity("External Moments", externalMoments)->setEnabled(true); + meshPolyscopeHandle->addNodeScalarQuantity("Internal force norm", internalForcesNorm) + ->setEnabled(true); + // polyscope::getCurveNetwork(meshPolyscopeLabel) + // ->addNodeVectorQuantity("Internal Axial force", internalAxialForces) + // ->setEnabled(false); + // polyscope::getCurveNetwork(meshPolyscopeLabel) + // ->addNodeVectorQuantity("First bending force-Translation", + // internalFirstBendingTranslationForces) + // ->setEnabled(false); + // polyscope::getCurveNetwork(meshPolyscopeLabel) + // ->addNodeVectorQuantity("First bending force-Rotation", + // internalFirstBendingRotationForces) + // ->setEnabled(false); + + // polyscope::getCurveNetwork(meshPolyscopeLabel) + // ->addNodeVectorQuantity("Second bending force-Translation", + // internalSecondBendingTranslationForces) + // ->setEnabled(false); + // polyscope::getCurveNetwork(meshPolyscopeLabel) + // ->addNodeVectorQuantity("Second bending force-Rotation", + // internalSecondBendingRotationForces) + // ->setEnabled(false); + polyscope::getCurveNetwork(meshPolyscopeLabel) + ->addNodeScalarQuantity("nR", nRs) + ->setEnabled(false); + // polyscope::getCurveNetwork(meshPolyscopeLabel) + // ->addNodeScalarQuantity("theta3", theta3) + // ->setEnabled(false); + // polyscope::getCurveNetwork(meshPolyscopeLabel) + // ->addNodeScalarQuantity("theta2", theta2) + // ->setEnabled(false); + polyscope::getCurveNetwork(meshPolyscopeLabel) + ->addNodeVectorQuantity("Residual force", residualForces) + ->setEnabled(false); + polyscope::getCurveNetwork(meshPolyscopeLabel) + ->addNodeScalarQuantity("Residual force norm", residualForcesNorm) + ->setEnabled(false); + // polyscope::getCurveNetwork(meshPolyscopeLabel) + // ->addNodeScalarQuantity("Node acceleration x", accelerationX); + + // Edge quantities + std::vector A(pMesh->EN()); + std::vector J(pMesh->EN()); + std::vector I2(pMesh->EN()); + std::vector I3(pMesh->EN()); + for (const EdgeType &e : pMesh->edge) { + const size_t ei = pMesh->getIndex(e); + A[ei] = pMesh->elements[e].A; + J[ei] = pMesh->elements[e].J; + I2[ei] = pMesh->elements[e].I2; + I3[ei] = pMesh->elements[e].I3; + } + + polyscope::getCurveNetwork(meshPolyscopeLabel)->addEdgeScalarQuantity("A", A); + polyscope::getCurveNetwork(meshPolyscopeLabel)->addEdgeScalarQuantity("J", J); + polyscope::getCurveNetwork(meshPolyscopeLabel) + ->addEdgeScalarQuantity("I2", I2); + polyscope::getCurveNetwork(meshPolyscopeLabel) + ->addEdgeScalarQuantity("I3", I3); + + // Specify the callback + polyscope::state::userCallback = [&]() { + // Since options::openImGuiWindowForUserCallback == true by default, + // we can immediately start using ImGui commands to build a UI + + ImGui::PushItemWidth(100); // Make ui elements 100 pixels wide, + // instead of full width. Must have + // matching PopItemWidth() below. + + ImGui::InputInt("Simulation drawing step", + &mSettings.drawingStep); // set a int variable + ImGui::Checkbox("Enable drawing", + &mSettings.shouldDraw); // set a int variable + ImGui::Text("Number of simulation steps: %zu", mCurrentSimulationStep); + + ImGui::PopItemWidth(); + }; + + if (!screenshotsFolder.empty()) { + static bool firstDraw = true; + if (firstDraw) { + for (const auto &entry : + std::filesystem::directory_iterator(screenshotsFolder)) + std::filesystem::remove_all(entry.path()); + // polyscope::view::processZoom(5); + firstDraw = false; + } + polyscope::screenshot( + std::filesystem::path(screenshotsFolder) + .append(std::to_string(mCurrentSimulationStep) + ".png") + .string(), + false); + } else { + polyscope::show(); + } +} +#endif + +SimulationResults DRMSimulationModel::executeSimulation(const std::shared_ptr &pJob, + const Settings &settings) +{ + assert(pJob->pMesh.operator bool()); + auto t1 = std::chrono::high_resolution_clock::now(); + reset(); + mSettings = settings; + + // if (!pJob->nodalExternalForces.empty()) { + // double externalForcesNorm = 0; + // for (const auto &externalForce : pJob->nodalExternalForces) { + // externalForcesNorm += externalForce.second.norm(); + // } + // mSettings.totalResidualForcesNormThreshold = externalForcesNorm * 1e-2; + // } + + constrainedVertices = pJob->constrainedVertices; + if (!pJob->nodalForcedDisplacements.empty()) { + for (std::pair viDisplPair : pJob->nodalForcedDisplacements) { + const VertexIndex vi = viDisplPair.first; + constrainedVertices[vi].insert({0, 1, 2}); + } + } + // if (!pJob->nodalForcedNormals.empty()) { + // for (std::pair viNormalPair : + // pJob->nodalForcedDisplacements) { + // assert(viNormalPair3second[2] >= 0); + // } + // } + + pMesh.reset(); + pMesh = std::make_unique(*pJob->pMesh); + if (mSettings.beVerbose) { + std::cout << "Executing simulation for mesh with:" << pMesh->VN() << " nodes and " + << pMesh->EN() << " elements." << std::endl; + } + computeRigidSupports(); + for (auto fixedVertex : pJob->constrainedVertices) { + assert(fixedVertex.first < pMesh->VN()); + } + +#ifdef POLYSCOPE_DEFINED + if (mSettings.shouldDraw ) { + initPolyscope(); + polyscope::registerCurveNetwork( + meshPolyscopeLabel, pMesh->getEigenVertices(), pMesh->getEigenEdges()); + // registerWorldAxes(); + } +#endif + + updateElementalFrames(); + updateNodalMasses(); + if (!pJob->nodalForcedDisplacements.empty() && pJob->nodalExternalForces.empty()) { + shouldApplyInitialDistortion = true; + } + updateNodalExternalForces(pJob->nodalExternalForces, constrainedVertices); + while (settings.maxDRMIterations == 0 || + mCurrentSimulationStep < settings.maxDRMIterations) { + // while (true) { + updateNormalDerivatives(); + updateT1Derivatives(); + updateRDerivatives(); + updateT2Derivatives(); + updateT3Derivatives(); + updateResidualForcesOnTheFly(constrainedVertices); + + // TODO: write parallel function for updating positions + // TODO: Make a single function out of updateResidualForcesOnTheFly + // updatePositionsOnTheFly + // updatePositionsOnTheFly(constrainedVertices); + updateNodalMasses(); + updateNodalAccelerations(); + updateNodalVelocities(); + updateNodalDisplacements(); + applyDisplacements(constrainedVertices); + if (!pJob->nodalForcedDisplacements.empty()) { + applyForcedDisplacements( + + pJob->nodalForcedDisplacements); + } + // if (!pJob->nodalForcedNormals.empty()) { + // applyForcedNormals(pJob->nodalForcedNormals); + // } + updateElementalLengths(); + // pMesh->previousTotalPotentialEnergykN = + // pMesh->currentTotalPotentialEnergykN; + // pMesh->currentTotalPotentialEnergykN = computeTotalPotentialEnergy() / 1000; + + if (mCurrentSimulationStep != 0) { + history.stepPulse(*pMesh); + } + + if (std::isnan(pMesh->currentTotalKineticEnergy)) { + if (mSettings.beVerbose) { + std::cout << "Infinite kinetic energy detected.Exiting.." << std::endl; + } + break; + } + + if (mSettings.shouldDraw && mCurrentSimulationStep % mSettings.drawingStep == 0) /* && +currentSimulationStep > maxDRMIterations*/ + { + // std::string saveTo = std::filesystem::current_path() + // .append("Debugging_files") + // .append("Screenshots") + // .string(); + draw(); + // yValues = history.kineticEnergy; + // plotHandle = matplot::scatter(xPlot, yValues); + // label = "Log of Kinetic energy"; + // plotHandle->legend_string(label); + + // shouldUseKineticEnergyThreshold = true; + } + if (mSettings.shouldCreatePlots && mCurrentSimulationStep % 10 == 0) { + printCurrentState(); + SimulationResultsReporter::createPlot( + "Number of Steps", "Log of Kinetic energy", history.kineticEnergy); + } + // t = t + Dt; + mCurrentSimulationStep++; + // std::cout << "Kinetic energy:" << mesh.currentTotalKineticEnergy + // << std::endl; + // std::cout << "Residual forces norm:" << mesh.totalResidualForcesNorm + // << std::endl; + // Kinetic energy convergence + if ((mSettings.shouldUseTranslationalKineticEnergyThreshold || + mCurrentSimulationStep > 100000) && + pMesh->currentTotalTranslationalKineticEnergy < + mSettings.totalTranslationalKineticEnergyThreshold) { + if (mSettings.beVerbose) { + std::cout << "Simulation converged." << std::endl; + printCurrentState(); + std::cout << "Total potential:" << pMesh->currentTotalPotentialEnergykN + << " kNm" << std::endl; + std::cout << "Warning: The kinetic energy of the system was " + " used as a convergence criterion" + << std::endl; + } + break; + } + // Residual forces norm convergence + if (pMesh->previousTotalKineticEnergy > pMesh->currentTotalKineticEnergy + /*|| +mesh->previousTotalPotentialEnergykN > +mesh->currentTotalPotentialEnergykN*/ + /*|| mesh->currentTotalPotentialEnergykN < minPotentialEnergy*/) { + if (pMesh->totalResidualForcesNorm < + mSettings.totalResidualForcesNormThreshold) { + if (mSettings.beVerbose) { + std::cout << "Simulation converged." << std::endl; + printCurrentState(); + std::cout << "Total potential:" + << pMesh->currentTotalPotentialEnergykN << " kNm" + << std::endl; + } + break; + // } + } + // for (VertexType &v : mesh->vert) { + // Node &node = mesh->nodes[v]; + // node.displacements = node.displacements - node.velocity * Dt; + // } + // applyDisplacements(constrainedVertices); + // if (!pJob->nodalForcedDisplacements.empty()) { + // applyForcedDisplacements( + + // pJob->nodalForcedDisplacements); + // } + // updateElementalLengths(); + resetVelocities(); + Dt = Dt * mSettings.xi; + ++numOfDampings; + } + + if (mSettings.debugMessages) { + printDebugInfo(); + } + } + + SimulationResults results = computeResults(pJob); + + if (mCurrentSimulationStep == settings.maxDRMIterations && + mCurrentSimulationStep != 0) { + std::cout << "Did not reach equilibrium before reaching the maximum number " + "of DRM steps (" + << settings.maxDRMIterations << "). Breaking simulation" + << std::endl; + results.converged = false; + } else if (std::isnan(pMesh->currentTotalKineticEnergy)) { + results.converged = false; + + } else if (mSettings.beVerbose) { + auto t2 = std::chrono::high_resolution_clock::now(); + double simulationDuration = + std::chrono::duration_cast(t2 - t1).count(); + simulationDuration /= 1000; + std::cout << "Simulation converged after " << simulationDuration << "s" + << std::endl; + std::cout << "Time/(node*iteration) " + << simulationDuration / + (static_cast(mCurrentSimulationStep) * pMesh->VN()) + << "s" << std::endl; + std::cout << "Number of dampings:" << numOfDampings << endl; + } + // mesh.printVertexCoordinates(mesh.VN() / 2); + if (settings.shouldDraw) { + draw(); + } + if (results.converged) { + results.rotationalDisplacementQuaternion.resize(pMesh->VN()); + for (int vi = 0; vi < pMesh->VN(); vi++) { + const Node &node = pMesh->nodes[vi]; + const Eigen::Vector3d initialNormal = node.initialNormal.ToEigenVector(); + const Eigen::Vector3d deformedNormal= + pMesh->vert[vi].cN().ToEigenVector(); + + Eigen::Quaternion q; + q.setFromTwoVectors(initialNormal, deformedNormal); + Eigen::Quaternion q_nr; + q_nr = Eigen::AngleAxis(pMesh->nodes[vi].nR, deformedNormal); + VectorType referenceT1_deformed = pMesh->elements[node.referenceElement].frame.t1; + const VectorType &n_deformed = pMesh->vert[vi].cN(); + const VectorType referenceF1_deformed = (referenceT1_deformed + - (n_deformed + * (referenceT1_deformed * n_deformed))) + .Normalize(); + + const VectorType referenceT1_initial + = computeT1Vector(pMesh->nodes[node.referenceElement->cV(0)].initialLocation, + pMesh->nodes[node.referenceElement->cV(1)].initialLocation); + const VectorType &n_initial = node.initialNormal; + const VectorType referenceF1_initial + = (referenceT1_initial - (n_deformed * (referenceT1_initial * n_deformed))).Normalize(); + Eigen::Quaternion q_f1; //nr is with respect to f1 + q_f1.setFromTwoVectors(referenceF1_initial.ToEigenVector(), + referenceF1_deformed.ToEigenVector()); + Eigen::Quaternion q_t1; + q_t1.setFromTwoVectors(referenceT1_initial.ToEigenVector(), + referenceT1_deformed.ToEigenVector()); + + results.rotationalDisplacementQuaternion[vi] = q * q_f1 * q_nr; + } + + results.internalPotentialEnergy = computeTotalInternalPotentialEnergy(); + } + + if (mSettings.shouldCreatePlots) { + SimulationResultsReporter reporter; + reporter.reportResults({results}, "Results", pJob->pMesh->getLabel()); + } + + return results; +} diff --git a/beamformfinder.hpp b/drmsimulationmodel.hpp similarity index 96% rename from beamformfinder.hpp rename to drmsimulationmodel.hpp index 825a9a4..9e4fc50 100755 --- a/beamformfinder.hpp +++ b/drmsimulationmodel.hpp @@ -1,9 +1,9 @@ #ifndef BEAMFORMFINDER_HPP #define BEAMFORMFINDER_HPP -#include "elementalmesh.hpp" +#include "simulationmesh.hpp" #include "matplot/matplot.h" -#include "simulationresult.hpp" +#include "simulation_structs.hpp" #include #include #include @@ -20,7 +20,8 @@ struct DifferentiateWithRespectTo { const DoFType &dofi; }; -class FormFinder { +class DRMSimulationModel +{ public: struct Settings { bool debugMessages{false}; @@ -108,7 +109,7 @@ private: const std::unordered_map> &fixedVertices); - void draw(const string &screenshotsFolder); + void draw(const string &screenshotsFolder= {}); void updateNodalInternalForce(Vector6d &nodalInternalForce, @@ -160,7 +161,7 @@ private: double computeTheta3(const EdgeType &e, const VertexType &v); double computeDerivativeTheta3(const EdgeType &e, const VertexType &v, const DifferentiateWithRespectTo &dui) const; - double computeTotalPotentialEnergy() const; + double computeTotalPotentialEnergy(); void computeRigidSupports(); void updateNormalDerivatives(); void updateT1Derivatives(); @@ -197,8 +198,10 @@ private: void printDebugInfo() const; -public: - FormFinder(); + double computeTotalInternalPotentialEnergy(); + + public: + DRMSimulationModel(); SimulationResults executeSimulation(const std::shared_ptr &pJob, const Settings &settings = Settings()); diff --git a/edgemesh.cpp b/edgemesh.cpp index 1cb56e0..436f49a 100755 --- a/edgemesh.cpp +++ b/edgemesh.cpp @@ -4,8 +4,8 @@ Eigen::MatrixX2i VCGEdgeMesh::getEigenEdges() const { return eigenEdges; } -Eigen::MatrixX3d VCGEdgeMesh::getEigenVertices() const { - // getVertices(eigenVertices); +Eigen::MatrixX3d VCGEdgeMesh::getEigenVertices() { + getVertices(eigenVertices); return eigenVertices; } @@ -13,16 +13,18 @@ Eigen::MatrixX3d VCGEdgeMesh::getEigenEdgeNormals() const { return eigenEdgeNormals; } -bool VCGEdgeMesh::savePly(const std::string plyFilename) { - unsigned int mask = 0; - mask |= nanoply::NanoPlyWrapper::IO_VERTCOORD; - mask |= nanoply::NanoPlyWrapper::IO_EDGEINDEX; - mask |= nanoply::NanoPlyWrapper::IO_VERTNORMAL; - if (nanoply::NanoPlyWrapper::SaveModel( - plyFilename.c_str(), *this, mask, false) != 0) { - return false; - } - return true; +bool VCGEdgeMesh::save(const string &plyFilename) +{ + assert(std::filesystem::path(plyFilename).extension() == ".ply"); + unsigned int mask = 0; + mask |= nanoply::NanoPlyWrapper::IO_VERTCOORD; + mask |= nanoply::NanoPlyWrapper::IO_EDGEINDEX; + mask |= nanoply::NanoPlyWrapper::IO_VERTNORMAL; + if (nanoply::NanoPlyWrapper::SaveModel(plyFilename.c_str(), *this, mask, false) + != 0) { + return false; + } + return true; } void VCGEdgeMesh::GeneratedRegularSquaredPattern( @@ -199,6 +201,8 @@ bool VCGEdgeMesh::load(const string &plyFilename) { std::cout << plyFilename << " was loaded successfuly." << std::endl; std::cout << "Mesh has " << EN() << " edges." << std::endl; } + + label=std::filesystem::path(plyFilename).stem(); return true; } @@ -275,7 +279,7 @@ void VCGEdgeMesh::updateEigenEdgeAndVertices() { #endif } -void VCGEdgeMesh::copy(VCGEdgeMesh &mesh) { +bool VCGEdgeMesh::copy(VCGEdgeMesh &mesh) { vcg::tri::Append::MeshCopy(*this, mesh); label = mesh.getLabel(); eigenEdges = mesh.getEigenEdges(); @@ -287,19 +291,47 @@ void VCGEdgeMesh::copy(VCGEdgeMesh &mesh) { getVertices(eigenVertices); } vcg::tri::UpdateTopology::VertexEdge(*this); + + return true; +} + +void VCGEdgeMesh::removeDuplicateVertices() { + vcg::tri::Clean::MergeCloseVertex(*this, 0.000061524); + vcg::tri::Allocator::CompactEveryVector(*this); + vcg::tri::UpdateTopology::VertexEdge(*this); +} + +void VCGEdgeMesh::deleteDanglingVertices() { + vcg::tri::Allocator::PointerUpdater pu; + deleteDanglingVertices(pu); +} + +void VCGEdgeMesh::deleteDanglingVertices(vcg::tri::Allocator::PointerUpdater &pu) { + for (VertexType &v : vert) { + std::vector incidentElements; + vcg::edge::VEStarVE(&v, incidentElements); + if (incidentElements.size() == 0 && !v.IsD()) { + vcg::tri::Allocator::DeleteVertex(*this, v); + } + } + + vcg::tri::Allocator::CompactVertexVector(*this, pu); + vcg::tri::Allocator::CompactEdgeVector(*this); + + updateEigenEdgeAndVertices(); } void VCGEdgeMesh::getVertices(Eigen::MatrixX3d &vertices) { - vertices = Eigen::MatrixX3d(); - vertices.resize(VN(), 3); - for (int vi = 0; vi < VN(); vi++) { - if (vert[vi].IsD()) { - continue; + vertices = Eigen::MatrixX3d(); + vertices.resize(VN(), 3); + for (int vi = 0; vi < VN(); vi++) { + if (vert[vi].IsD()) { + continue; + } + VCGEdgeMesh::CoordType vertexCoordinates = + vert[static_cast(vi)].cP(); + vertices.row(vi) = vertexCoordinates.ToEigenVector(); } - VCGEdgeMesh::CoordType vertexCoordinates = - vert[static_cast(vi)].cP(); - vertices.row(vi) = vertexCoordinates.ToEigenVector(); - } } std::string VCGEdgeMesh::getLabel() const { return label; } @@ -329,18 +361,25 @@ void VCGEdgeMesh::printVertexCoordinates(const size_t &vi) const { } #ifdef POLYSCOPE_DEFINED -void VCGEdgeMesh::registerForDrawing( - const std::optional &desiredColor, - const bool &shouldEnable) const { - initPolyscope(); - const double drawingRadius = 0.002; - auto polyscopeHandle_edgeMesh = polyscope::registerCurveNetwork( - label, getEigenVertices(), getEigenEdges()); - polyscopeHandle_edgeMesh->setEnabled(shouldEnable); - polyscopeHandle_edgeMesh->setRadius(drawingRadius, true); - if (desiredColor.has_value()) { - polyscopeHandle_edgeMesh->setColor(desiredColor.value()); - } +//TODO: make const getEigenVertices is not +polyscope::CurveNetwork *VCGEdgeMesh::registerForDrawing( + const std::optional> &desiredColor, const bool &shouldEnable) +{ + initPolyscope(); + const double drawingRadius = 0.002; + polyscope::CurveNetwork *polyscopeHandle_edgeMesh( + polyscope::registerCurveNetwork(label, getEigenVertices(), getEigenEdges())); + + polyscopeHandle_edgeMesh->setEnabled(shouldEnable); + polyscopeHandle_edgeMesh->setRadius(drawingRadius, true); + if (desiredColor.has_value()) { + const glm::vec3 desiredColor_glm(desiredColor.value()[0], + desiredColor.value()[1], + desiredColor.value()[2]); + polyscopeHandle_edgeMesh->setColor(desiredColor_glm); + } + + return polyscopeHandle_edgeMesh; } void VCGEdgeMesh::unregister() const { @@ -352,4 +391,25 @@ void VCGEdgeMesh::unregister() const { } polyscope::removeCurveNetwork(label); } + +void VCGEdgeMesh::drawInitialFrames(polyscope::CurveNetwork *polyscopeHandle_initialMesh) const +{ + Eigen::MatrixX3d frameInitialX(VN(), 3); + Eigen::MatrixX3d frameInitialY(VN(), 3); + Eigen::MatrixX3d frameInitialZ(VN(), 3); + for (int vi = 0; vi < VN(); vi++) { + frameInitialX.row(vi) = Eigen::Vector3d(1, 0, 0); + frameInitialY.row(vi) = Eigen::Vector3d(0, 1, 0); + frameInitialZ.row(vi) = Eigen::Vector3d(0, 0, 1); + } + polyscopeHandle_initialMesh->addNodeVectorQuantity("FrameX", frameInitialX) + ->setVectorColor(glm::vec3(1, 0, 0)) + ->setEnabled(true); + polyscopeHandle_initialMesh->addNodeVectorQuantity("FrameY", frameInitialY) + ->setVectorColor(glm::vec3(0, 1, 0)) + ->setEnabled(true); + polyscopeHandle_initialMesh->addNodeVectorQuantity("FrameZ", frameInitialZ) + ->setVectorColor(glm::vec3(0, 0, 1)) + ->setEnabled(true); +} #endif diff --git a/edgemesh.hpp b/edgemesh.hpp index 85497ec..46d3a21 100755 --- a/edgemesh.hpp +++ b/edgemesh.hpp @@ -45,34 +45,44 @@ class VCGEdgeMesh : public vcg::tri::TriMesh, return vcg::tri::Index(*this, meshElement); } void updateEigenEdgeAndVertices(); - virtual void copy(VCGEdgeMesh &mesh); + /* + * The copy function shold be a virtual function of the base interface Mesh class. + * https://stackoverflow.com/questions/2354210/can-a-class-member-function-template-be-virtual + * use type erasure (?) + * */ + bool copy(VCGEdgeMesh &mesh); + +void removeDuplicateVertices(); +void deleteDanglingVertices(); +void deleteDanglingVertices(vcg::tri::Allocator::PointerUpdater &pu); + void getEdges(Eigen::MatrixX3d &edgeStartingPoints, Eigen::MatrixX3d &edgeEndingPoints) const; Eigen::MatrixX3d getNormals() const; - bool plyFileHasAllRequiredFields(const std::string &plyFilename); + bool plyFileHasAllRequiredFields(const std::string &plyFilename); - bool loadUsingNanoply(const std::string &plyFilename); + bool loadUsingNanoply(const std::string &plyFilename); - bool load(const std::string &plyFilename) override; + bool load(const std::string &plyFilename) override; + bool save(const std::string &plyFilename) override; - bool savePly(const std::string plyFilename); + bool createSpanGrid(const size_t squareGridDimension); + bool createSpanGrid(const size_t desiredWidth, const size_t desiredHeight); + void createSpiral(const float °reesOfArm, const size_t &numberOfSamples); - bool createSpanGrid(const size_t squareGridDimension); - bool createSpanGrid(const size_t desiredWidth, const size_t desiredHeight); - void createSpiral(const float °reesOfArm, const size_t &numberOfSamples); - - Eigen::MatrixX2i getEigenEdges() const; - Eigen::MatrixX3d getEigenVertices() const; + Eigen::MatrixX2i getEigenEdges() const; + Eigen::MatrixX3d getEigenVertices(); Eigen::MatrixX3d getEigenEdgeNormals() const; void printVertexCoordinates(const size_t &vi) const; #ifdef POLYSCOPE_DEFINED - void registerForDrawing( - const std::optional &desiredColor = std::nullopt, - const bool &shouldEnable = true) const; - void unregister() const; + polyscope::CurveNetwork *registerForDrawing( + const std::optional> &desiredColor = std::nullopt, + const bool &shouldEnable = true); + void unregister() const; + void drawInitialFrames(polyscope::CurveNetwork *polyscopeHandle_initialMesh) const; #endif std::string getLabel() const; void setLabel(const std::string &value); diff --git a/linearsimulationmodel.hpp b/linearsimulationmodel.hpp new file mode 100644 index 0000000..26a5c94 --- /dev/null +++ b/linearsimulationmodel.hpp @@ -0,0 +1,313 @@ +#ifndef LINEARSIMULATIONMODEL_HPP +#define LINEARSIMULATIONMODEL_HPP + +//#include "beam.hpp" +#include "simulation_structs.hpp" +#include "threed_beam_fea.h" +#include +#include + +// struct BeamSimulationProperties { +// float crossArea; +// float I2; +// float I3; +// float polarInertia; +// float G; +// // Properties used by fea +// float EA; +// float EIz; +// float EIy; +// float GJ; + +// BeamSimulationProperties(const BeamDimensions &dimensions, +// const BeamMaterial &material); +//}; + +// struct NodalForce { +// int index; +// int dof; +// double magnitude; +//}; + +// struct SimulationJob { +// Eigen::MatrixX3d nodes; +// Eigen::MatrixX2i elements; +// Eigen::MatrixX3d elementalNormals; +// Eigen::VectorXi fixedNodes; +// std::vector nodalForces; +// std::vector beamDimensions; +// std::vector beamMaterial; +//}; + +// struct SimulationResults { +// std::vector edgeForces; ///< Force values per force +// component +// ///< #force components x #edges +// Eigen::MatrixXd +// nodalDisplacements; ///< The displacement of each node #nodes x 3 +// SimulationResults(const fea::Summary &feaSummary); +// SimulationResults() {} +//}; + +class LinearSimulationModel { +public: + LinearSimulationModel(){ + + } + static std::vector + getFeaElements(const std::shared_ptr &job) { + const int numberOfEdges = job->pMesh->EN(); + std::vector elements(numberOfEdges); + for (int edgeIndex = 0; edgeIndex < numberOfEdges; edgeIndex++) { + const SimulationMesh::CoordType &evn0 = + job->pMesh->edge[edgeIndex].cV(0)->cN(); + const SimulationMesh::CoordType &evn1 = job->pMesh->edge[edgeIndex].cV(1)->cN(); + const std::vector nAverageVector{(evn0[0] + evn1[0]) / 2, + (evn0[1] + evn1[1]) / 2, + (evn0[2] + evn1[2]) / 2}; + const Element &element = job->pMesh->elements[edgeIndex]; + const double E = element.material.youngsModulus; + fea::Props feaProperties(E * element.A, E * element.I3, E * element.I2, + element.G * element.J, nAverageVector); + const int vi0 = job->pMesh->getIndex(job->pMesh->edge[edgeIndex].cV(0)); + const int vi1 = job->pMesh->getIndex(job->pMesh->edge[edgeIndex].cV(1)); + elements[edgeIndex] = fea::Elem(vi0, vi1, feaProperties); + } + + return elements; + } + + static std::vector + getFeaNodes(const std::shared_ptr &job) { + const int numberOfNodes = job->pMesh->VN(); + std::vector feaNodes(numberOfNodes); + for (int vi = 0; vi < numberOfNodes; vi++) { + const CoordType &p = job->pMesh->vert[vi].cP(); + feaNodes[vi] = fea::Node(p[0], p[1], p[2]); + } + + return feaNodes; + } + static std::vector + getFeaFixedNodes(const std::shared_ptr &job) { + std::vector boundaryConditions; + boundaryConditions.reserve(job->constrainedVertices.size() * 6 + + job->nodalForcedDisplacements.size() * 3); + for (auto fixedVertex : job->constrainedVertices) { + const int vertexIndex = fixedVertex.first; + for (int dofIndex : fixedVertex.second) { + boundaryConditions.emplace_back( + fea::BC(vertexIndex, static_cast(dofIndex), 0)); + } + } + + for (auto forcedDisplacement : job->nodalForcedDisplacements) { + const int vi = forcedDisplacement.first; + for (int dofIndex = 0; dofIndex < 3; dofIndex++) { + boundaryConditions.emplace_back(fea::BC(vi, + static_cast(dofIndex), + forcedDisplacement.second[dofIndex])); + } + } + + return boundaryConditions; + } + + static std::vector + getFeaNodalForces(const std::shared_ptr &job) { + std::vector nodalForces; + nodalForces.reserve(job->nodalExternalForces.size() * 6); + + for (auto nodalForce : job->nodalExternalForces) { + for (int dofIndex = 0; dofIndex < 6; dofIndex++) { + if (nodalForce.second[dofIndex] == 0) { + continue; + } + nodalForces.emplace_back( + fea::Force(nodalForce.first, dofIndex, nodalForce.second[dofIndex])); + } + } + + return nodalForces; + } + static SimulationResults getResults(const fea::Summary &feaSummary, + const std::shared_ptr &simulationJob) + { + SimulationResults results; + results.converged = feaSummary.converged; + if (!results.converged) { + return results; + } + + results.executionTime = feaSummary.total_time_in_ms * 1000; + // displacements + results.displacements.resize(feaSummary.num_nodes); + results.rotationalDisplacementQuaternion.resize(feaSummary.num_nodes); + for (int vi = 0; vi < feaSummary.num_nodes; vi++) { + results.displacements[vi] = Vector6d(feaSummary.nodal_displacements[vi]); + + const Vector6d &nodalDisplacement = results.displacements[vi]; + Eigen::Quaternion q_nx; + q_nx = Eigen::AngleAxis(nodalDisplacement[3], Eigen::Vector3d(1, 0, 0)); + Eigen::Quaternion q_ny; + q_ny = Eigen::AngleAxis(nodalDisplacement[4], Eigen::Vector3d(0, 1, 0)); + Eigen::Quaternion q_nz; + q_nz = Eigen::AngleAxis(nodalDisplacement[5], Eigen::Vector3d(0, 0, 1)); + results.rotationalDisplacementQuaternion[vi] = q_nx * q_ny * q_nz; + // results.rotationalDisplacementQuaternion[vi] = q_nz * q_ny * q_nx; + // results.rotationalDisplacementQuaternion[vi] = q_nz * q_nx * q_ny; + } + + // // Convert forces + // // Convert to vector of eigen matrices of the form force component-> per + // // Edge + // const int numDof = 6; + // const size_t numberOfEdges = feaSummary.element_forces.size(); + // edgeForces = + // std::vector(numDof, Eigen::VectorXd(2 * + // numberOfEdges)); + // for (gsl::index edgeIndex = 0; edgeIndex < numberOfEdges; edgeIndex++) { + // for (gsl::index forceComponentIndex = 0; forceComponentIndex < numDof; + // forceComponentIndex++) { + // (edgeForces[forceComponentIndex])(2 * edgeIndex) = + // feaSummary.element_forces[edgeIndex][forceComponentIndex]; + // (edgeForces[forceComponentIndex])(2 * edgeIndex + 1) = + // feaSummary.element_forces[edgeIndex][numDof + + // forceComponentIndex]; + // } + // } + + for (int ei = 0; ei < feaSummary.num_elems; ei++) { + const std::vector &elementForces = feaSummary.element_forces[ei]; + const Element &element = simulationJob->pMesh->elements[ei]; + //Axial + const double elementPotentialEnergy_axial_v0 = std::pow(elementForces[0], 2) + * element.initialLength + / (4 * element.material.youngsModulus + * element.A); + const double elementPotentialEnergy_axial_v1 = std::pow(elementForces[6], 2) + * element.initialLength + / (4 * element.material.youngsModulus + * element.A); + const double elementPotentialEnergy_axial = elementPotentialEnergy_axial_v0 + + elementPotentialEnergy_axial_v1; + //Shear + const double elementPotentialEnergy_shearY_v0 = std::pow(elementForces[1], 2) + * element.initialLength + / (4 * element.A * element.G); + const double elementPotentialEnergy_shearZ_v0 = std::pow(elementForces[2], 2) + * element.initialLength + / (4 * element.A * element.G); + const double elementPotentialEnergy_shearY_v1 = std::pow(elementForces[7], 2) + * element.initialLength + / (4 * element.A * element.G); + const double elementPotentialEnergy_shearZ_v1 = std::pow(elementForces[8], 2) + * element.initialLength + / (4 * element.A * element.G); + const double elementPotentialEnergy_shear = elementPotentialEnergy_shearY_v0 + + elementPotentialEnergy_shearZ_v0 + + elementPotentialEnergy_shearY_v1 + + elementPotentialEnergy_shearZ_v1; + //Bending + const double elementPotentialEnergy_bendingY_v0 = std::pow(elementForces[4], 2) + * element.initialLength + / (4 * element.material.youngsModulus + * element.I2); + const double elementPotentialEnergy_bendingZ_v0 = std::pow(elementForces[5], 2) + * element.initialLength + / (4 * element.material.youngsModulus + * element.I3); + const double elementPotentialEnergy_bendingY_v1 = std::pow(elementForces[10], 2) + * element.initialLength + / (4 * element.material.youngsModulus + * element.I2); + const double elementPotentialEnergy_bendingZ_v1 = std::pow(elementForces[11], 2) + * element.initialLength + / (4 * element.material.youngsModulus + * element.I3); + const double elementPotentialEnergy_bending = elementPotentialEnergy_bendingY_v0 + + elementPotentialEnergy_bendingZ_v0 + + elementPotentialEnergy_bendingY_v1 + + elementPotentialEnergy_bendingZ_v1; + //Torsion + const double elementPotentialEnergy_torsion_v0 = std::pow(elementForces[3], 2) + * element.initialLength + / (4 * element.J * element.G); + const double elementPotentialEnergy_torsion_v1 = std::pow(elementForces[9], 2) + * element.initialLength + / (4 * element.J * element.G); + const double elementPotentialEnergy_torsion = elementPotentialEnergy_torsion_v0 + + elementPotentialEnergy_torsion_v1; + const double elementInternalPotentialEnergy = elementPotentialEnergy_axial + + elementPotentialEnergy_shear + + elementPotentialEnergy_bending + + elementPotentialEnergy_torsion; + results.internalPotentialEnergy += elementInternalPotentialEnergy; + } + + return results; + } + + SimulationResults + executeSimulation(const std::shared_ptr &simulationJob) { + assert(simulationJob->pMesh->VN() != 0); + fea::Job job(getFeaNodes(simulationJob), getFeaElements(simulationJob)); + // printInfo(job); + + // create the default options + fea::Options opts; + opts.save_elemental_forces = false; + opts.save_nodal_displacements = false; + opts.save_nodal_forces = false; + opts.save_report = false; + opts.save_tie_forces = false; + // if (!elementalForcesOutputFilepath.empty()) { + // opts.save_elemental_forces = true; + // opts.elemental_forces_filename = elementalForcesOutputFilepath; + // } + // if (!nodalDisplacementsOutputFilepath.empty()) { + // opts.save_nodal_displacements = true; + // opts.nodal_displacements_filename = nodalDisplacementsOutputFilepath; + // } + + // have the program output status updates + opts.verbose = false; + + // form an empty vector of ties + std::vector ties; + + // also create an empty list of equations + std::vector equations; + auto fixedVertices = getFeaFixedNodes(simulationJob); + auto nodalForces = getFeaNodalForces(simulationJob); + fea::Summary feaResults = + fea::solve(job, fixedVertices, nodalForces, ties, equations, opts); + + SimulationResults results = getResults(feaResults, simulationJob); + results.job = simulationJob; + return results; + } + // SimulationResults getResults() const; + // void setResultsNodalDisplacementCSVFilepath(const std::string + // &outputPath); void setResultsElementalForcesCSVFilepath(const std::string + // &outputPath); + +private: + // std::string nodalDisplacementsOutputFilepath{"nodal_displacement.csv"}; + // std::string elementalForcesOutputFilepath{"elemental_forces.csv"}; + // SimulationResults results; + + static void printInfo(const fea::Job &job) { + std::cout << "Details regarding the fea::Job:" << std::endl; + std::cout << "Nodes:" << std::endl; + for (fea::Node n : job.nodes) { + std::cout << n << std::endl; + } + std::cout << "Elements:" << std::endl; + for (Eigen::Vector2i e : job.elems) { + std::cout << e << std::endl; + } + } +}; + +#endif // LINEARSIMULATIONMODEL_HPP diff --git a/mesh.hpp b/mesh.hpp index 4e708cc..f2d6b7d 100755 --- a/mesh.hpp +++ b/mesh.hpp @@ -1,14 +1,19 @@ #ifndef MESH_HPP #define MESH_HPP +#include #include -class Mesh { - std::string label; +class Mesh +{ +protected: + std::string label; public: virtual ~Mesh() = default; virtual bool load(const std::string &filePath) { return false; } + virtual bool save(const std::string &filePath) { return false; } + std::string getLabel() const; void setLabel(const std::string &newLabel); }; diff --git a/optimizationstructs.hpp b/optimizationstructs.hpp deleted file mode 100755 index 80077d0..0000000 --- a/optimizationstructs.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef OPTIMIZATIONSTRUCTS_HPP -#define OPTIMIZATIONSTRUCTS_HPP -#include "beamformfinder.hpp" - -#endif // OPTIMIZATIONSTRUCTS_HPP diff --git a/patternIO.cpp b/patternIO.cpp index 738d8a5..6cfe8ae 100755 --- a/patternIO.cpp +++ b/patternIO.cpp @@ -46,10 +46,10 @@ void PatternIO::exportPLY(const std::string &patternSetFilePath, PatternGeometry p; p.add(vertices, patterns[patternIndex].edges); auto tiled = p.createTile(p); - p.savePly( + p.save( std::filesystem::path(outputDirectoryPath) .append(std::to_string(patterns[patternIndex].name) + ".ply")); - tiled.savePly(std::filesystem::path(outputDirectoryPath) + tiled.save(std::filesystem::path(outputDirectoryPath) .append("tiled_" + std::to_string(patterns[patternIndex].name) + ".ply")); diff --git a/polymesh.hpp b/polymesh.hpp index f5cda8d..843b2cc 100755 --- a/polymesh.hpp +++ b/polymesh.hpp @@ -3,73 +3,206 @@ #include "mesh.hpp" #include "vcg/complex/complex.h" #include +#include #include class PFace; class PVertex; +class PEdge; struct PUsedTypes : public vcg::UsedTypes::AsVertexType, - vcg::Use::AsFaceType> {}; + vcg::Use::AsFaceType, + vcg::Use::AsEdgeType> +{}; -class PVertex : public vcg::Vertex {}; +class PVertex : public vcg::Vertex +{}; +class PEdge : public vcg::Edge +{}; -class PFace - : public vcg::Face< - PUsedTypes, - vcg::face::PolyInfo // this is necessary if you use component in - // vcg/simplex/face/component_polygon.h - // It says "this class is a polygon and the memory for its components - // (e.g. pointer to its vertices will be allocated dynamically") - , - vcg::face::PFVAdj // Pointer to the vertices (just like FVAdj ) - , - vcg::face::PFVAdj, - vcg::face::PFFAdj // Pointer to edge-adjacent face (just like FFAdj ) - , - vcg::face::BitFlags // bit flags - , - vcg::face::Qualityf // quality - , - vcg::face::Normal3f // normal - > {}; +class PFace : public vcg::Face +{}; class VCGPolyMesh - : public vcg::tri::TriMesh, std::vector>, - public Mesh { + : public vcg::tri::TriMesh, std::vector, std::vector>, + public Mesh +{ public: - virtual bool load(const std::string &filename) override { - int mask; - vcg::tri::io::Importer::LoadMask(filename.c_str(), mask); - int error = vcg::tri::io::Importer::Open( - *this, filename.c_str(), mask); - if (error != 0) { - return false; - } - // vcg::tri::io::ImporterOBJ::Open(); - // unsigned int mask = 0; - // mask |= nanoply::NanoPlyWrapper::IO_VERTCOORD; - // mask |= nanoply::NanoPlyWrapper::IO_VERTNORMAL; - // mask |= nanoply::NanoPlyWrapper::IO_VERTCOLOR; - // mask |= nanoply::NanoPlyWrapper::IO_EDGEINDEX; - // mask |= nanoply::NanoPlyWrapper::IO_FACEINDEX; - // if (nanoply::NanoPlyWrapper::LoadModel( - // std::filesystem::absolute(filename).c_str(), *this, mask) != - // 0) { - // std::cout << "Could not load tri mesh" << std::endl; + // virtual bool load(const std::string &filename) override { + // int mask; + // vcg::tri::io::Importer::LoadMask(filename.c_str(), mask); + // int error = vcg::tri::io::Importer::Open( + // *this, filename.c_str(), mask); + // if (error != 0) { + // return false; // } - vcg::tri::UpdateTopology::FaceFace(*this); - // vcg::tri::UpdateTopology::VertexFace(*this); - vcg::tri::UpdateNormal::PerVertexNormalized(*this); - return true; + // // vcg::tri::io::ImporterOBJ::Open(); + // // unsigned int mask = 0; + // // mask |= nanoply::NanoPlyWrapper::IO_VERTCOORD; + // // mask |= nanoply::NanoPlyWrapper::IO_VERTNORMAL; + // // mask |= nanoply::NanoPlyWrapper::IO_VERTCOLOR; + // // mask |= nanoply::NanoPlyWrapper::IO_EDGEINDEX; + // // mask |= nanoply::NanoPlyWrapper::IO_FACEINDEX; + // // if (nanoply::NanoPlyWrapper::LoadModel( + // // std::filesystem::absolute(filename).c_str(), *this, mask) != + // // 0) { + // // std::cout << "Could not load tri mesh" << std::endl; + // // } + // vcg::tri::UpdateTopology::FaceFace(*this); + // vcg::tri::UpdateTopology::VertexEdge(*this); + // // vcg::tri::UpdateTopology::VertexFace(*this); + // vcg::tri::UpdateNormal::PerVertexNormalized(*this); + // return true; + // } + Eigen::MatrixX3d getVertices() const + { + Eigen::MatrixX3d vertices(VN(), 3); + for (size_t vi = 0; vi < VN(); vi++) { + VCGPolyMesh::CoordType vertexCoordinates = vert[vi].cP(); + vertices.row(vi) = vertexCoordinates.ToEigenVector(); + } + return vertices; + } + std::vector> getFaces() const + { + std::vector> faces(FN()); + for (const VCGPolyMesh::FaceType &f : this->face) { + const int fi = vcg::tri::Index(*this, f); + for (size_t vi = 0; vi < f.VN(); vi++) { + const size_t viGlobal = vcg::tri::Index(*this, f.cV(vi)); + faces[fi].push_back(viGlobal); + } + } + + return faces; } #ifdef POLYSCOPE_DEFINED - void registerForDrawing(){ + std::shared_ptr registerForDrawing( + const std::optional> &desiredColor = std::nullopt, + const bool &shouldEnable = true) + { + auto vertices = getVertices(); + auto faces = getFaces(); + initPolyscope(); + std::shared_ptr polyscopeHandle_mesh( + polyscope::registerSurfaceMesh(label, vertices, faces)); + + const double drawingRadius = 0.002; + polyscopeHandle_mesh->setEnabled(shouldEnable); + polyscopeHandle_mesh->setEdgeWidth(drawingRadius); + + if (desiredColor.has_value()) { + const glm::vec3 desiredColor_glm(desiredColor.value()[0], + desiredColor.value()[1], + desiredColor.value()[2]); + polyscopeHandle_mesh->setSurfaceColor(desiredColor_glm); + } + + return polyscopeHandle_mesh; } #endif + void moveToCenter() + { + CoordType centerOfMass(0, 0, 0); + + for (int vi = 0; vi < VN(); vi++) { + centerOfMass += vert[vi].cP(); + } + centerOfMass /= VN(); + vcg::tri::UpdatePosition::Translate(*this, -centerOfMass); + } + + /* + * Returns the average distance from the center of each edge to the center of its face over the whole mesh + * */ + double getAverageFaceRadius() const + { + double averageFaceRadius = 0; + for (int fi = 0; fi < FN(); fi++) { + const VCGPolyMesh::FaceType &f = face[fi]; + CoordType centerOfFace(0, 0, 0); + for (int vi = 0; vi < f.VN(); vi++) { + centerOfFace = centerOfFace + f.cP(vi); + } + centerOfFace /= f.VN(); + + double faceRadius = 0; + // for (int face_ei = 0; face_ei < f.EN(); face_ei++) { + // std::cout << "fi:" << getIndex(f) << std::endl; + // auto vps = f.FVp(0); + // auto vpe = vps; + for (int i = 0; i < f.VN(); i++) { + faceRadius += vcg::Distance(centerOfFace, (f.cP0(i) + f.cP1(i)) / 2); + } + + // } + const int faceEdges = f.VN(); //NOTE: When does this not hold? + faceRadius /= faceEdges; + averageFaceRadius += faceRadius; + } + averageFaceRadius /= FN(); + return averageFaceRadius; + } + + bool copy(VCGPolyMesh ©From) + { + vcg::tri::Append::MeshCopy(*this, copyFrom); + label = copyFrom.getLabel(); + // eigenEdges = mesh.getEigenEdges(); + // if (eigenEdges.rows() == 0) { + // getEdges(eigenEdges); + // } + // eigenVertices = mesh.getEigenVertices(); + // if (eigenVertices.rows() == 0) { + // getVertices(); + // } + vcg::tri::UpdateTopology::VertexEdge(*this); + vcg::tri::UpdateTopology::EdgeEdge(*this); + // vcg::tri::UpdateTopology::VertexFace(*this); + + return true; + } + + VCGPolyMesh(VCGPolyMesh ©From) { copy(copyFrom); } + VCGPolyMesh() {} + template + size_t getIndex(const MeshElement &meshElement) const + { + return vcg::tri::Index(*this, meshElement); + } }; #endif // POLYMESH_HPP diff --git a/reducedmodeloptimizer_structs.hpp b/reducedmodeloptimizer_structs.hpp new file mode 100644 index 0000000..add823c --- /dev/null +++ b/reducedmodeloptimizer_structs.hpp @@ -0,0 +1,528 @@ +#ifndef REDUCEDMODELOPTIMIZER_STRUCTS_HPP +#define REDUCEDMODELOPTIMIZER_STRUCTS_HPP +#include "csvfile.hpp" +#include "drmsimulationmodel.hpp" +#include "linearsimulationmodel.hpp" +#include "simulation_structs.hpp" +#include "unordered_map" +#include + +namespace ReducedPatternOptimization { + +enum BaseSimulationScenario { + Axial, + Shear, + Bending, + Dome, + Saddle, + NumberOfBaseSimulationScenarios +}; +inline static std::vector baseSimulationScenarioNames{"Axial", + "Shear", + "Bending", + "Dome", + "Saddle"}; + +struct Colors +{ + inline static std::array fullInitial{0.416666, 0.109804, 0.890196}; + inline static std::array fullDeformed{0.583333, 0.890196, 0.109804}; + inline static std::array reducedInitial{0.890196, 0.109804, 0.193138}; + inline static std::array reducedDeformed{0.109804, 0.890196, 0.806863}; +}; + + struct xRange{ + std::string label; + double min; + double max; + std::string toString() const { + return label + "=[" + std::to_string(min) + "," + std::to_string(max) + + "]"; + } + }; + struct Settings { + enum NormalizationStrategy { + NonNormalized, + Epsilon, + MaxDisplacement, + EqualDisplacements + }; + inline static vector normalizationStrategyStrings{ + "NonNormalized", "Epsilon", "MaxDsiplacement", "EqualDisplacements"}; + std::vector xRanges; + int numberOfFunctionCalls{100}; + double solutionAccuracy{1e-2}; + NormalizationStrategy normalizationStrategy{Epsilon}; + double normalizationParameter{0.003}; + + std::string toString() const { + std::string settingsString; + if (!xRanges.empty()) { + std::string xRangesString; + for (const xRange &range : xRanges) { + xRangesString += range.toString() + " "; + } + settingsString += xRangesString; + } + settingsString += + "FuncCalls=" + std::to_string(numberOfFunctionCalls) + + " Accuracy=" + std::to_string(solutionAccuracy) + + " Norm=" + normalizationStrategyStrings[normalizationStrategy]; + + return settingsString; + } + + void writeHeaderTo(csvFile &os) const { + if (!xRanges.empty()) { + for (const xRange &range : xRanges) { + os << range.label + " max"; + os << range.label + " min"; + } + } + os << "Function Calls"; + os << "Solution Accuracy"; + os << "Normalization strategy"; + // os << std::endl; + } + + void writeSettingsTo(csvFile &os) const { + if (!xRanges.empty()) { + for (const xRange &range : xRanges) { + os << range.max; + os << range.min; + } + } + os << numberOfFunctionCalls; + os << solutionAccuracy; + os << normalizationStrategyStrings[normalizationStrategy] + "_" + + std::to_string(normalizationParameter); + } + }; + + inline void updateMeshWithOptimalVariables(const std::vector &x, SimulationMesh &m) + { + const double E = x[0]; + const double A = x[1]; + const double beamWidth = std::sqrt(A); + const double beamHeight = beamWidth; + + const double J = x[2]; + const double I2 = x[3]; + const double I3 = x[4]; + for (EdgeIndex ei = 0; ei < m.EN(); ei++) { + Element &e = m.elements[ei]; + e.setDimensions(RectangularBeamDimensions(beamWidth, beamHeight)); + e.setMaterial(ElementMaterial(e.material.poissonsRatio, E)); + e.J = J; + e.I2 = I2; + e.I3 = I3; + } + + CoordType center_barycentric(1, 0, 0); + CoordType interfaceEdgeMiddle_barycentric(0, 0.5, 0.5); + CoordType movableVertex_barycentric((center_barycentric + interfaceEdgeMiddle_barycentric) + * x[x.size() - 2]); + + CoordType patternCoord0 = CoordType(0, 0, 0); + double bottomEdgeHalfSize = 0.03 / std::tan(M_PI / 3); + CoordType interfaceNodePosition(0, -0.03, 0); + CoordType patternBottomRight = interfaceNodePosition + CoordType(bottomEdgeHalfSize, 0, 0); + CoordType patternBottomLeft = interfaceNodePosition - CoordType(bottomEdgeHalfSize, 0, 0); + vcg::Triangle3 baseTriangle(patternCoord0, patternBottomLeft, patternBottomRight); + CoordType baseTriangleMovableVertexPosition = baseTriangle.cP(0) + * movableVertex_barycentric[0] + + baseTriangle.cP(1) + * movableVertex_barycentric[1] + + baseTriangle.cP(2) + * movableVertex_barycentric[2]; + VectorType patternPlaneNormal(0, 0, 1); + baseTriangleMovableVertexPosition = vcg::RotationMatrix(patternPlaneNormal, + vcg::math::ToRad(x[x.size() - 1])) + * baseTriangleMovableVertexPosition; + + const int fanSize = 6; + for (int rotationCounter = 0; rotationCounter < fanSize; rotationCounter++) { + m.vert[2 * rotationCounter + 1].P() = vcg::RotationMatrix(patternPlaneNormal, + vcg::math::ToRad( + 60.0 * rotationCounter)) + * baseTriangleMovableVertexPosition; + } + + m.reset(); + } + + struct Results + { + double time{-1}; + int numberOfSimulationCrashes{0}; + + struct ObjectiveValues + { + double totalRaw; + double total; + std::vector perSimulationScenario_rawTranslational; + std::vector perSimulationScenario_rawRotational; + std::vector perSimulationScenario_translational; + std::vector perSimulationScenario_rotational; + std::vector perSimulationScenario_total; + } objectiveValue; + + // std::vector> optimalXNameValuePairs; + std::vector> optimalXNameValuePairs; + std::vector> fullPatternSimulationJobs; + std::vector> reducedPatternSimulationJobs; + vcg::Triangle3 baseTriangle; + struct JsonKeys + { + inline static std::string filename{"OptimizationResults.json"}; + inline static std::string optimizationVariables{"OptimizationVariables"}; + inline static std::string baseTriangle{"BaseTriangle"}; + }; + + void save(const string &saveToPath) const + { + assert(std::filesystem::is_directory(saveToPath)); + //clear directory + for (const auto &entry : std::filesystem::directory_iterator(saveToPath)) + std::filesystem::remove_all(entry.path()); + + //Save optimal X + nlohmann::json json_optimizationResults; + std::string jsonValue_optimizationVariables; + for (const auto &optimalXNameValuePair : optimalXNameValuePairs) { + jsonValue_optimizationVariables.append(optimalXNameValuePair.first + ","); + } + jsonValue_optimizationVariables.pop_back(); // for deleting the last comma + json_optimizationResults[JsonKeys::optimizationVariables] = jsonValue_optimizationVariables; + + for (const auto &optimalXNameValuePair : optimalXNameValuePairs) { + json_optimizationResults[optimalXNameValuePair.first] = optimalXNameValuePair.second; + } + //base triangle + json_optimizationResults[JsonKeys::baseTriangle] + = std::vector{baseTriangle.cP0(0)[0], + baseTriangle.cP0(0)[1], + baseTriangle.cP0(0)[2], + baseTriangle.cP1(0)[0], + baseTriangle.cP1(0)[1], + baseTriangle.cP1(0)[2], + baseTriangle.cP2(0)[0], + baseTriangle.cP2(0)[1], + baseTriangle.cP2(0)[2]}; + ////Save to json file + std::filesystem::path jsonFilePath( + std::filesystem::path(saveToPath).append(JsonKeys::filename)); + std::ofstream jsonFile_optimizationResults(jsonFilePath.string()); + jsonFile_optimizationResults << json_optimizationResults; + + /*TODO: Refactor since the meshes are saved for each simulation scenario although they do not change*/ + //Save jobs and meshes for each simulation scenario + + //Save the reduced and full patterns + + const int numberOfSimulationJobs = fullPatternSimulationJobs.size(); + for (int simulationScenarioIndex = 0; simulationScenarioIndex < numberOfSimulationJobs; + simulationScenarioIndex++) { + const std::shared_ptr &pFullPatternSimulationJob + = fullPatternSimulationJobs[simulationScenarioIndex]; + std::filesystem::path simulationJobFolderPath( + std::filesystem::path(saveToPath).append(pFullPatternSimulationJob->label)); + std::filesystem::create_directory(simulationJobFolderPath); + const auto fullPatternDirectoryPath = std::filesystem::path(simulationJobFolderPath) + .append("Full"); + std::filesystem::create_directory(fullPatternDirectoryPath); + pFullPatternSimulationJob->save(fullPatternDirectoryPath.string()); + const std::shared_ptr &pReducedPatternSimulationJob + = reducedPatternSimulationJobs[simulationScenarioIndex]; + const auto reducedPatternDirectoryPath + = std::filesystem::path(simulationJobFolderPath).append("Reduced"); + if (!std::filesystem::exists(reducedPatternDirectoryPath)) { + std::filesystem::create_directory(reducedPatternDirectoryPath); + } + pReducedPatternSimulationJob->save(reducedPatternDirectoryPath.string()); + } + + std::cout << "Saved optimization results to:" << saveToPath << std::endl; + } + + template + static void applyOptimizationResults_innerHexagon( + const ReducedPatternOptimization::Results &reducedPattern_optimizationResults, + const vcg::Triangle3 &patternBaseTriangle, + MeshType &reducedPattern) + { + std::unordered_map + optimalXVariables(reducedPattern_optimizationResults.optimalXNameValuePairs.begin(), + reducedPattern_optimizationResults.optimalXNameValuePairs.end()); + assert(optimalXVariables.contains("HexSize") && optimalXVariables.contains("HexAngle")); + + //Set optimal geometrical params of the reduced pattern + CoordType center_barycentric(1, 0, 0); + CoordType interfaceEdgeMiddle_barycentric(0, 0.5, 0.5); + CoordType movableVertex_barycentric((center_barycentric + interfaceEdgeMiddle_barycentric) + * optimalXVariables.at("HexSize")); + reducedPattern.vert[1].P() = patternBaseTriangle.cP(0) * movableVertex_barycentric[0] + + patternBaseTriangle.cP(1) * movableVertex_barycentric[1] + + patternBaseTriangle.cP(2) * movableVertex_barycentric[2]; + reducedPattern.vert[1].P() = vcg::RotationMatrix(CoordType{0, 0, 1}, + vcg::math::ToRad( + optimalXVariables.at("HexAngle"))) + * reducedPattern.vert[1].cP(); + } + + static void applyOptimizationResults_elements( + const ReducedPatternOptimization::Results &reducedPattern_optimizationResults, + const shared_ptr &pTiledReducedPattern_simulationMesh) + { + // Set reduced parameters fron the optimization results + std::unordered_map + optimalXVariables(reducedPattern_optimizationResults.optimalXNameValuePairs.begin(), + reducedPattern_optimizationResults.optimalXNameValuePairs.end()); + + const std::string ALabel = "A"; + assert(optimalXVariables.contains(ALabel)); + const double A = optimalXVariables.at(ALabel); + const double beamWidth = std::sqrt(A); + const double beamHeight = beamWidth; + RectangularBeamDimensions elementDimensions(beamWidth, beamHeight); + + const double poissonsRatio = 0.3; + const std::string ymLabel = "E"; + assert(optimalXVariables.contains(ymLabel)); + const double E = optimalXVariables.at(ymLabel); + const ElementMaterial elementMaterial(poissonsRatio, E); + + const std::string JLabel = "J"; + assert(optimalXVariables.contains(JLabel)); + const double J = optimalXVariables.at(JLabel); + + const std::string I2Label = "I2"; + assert(optimalXVariables.contains(I2Label)); + const double I2 = optimalXVariables.at(I2Label); + + const std::string I3Label = "I3"; + assert(optimalXVariables.contains(I3Label)); + const double I3 = optimalXVariables.at(I3Label); + for (int ei = 0; ei < pTiledReducedPattern_simulationMesh->EN(); ei++) { + Element &e = pTiledReducedPattern_simulationMesh->elements[ei]; + e.setDimensions(elementDimensions); + e.setMaterial(elementMaterial); + e.J = J; + e.I2 = I2; + e.I3 = I3; + } + pTiledReducedPattern_simulationMesh->reset(); + } + + void load(const string &loadFromPath) + { + assert(std::filesystem::is_directory(loadFromPath)); + //Load optimal X + nlohmann::json json_optimizationResults; + std::ifstream ifs(std::filesystem::path(loadFromPath).append(JsonKeys::filename)); + ifs >> json_optimizationResults; + + std::string optimizationVariablesString = *json_optimizationResults.find( + JsonKeys::optimizationVariables); + std::string optimizationVariablesDelimeter = ","; + size_t pos = 0; + std::vector optimizationVariablesNames; + while ((pos = optimizationVariablesString.find(optimizationVariablesDelimeter)) + != std::string::npos) { + const std::string variableName = optimizationVariablesString.substr(0, pos); + optimizationVariablesNames.push_back(variableName); + optimizationVariablesString.erase(0, pos + optimizationVariablesDelimeter.length()); + } + optimizationVariablesNames.push_back(optimizationVariablesString); //add last variable name + + optimalXNameValuePairs.resize(optimizationVariablesNames.size()); + for (int xVariable_index = 0; xVariable_index < optimizationVariablesNames.size(); + xVariable_index++) { + const std::string xVariable_name = optimizationVariablesNames[xVariable_index]; + const double xVariable_value = *json_optimizationResults.find(xVariable_name); + optimalXNameValuePairs[xVariable_index] = std::make_pair(xVariable_name, + xVariable_value); + } + + std::vector baseTriangleVertices = json_optimizationResults.at( + JsonKeys::baseTriangle); + baseTriangle.P0(0) = CoordType(baseTriangleVertices[0], + baseTriangleVertices[1], + baseTriangleVertices[2]); + baseTriangle.P1(0) = CoordType(baseTriangleVertices[3], + baseTriangleVertices[4], + baseTriangleVertices[5]); + baseTriangle.P2(0) = CoordType(baseTriangleVertices[6], + baseTriangleVertices[7], + baseTriangleVertices[8]); + + for (const auto &directoryEntry : filesystem::directory_iterator(loadFromPath)) { + const auto simulationScenarioPath = directoryEntry.path(); + if (!std::filesystem::is_directory(simulationScenarioPath)) { + continue; + } + // Load reduced pattern files + for (const auto &fileEntry : filesystem::directory_iterator( + std::filesystem::path(simulationScenarioPath).append("Full"))) { + const auto filepath = fileEntry.path(); + if (filepath.extension() == ".json") { + SimulationJob job; + + job.load(filepath.string()); + fullPatternSimulationJobs.push_back(std::make_shared(job)); + } + } + + // Load full pattern files + for (const auto &fileEntry : filesystem::directory_iterator( + std::filesystem::path(simulationScenarioPath).append("Reduced"))) { + const auto filepath = fileEntry.path(); + if (filepath.extension() == ".json") { + SimulationJob job; + job.load(filepath.string()); + applyOptimizationResults_innerHexagon(*this, baseTriangle, *job.pMesh); + applyOptimizationResults_elements(*this, job.pMesh); + reducedPatternSimulationJobs.push_back(std::make_shared(job)); + } + } + } + } +#if POLYSCOPE_DEFINED + void draw() const { + initPolyscope(); + DRMSimulationModel drmSimulator; + LinearSimulationModel linearSimulator; + assert(fullPatternSimulationJobs.size() == + reducedPatternSimulationJobs.size()); + fullPatternSimulationJobs[0]->pMesh->registerForDrawing( + Colors::fullInitial); + reducedPatternSimulationJobs[0]->pMesh->registerForDrawing( + Colors::reducedInitial, false); + + const int numberOfSimulationJobs = fullPatternSimulationJobs.size(); + for (int simulationJobIndex = 0; + simulationJobIndex < numberOfSimulationJobs; simulationJobIndex++) { + // Drawing of full pattern results + const std::shared_ptr &pFullPatternSimulationJob = + fullPatternSimulationJobs[simulationJobIndex]; + pFullPatternSimulationJob->registerForDrawing( + fullPatternSimulationJobs[0]->pMesh->getLabel()); + SimulationResults fullModelResults = drmSimulator.executeSimulation( + pFullPatternSimulationJob); + fullModelResults.registerForDrawing(Colors::fullDeformed, true, true); + // SimulationResults fullModelLinearResults = + // linearSimulator.executeSimulation(pFullPatternSimulationJob); + // fullModelLinearResults.setLabelPrefix("linear"); + // fullModelLinearResults.registerForDrawing(Colors::fullDeformed,false); + // Drawing of reduced pattern results + const std::shared_ptr &pReducedPatternSimulationJob + = reducedPatternSimulationJobs[simulationJobIndex]; + // SimulationResults reducedModelResults = drmSimulator.executeSimulation( + // pReducedPatternSimulationJob); + // reducedModelResults.registerForDrawing(Colors::reducedDeformed, false); + SimulationResults reducedModelLinearResults = + linearSimulator.executeSimulation(pReducedPatternSimulationJob); + reducedModelLinearResults.setLabelPrefix("linear"); + reducedModelLinearResults.registerForDrawing(Colors::reducedDeformed, true, true); + polyscope::options::programName = + fullPatternSimulationJobs[0]->pMesh->getLabel(); + polyscope::view::resetCameraToHomeView(); + polyscope::show(); + // Save a screensh + const std::string screenshotFilename = + "/home/iason/Coding/Projects/Approximating shapes with flat " + "patterns/RodModelOptimizationForPatterns/Results/Images/" + + fullPatternSimulationJobs[0]->pMesh->getLabel() + "_" + + pFullPatternSimulationJob->getLabel(); + polyscope::screenshot(screenshotFilename, false); + fullModelResults.unregister(); + // reducedModelResults.unregister(); + reducedModelLinearResults.unregister(); + // fullModelLinearResults.unregister(); + // double error = computeError( + // reducedModelResults.displacements,fullModelResults.displacements, + // ); + // std::cout << "Error of simulation scenario " + // << + // simula simulationScenarioStrings[simulationScenarioIndex] + // << " is " + // << error << std::endl; + } + } +#endif // POLYSCOPE_DEFINED + void saveMeshFiles() const { + const int numberOfSimulationJobs = fullPatternSimulationJobs.size(); + assert(numberOfSimulationJobs != 0 && + fullPatternSimulationJobs.size() == + reducedPatternSimulationJobs.size()); + + fullPatternSimulationJobs[0]->pMesh->save( + "undeformed " + fullPatternSimulationJobs[0]->pMesh->getLabel() + ".ply"); + reducedPatternSimulationJobs[0]->pMesh->save( + "undeformed " + reducedPatternSimulationJobs[0]->pMesh->getLabel() + ".ply"); + DRMSimulationModel simulator_drm; + LinearSimulationModel simulator_linear; + for (int simulationJobIndex = 0; simulationJobIndex < numberOfSimulationJobs; + simulationJobIndex++) { + // Drawing of full pattern results + const std::shared_ptr &pFullPatternSimulationJob + = fullPatternSimulationJobs[simulationJobIndex]; + SimulationResults fullModelResults = simulator_drm.executeSimulation( + pFullPatternSimulationJob); + fullModelResults.saveDeformedModel(); + + // Drawing of reduced pattern results + const std::shared_ptr &pReducedPatternSimulationJob + = reducedPatternSimulationJobs[simulationJobIndex]; + SimulationResults reducedModelResults = simulator_linear.executeSimulation( + pReducedPatternSimulationJob); + reducedModelResults.saveDeformedModel(); + } + } + + void writeHeaderTo(csvFile &os) + { + os << "Total raw Obj value"; + os << "Total Obj value"; + for (int simulationScenarioIndex = 0; + simulationScenarioIndex < fullPatternSimulationJobs.size(); + simulationScenarioIndex++) { + const std::string simulationScenarioName + = fullPatternSimulationJobs[simulationScenarioIndex]->getLabel(); + os << "Obj value Trans " + simulationScenarioName; + os << "Obj value Rot " + simulationScenarioName; + os << "Obj value Total " + simulationScenarioName; + } + for (const auto &nameValuePair : optimalXNameValuePairs) { + os << nameValuePair.first; + } + os << "Time(s)"; + os << "#Crashes"; + } + + void writeResultsTo(const Settings &settings_optimization, csvFile &os) const + { + os << objectiveValue.totalRaw; + os << objectiveValue.total; + for (int simulationScenarioIndex = 0; + simulationScenarioIndex < fullPatternSimulationJobs.size(); + simulationScenarioIndex++) { + os << objectiveValue.perSimulationScenario_translational[simulationScenarioIndex]; + os << objectiveValue.perSimulationScenario_rotational[simulationScenarioIndex]; + os << objectiveValue.perSimulationScenario_total[simulationScenarioIndex]; + } + for (const auto &optimalXNameValuePair : optimalXNameValuePairs) { + os << optimalXNameValuePair.second; + } + + os << time; + if (numberOfSimulationCrashes == 0) { + os << "-"; + } else { + os << numberOfSimulationCrashes; + } + } + }; + + } // namespace ReducedPatternOptimization +#endif // REDUCEDMODELOPTIMIZER_STRUCTS_HPP diff --git a/simulationresult.hpp b/simulation_structs.hpp similarity index 78% rename from simulationresult.hpp rename to simulation_structs.hpp index 85968a1..159dae5 100755 --- a/simulationresult.hpp +++ b/simulation_structs.hpp @@ -1,7 +1,7 @@ -#ifndef SIMULATIONHISTORY_HPP -#define SIMULATIONHISTORY_HPP +#ifndef SIMULATIONSTRUCTS_HPP +#define SIMULATIONSTRUCTS_HPP -#include "elementalmesh.hpp" +#include "simulationmesh.hpp" #include "nlohmann/json.hpp" struct SimulationHistory { @@ -32,6 +32,7 @@ struct SimulationHistory { } }; + namespace nlohmann { template <> struct adl_serializer> { @@ -202,7 +203,7 @@ public: std::filesystem::path(pathFolderDirectory))) .append(pMesh->getLabel() + ".ply") .string(); - returnValue = pMesh->savePly(meshFilename); + returnValue = pMesh->save(meshFilename); nlohmann::json json; json[jsonLabels.meshFilename] = std::filesystem::relative( std::filesystem::path(meshFilename), @@ -333,6 +334,8 @@ struct SimulationResults { std::shared_ptr job; SimulationHistory history; std::vector displacements; + std::vector> rotationalDisplacementQuaternion; //per vertex + double internalPotentialEnergy{0}; double executionTime{0}; std::string labelPrefix{"deformed"}; inline static char deliminator{' '}; @@ -364,7 +367,7 @@ struct SimulationResults { m.vert[vi].P() + CoordType(displacements[vi][0], displacements[vi][1], displacements[vi][2]); } - m.savePly(getLabel() + ".ply"); + m.save(getLabel() + ".ply"); } void save() { @@ -395,41 +398,71 @@ struct SimulationResults { } polyscope::removeCurveNetwork(getLabel()); } - void registerForDrawing( - const std::optional &desiredColor = std::nullopt, - const bool &shouldEnable = true) const { - polyscope::options::groundPlaneEnabled = false; - polyscope::view::upDir = polyscope::view::UpDir::ZUp; - const std::string branchName = "Branch:Polyscope"; - polyscope::options::programName = branchName; - if (!polyscope::state::initialized) { - polyscope::init(); - } /* else { + void registerForDrawing(const std::optional> &desiredColor = std::nullopt, + const bool &shouldEnable = true, + const bool &showFrames = false) const + { + polyscope::options::groundPlaneEnabled = false; + polyscope::view::upDir = polyscope::view::UpDir::ZUp; + if (!polyscope::state::initialized) { + polyscope::init(); + } /* else { polyscope::removeAllStructures(); }*/ - // const std::string undeformedMeshName = "Undeformed_" + label; - // polyscope::registerCurveNetwork( - // undeformedMeshName, mesh->getEigenVertices(), - // mesh->getEigenEdges()); + // const std::string undeformedMeshName = "Undeformed_" + label; + // polyscope::registerCurveNetwork( + // undeformedMeshName, mesh->getEigenVertices(), + // mesh->getEigenEdges()); - const std::shared_ptr &mesh = job->pMesh; - auto polyscopeHandle_deformedEdmeMesh = polyscope::registerCurveNetwork( - getLabel(), mesh->getEigenVertices(), mesh->getEigenEdges()); - polyscopeHandle_deformedEdmeMesh->setEnabled(shouldEnable); - polyscopeHandle_deformedEdmeMesh->setRadius(0.002, true); - if (desiredColor.has_value()) { - polyscopeHandle_deformedEdmeMesh->setColor(desiredColor.value()); - } - Eigen::MatrixX3d nodalDisplacements(mesh->VN(), 3); - for (VertexIndex vi = 0; vi < mesh->VN(); vi++) { - const Vector6d &nodalDisplacement = displacements[vi]; - nodalDisplacements.row(vi) = Eigen::Vector3d( - nodalDisplacement[0], nodalDisplacement[1], nodalDisplacement[2]); - } - polyscopeHandle_deformedEdmeMesh->updateNodePositions( - mesh->getEigenVertices() + nodalDisplacements); + const std::shared_ptr &mesh = job->pMesh; + auto polyscopeHandle_deformedEdmeMesh + = polyscope::registerCurveNetwork(getLabel(), + mesh->getEigenVertices(), + mesh->getEigenEdges()); + polyscopeHandle_deformedEdmeMesh->setEnabled(shouldEnable); + polyscopeHandle_deformedEdmeMesh->setRadius(0.002, true); + if (desiredColor.has_value()) { + const glm::vec3 desiredColor_glm(desiredColor.value()[0], + desiredColor.value()[1], + desiredColor.value()[2]); + polyscopeHandle_deformedEdmeMesh->setColor(desiredColor_glm); + } + Eigen::MatrixX3d nodalDisplacements(mesh->VN(), 3); + Eigen::MatrixX3d framesX(mesh->VN(), 3); + Eigen::MatrixX3d framesY(mesh->VN(), 3); + Eigen::MatrixX3d framesZ(mesh->VN(), 3); + for (VertexIndex vi = 0; vi < mesh->VN(); vi++) { + const Vector6d &nodalDisplacement = displacements[vi]; + nodalDisplacements.row(vi) = Eigen::Vector3d(nodalDisplacement[0], + nodalDisplacement[1], + nodalDisplacement[2]); + auto deformedNormal = rotationalDisplacementQuaternion[vi] * Eigen::Vector3d(0, 0, 1); + auto deformedFrameY = rotationalDisplacementQuaternion[vi] * Eigen::Vector3d(0, 1, 0); + auto deformedFrameX = rotationalDisplacementQuaternion[vi] * Eigen::Vector3d(1, 0, 0); + framesX.row(vi) = Eigen::Vector3d(deformedFrameX[0], deformedFrameX[1], deformedFrameX[2]); + framesY.row(vi) = Eigen::Vector3d(deformedFrameY[0], deformedFrameY[1], deformedFrameY[2]); + framesZ.row(vi) = Eigen::Vector3d(deformedNormal[0], deformedNormal[1], deformedNormal[2]); + } + polyscopeHandle_deformedEdmeMesh->updateNodePositions(mesh->getEigenVertices() + + nodalDisplacements); - job->registerForDrawing(getLabel()); + polyscopeHandle_deformedEdmeMesh->addNodeVectorQuantity("FrameX", framesX) + ->setVectorLengthScale(0.1) + ->setVectorRadius(0.01) + ->setVectorColor(/*polyscope::getNextUniqueColor()*/ glm::vec3(1, 0, 0)) + ->setEnabled(showFrames); + polyscopeHandle_deformedEdmeMesh->addNodeVectorQuantity("FrameY", framesY) + ->setVectorLengthScale(0.1) + ->setVectorRadius(0.01) + ->setVectorColor(/*polyscope::getNextUniqueColor()*/ glm::vec3(0, 1, 0)) + ->setEnabled(showFrames); + polyscopeHandle_deformedEdmeMesh->addNodeVectorQuantity("FrameZ", framesZ) + ->setVectorLengthScale(0.1) + ->setVectorRadius(0.01) + ->setVectorColor(/*polyscope::getNextUniqueColor()*/ glm::vec3(0, 0, 1)) + ->setEnabled(showFrames); + + job->registerForDrawing(getLabel()); } #endif }; diff --git a/simulationhistoryplotter.hpp b/simulationhistoryplotter.hpp index 02ea8e6..9beb5fd 100755 --- a/simulationhistoryplotter.hpp +++ b/simulationhistoryplotter.hpp @@ -1,8 +1,8 @@ #ifndef SIMULATIONHISTORYPLOTTER_HPP #define SIMULATIONHISTORYPLOTTER_HPP -#include "elementalmesh.hpp" -#include "simulationresult.hpp" +#include "simulationmesh.hpp" +#include "simulation_structs.hpp" #include "utilities.hpp" #include #include diff --git a/elementalmesh.cpp b/simulationmesh.cpp similarity index 68% rename from elementalmesh.cpp rename to simulationmesh.cpp index 264f38a..ec3ad3e 100755 --- a/elementalmesh.cpp +++ b/simulationmesh.cpp @@ -1,83 +1,59 @@ -#include "elementalmesh.hpp" +#include "simulationmesh.hpp" #include SimulationMesh::SimulationMesh() { - elements = vcg::tri::Allocator::GetPerEdgeAttribute( + elements = vcg::tri::Allocator::GetPerEdgeAttribute( *this, std::string("Elements")); - nodes = vcg::tri::Allocator::GetPerVertexAttribute( + nodes = vcg::tri::Allocator::GetPerVertexAttribute( *this, std::string("Nodes")); } SimulationMesh::SimulationMesh(VCGEdgeMesh &mesh) { vcg::tri::MeshAssert::VertexNormalNormalized(mesh); - // bool containsNormals = true; - // for (VertexIterator vi = mesh.vert.begin(); vi != mesh.vert.end(); ++vi) - // if (!vi->IsD()) { - // if (fabs(vi->cN().Norm() - 1.0) > 0.000001) { - // containsNormals = false; - // break; - // } - // } - // if (!containsNormals) { - // for (VertexIterator vi = mesh.vert.begin(); vi != mesh.vert.end(); ++vi) - // if (!vi->IsD()) { - // vi->N() = CoordType(1, 0, 0); - // } - // } + VCGEdgeMesh::copy(mesh); - vcg::tri::Append::MeshCopy(*this, mesh); - elements = vcg::tri::Allocator::GetPerEdgeAttribute( + elements = vcg::tri::Allocator::GetPerEdgeAttribute( *this, std::string("Elements")); - nodes = vcg::tri::Allocator::GetPerVertexAttribute( + nodes = vcg::tri::Allocator::GetPerVertexAttribute( *this, std::string("Nodes")); - vcg::tri::UpdateTopology::VertexEdge(*this); initializeNodes(); initializeElements(); - label = mesh.getLabel(); - eigenEdges = mesh.getEigenEdges(); - eigenVertices = mesh.getEigenVertices(); } SimulationMesh::~SimulationMesh() { - vcg::tri::Allocator::DeletePerEdgeAttribute(*this, + vcg::tri::Allocator::DeletePerEdgeAttribute(*this, elements); - vcg::tri::Allocator::DeletePerVertexAttribute(*this, + vcg::tri::Allocator::DeletePerVertexAttribute(*this, nodes); } SimulationMesh::SimulationMesh(PatternGeometry &pattern) { vcg::tri::MeshAssert::VertexNormalNormalized(pattern); + VCGEdgeMesh::copy(pattern); - vcg::tri::Append::MeshCopy(*this, pattern); - elements = vcg::tri::Allocator::GetPerEdgeAttribute( + elements = vcg::tri::Allocator::GetPerEdgeAttribute( *this, std::string("Elements")); - nodes = vcg::tri::Allocator::GetPerVertexAttribute( + nodes = vcg::tri::Allocator::GetPerVertexAttribute( *this, std::string("Nodes")); - vcg::tri::UpdateTopology::VertexEdge(*this); initializeNodes(); initializeElements(); - label = pattern.getLabel(); - eigenEdges = pattern.getEigenEdges(); - eigenVertices = pattern.getEigenVertices(); } SimulationMesh::SimulationMesh(SimulationMesh &mesh) { - vcg::tri::Append::MeshCopy(*this, mesh); - elements = vcg::tri::Allocator::GetPerEdgeAttribute( + vcg::tri::MeshAssert::VertexNormalNormalized(mesh); + VCGEdgeMesh::copy(mesh); + + elements = vcg::tri::Allocator::GetPerEdgeAttribute( *this, std::string("Elements")); - nodes = vcg::tri::Allocator::GetPerVertexAttribute( + nodes = vcg::tri::Allocator::GetPerVertexAttribute( *this, std::string("Nodes")); - vcg::tri::UpdateTopology::VertexEdge(*this); initializeNodes(); for (size_t ei = 0; ei < EN(); ei++) { elements[ei] = mesh.elements[ei]; } - - label = mesh.label; - eigenEdges = mesh.getEigenEdges(); - eigenVertices = mesh.getEigenVertices(); + reset(); } void SimulationMesh::computeElementalProperties() { @@ -106,36 +82,48 @@ void SimulationMesh::initializeNodes() { node.displacements[3] = v.cN()[0]; // initialize nx diplacement with vertex normal x // component - node.displacements[4] = - v.cN()[1]; // initialize ny(in the paper) diplacement with vertex + node.displacements[4] = v.cN()[1]; // initialize ny(in the paper) diplacement with vertex // normal // y component. // Initialize incident elements std::vector incidentElements; vcg::edge::VEStarVE(&v, incidentElements); - assert( - vcg::tri::IsValidPointer(*this, incidentElements[0]) && - incidentElements.size() > 0); - nodes[v].incidentElements = incidentElements; - node.referenceElement = getReferenceElement(v); - // Initialze alpha angles + // assert( + // vcg::tri::IsValidPointer(*this, incidentElements[0]) && + // incidentElements.size() > 0); + if (incidentElements.size() != 0) { + nodes[v].incidentElements = incidentElements; + node.referenceElement = getReferenceElement(v); + // std::vector incidentElementsIndices(node.incidentElements.size()); + // if (drawGlobal && vi == 5) { + // std::vector edgeColors(EN(), glm::vec3(0, 1, 0)); + // std::vector vertexColors(VN(), glm::vec3(0, 1, 0)); + // vertexColors[vi] = glm::vec3(0, 0, 1); + // for (int iei = 0; iei < incidentElementsIndices.size(); iei++) { + // incidentElementsIndices[iei] = this->getIndex(node.incidentElements[iei]); + // edgeColors[incidentElementsIndices[iei]] = glm::vec3(1, 0, 0); + // } + // polyHandle->addEdgeColorQuantity("chosenE", edgeColors)->setEnabled(true); + // polyHandle->addNodeColorQuantity("chosenV", vertexColors)->setEnabled(true); + // draw(); + // } + // const int referenceElementIndex = getIndex(node.referenceElement); + // Initialze alpha angles - const EdgeType &referenceElement = *getReferenceElement(v); - const VectorType t01 = - computeT1Vector(referenceElement.cP(0), referenceElement.cP(1)); - const VectorType f01 = (t01 - (v.cN() * (t01.dot(v.cN())))).Normalize(); + const EdgeType &referenceElement = *node.referenceElement; + const VectorType t01 = computeT1Vector(referenceElement.cP(0), referenceElement.cP(1)); + const VectorType f01 = (t01 - (v.cN() * (t01.dot(v.cN())))).Normalize(); - for (const VCGEdgeMesh::EdgePointer &ep : nodes[v].incidentElements) { - assert(referenceElement.cV(0) == ep->cV(0) || - referenceElement.cV(0) == ep->cV(1) || - referenceElement.cV(1) == ep->cV(0) || - referenceElement.cV(1) == ep->cV(1)); - const VectorType t1 = computeT1Vector(*ep); - const VectorType f1 = t1 - (v.cN() * (t1.dot(v.cN()))).Normalize(); - const EdgeIndex ei = getIndex(ep); - const double alphaAngle = computeAngle(f01, f1, v.cN()); - node.alphaAngles[ei] = alphaAngle; + for (const VCGEdgeMesh::EdgePointer &ep : nodes[v].incidentElements) { + assert(referenceElement.cV(0) == ep->cV(0) || referenceElement.cV(0) == ep->cV(1) + || referenceElement.cV(1) == ep->cV(0) || referenceElement.cV(1) == ep->cV(1)); + const VectorType t1 = computeT1Vector(*ep); + const VectorType f1 = t1 - (v.cN() * (t1.dot(v.cN()))).Normalize(); + const EdgeIndex ei = getIndex(ep); + const double alphaAngle = computeAngle(f01, f1, v.cN()); + node.alphaAngles[ei] = alphaAngle; + } } } } @@ -159,29 +147,24 @@ void SimulationMesh::reset() { node.initialNormal = v.cN(); node.derivativeOfNormal.resize(6, VectorType(0, 0, 0)); - node.displacements[3] = - v.cN()[0]; // initialize nx diplacement with vertex normal x - // component - node.displacements[4] = - v.cN()[1]; // initialize ny(in the paper) diplacement with vertex + node.displacements[3] = v.cN()[0]; // initialize nx diplacement with vertex normal x + // component + node.displacements[4] = v.cN()[1]; // initialize ny(in the paper) diplacement with vertex // normal // y component. const EdgeType &referenceElement = *getReferenceElement(v); - const VectorType t01 = - computeT1Vector(referenceElement.cP(0), referenceElement.cP(1)); + const VectorType t01 = computeT1Vector(referenceElement.cP(0), referenceElement.cP(1)); const VectorType f01 = (t01 - (v.cN() * (t01.dot(v.cN())))).Normalize(); for (const VCGEdgeMesh::EdgePointer &ep : nodes[v].incidentElements) { - assert(referenceElement.cV(0) == ep->cV(0) || - referenceElement.cV(0) == ep->cV(1) || - referenceElement.cV(1) == ep->cV(0) || - referenceElement.cV(1) == ep->cV(1)); - const VectorType t1 = computeT1Vector(*ep); - const VectorType f1 = t1 - (v.cN() * (t1.dot(v.cN()))).Normalize(); - const EdgeIndex ei = getIndex(ep); - const double alphaAngle = computeAngle(f01, f1, v.cN()); - node.alphaAngles[ei] = alphaAngle; + assert(referenceElement.cV(0) == ep->cV(0) || referenceElement.cV(0) == ep->cV(1) + || referenceElement.cV(1) == ep->cV(0) || referenceElement.cV(1) == ep->cV(1)); + const VectorType t1 = computeT1Vector(*ep); + const VectorType f1 = t1 - (v.cN() * (t1.dot(v.cN()))).Normalize(); + const EdgeIndex ei = getIndex(ep); + const double alphaAngle = computeAngle(f01, f1, v.cN()); + node.alphaAngles[ei] = alphaAngle; } } } @@ -358,38 +341,44 @@ bool SimulationMesh::load(const string &plyFilename) { return true; } -bool SimulationMesh::savePly(const std::string &plyFilename) { - nanoply::NanoPlyWrapper::CustomAttributeDescriptor customAttrib; - customAttrib.GetMeshAttrib(plyFilename); +bool SimulationMesh::save(const std::string &plyFilename) +{ + nanoply::NanoPlyWrapper::CustomAttributeDescriptor customAttrib; + customAttrib.GetMeshAttrib(plyFilename); - std::vector dimensions = getBeamDimensions(); - customAttrib.AddEdgeAttribDescriptor( - plyPropertyBeamDimensionsID, nanoply::NNP_LIST_INT8_FLOAT64, - dimensions.data()); - std::vector material = getBeamMaterial(); - customAttrib.AddEdgeAttribDescriptor( - plyPropertyBeamMaterialID, nanoply::NNP_LIST_INT8_FLOAT64, - material.data()); - unsigned int mask = 0; - mask |= nanoply::NanoPlyWrapper::IO_VERTCOORD; - mask |= nanoply::NanoPlyWrapper::IO_EDGEINDEX; - mask |= nanoply::NanoPlyWrapper::IO_EDGEATTRIB; - mask |= nanoply::NanoPlyWrapper::IO_VERTNORMAL; - if (nanoply::NanoPlyWrapper::SaveModel( - plyFilename.c_str(), *this, mask, customAttrib, false) != 1) { - return false; - } - return true; + std::vector dimensions = getBeamDimensions(); + customAttrib.AddEdgeAttribDescriptor(plyPropertyBeamDimensionsID, + nanoply::NNP_LIST_INT8_FLOAT64, + dimensions.data()); + std::vector material = getBeamMaterial(); + customAttrib.AddEdgeAttribDescriptor(plyPropertyBeamMaterialID, + nanoply::NNP_LIST_INT8_FLOAT64, + material.data()); + unsigned int mask = 0; + mask |= nanoply::NanoPlyWrapper::IO_VERTCOORD; + mask |= nanoply::NanoPlyWrapper::IO_EDGEINDEX; + mask |= nanoply::NanoPlyWrapper::IO_EDGEATTRIB; + mask |= nanoply::NanoPlyWrapper::IO_VERTNORMAL; + if (nanoply::NanoPlyWrapper::SaveModel(plyFilename.c_str(), + *this, + mask, + customAttrib, + false) + != 1) { + return false; + } + return true; } SimulationMesh::EdgePointer SimulationMesh::getReferenceElement(const VCGEdgeMesh::VertexType &v) { - const VertexIndex vi = getIndex(v); - return nodes[v].incidentElements[0]; + const VertexIndex vi = getIndex(v); + return nodes[v].incidentElements[0]; } -VectorType computeT1Vector(const SimulationMesh::EdgeType &e) { - return computeT1Vector(e.cP(0), e.cP(1)); +VectorType computeT1Vector(const SimulationMesh::EdgeType &e) +{ + return computeT1Vector(e.cP(0), e.cP(1)); } VectorType computeT1Vector(const CoordType &p0, const CoordType &p1) { diff --git a/elementalmesh.hpp b/simulationmesh.hpp similarity index 96% rename from elementalmesh.hpp rename to simulationmesh.hpp index 1e46590..e815e51 100755 --- a/elementalmesh.hpp +++ b/simulationmesh.hpp @@ -1,10 +1,11 @@ -#ifndef ELEMENTALMESH_HPP -#define ELEMENTALMESH_HPP +#ifndef SIMULATIONMESH_HPP +#define SIMULATIONMESH_HPP #include "Eigen/Dense" #include "edgemesh.hpp" #include "trianglepatterngeometry.hpp" +//extern bool drawGlobal; struct Element; struct Node; using CrossSectionType = RectangularBeamDimensions; @@ -28,6 +29,7 @@ public: SimulationMesh(ConstVCGEdgeMesh &edgeMesh); SimulationMesh(SimulationMesh &elementalMesh); void updateElementalLengths(); + void updateIncidentElements(); std::vector getIncidentElements(const VCGEdgeMesh::VertexType &v); @@ -42,7 +44,7 @@ public: double totalResidualForcesNorm{0}; double currentTotalPotentialEnergykN{0}; double previousTotalPotentialEnergykN{0}; - bool savePly(const std::string &plyFilename); + bool save(const std::string &plyFilename); void setBeamCrossSection(const CrossSectionType &beamDimensions); void setBeamMaterial(const double &pr, const double &ym); void reset(); @@ -162,7 +164,7 @@ struct Node { Element::LocalFrame computeElementFrame(const CoordType &p0, const CoordType &p1, const VectorType &elementNormal); -VectorType computeT1Vector(const ::SimulationMesh::EdgeType &e); +VectorType computeT1Vector(const SimulationMesh::EdgeType &e); VectorType computeT1Vector(const CoordType &p0, const CoordType &p1); double computeAngle(const VectorType &vector0, const VectorType &vector1, diff --git a/simulationresult.hpp.orig b/simulationresult.hpp.orig deleted file mode 100755 index 4a65a58..0000000 --- a/simulationresult.hpp.orig +++ /dev/null @@ -1,397 +0,0 @@ -#ifndef SIMULATIONHISTORY_HPP -#define SIMULATIONHISTORY_HPP - -#include "elementalmesh.hpp" -#include "nlohmann/json.hpp" - -struct SimulationHistory { - SimulationHistory() {} - - size_t numberOfSteps{0}; - std::string label; - std::vector residualForces; - std::vector kineticEnergy; - std::vector potentialEnergies; - std::vector redMarks; - std::vector greenMarks; - - void markRed(const size_t &stepNumber) { redMarks.push_back(stepNumber); } - - void markGreen(const size_t &stepNumber) { greenMarks.push_back(stepNumber); } - - void stepPulse(const SimulationMesh &mesh) { - kineticEnergy.push_back(log(mesh.currentTotalKineticEnergy)); - // potentialEnergy.push_back(mesh.totalPotentialEnergykN); - residualForces.push_back(mesh.totalResidualForcesNorm); - } - - void clear() { - residualForces.clear(); - kineticEnergy.clear(); - potentialEnergies.clear(); - } -}; - -namespace nlohmann { - -template <> struct adl_serializer> { - static void to_json(json &j, - const std::unordered_map &value) { - // calls the "to_json" method in T's namespace - } - - static void from_json(const nlohmann::json &j, - std::unordered_map &m) { - std::cout << "Entered." << std::endl; - for (const auto &p : j) { - m.emplace(p.at(0).template get(), - p.at(1).template get>()); - } - } -}; -} // namespace nlohmann - -class SimulationJob { - // const std::unordered_map nodalForcedNormals; - // json labels - struct JSONLabels { - inline static std::string meshFilename{"mesh filename"}; - inline static std::string constrainedVertices{"fixed vertices"}; - inline static std::string nodalForces{"forces"}; - inline static std::string label{"label"}; - inline static std::string meshLabel{ - "label"}; // TODO: should be in the savePly function of the simulation - // mesh class - } jsonLabels; - -public: - std::shared_ptr pMesh; - std::string label{"empty_job"}; - std::unordered_map> constrainedVertices; - std::unordered_map nodalExternalForces; - std::unordered_map nodalForcedDisplacements; - SimulationJob( - const std::shared_ptr &m, const std::string &label, - const std::unordered_map> &cv, - const std::unordered_map &ef = {}, - const std::unordered_map &fd = {}) - : pMesh(m), label(label), constrainedVertices(cv), - nodalExternalForces(ef), nodalForcedDisplacements(fd) {} - SimulationJob() {} - SimulationJob(const std::string &jsonFilename) { load(jsonFilename); } - std::string getLabel() const { return label; } - - std::string toString() const { - nlohmann::json json; - if (!constrainedVertices.empty()) { - json[jsonLabels.constrainedVertices] = constrainedVertices; - } - if (!nodalExternalForces.empty()) { - std::unordered_map> arrForces; - for (const auto &f : nodalExternalForces) { - arrForces[f.first] = f.second; - } - json[jsonLabels.nodalForces] = arrForces; - } - - return json.dump(); - } - - void registerForDrawing(const std::string &meshLabel) const { - initPolyscope(); - if (meshLabel.empty()) { - assert(false); - std::cerr << "Expects a mesh label on which to draw the simulation job." - << std::endl; - return; - } - - auto structs = polyscope::state::structures; - if (!polyscope::hasCurveNetwork(meshLabel)) { - assert(false); - std::cerr << "Expects mesh already being registered to draw the " - "simulation job. No struct named " + - meshLabel - << std::endl; - return; - } - std::vector> nodeColors(pMesh->VN()); - for (auto fixedVertex : constrainedVertices) { - nodeColors[fixedVertex.first] = {0, 0, 1}; - } - if (!nodalForcedDisplacements.empty()) { - for (std::pair viDisplPair : - nodalForcedDisplacements) { - const VertexIndex vi = viDisplPair.first; - nodeColors[vi][0] += 1; - nodeColors[vi][0] /= 2; - nodeColors[vi][1] += 0; - nodeColors[vi][1] /= 2; - nodeColors[vi][2] += 0; - nodeColors[vi][2] /= 2; - } - } - std::for_each(nodeColors.begin(), nodeColors.end(), - [](std::array &color) { - const double norm = - sqrt(std::pow(color[0], 2) + std::pow(color[1], 2) + - std::pow(color[2], 2)); - if (norm > std::pow(10, -7)) { - color[0] /= norm; - color[1] /= norm; - color[2] /= norm; - } - }); - - auto cn = polyscope::getCurveNetwork(meshLabel); - if (!nodeColors.empty()) { - polyscope::getCurveNetwork(meshLabel) - ->addNodeColorQuantity("Boundary conditions_" + label, nodeColors) - ->setEnabled(false); - } - - // per node external forces - std::vector> externalForces(pMesh->VN()); - for (const auto &forcePair : nodalExternalForces) { - auto index = forcePair.first; - auto force = forcePair.second; - externalForces[index] = {force[0], force[1], force[2]}; - } - - if (!externalForces.empty()) { - polyscope::getCurveNetwork(meshLabel) - ->addNodeVectorQuantity("External force_" + label, externalForces) - ->setEnabled(false); - } - } - - bool load(const std::string &jsonFilename) { - if (std::filesystem::path(jsonFilename).extension() != ".json") { - std::cerr << "A json file is expected as input. The given file has the " - "following extension:" - << std::filesystem::path(jsonFilename).extension() << std::endl; - assert(false); - return false; - } - - if (!std::filesystem::exists(std::filesystem::path(jsonFilename))) { - std::cerr << "The json file does not exist. Json file provided:" - << jsonFilename << std::endl; - assert(false); - return false; - } - - std::cout << "Loading json file:" << jsonFilename << std::endl; - nlohmann::json json; - std::ifstream ifs(jsonFilename); - json << ifs; - - pMesh = std::make_shared(); - if (json.contains(jsonLabels.meshFilename)) { - pMesh->loadPly(json[jsonLabels.meshFilename]); - } - - if (json.contains(jsonLabels.constrainedVertices)) { - constrainedVertices = - // auto conV = -<<<<<<< HEAD - std::unordered_map>( - json[jsonLabels.constrainedVertices]); -======= - json[jsonLabel_constrainedVertices].get>>(); ->>>>>>> 2599d472614f403f8249060272ce288033a919d4 - std::cout << "Loaded constrained vertices. Number of constrained " - "vertices found:" - << constrainedVertices.size() << std::endl; - } - -<<<<<<< HEAD - if (json.contains(jsonLabels.nodalForces)) { - auto f = std::unordered_map>( - json[jsonLabels.nodalForces]); -======= - if (json.contains(jsonLabel_nodalForces)) { - auto f ( - json[jsonLabel_nodalForces].get>>()); ->>>>>>> 2599d472614f403f8249060272ce288033a919d4 - for (const auto &forces : f) { - nodalExternalForces[forces.first] = Vector6d(forces.second); - } - std::cout << "Loaded forces. Number of forces found:" - << nodalExternalForces.size() << std::endl; - } - - if (json.contains(jsonLabels.label)) { - label = json[jsonLabels.label]; - } - - if (json.contains(jsonLabels.meshLabel)) { - pMesh->setLabel(json[jsonLabels.meshLabel]); - } - - return true; - } - - bool save(const std::string &folderDirectory) const { - const std::filesystem::path pathFolderDirectory(folderDirectory); - if (!std::filesystem::is_directory(pathFolderDirectory)) { - std::cerr << "A folder directory is expected for saving the simulation " - "job. Exiting.." - << std::endl; - return false; - } - - bool returnValue = true; - const std::string meshFilename = - std::filesystem::absolute( - std::filesystem::canonical( - std::filesystem::path(pathFolderDirectory))) - .append(pMesh->getLabel() + ".ply").string(); - returnValue = pMesh->savePly(meshFilename); - nlohmann::json json; - json[jsonLabels.meshFilename] = meshFilename; - if (!constrainedVertices.empty()) { - json[jsonLabels.constrainedVertices] = constrainedVertices; - } - if (!nodalExternalForces.empty()) { - std::unordered_map> arrForces; - for (const auto &f : nodalExternalForces) { - arrForces[f.first] = f.second; - } - json[jsonLabels.nodalForces] = arrForces; - } - - if (!label.empty()) { - json[jsonLabels.label] = label; - } - if (!pMesh->getLabel().empty()) { - json[jsonLabels.meshLabel] = pMesh->getLabel(); - } - - std::string jsonFilename( - std::filesystem::path(pathFolderDirectory) -<<<<<<< HEAD - .append(label + "_" + pMesh->getLabel() + "_simulationJob.json")); -======= - .append(pMesh->getLabel() + "_simScenario.json").string()); ->>>>>>> 2599d472614f403f8249060272ce288033a919d4 - std::ofstream jsonFile(jsonFilename); - jsonFile << json; - std::cout << "Saved simulation job as:" << jsonFilename << std::endl; - - return returnValue; - } -}; - -namespace Eigen { -template -void write_binary(const std::string &filename, const Matrix &matrix) { - std::ofstream out(filename, - std::ios::out | std::ios::binary | std::ios::trunc); - typename Matrix::Index rows = matrix.rows(), cols = matrix.cols(); - out.write((char *)(&rows), sizeof(typename Matrix::Index)); - out.write((char *)(&cols), sizeof(typename Matrix::Index)); - out.write((char *)matrix.data(), - rows * cols * sizeof(typename Matrix::Scalar)); - out.close(); -} -template -void read_binary(const std::string &filename, Matrix &matrix) { - std::ifstream in(filename, std::ios::in | std::ios::binary); - typename Matrix::Index rows = 0, cols = 0; - in.read((char *)(&rows), sizeof(typename Matrix::Index)); - in.read((char *)(&cols), sizeof(typename Matrix::Index)); - matrix.resize(rows, cols); - in.read((char *)matrix.data(), rows * cols * sizeof(typename Matrix::Scalar)); - in.close(); -} -} // namespace Eigen - -struct SimulationResults { - bool converged{true}; - std::shared_ptr job; - SimulationHistory history; - std::vector displacements; - double executionTime{0}; - std::string labelPrefix{"deformed"}; - inline static char deliminator{' '}; - - void setLabelPrefix(const std::string &lp) { - labelPrefix += deliminator + lp; - } - std::string getLabel() const { - return labelPrefix + deliminator + job->pMesh->getLabel() + deliminator + - job->getLabel(); - } - void unregister() const { - if (!polyscope::hasCurveNetwork(getLabel())) { - std::cerr << "No curve network registered with a name: " << getLabel() - << std::endl; - std::cerr << "Nothing to remove." << std::endl; - return; - } - polyscope::removeCurveNetwork(getLabel()); - } - void registerForDrawing() const { - polyscope::options::groundPlaneEnabled = false; - polyscope::view::upDir = polyscope::view::UpDir::ZUp; - const std::string branchName = "Branch:Polyscope"; - polyscope::options::programName = branchName; - if (!polyscope::state::initialized) { - polyscope::init(); - } /* else { - polyscope::removeAllStructures(); - }*/ - // const std::string undeformedMeshName = "Undeformed_" + label; - // polyscope::registerCurveNetwork( - // undeformedMeshName, mesh->getEigenVertices(), - // mesh->getEigenEdges()); - - const std::shared_ptr &mesh = job->pMesh; - polyscope::registerCurveNetwork(getLabel(), mesh->getEigenVertices(), - mesh->getEigenEdges()) - ->setRadius(0.0007, false); - Eigen::MatrixX3d nodalDisplacements(mesh->VN(), 3); - for (VertexIndex vi = 0; vi < mesh->VN(); vi++) { - const Vector6d &nodalDisplacement = displacements[vi]; - nodalDisplacements.row(vi) = Eigen::Vector3d( - nodalDisplacement[0], nodalDisplacement[1], nodalDisplacement[2]); - } - polyscope::getCurveNetwork(getLabel()) - ->updateNodePositions(mesh->getEigenVertices() + nodalDisplacements); - - job->registerForDrawing(getLabel()); - } - - void saveDeformedModel() { - VCGEdgeMesh m; - vcg::tri::Append::MeshCopy(m, *job->pMesh); - - for (int vi = 0; vi < m.VN(); vi++) { - m.vert[vi].P() = - m.vert[vi].P() + CoordType(displacements[vi][0], displacements[vi][1], - displacements[vi][2]); - } - m.savePly(getLabel() + ".ply"); - } - - void save() { - const std::string filename(getLabel() + "_displacements.eigenBin"); - - Eigen::MatrixXd m = Utilities::toEigenMatrix(displacements); - Eigen::write_binary(filename, m); - } - - // The comparison of the results happens comparing the 6-dof nodal - // displacements - bool isEqual(const Eigen::MatrixXd &nodalDisplacements) { - assert(nodalDisplacements.cols() == 6); - Eigen::MatrixXd eigenDisplacements = - Utilities::toEigenMatrix(this->displacements); - const double errorNorm = (eigenDisplacements - nodalDisplacements).norm(); - return errorNorm < 1e-10; - // return eigenDisplacements.isApprox(nodalDisplacements); - } -}; - -#endif // SIMULATIONHISTORY_HPP diff --git a/topologyenumerator.cpp b/topologyenumerator.cpp old mode 100644 new mode 100755 index e69de29..a8efcec --- a/topologyenumerator.cpp +++ b/topologyenumerator.cpp @@ -0,0 +1,637 @@ +#include "topologyenumerator.hpp" +#include +#include +#include +#include +#include + +const bool debugIsOn{false}; +const bool exportArticulationPointsPatterns{false}; +const bool savePlyFiles{false}; + +// size_t binomialCoefficient(size_t n, size_t m) { +// assert(n > m); +// return tgamma(n + 1) / (tgamma(m + 1) * tgamma(n - m + 1)); +//} + +// void TopologyEnumerator::createLabelMesh( +// const std::vector vertices, +// const std::filesystem::path &savePath) const { +// const std::string allOnes(patternTopology.getNumberOfPossibleEdges(), '1'); +// const std::vector allEdges = +// TrianglePatternTopology::convertToEdges(allOnes, vertices.size()); +// TrianglePatternGeometry labelMesh; +// std::vector labelVertices(allEdges.size()); +// for (size_t edgeIndex = 0; edgeIndex < allEdges.size(); edgeIndex++) { +// const vcg::Point3d edgeMidpoint = +// (vertices[allEdges[edgeIndex][0]] + vertices[allEdges[edgeIndex][1]]) +// / 2; +// labelVertices[edgeIndex] = edgeMidpoint; +// } +// labelMesh.set(labelVertices); +// labelMesh.savePly(std::filesystem::path(savePath) +// .append(std::string("labelMesh.ply")) +// .string()); +//} + +size_t TopologyEnumerator::getEdgeIndex(size_t ni0, size_t ni1) const { + if (ni1 <= ni0) { + std::swap(ni0, ni1); + } + assert(ni1 > ni0); + const size_t &n = numberOfNodes; + return (n * (n - 1) / 2) - (n - ni0) * ((n - ni0) - 1) / 2 + ni1 - ni0 - 1; +} + +TopologyEnumerator::TopologyEnumerator() {} + +void TopologyEnumerator::computeValidPatterns(const std::vector &reducedNumberOfNodesPerSlot,const std::string& desiredResultsPath, const int &numberOfDesiredEdges) { + assert(reducedNumberOfNodesPerSlot.size() == 5); + assert(reducedNumberOfNodesPerSlot[0] == 0 || + reducedNumberOfNodesPerSlot[0] == 1); + assert(reducedNumberOfNodesPerSlot[1] == 0 || + reducedNumberOfNodesPerSlot[1] == 1); + std::vector numberOfNodesPerSlot{ + reducedNumberOfNodesPerSlot[0], reducedNumberOfNodesPerSlot[1], + reducedNumberOfNodesPerSlot[1], reducedNumberOfNodesPerSlot[2], + reducedNumberOfNodesPerSlot[3], reducedNumberOfNodesPerSlot[2], + reducedNumberOfNodesPerSlot[4]}; + // Generate an edge mesh wih all possible edges + numberOfNodes = std::accumulate(numberOfNodesPerSlot.begin(), + numberOfNodesPerSlot.end(), 0); + const size_t numberOfAllPossibleEdges = + numberOfNodes * (numberOfNodes - 1) / 2; + + std::vector allPossibleEdges(numberOfAllPossibleEdges); + const int &n = numberOfNodes; + for (size_t edgeIndex = 0; edgeIndex < numberOfAllPossibleEdges; + edgeIndex++) { + const int ni0 = + n - 2 - + std::floor(std::sqrt(-8 * edgeIndex + 4 * n * (n - 1) - 7) / 2.0 - 0.5); + const int ni1 = + edgeIndex + ni0 + 1 - n * (n - 1) / 2 + (n - ni0) * ((n - ni0) - 1) / 2; + allPossibleEdges[edgeIndex] = vcg::Point2i(ni0, ni1); + } + PatternGeometry patternGeometryAllEdges; + patternGeometryAllEdges.add(numberOfNodesPerSlot, allPossibleEdges); + // Create Results path + auto resultPath=std::filesystem::path(desiredResultsPath); + assert(std::filesystem::exists(resultPath)); + + auto allResultsPath = resultPath.append("Results"); + std::filesystem::create_directory(allResultsPath); + std::string setupString; + // for (size_t numberOfNodes : reducedNumberOfNodesPerSlot) { + for (size_t numberOfNodesPerSlotIndex = 0; + numberOfNodesPerSlotIndex < reducedNumberOfNodesPerSlot.size(); + numberOfNodesPerSlotIndex++) { + std::string elemID; + if (numberOfNodesPerSlotIndex == 0 || numberOfNodesPerSlotIndex == 1) { + elemID = "v"; + } else if (numberOfNodesPerSlotIndex == 2 || + numberOfNodesPerSlotIndex == 3) { + elemID = "e"; + } else { + elemID = "c"; + } + setupString += + std::to_string(reducedNumberOfNodesPerSlot[numberOfNodesPerSlotIndex]) + + elemID + "_"; + } + setupString += std::to_string(PatternGeometry().getFanSize()) + "fan"; + if (debugIsOn) { + setupString += "_debug"; + } + auto resultsPath = std::filesystem::path(allResultsPath).append(setupString); + // std::filesystem::remove_all(resultsPath); // delete previous results + std::filesystem::create_directory(resultsPath); + + if (debugIsOn) { + patternGeometryAllEdges.save(std::filesystem::path(resultsPath) + .append("allPossibleEdges.ply") + .string()); + } + // statistics.numberOfPossibleEdges = numberOfAllPossibleEdges; + + std::vector validEdges = + getValidEdges(numberOfNodesPerSlot, resultsPath, patternGeometryAllEdges, + allPossibleEdges); + PatternGeometry patternAllValidEdges; + patternAllValidEdges.add(patternGeometryAllEdges.getVertices(), validEdges); + if (debugIsOn) { + // Export all valid edges in a ply + patternAllValidEdges.save(std::filesystem::path(resultsPath) + .append("allValidEdges.ply") + .string()); + } + // statistics.numberOfValidEdges = validEdges.size(); + + // Find pairs of intersecting edges + std::unordered_map> intersectingEdges = + patternAllValidEdges.getIntersectingEdges( + statistics.numberOfIntersectingEdgePairs); + if (debugIsOn) { + auto intersectingEdgesPath = std::filesystem::path(resultsPath) + .append("All_intersecting_edge_pairs"); + std::filesystem::create_directory(intersectingEdgesPath); + // Export intersecting pairs in ply files + for (auto mapIt = intersectingEdges.begin(); + mapIt != intersectingEdges.end(); mapIt++) { + for (auto setIt = mapIt->second.begin(); setIt != mapIt->second.end(); + setIt++) { + PatternGeometry intersectingEdgePair; + const size_t ei0 = mapIt->first; + const size_t ei1 = *setIt; + vcg::tri::Allocator::AddEdge( + intersectingEdgePair, + patternGeometryAllEdges.getVertices()[validEdges[ei0][0]], + patternGeometryAllEdges.getVertices()[validEdges[ei0][1]]); + vcg::tri::Allocator::AddEdge( + intersectingEdgePair, + patternGeometryAllEdges.getVertices()[validEdges[ei1][0]], + patternGeometryAllEdges.getVertices()[validEdges[ei1][1]]); + intersectingEdgePair.save( + std::filesystem::path(intersectingEdgesPath) + .append(std::to_string(mapIt->first) + "_" + + std::to_string(*setIt) + ".ply") + .string()); + } + } + } + + // assert(validEdges.size() == allPossibleEdges.size() - + // coincideEdges.size() - + // duplicateEdges.size()); + + // PatternSet patternSet; + // const std::vector nodes = + // patternGeometryAllEdges.getVertices(); const size_t numberOfNodes = + // nodes.size(); patternSet.nodes.resize(numberOfNodes); for (size_t + // nodeIndex = 0; nodeIndex < numberOfNodes; nodeIndex++) { + // patternSet.nodes[nodeIndex] = + // vcg::Point3d(nodes[nodeIndex][0], nodes[nodeIndex][1],0); + // } + // if (std::filesystem::exists(std::filesystem::path(resultsPath) + // .append("patterns.patt") + // .string())) { + // std::filesystem::remove( + // std::filesystem::path(resultsPath).append("patterns.patt")); + // } + if(numberOfDesiredEdges==-1){ + for (size_t numberOfEdges = 2; numberOfEdges < validEdges.size(); + numberOfEdges++) { + std::cout << "Computing " + setupString << " with " << numberOfEdges + << " edges." << std::endl; + + auto perEdgeResultPath = std::filesystem::path(resultsPath) + .append(std::to_string(numberOfEdges)); + if (std::filesystem::exists(perEdgeResultPath)) { + continue; + } + std::filesystem::create_directory(perEdgeResultPath); + computeValidPatterns(numberOfNodesPerSlot, numberOfEdges, perEdgeResultPath, + patternGeometryAllEdges.getVertices(), + intersectingEdges, validEdges); + // statistics.print(setupString, perEdgeResultPath); + } + } + else{ + std::cout << "Computing " + setupString << " with " << numberOfDesiredEdges + << " edges." << std::endl; + + auto perEdgeResultPath = std::filesystem::path(resultsPath) + .append(std::to_string(numberOfDesiredEdges)); + if (std::filesystem::exists(perEdgeResultPath)) { + return; + } + std::filesystem::create_directory(perEdgeResultPath); + computeValidPatterns(numberOfNodesPerSlot,numberOfDesiredEdges, perEdgeResultPath, + patternGeometryAllEdges.getVertices(), + intersectingEdges, validEdges); + + } +} + +void TopologyEnumerator::computeEdgeNodes( + const std::vector &numberOfNodesPerSlot, + std::vector &nodesEdge0, std::vector &nodesEdge1, + std::vector &nodesEdge2) { + // Create vectors holding the node indices of each pattern node of each + // triangle edge + size_t nodeIndex = 0; + if (numberOfNodesPerSlot[0] != 0) { + nodesEdge0.push_back(nodeIndex++); + } + if (numberOfNodesPerSlot[1] != 0) + nodesEdge1.push_back(nodeIndex++); + if (numberOfNodesPerSlot[2] != 0) + nodesEdge2.push_back(nodeIndex++); + + if (numberOfNodesPerSlot[3] != 0) { + for (size_t edgeNodeIndex = 0; edgeNodeIndex < numberOfNodesPerSlot[3]; + edgeNodeIndex++) { + nodesEdge0.push_back(nodeIndex++); + } + } + if (numberOfNodesPerSlot[4] != 0) { + for (size_t edgeNodeIndex = 0; edgeNodeIndex < numberOfNodesPerSlot[4]; + edgeNodeIndex++) { + nodesEdge1.push_back(nodeIndex++); + } + } + + if (numberOfNodesPerSlot[5] != 0) { + for (size_t edgeNodeIndex = 0; edgeNodeIndex < numberOfNodesPerSlot[5]; + edgeNodeIndex++) { + nodesEdge2.push_back(nodeIndex++); + } + } + if (numberOfNodesPerSlot[1] != 0) { + assert(numberOfNodesPerSlot[2]); + nodesEdge0.push_back(1); + nodesEdge1.push_back(2); + } + + if (numberOfNodesPerSlot[0] != 0) { + nodesEdge2.push_back(0); + } +} + +std::unordered_set TopologyEnumerator::computeCoincideEdges( + const std::vector &numberOfNodesPerSlot) { + /* + * A coincide edge is defined as an edge connection between two nodes that lay + * on a triangle edge and which have another node in between + * */ + std::vector nodesEdge0; // left edge + std::vector nodesEdge1; // bottom edge + std::vector nodesEdge2; // right edge + computeEdgeNodes(numberOfNodesPerSlot, nodesEdge0, nodesEdge1, nodesEdge2); + + std::vector coincideEdges0 = getCoincideEdges(nodesEdge0); + std::vector coincideEdges1 = getCoincideEdges(nodesEdge1); + std::vector coincideEdges2 = getCoincideEdges(nodesEdge2); + std::unordered_set coincideEdges{coincideEdges0.begin(), + coincideEdges0.end()}; + std::copy(coincideEdges1.begin(), coincideEdges1.end(), + std::inserter(coincideEdges, coincideEdges.end())); + std::copy(coincideEdges2.begin(), coincideEdges2.end(), + std::inserter(coincideEdges, coincideEdges.end())); + + if (numberOfNodesPerSlot[0] && numberOfNodesPerSlot[1]) { + coincideEdges.insert(getEdgeIndex(0, 2)); + } + + if (numberOfNodesPerSlot[0] && numberOfNodesPerSlot[2]) { + assert(numberOfNodesPerSlot[1]); + coincideEdges.insert(getEdgeIndex(0, 3)); + } + + return coincideEdges; +} + +std::unordered_set TopologyEnumerator::computeDuplicateEdges( + const std::vector &numberOfNodesPerSlot) { + /* + * A duplicate edges are all edges the "right" edge since due to rotational + * symmetry "left" edge=="right" edge + * */ + std::unordered_set duplicateEdges; + std::vector nodesEdge0; // left edge + std::vector nodesEdge1; // bottom edge + std::vector nodesEdge2; // right edge + computeEdgeNodes(numberOfNodesPerSlot, nodesEdge0, nodesEdge1, nodesEdge2); + if (numberOfNodesPerSlot[5]) { + for (size_t edge2NodeIndex = 0; edge2NodeIndex < nodesEdge2.size() - 1; + edge2NodeIndex++) { + const size_t nodeIndex = nodesEdge2[edge2NodeIndex]; + const size_t nextNodeIndex = nodesEdge2[edge2NodeIndex + 1]; + duplicateEdges.insert(getEdgeIndex(nodeIndex, nextNodeIndex)); + } + } + + return duplicateEdges; +} + +std::vector TopologyEnumerator::getValidEdges( + const std::vector &numberOfNodesPerSlot, + const std::filesystem::path &resultsPath, + const PatternGeometry &patternGeometryAllEdges, + const std::vector &allPossibleEdges) { + + std::unordered_set coincideEdges = + computeCoincideEdges(numberOfNodesPerSlot); + // Export each coincide edge into a ply file + if (!coincideEdges.empty() && debugIsOn) { + auto coincideEdgesPath = + std::filesystem::path(resultsPath).append("Coincide_edges"); + std::filesystem::create_directories(coincideEdgesPath); + for (auto coincideEdgeIndex : coincideEdges) { + PatternGeometry::EdgeType e = + patternGeometryAllEdges.edge[coincideEdgeIndex]; + PatternGeometry singleEdgeMesh; + vcg::Point3d p0 = e.cP(0); + vcg::Point3d p1 = e.cP(1); + std::vector edgeVertices; + edgeVertices.push_back(p0); + edgeVertices.push_back(p1); + singleEdgeMesh.add(edgeVertices); + singleEdgeMesh.add(std::vector{vcg::Point2i{0, 1}}); + singleEdgeMesh.save(std::filesystem::path(coincideEdgesPath) + .append(std::to_string(coincideEdgeIndex)) + .string() + + ".ply"); + } + } + statistics.numberOfCoincideEdges = coincideEdges.size(); + + // Compute duplicate edges + std::unordered_set duplicateEdges = + computeDuplicateEdges(numberOfNodesPerSlot); + if (!duplicateEdges.empty() && debugIsOn) { + // Export duplicate edges in a single ply file + auto duplicateEdgesPath = + std::filesystem::path(resultsPath).append("duplicate"); + std::filesystem::create_directory(duplicateEdgesPath); + PatternGeometry patternDuplicateEdges; + for (auto duplicateEdgeIndex : duplicateEdges) { + PatternGeometry::EdgeType e = + patternGeometryAllEdges.edge[duplicateEdgeIndex]; + vcg::Point3d p0 = e.cP(0); + vcg::Point3d p1 = e.cP(1); + vcg::tri::Allocator::AddEdge(patternDuplicateEdges, p0, + p1); + } + patternDuplicateEdges.save(std::filesystem::path(duplicateEdgesPath) + .append("duplicateEdges.ply") + .string()); + } + statistics.numberOfDuplicateEdges = duplicateEdges.size(); + + // Create the set of all possible edges without coincide and duplicate edges + std::vector validEdges; + for (size_t edgeIndex = 0; edgeIndex < allPossibleEdges.size(); edgeIndex++) { + if (coincideEdges.count(edgeIndex) == 0 && + duplicateEdges.count(edgeIndex) == 0) { + validEdges.push_back(allPossibleEdges[edgeIndex]); + } + } + + return validEdges; +} + +void TopologyEnumerator::computeValidPatterns( + const std::vector &numberOfNodesPerSlot, + const size_t &numberOfDesiredEdges, + const std::filesystem::path &resultsPath, + const std::vector &allVertices, + const std::unordered_map> + &intersectingEdges, + const std::vector &validEdges) { + assert(numberOfNodesPerSlot.size() == 7); + // Iterate over all patterns which have numberOfDesiredEdges edges from + // from the validEdges Identify patterns that contain dangling edges + const bool enoughValidEdgesExist = validEdges.size() >= numberOfDesiredEdges; + if (!enoughValidEdgesExist) { + std::filesystem::remove_all(resultsPath); // delete previous results folder + return; + } + assert(enoughValidEdgesExist); + + // Create pattern result paths + auto validPatternsPath = std::filesystem::path(resultsPath).append("Valid"); + std::filesystem::create_directory(validPatternsPath); + // std::ofstream validPatternsFileStream; + // validPatternsFileStream.open( + // validPatternsPath.append("patterns.patt").string()); + const std::string validPatternsFilePath = + validPatternsPath.append("patterns.patt").string(); + PatternIO::PatternSet patternSet; + patternSet.nodes = allVertices; + const int patternSetBufferSize = 10000; + + const size_t numberOfPatterns = PatternGeometry::binomialCoefficient( + validEdges.size(), numberOfDesiredEdges); + statistics.numberOfPatterns = numberOfPatterns; + + // Initialize pattern binary representation + std::string patternBinaryRepresentation; + patternBinaryRepresentation = std::string(numberOfDesiredEdges, '1'); + patternBinaryRepresentation += + std::string(validEdges.size() - numberOfDesiredEdges, '0'); + std::sort(patternBinaryRepresentation.begin(), + patternBinaryRepresentation.end()); + /*TODO: Performance could be improved by changing the patternGeometry with + * respect to the previous one. Maybe I could xor the binaryRepresentation + * to the previous one.*/ + // std::string previousPatternBinaryRepresentation(validEdges.size(),'0'); + size_t patternIndex = 0; + do { + patternIndex++; + const std::string patternName = std::to_string(patternIndex); + // std::cout << "Pattern name:" + patternBinaryRepresentation << + // std::endl; isValidPattern(patternBinaryRepresentation, validEdges, + // numberOfDesiredEdges); + // Create the geometry of the pattern + // Compute the pattern edges from the binary representation + std::vector patternEdges(numberOfDesiredEdges); + size_t patternEdgeIndex = 0; + for (size_t validEdgeIndex = 0; + validEdgeIndex < patternBinaryRepresentation.size(); + validEdgeIndex++) { + if (patternBinaryRepresentation[validEdgeIndex] == '1') { + assert(patternEdgeIndex < numberOfDesiredEdges); + patternEdges[patternEdgeIndex++] = validEdges[validEdgeIndex]; + } + } + + PatternGeometry patternGeometry; + patternGeometry.add(allVertices, patternEdges); + + // Check if pattern contains intersecting edges + const bool patternContainsIntersectingEdges = + patternGeometry.hasIntersectingEdges(patternBinaryRepresentation, + intersectingEdges); + // Export the tiled ply file if it contains intersecting edges + if (patternContainsIntersectingEdges) { + // create the tiled geometry of the pattern + statistics.numberOfPatternsWithIntersectingEdges++; + if (debugIsOn) { + if (savePlyFiles) { + PatternGeometry tiledPatternGeometry = + PatternGeometry::createTile(patternGeometry); + auto intersectingPatternsPath = + std::filesystem::path(resultsPath).append("Intersecting"); + std::filesystem::create_directory(intersectingPatternsPath); + patternGeometry.save( + std::filesystem::path(intersectingPatternsPath) + .append(patternName) + .string() + + ".ply"); + tiledPatternGeometry.save( + std::filesystem::path(intersectingPatternsPath) + .append(patternName + "_tiled") + .string() + + ".ply"); + } + } else { + continue; // should be uncommented in order to improve performance + } + } + + // Compute the tiled valence + const bool tiledPatternHasDanglingEdges = patternGeometry.hasDanglingEdges( + numberOfNodesPerSlot); // marks the nodes with valence>=1 + // Create the tiled geometry of the pattern + const bool hasFloatingComponents = + !patternGeometry.isFullyConnectedWhenTiled(); + FlatPatternTopology topology(numberOfNodesPerSlot, patternEdges); + const bool hasArticulationPoints = topology.containsArticulationPoints(); + // duplicated here + // Check dangling edges with vcg method + // const bool vcg_tiledPatternHasDangling = + // tiledPatternGeometry.hasUntiledDanglingEdges(); + if (tiledPatternHasDanglingEdges /*&& !hasFloatingComponents && + !hasArticulationPoints*/) { + statistics.numberOfPatternsWithADanglingEdgeOrNode++; + if (debugIsOn) { + if (savePlyFiles) { + auto danglingEdgesPath = + std::filesystem::path(resultsPath).append("Dangling"); + std::filesystem::create_directory(danglingEdgesPath); + patternGeometry.save(std::filesystem::path(danglingEdgesPath) + .append(patternName) + .string() + + ".ply"); + PatternGeometry tiledPatternGeometry = PatternGeometry::createTile( + patternGeometry); // the marked nodes of hasDanglingEdges are + tiledPatternGeometry.save(std::filesystem::path(danglingEdgesPath) + .append(patternName + "_tiled") + .string() + + ".ply"); + } + } else { + continue; + } + } + + if (hasFloatingComponents /*&& !hasArticulationPoints && + !tiledPatternHasDanglingEdges*/) { + statistics.numberOfPatternsWithMoreThanASingleCC++; + if (debugIsOn) { + if (savePlyFiles) { + auto moreThanOneCCPath = + std::filesystem::path(resultsPath).append("MoreThanOneCC"); + std::filesystem::create_directory(moreThanOneCCPath); + patternGeometry.save(std::filesystem::path(moreThanOneCCPath) + .append(patternName) + .string() + + ".ply"); + PatternGeometry tiledPatternGeometry = PatternGeometry::createTile( + patternGeometry); // the marked nodes of hasDanglingEdges are + tiledPatternGeometry.save(std::filesystem::path(moreThanOneCCPath) + .append(patternName + "_tiled") + .string() + + ".ply"); + } + } else { + continue; + } + } + + if (hasArticulationPoints /*&& !hasFloatingComponents && + !tiledPatternHasDanglingEdges*/) { + statistics.numberOfPatternsWithArticulationPoints++; + if (exportArticulationPointsPatterns || debugIsOn) { + if (savePlyFiles) { + auto articulationPointsPath = + std::filesystem::path(resultsPath).append("ArticulationPoints"); + std::filesystem::create_directory(articulationPointsPath); + patternGeometry.save(std::filesystem::path(articulationPointsPath) + .append(patternName) + .string() + + ".ply"); + PatternGeometry tiledPatternGeometry = PatternGeometry::createTile( + patternGeometry); // the marked nodes of hasDanglingEdges are + tiledPatternGeometry.save( + std::filesystem::path(articulationPointsPath) + .append(patternName + "_tiled") + .string() + + ".ply"); + + // std::cout << "Pattern:" << patternName << std::endl; + } + } else { + continue; + } + } + + const bool isValidPattern = + !patternContainsIntersectingEdges && !tiledPatternHasDanglingEdges && + !hasFloatingComponents && !hasArticulationPoints; + if (isValidPattern) { + statistics.numberOfValidPatterns++; + if (savePlyFiles) { + // if (numberOfDesiredEdges == 4) { + // std::cout << "Saving:" + // << std::filesystem::path(validPatternsPath) + // .append(patternName) + // .string() + + // ".ply" + // << std::endl; + // } + patternGeometry.save(std::filesystem::path(validPatternsPath) + .append(patternName) + .string() + + ".ply"); + PatternGeometry tiledPatternGeometry = PatternGeometry::createTile( + patternGeometry); // the marked nodes of hasDanglingEdges are + tiledPatternGeometry.save(std::filesystem::path(validPatternsPath) + .append(patternName + "_tiled") + .string() + + ".ply"); + } + PatternIO::Pattern pattern; + pattern.edges = patternEdges; + pattern.name = patternIndex; + patternSet.patterns.emplace_back(pattern); + // Save valid patterns + if (patternIndex % patternSetBufferSize == 0) { + PatternIO::save(validPatternsFilePath, patternSet); + patternSet.patterns.clear(); + patternSet.patterns.reserve(patternSetBufferSize); + } + } + + // assert(vcg_tiledPatternHasDangling == tiledPatternHasDanglingEdges); + } while (std::next_permutation(patternBinaryRepresentation.begin(), + patternBinaryRepresentation.end())); + if (!patternSet.patterns.empty()) { + PatternIO::save(validPatternsFilePath, patternSet); + } +} + +std::vector TopologyEnumerator::getCoincideEdges( + const std::vector &edgeNodeIndices) const { + std::vector coincideEdges; + if (edgeNodeIndices.size() < 3) + return coincideEdges; + for (size_t edgeNodeIndex = 0; edgeNodeIndex < edgeNodeIndices.size() - 2; + edgeNodeIndex++) { + const size_t &firstNodeIndex = edgeNodeIndices[edgeNodeIndex]; + for (size_t secondEdgeNodeIndex = edgeNodeIndex + 2; + secondEdgeNodeIndex < edgeNodeIndices.size(); secondEdgeNodeIndex++) { + const size_t &secondNodeIndex = edgeNodeIndices[secondEdgeNodeIndex]; + coincideEdges.push_back(getEdgeIndex(firstNodeIndex, secondNodeIndex)); + } + } + return coincideEdges; +} + +bool TopologyEnumerator::isValidPattern( + const std::string &patternBinaryRepresentation, + const std::vector &validEdges, + const size_t &numberOfDesiredEdges) const { + return true; +} diff --git a/topologyenumerator.hpp b/topologyenumerator.hpp index 058d70d..a11855e 100755 --- a/topologyenumerator.hpp +++ b/topologyenumerator.hpp @@ -146,7 +146,7 @@ public: TopologyEnumerator(); void - computeValidPatterns(const std::vector &reducedNumberOfNodesPerSlot); + computeValidPatterns(const std::vector &reducedNumberOfNodesPerSlot, const std::string &desiredResultsPath, const int& numberOfDesiredEdges=-1); private: std::vector @@ -176,6 +176,5 @@ private: const std::unordered_map> &intersectingEdges, const std::vector &validEdges); -}ges, PatternSet &patternsSet); }; -# \ No newline at end of file +#endif // TOPOLOGYENUMERATOR_HPP diff --git a/trianglepatterngeometry.cpp b/trianglepatterngeometry.cpp index 7058c66..564ca84 100755 --- a/trianglepatterngeometry.cpp +++ b/trianglepatterngeometry.cpp @@ -140,7 +140,7 @@ PatternGeometry::PatternGeometry(PatternGeometry &other) { vcg::tri::UpdateTopology::EdgeEdge(*this); } -bool PatternGeometry::savePly(const std::string plyFilename) { +bool PatternGeometry::save(const std::string plyFilename) { int returnValue = vcg::tri::io::ExporterPLY::Save( *this, plyFilename.c_str(), @@ -584,8 +584,9 @@ PatternGeometry::PatternGeometry(const std::string &filename, updateEigenEdgeAndVertices(); } -double PatternGeometry::computeBaseTriangleHeight() const { - return (vert[0].cP() - vert[3].cP()).Norm(); +double PatternGeometry::computeBaseTriangleHeight() const +{ + return vcg::Distance(vert[0].cP(), vert[interfaceNodeIndex].cP()); } void PatternGeometry::addNormals() { @@ -597,6 +598,17 @@ void PatternGeometry::addNormals() { } } +vcg::Triangle3 PatternGeometry::getBaseTriangle() const +{ + double bottomEdgeHalfSize = computeBaseTriangleHeight() / std::tan(M_PI / 3); + CoordType patternCoord0 = vert[0].cP(); + CoordType patternBottomRight = vert[interfaceNodeIndex].cP() + + CoordType(bottomEdgeHalfSize, 0, 0); + CoordType patternBottomLeft = vert[interfaceNodeIndex].cP() + - CoordType(bottomEdgeHalfSize, 0, 0); + return vcg::Triangle3(patternCoord0, patternBottomLeft, patternBottomRight); +} + PatternGeometry::PatternGeometry( const std::vector &numberOfNodesPerSlot, const std::vector &edges) { @@ -659,336 +671,53 @@ void PatternGeometry::copy(PatternGeometry ©From) { baseTriangleHeight = copyFrom.getBaseTriangleHeight(); } -void PatternGeometry::deleteDanglingEdges() { - for (VertexType &v : vert) { - std::vector incidentElements; - vcg::edge::VEStarVE(&v, incidentElements); - if (incidentElements.size() == 1) { - vcg::tri::Allocator::DeleteEdge(*this, *incidentElements[0]); - } - if (incidentElements.size() == 1) { - vcg::tri::Allocator::DeleteVertex(*this, v); - } - } - - vcg::tri::Clean::RemoveDegenerateVertex(*this); - vcg::tri::Clean::RemoveDegenerateEdge(*this); - vcg::tri::Allocator::CompactEveryVector(*this); -} - -void PatternGeometry::scale(const double &desiredBaseTriangleCentralEdgeSize) { +void PatternGeometry::scale(const double &desiredBaseTriangleCentralEdgeSize,const int& interfaceNodeIndex) { this->baseTriangleHeight = desiredBaseTriangleCentralEdgeSize; - const double baseTriangleCentralEdgeSize = - (vert[0].cP() - vert[3].cP()).Norm(); + const double baseTriangleCentralEdgeSize = getBaseTriangleHeight(); const double scaleRatio = desiredBaseTriangleCentralEdgeSize / baseTriangleCentralEdgeSize; vcg::tri::UpdatePosition::Scale(*this, scaleRatio); } -void PatternGeometry::deleteDanglingVertices() { - vcg::tri::Allocator::PointerUpdater pu; - deleteDanglingVertices(pu); -} - -void PatternGeometry::deleteDanglingVertices( - vcg::tri::Allocator::PointerUpdater &pu) { - for (VertexType &v : vert) { - std::vector incidentElements; - vcg::edge::VEStarVE(&v, incidentElements); - if (incidentElements.size() == 0 && !v.IsD()) { - vcg::tri::Allocator::DeleteVertex(*this, v); +void PatternGeometry::createFan(const std::vector &connectToNeighborsVi, const size_t &fanSize) +{ + PatternGeometry rotatedPattern; + vcg::tri::Append::MeshCopy(rotatedPattern, *this); + for (int rotationCounter = 1; rotationCounter < fanSize; rotationCounter++) { + vcg::Matrix44d R; + auto rotationAxis = vcg::Point3d(0, 0, 1); + R.SetRotateDeg(360 / fanSize, rotationAxis); + vcg::tri::UpdatePosition::Matrix(rotatedPattern, R); + vcg::tri::Append::Mesh(*this, rotatedPattern); + //Add edges for connecting the desired vertices + if (!connectToNeighborsVi.empty()) { + if (rotationCounter == fanSize - 1) { + for (int connectToNeighborIndex = 0; + connectToNeighborIndex < connectToNeighborsVi.size(); + connectToNeighborIndex++) { + vcg::tri::Allocator::AddEdge( + *this, + connectToNeighborsVi[connectToNeighborIndex], + this->VN() - rotatedPattern.VN() + + connectToNeighborsVi[connectToNeighborIndex]); + } + } + for (int connectToNeighborIndex = 0; + connectToNeighborIndex < connectToNeighborsVi.size(); + connectToNeighborIndex++) { + vcg::tri::Allocator::AddEdge( + *this, + this->VN() - 2 * rotatedPattern.VN() + + connectToNeighborsVi[connectToNeighborIndex], + this->VN() - rotatedPattern.VN() + connectToNeighborsVi[connectToNeighborIndex]); + } + } + removeDuplicateVertices(); + updateEigenEdgeAndVertices(); } - } - - vcg::tri::Allocator::CompactVertexVector(*this, pu); - vcg::tri::Allocator::CompactEdgeVector(*this); - - updateEigenEdgeAndVertices(); -} - -void PatternGeometry::tilePattern(VCGEdgeMesh& pattern, VCGPolyMesh &tileInto,const int& interfaceNodeIndex, - const bool &shouldDeleteDanglingEdges) { - double xOffset = - vcg::Distance(pattern.vert[0].cP(), pattern.vert[interfaceNodeIndex].cP()) / - std::tan(M_PI / 3); - CoordType patternCoord0 = pattern.vert[0].cP(); - CoordType patternBottomRight = - pattern.vert[interfaceNodeIndex].cP() + CoordType(xOffset, 0, 0); - CoordType patternBottomLeft = - pattern.vert[interfaceNodeIndex].cP() - CoordType(xOffset, 0, 0); - std::vector patternTrianglePoints{ - patternCoord0, patternBottomRight, patternBottomLeft}; - CoordType pointOnPattern = - patternCoord0 + (patternBottomLeft - patternCoord0) ^ - (patternBottomRight - patternCoord0); - - std::vector faceCenters(FN()); - VCGTriMesh tileIntoEdgeMesh; - - for (VCGPolyMesh::FaceType &f : tileInto.face) { - std::vector incidentVertices; - vcg::face::VFIterator vfi( - &f, 0); // initialize the iterator to the first face - // vcg::face::Pos p(vfi.F(), f.cV(0)); - // vcg::face::VVOrderedStarFF(p, incidentVertices); - size_t numberOfNodes = 0; - CoordType centerOfFace(0, 0, 0); - for (size_t vi = 0; vi < f.VN(); vi++) { - numberOfNodes++; - centerOfFace = centerOfFace + f.cP(vi); - } - centerOfFace /= f.VN(); - vcg::tri::Allocator::AddVertex(tileIntoEdgeMesh, centerOfFace, - vcg::Color4b::Yellow); - - // const size_t vi = vcg::tri::Index(tileInto, v); - // std::cout << "vertex " << vi << " has incident vertices:" << - // std::endl; - for (size_t vi = 0; vi < f.VN(); vi++) { - // size_t f = 0; - // std::cout << vcg::tri::Index(tileInto, - // / incidentVertices[f]) / << std::endl; - - // Compute transformation matrix M - // vcg::Matrix44d M; - std::vector meshTrianglePoints{ - centerOfFace, f.cP(vi), vi + 1 == f.VN() ? f.cP(0) : f.cP(vi + 1)}; - CoordType faceNormal = ((meshTrianglePoints[1] - meshTrianglePoints[0]) ^ - (meshTrianglePoints[2] - meshTrianglePoints[0])) - .Normalize(); - auto fit = vcg::tri::Allocator::AddFace( - tileIntoEdgeMesh, meshTrianglePoints[0], meshTrianglePoints[1], - meshTrianglePoints[2]); - fit->N() = faceNormal; - CoordType pointOnTriMesh = - meshTrianglePoints[0] + - (meshTrianglePoints[1] - meshTrianglePoints[0]) ^ - (meshTrianglePoints[2] - meshTrianglePoints[0]); - vcg::Matrix44d M; - // vcg::ComputeRigidMatchMatrix(meshTrianglePoints, - // patternTrianglePoints, - // M); - vcg::Matrix44d A_prime; - A_prime[0][0] = meshTrianglePoints[0][0]; - A_prime[1][0] = meshTrianglePoints[0][1]; - A_prime[2][0] = meshTrianglePoints[0][2]; - A_prime[3][0] = 1; - A_prime[0][1] = meshTrianglePoints[1][0]; - A_prime[1][1] = meshTrianglePoints[1][1]; - A_prime[2][1] = meshTrianglePoints[1][2]; - A_prime[3][1] = 1; - A_prime[0][2] = meshTrianglePoints[2][0]; - A_prime[1][2] = meshTrianglePoints[2][1]; - A_prime[2][2] = meshTrianglePoints[2][2]; - A_prime[3][2] = 1; - A_prime[0][3] = pointOnTriMesh[0]; - A_prime[1][3] = pointOnTriMesh[1]; - A_prime[2][3] = pointOnTriMesh[2]; - A_prime[3][3] = 1; - vcg::Matrix44d A; - A[0][0] = patternTrianglePoints[0][0]; - A[1][0] = patternTrianglePoints[0][1]; - A[2][0] = patternTrianglePoints[0][2]; - A[3][0] = 1; - A[0][1] = patternTrianglePoints[1][0]; - A[1][1] = patternTrianglePoints[1][1]; - A[2][1] = patternTrianglePoints[1][2]; - A[3][1] = 1; - A[0][2] = patternTrianglePoints[2][0]; - A[1][2] = patternTrianglePoints[2][1]; - A[2][2] = patternTrianglePoints[2][2]; - A[3][2] = 1; - A[0][3] = pointOnPattern[0]; - A[1][3] = pointOnPattern[1]; - A[2][3] = pointOnPattern[2]; - A[3][3] = 1; - M = A_prime * vcg::Inverse(A); - - VCGEdgeMesh transformedPattern; - vcg::tri::Append::MeshCopy(transformedPattern, - pattern); - vcg::tri::UpdatePosition::Matrix(transformedPattern, M); - for (VertexType &v : transformedPattern.vert) { - v.N() = faceNormal; - } - - vcg::tri::Append::Mesh(*this, - transformedPattern); - } - } - - // vcg::tri::Clean::MergeCloseVertex(*this, 0.0000000001); - // vcg::tri::Clean::RemoveDegenerateVertex(*this); - // vcg::tri::Clean::RemoveDegenerateEdge(*this); - // vcg::tri::Allocator::CompactEveryVector(*this); - - // vcg::tri::Clean::RemoveDuplicateVertex(*this); - vcg::tri::Clean::MergeCloseVertex(*this, 0.000061524); - vcg::tri::UpdateTopology::VertexEdge(*this); - - // vcg::tri::Clean::RemoveUnreferencedVertex(*this); - // vcg::tri::UpdateTopology::VertexEdge(*this); - // vcg::tri::Clean::RemoveDegenerateVertex(*this); - // vcg::tri::Clean::RemoveDegenerateEdge(*this); - // vcg::tri::Allocator::CompactEveryVector(*this); - deleteDanglingVertices(); - vcg::tri::Allocator::CompactEveryVector(*this); - // vcg::tri::Clean::RemoveDegenerateVertex(*this); - // vcg::tri::Clean::RemoveUnreferencedVertex(*this); - // vcg::tri::UpdateTopology::VertexEdge(*this); - // deleteDanglingEdges(); - // vcg::tri::Clean::RemoveUnreferencedVertex(*this); - // vcg::tri::Clean::RemoveDegenerateVertex(*this); - // vcg::tri::Clean::RemoveDegenerateEdge(*this); - // vcg::tri::Allocator::CompactEveryVector(*this); - updateEigenEdgeAndVertices(); - savePly("tiledPattern.ply"); -} - -void PatternGeometry::createFan(const size_t &fanSize) { - PatternGeometry rotatedPattern; - vcg::tri::Append::MeshCopy(rotatedPattern, - *this); - for (int rotationCounter = 1; rotationCounter < fanSize; rotationCounter++) { - vcg::Matrix44d R; - auto rotationAxis = vcg::Point3d(0, 0, 1); - R.SetRotateDeg(360 / fanSize, rotationAxis); - vcg::tri::UpdatePosition::Matrix(rotatedPattern, R); - vcg::tri::Append::Mesh(*this, - rotatedPattern); - } - - removeDuplicateVertices(); - // const double precision = 1e-4; - // for (size_t vi = 0; vi < VN(); vi++) { - // vert[vi].P()[0] = std::round(vert[vi].P()[0] * (1 / precision)) * - // precision; vert[vi].P()[1] = std::round(vert[vi].P()[1] * (1 / - // precision)) * precision; vert[vi].P()[2] = std::round(vert[vi].P()[2] - // * (1 / precision)) * precision; - // } - - updateEigenEdgeAndVertices(); -} - -void PatternGeometry::removeDuplicateVertices() { - vcg::tri::Clean::MergeCloseVertex(*this, 0.0000000001); - vcg::tri::Clean::RemoveDegenerateVertex(*this); - vcg::tri::Clean::RemoveDegenerateEdge(*this); - vcg::tri::Allocator::CompactEveryVector(*this); - vcg::tri::UpdateTopology::VertexEdge(*this); } double PatternGeometry::getBaseTriangleHeight() const { return baseTriangleHeight; } - -void PatternGeometry::tilePattern(PatternGeometry &pattern, VCGTriMesh &tileInto) { - - const size_t middleIndex = 3; - double xOffset = - vcg::Distance(pattern.vert[0].cP(), pattern.vert[middleIndex].cP()) / - std::tan(M_PI / 3); - CoordType patternCoord0 = pattern.vert[0].cP(); - CoordType patternBottomRight = - pattern.vert[middleIndex].cP() + CoordType(xOffset, 0, 0); - CoordType patternBottomLeft = - pattern.vert[middleIndex].cP() - CoordType(xOffset, 0, 0); - std::vector patternTrianglePoints{ - patternCoord0, patternBottomRight, patternBottomLeft}; - CoordType pointOnPattern = - patternCoord0 + (patternBottomLeft - patternCoord0) ^ - (patternBottomRight - patternCoord0); - - for (VCGTriMesh::VertexType &v : tileInto.vert) { - const auto centralNodeColor = vcg::Color4(64, 64, 64, 255); - const bool isCentralNode = v.cC() == centralNodeColor; - if (isCentralNode) { - std::vector incidentVertices; - vcg::face::VFIterator vfi( - &v); // initialize the iterator tohe first face - vcg::face::Pos p(vfi.F(), &v); - vcg::face::VVOrderedStarFF(p, incidentVertices); - - const size_t vi = vcg::tri::Index(tileInto, v); - std::cout << "vertex " << vi << " has incident vertices:" << std::endl; - for (size_t f = 0; f < incidentVertices.size(); f++) { - // size_t f = 0; - std::cout << vcg::tri::Index(tileInto, incidentVertices[f]) - << std::endl; - - // Compute transformation matrix M - // vcg::Matrix44d M; - std::vector meshTrianglePoints{ - v.cP(), incidentVertices[f]->cP(), - f + 1 == incidentVertices.size() ? incidentVertices[0]->cP() - : incidentVertices[f + 1]->cP()}; - CoordType faceNormal = - ((meshTrianglePoints[1] - meshTrianglePoints[0]) ^ - (meshTrianglePoints[2] - meshTrianglePoints[0])) - .Normalize(); - CoordType pointOnTriMesh = - meshTrianglePoints[0] + - (meshTrianglePoints[1] - meshTrianglePoints[0]) ^ - (meshTrianglePoints[2] - meshTrianglePoints[0]); - vcg::Matrix44d M; - // vcg::ComputeRigidMatchMatrix(meshTrianglePoints, - // patternTrianglePoints, - // M); - vcg::Matrix44d A_prime; - A_prime[0][0] = meshTrianglePoints[0][0]; - A_prime[1][0] = meshTrianglePoints[0][1]; - A_prime[2][0] = meshTrianglePoints[0][2]; - A_prime[3][0] = 1; - A_prime[0][1] = meshTrianglePoints[1][0]; - A_prime[1][1] = meshTrianglePoints[1][1]; - A_prime[2][1] = meshTrianglePoints[1][2]; - A_prime[3][1] = 1; - A_prime[0][2] = meshTrianglePoints[2][0]; - A_prime[1][2] = meshTrianglePoints[2][1]; - A_prime[2][2] = meshTrianglePoints[2][2]; - A_prime[3][2] = 1; - A_prime[0][3] = pointOnTriMesh[0]; - A_prime[1][3] = pointOnTriMesh[1]; - A_prime[2][3] = pointOnTriMesh[2]; - A_prime[3][3] = 1; - vcg::Matrix44d A; - A[0][0] = patternTrianglePoints[0][0]; - A[1][0] = patternTrianglePoints[0][1]; - A[2][0] = patternTrianglePoints[0][2]; - A[3][0] = 1; - A[0][1] = patternTrianglePoints[1][0]; - A[1][1] = patternTrianglePoints[1][1]; - A[2][1] = patternTrianglePoints[1][2]; - A[3][1] = 1; - A[0][2] = patternTrianglePoints[2][0]; - A[1][2] = patternTrianglePoints[2][1]; - A[2][2] = patternTrianglePoints[2][2]; - A[3][2] = 1; - A[0][3] = pointOnPattern[0]; - A[1][3] = pointOnPattern[1]; - A[2][3] = pointOnPattern[2]; - A[3][3] = 1; - M = A_prime * vcg::Inverse(A); - - VCGEdgeMesh transformedPattern; - vcg::tri::Append::MeshCopy(transformedPattern, - pattern); - vcg::tri::UpdatePosition::Matrix(transformedPattern, M); - for (VertexType &v : transformedPattern.vert) { - v.N() = faceNormal; - } - - vcg::tri::Append::Mesh(*this, - transformedPattern); - } - } - } - - vcg::tri::UpdateTopology::VertexEdge(*this); - deleteDanglingVertices(); - deleteDanglingEdges(); - vcg::tri::Allocator::CompactEveryVector(*this); - - updateEigenEdgeAndVertices(); -} diff --git a/trianglepatterngeometry.hpp b/trianglepatterngeometry.hpp index 7efb9b8..2902044 100755 --- a/trianglepatterngeometry.hpp +++ b/trianglepatterngeometry.hpp @@ -16,11 +16,10 @@ private: size_t getNodeSlot(const size_t &nodeIndex) const; void addNormals(); - void deleteDanglingEdges(); - void removeDuplicateVertices(); double baseTriangleHeight; double computeBaseTriangleHeight() const; + const int interfaceNodeIndex{3}; const size_t fanSize{6}; std::vector vertices; const double triangleEdgeSize{1}; // radius edge @@ -41,7 +40,7 @@ public: * vcglib interface. * */ PatternGeometry(PatternGeometry &other); - bool savePly(const std::string plyFilename); + bool save(const std::string plyFilename); void add(const std::vector &vertices); void add(const std::vector &edges); void add(const std::vector &vertices, @@ -83,20 +82,251 @@ public: void copy(PatternGeometry ©From); void tilePattern(PatternGeometry &pattern, VCGTriMesh &tileInto); - void tilePattern(VCGEdgeMesh &pattern, VCGPolyMesh &tileInto, const int &interfaceNodeIndex, + void tilePattern(VCGEdgeMesh &pattern, + VCGPolyMesh &tileInto, + const int &interfaceNodeIndex, const bool &shouldDeleteDanglingEdges); - void createFan(const size_t &fanSize = 6); - - void deleteDanglingVertices( - vcg::tri::Allocator::PointerUpdater &pu); - void deleteDanglingVertices(); - void scale(const double &desiredBaseTriangleCentralEdgeSize); + void scale(const double &desiredBaseTriangleCentralEdgeSize, const int &interfaceNodeIndex); double getBaseTriangleHeight() const; + vcg::Triangle3 getBaseTriangle() const; - PatternGeometry(const std::vector &vertices, - const std::vector &edges); + PatternGeometry(const std::vector &vertices, const std::vector &edges); + // static std::shared_ptr tilePattern(PatternGeometry &pattern, + // VCGPolyMesh &tileInto, + // const int &interfaceNodeIndex, + // const bool &shouldDeleteDanglingEdges) + // { + // PatternGeometry tiledPattern; + // double bottomEdgeHalfSize = vcg::Distance(pattern.vert[0].cP(), + // pattern.vert[interfaceNodeIndex].cP()) + // / std::tan(M_PI / 3); + // CoordType patternCoord0 = pattern.vert[0].cP(); + // CoordType patternBottomRight = pattern.vert[interfaceNodeIndex].cP() + // + CoordType(bottomEdgeHalfSize, 0, 0); + // CoordType patternBottomLeft = pattern.vert[interfaceNodeIndex].cP() + // - CoordType(bottomEdgeHalfSize, 0, 0); + // std::vector patternTrianglePoints{patternCoord0, + // patternBottomRight, + // patternBottomLeft}; + // CoordType pointOnPattern = patternCoord0 + (patternBottomLeft - patternCoord0) + // ^ (patternBottomRight - patternCoord0); + // // patternTrianglePoints.push_back(pointOnPattern); + + // VCGTriMesh tileIntoEdgeMesh; + + // for (VCGPolyMesh::FaceType &f : tileInto.face) { + // size_t numberOfNodes = 0; + // CoordType centerOfFace(0, 0, 0); + // for (size_t vi = 0; vi < f.VN(); vi++) { + // numberOfNodes++; + // centerOfFace = centerOfFace + f.cP(vi); + // } + // centerOfFace /= f.VN(); + // vcg::tri::Allocator::AddVertex(tileIntoEdgeMesh, + // centerOfFace, + // vcg::Color4b::Yellow); + // for (size_t vi = 0; vi < f.VN(); vi++) { + // // Compute transformation matrix M + // // vcg::Matrix44d M; + // std::vector meshTrianglePoints{centerOfFace, + // f.cP(vi), + // vi + 1 == f.VN() ? f.cP(0) + // : f.cP(vi + 1)}; + // CoordType faceNormal = ((meshTrianglePoints[1] - meshTrianglePoints[0]) + // ^ (meshTrianglePoints[2] - meshTrianglePoints[0])) + // .Normalize(); + // auto fit = vcg::tri::Allocator::AddFace(tileIntoEdgeMesh, + // meshTrianglePoints[0], + // meshTrianglePoints[1], + // meshTrianglePoints[2]); + // fit->N() = faceNormal; + // CoordType pointOnTriMesh = meshTrianglePoints[0] + // + (meshTrianglePoints[1] - meshTrianglePoints[0]) + // ^ (meshTrianglePoints[2] - meshTrianglePoints[0]); + // vcg::Matrix44d M; + + // // meshTrianglePoints.push_back(pointOnTriMesh); + // // vcg::ComputeRigidMatchMatrix(meshTrianglePoints, patternTrianglePoints, M); + + // vcg::Matrix44d A_prime; + // A_prime[0][0] = meshTrianglePoints[0][0]; + // A_prime[1][0] = meshTrianglePoints[0][1]; + // A_prime[2][0] = meshTrianglePoints[0][2]; + // A_prime[3][0] = 1; + // A_prime[0][1] = meshTrianglePoints[1][0]; + // A_prime[1][1] = meshTrianglePoints[1][1]; + // A_prime[2][1] = meshTrianglePoints[1][2]; + // A_prime[3][1] = 1; + // A_prime[0][2] = meshTrianglePoints[2][0]; + // A_prime[1][2] = meshTrianglePoints[2][1]; + // A_prime[2][2] = meshTrianglePoints[2][2]; + // A_prime[3][2] = 1; + // A_prime[0][3] = pointOnTriMesh[0]; + // A_prime[1][3] = pointOnTriMesh[1]; + // A_prime[2][3] = pointOnTriMesh[2]; + // A_prime[3][3] = 1; + // vcg::Matrix44d A; + // A[0][0] = patternTrianglePoints[0][0]; + // A[1][0] = patternTrianglePoints[0][1]; + // A[2][0] = patternTrianglePoints[0][2]; + // A[3][0] = 1; + // A[0][1] = patternTrianglePoints[1][0]; + // A[1][1] = patternTrianglePoints[1][1]; + // A[2][1] = patternTrianglePoints[1][2]; + // A[3][1] = 1; + // A[0][2] = patternTrianglePoints[2][0]; + // A[1][2] = patternTrianglePoints[2][1]; + // A[2][2] = patternTrianglePoints[2][2]; + // A[3][2] = 1; + // A[0][3] = pointOnPattern[0]; + // A[1][3] = pointOnPattern[1]; + // A[2][3] = pointOnPattern[2]; + // A[3][3] = 1; + // M = A_prime * vcg::Inverse(A); + + // PatternGeometry transformedPattern; + // transformedPattern.copy(pattern); + // vcg::tri::UpdatePosition::Matrix(transformedPattern, M); + // for (VertexType &v : transformedPattern.vert) { + // v.N() = faceNormal; + // } + // // const double innerHexagonInitialRotationAngle = 30; + // // vcg::Matrix44d Rlocal; + // // Rlocal.SetRotateDeg( + // // (0 + // // // optimizationResults.optimalXNameValuePairs["HexAngle"] + // // - innerHexagonInitialRotationAngle), + // // VectorType(0, 0, 1)); + // // vcg::Matrix44d T; + // // T.SetTranslate(-transformedPattern.vert[0].cP()); + // // vcg::Matrix44d Trev; + // // Trev.SetTranslate(transformedPattern.vert[0].cP()); + // // auto R = T * Rlocal * Trev; + // // transformedPattern.vert[1].P() + // // = R * meshTrianglePoints[2] + // // * 0.5; //optimizationResults.optimalXNameValuePairs["HexSize"]; + // // transformedPattern.vert[5].P() + // // = R * meshTrianglePoints[1] + // // * 0.5; //optimizationResults.optimalXNameValuePairs["HexSize"]; + + // vcg::tri::Append::Mesh(tiledPattern, transformedPattern); + // tiledPattern.updateEigenEdgeAndVertices(); + // tiledPattern.registerForDrawing(); + + // polyscope::show(); + // } + // } + // tiledPattern.removeDuplicateVertices(); + // tiledPattern.deleteDanglingVertices(); + // vcg::tri::Allocator::CompactEveryVector(tiledPattern); + // tiledPattern.updateEigenEdgeAndVertices(); + // return std::make_shared(tiledPattern); + // } + static std::shared_ptr tilePattern(PatternGeometry &pattern, + const vcg::Triangle3 &baseTriangle, + const std::vector &connectToNeighborsVi, + const VCGPolyMesh &tileInto) + { + std::shared_ptr pTiledPattern(new PatternGeometry); + + //Compute the barycentric coords of the verts in the base triangle pattern + std::vector barycentricCoordinates(pattern.VN()); + for (int vi = 0; vi < pattern.VN(); vi++) { + CoordType barycentricCoords_vi; + vcg::InterpolationParameters, double>(baseTriangle, + pattern.vert[vi].cP(), + barycentricCoords_vi); + barycentricCoordinates[vi] = barycentricCoords_vi; + } + VCGTriMesh tileIntoEdgeMesh; + + for (const VCGPolyMesh::FaceType &f : tileInto.face) { + CoordType centerOfFace(0, 0, 0); + for (size_t vi = 0; vi < f.VN(); vi++) { + centerOfFace = centerOfFace + f.cP(vi); + } + centerOfFace /= f.VN(); + vcg::tri::Allocator::AddVertex(tileIntoEdgeMesh, + centerOfFace, + vcg::Color4b::Yellow); + + std::vector firstInFanConnectToNeighbor_vi(connectToNeighborsVi); + for (int &vi : firstInFanConnectToNeighbor_vi) { + vi += pTiledPattern->VN(); + } + + for (size_t vi = 0; vi < f.VN(); vi++) { + std::vector meshTrianglePoints{centerOfFace, + f.cP(vi), + vi + 1 == f.VN() ? f.cP(0) + : f.cP(vi + 1)}; + auto fit = vcg::tri::Allocator::AddFace(tileIntoEdgeMesh, + meshTrianglePoints[0], + meshTrianglePoints[1], + meshTrianglePoints[2]); + CoordType faceNormal = ((meshTrianglePoints[1] - meshTrianglePoints[0]) + ^ (meshTrianglePoints[2] - meshTrianglePoints[0])) + .Normalize(); + fit->N() = faceNormal; + + PatternGeometry transformedPattern; + transformedPattern.copy(pattern); + //Transform the base triangle nodes to the mesh triangle using barycentric coords + for (int vi = 0; vi < transformedPattern.VN(); vi++) { + transformedPattern.vert[vi].P() = CoordType( + meshTrianglePoints[0] * barycentricCoordinates[vi][0] + + meshTrianglePoints[1] * barycentricCoordinates[vi][1] + + meshTrianglePoints[2] * barycentricCoordinates[vi][2]); + } + + for (VertexType &v : transformedPattern.vert) { + v.N() = faceNormal; + } + + vcg::tri::Append::Mesh(*pTiledPattern, + transformedPattern); + //Add edges for connecting the desired vertices + if (!connectToNeighborsVi.empty()) { + if (vi + 1 == f.VN()) { + for (int connectToNeighborIndex = 0; + connectToNeighborIndex < connectToNeighborsVi.size(); + connectToNeighborIndex++) { + vcg::tri::Allocator::AddEdge( + *pTiledPattern, + firstInFanConnectToNeighbor_vi[connectToNeighborIndex], + pTiledPattern->VN() - pattern.VN() + + connectToNeighborsVi[connectToNeighborIndex]); + } + } + if (vi != 0) { + for (int connectToNeighborIndex = 0; + connectToNeighborIndex < connectToNeighborsVi.size(); + connectToNeighborIndex++) { + vcg::tri::Allocator::AddEdge( + *pTiledPattern, + pTiledPattern->VN() - 2 * pattern.VN() + + connectToNeighborsVi[connectToNeighborIndex], + pTiledPattern->VN() - pattern.VN() + + connectToNeighborsVi[connectToNeighborIndex]); + } + } + } + // tiledPattern.updateEigenEdgeAndVertices(); + // tiledPattern.registerForDrawing(); + + // polyscope::show(); + } + } + pTiledPattern->removeDuplicateVertices(); + pTiledPattern->deleteDanglingVertices(); + vcg::tri::Allocator::CompactEveryVector(*pTiledPattern); + pTiledPattern->updateEigenEdgeAndVertices(); + return pTiledPattern; + } + void createFan(const std::vector &connectToNeighborsVi=std::vector(), const size_t &fanSize=6); }; + #endif // FLATPATTERNGEOMETRY_HPP diff --git a/utilities.hpp b/utilities.hpp index 41b4ec1..2424397 100755 --- a/utilities.hpp +++ b/utilities.hpp @@ -176,6 +176,7 @@ inline void deinitPolyscope() { polyscope::shutdown(); } + inline void initPolyscope() { if (polyscope::state::initialized) { return; diff --git a/vcgtrimesh.cpp b/vcgtrimesh.cpp index 23f1eb0..dc938cb 100755 --- a/vcgtrimesh.cpp +++ b/vcgtrimesh.cpp @@ -1,79 +1,140 @@ #include "vcgtrimesh.hpp" -#include +#include "utilities.hpp" #include "wrap/io_trimesh/import_obj.h" #include "wrap/io_trimesh/import_off.h" #include +#include +#include -void VCGTriMesh::loadFromPlyFile(const std::string &filename) { - unsigned int mask = 0; - mask |= nanoply::NanoPlyWrapper::IO_VERTCOORD; - mask |= nanoply::NanoPlyWrapper::IO_VERTNORMAL; - mask |= nanoply::NanoPlyWrapper::IO_VERTCOLOR; - mask |= nanoply::NanoPlyWrapper::IO_EDGEINDEX; - mask |= nanoply::NanoPlyWrapper::IO_FACEINDEX; - if (nanoply::NanoPlyWrapper::LoadModel( - std::filesystem::absolute(filename).string().c_str(), *this, mask) != 0) { - std::cout << "Could not load tri mesh" << std::endl; - } +bool VCGTriMesh::load(const std::string &filename) { + // unsigned int mask = 0; + // mask |= nanoply::NanoPlyWrapper::IO_VERTCOORD; + // mask |= nanoply::NanoPlyWrapper::IO_VERTNORMAL; + // mask |= nanoply::NanoPlyWrapper::IO_VERTCOLOR; + // mask |= nanoply::NanoPlyWrapper::IO_EDGEINDEX; + // mask |= nanoply::NanoPlyWrapper::IO_FACEINDEX; + + // if (nanoply::NanoPlyWrapper::LoadModel( + // std::filesystem::absolute(filename).string().c_str(), *this, mask) != 0) { + if (vcg::tri::io::Importer::Open(*this, + std::filesystem::absolute(filename).string().c_str()) + != 0) { + std::cout << "Could not load tri mesh" << std::endl; + return false; + } vcg::tri::UpdateTopology::FaceFace(*this); vcg::tri::UpdateTopology::VertexFace(*this); vcg::tri::UpdateNormal::PerVertexNormalized(*this); -} -Eigen::MatrixX3d VCGTriMesh::getVertices() const { - Eigen::MatrixX3d vertices(VN(), 3); - for (size_t vi = 0; vi < VN(); vi++) { - VCGTriMesh::CoordType vertexCoordinates = vert[vi].cP(); - vertices.row(vi) = vertexCoordinates.ToEigenVector(); - } - return vertices; -} - -Eigen::MatrixX3i VCGTriMesh::getFaces() const { - Eigen::MatrixX3i faces(FN(), 3); - for (int fi = 0; fi < FN(); fi++) { - const VCGTriMesh::FaceType &face = this->face[fi]; - const size_t v0 = vcg::tri::Index(*this, face.cV(0)); - const size_t v1 = vcg::tri::Index(*this, face.cV(1)); - const size_t v2 = vcg::tri::Index(*this, face.cV(2)); - faces.row(fi) = Eigen::Vector3i(v0, v1, v2); - } - return faces; -} - -bool VCGTriMesh::savePly(const std::string plyFilename) { - // Load the ply file - unsigned int mask = 0; - mask |= nanoply::NanoPlyWrapper::IO_VERTCOORD; - mask |= nanoply::NanoPlyWrapper::IO_VERTCOLOR; - mask |= nanoply::NanoPlyWrapper::IO_FACEINDEX; - mask |= nanoply::NanoPlyWrapper::IO_FACENORMAL; - if (nanoply::NanoPlyWrapper::SaveModel(plyFilename.c_str(), *this, - mask, false) != 0) { - return false; - } + label = std::filesystem::path(filename).stem(); return true; } +Eigen::MatrixX3d VCGTriMesh::getVertices() const +{ + // vcg::tri::Allocator::CompactVertexVector(m); + Eigen::MatrixX3d vertices(VN(), 3); + for (size_t vi = 0; vi < VN(); vi++) { + VCGTriMesh::CoordType vertexCoordinates = vert[vi].cP(); + vertices.row(vi) = vertexCoordinates.ToEigenVector(); + } + return vertices; +} + +Eigen::MatrixX3i VCGTriMesh::getFaces() const +{ + // vcg::tri::Allocator::CompactFaceVector(m); + Eigen::MatrixX3i faces(FN(), 3); + for (int fi = 0; fi < FN(); fi++) { + const VCGTriMesh::FaceType &face = this->face[fi]; + const size_t v0 = vcg::tri::Index(*this, face.cV(0)); + const size_t v1 = vcg::tri::Index(*this, face.cV(1)); + const size_t v2 = vcg::tri::Index(*this, face.cV(2)); + faces.row(fi) = Eigen::Vector3i(v0, v1, v2); + } + return faces; +} + +Eigen::MatrixX2i VCGTriMesh::getEdges() const +{ + // vcg::tri::Allocator::CompactFaceVector(m); + Eigen::MatrixX2i edges(EN(), 2); + for (int ei = 0; ei < EN(); ei++) { + const VCGTriMesh::EdgeType &edge = this->edge[ei]; + const size_t v0 = vcg::tri::Index(*this, edge.cV(0)); + const size_t v1 = vcg::tri::Index(*this, edge.cV(1)); + edges.row(ei) = Eigen::Vector2i(v0, v1); + } + return edges; +} + +bool VCGTriMesh::save(const std::string plyFilename) +{ + // vcg::tri::Allocator::CompactEveryVector(*this); + assert(std::filesystem::path(plyFilename).extension() == ".ply"); + // Load the ply file + unsigned int mask = 0; + mask |= nanoply::NanoPlyWrapper::IO_VERTCOORD; + mask |= nanoply::NanoPlyWrapper::IO_VERTCOLOR; + mask |= nanoply::NanoPlyWrapper::IO_FACEINDEX; + mask |= nanoply::NanoPlyWrapper::IO_FACENORMAL; + if (nanoply::NanoPlyWrapper::SaveModel(plyFilename.c_str(), *this, mask, false) + != 0) { + return false; + } + return true; +} + +polyscope::SurfaceMesh *VCGTriMesh::registerForDrawing( + const std::optional> &desiredColor, const bool &shouldEnable) const +{ + auto vertices = getVertices(); + auto faces = getFaces(); + initPolyscope(); + + if (faces.rows() == 0) { + auto edges = getEdges(); + polyscope::CurveNetwork *polyscopeHandle_mesh( + polyscope::registerCurveNetwork(label, vertices, edges)); + return nullptr; + } + + polyscope::SurfaceMesh *polyscopeHandle_mesh( + polyscope::registerSurfaceMesh(label, vertices, faces)); + + polyscopeHandle_mesh->setEnabled(shouldEnable); + const double drawingRadius = 0.002; + polyscopeHandle_mesh->setEdgeWidth(drawingRadius); + + if (desiredColor.has_value()) { + const glm::vec3 desiredColor_glm(desiredColor.value()[0], + desiredColor.value()[1], + desiredColor.value()[2]); + polyscopeHandle_mesh->setSurfaceColor(desiredColor_glm); + } + return polyscopeHandle_mesh; +} + VCGTriMesh::VCGTriMesh() {} -VCGTriMesh::VCGTriMesh(const std::string &filename) { - const std::string extension = std::filesystem::path(filename).extension().string(); - if (extension == ".ply") { - loadFromPlyFile(filename); - } else if (extension == ".obj") { - vcg::tri::io::ImporterOBJ::Info info; - vcg::tri::io::ImporterOBJ::Open(*this, filename.c_str(), info); - } else if (extension == ".off") { - vcg::tri::io::ImporterOFF::Open(*this, filename.c_str()); +VCGTriMesh::VCGTriMesh(const std::string &filename) +{ + const std::string extension = std::filesystem::path(filename).extension().string(); + if (extension == ".ply") { + load(filename); + } else if (extension == ".obj") { + vcg::tri::io::ImporterOBJ::Info info; + vcg::tri::io::ImporterOBJ::Open(*this, filename.c_str(), info); + } else if (extension == ".off") { + vcg::tri::io::ImporterOFF::Open(*this, filename.c_str()); - } else { - std::cerr << "Uknown file extension " << extension << ". Could not open " - << filename << std::endl; - assert(false); - } - vcg::tri::UpdateTopology::AllocateEdge(*this); - vcg::tri::UpdateTopology::FaceFace(*this); - vcg::tri::UpdateTopology::VertexFace(*this); - vcg::tri::UpdateNormal::PerVertexNormalized(*this); + } else { + std::cerr << "Uknown file extension " << extension << ". Could not open " << filename + << std::endl; + assert(false); + } + vcg::tri::UpdateTopology::AllocateEdge(*this); + vcg::tri::UpdateTopology::FaceFace(*this); + vcg::tri::UpdateTopology::VertexFace(*this); + vcg::tri::UpdateNormal::PerVertexNormalized(*this); } diff --git a/vcgtrimesh.hpp b/vcgtrimesh.hpp index 7be0d69..582a074 100755 --- a/vcgtrimesh.hpp +++ b/vcgtrimesh.hpp @@ -1,5 +1,7 @@ #ifndef VCGTRIMESH_HPP #define VCGTRIMESH_HPP +#include "mesh.hpp" +#include #include using VertexIndex = size_t; @@ -10,10 +12,14 @@ struct VCGTriMeshUsedTypes : public vcg::UsedTypes::AsVertexType, vcg::Use::AsEdgeType, vcg::Use::AsFaceType> {}; -class VCGTriMeshVertex - : public vcg::Vertex {}; +class VCGTriMeshVertex : public vcg::Vertex +{}; class VCGTriMeshFace : public vcg::Face, std::vector, - std::vector> { + std::vector>, + public Mesh +{ public: VCGTriMesh(); VCGTriMesh(const std::string &filename); - void loadFromPlyFile(const std::string &filename); + bool load(const std::string &filename) override; Eigen::MatrixX3d getVertices() const; Eigen::MatrixX3i getFaces() const; - bool savePly(const std::string plyFilename); + bool save(const std::string plyFilename); template size_t getIndex(const MeshElement &element) { return vcg::tri::Index(*this, element); } + +#ifdef POLYSCOPE_DEFINED + polyscope::SurfaceMesh *registerForDrawing( + const std::optional> &desiredColor = std::nullopt, + const bool &shouldEnable = true) const; +#endif + Eigen::MatrixX2i getEdges() const; }; #endif // VCGTRIMESH_HPP