Moved files to DRMSimulationModel project

This commit is contained in:
iasonmanolas 2022-07-28 13:23:54 +03:00
parent 90551e5485
commit 7780e4ab38
16 changed files with 111 additions and 7355 deletions

View File

@ -2,11 +2,12 @@ cmake_minimum_required(VERSION 3.0)
project(MySources)
file(GLOB MySourcesFiles ${CMAKE_CURRENT_LIST_DIR}/*.hpp ${CMAKE_CURRENT_LIST_DIR}/*.cpp)
add_library(${PROJECT_NAME} STATIC ${MySourcesFiles} )
add_library(${PROJECT_NAME} ${MySourcesFiles} )
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)
#Download external dependencies NOTE: If the user has one of these libs it shouldn't be downloaded again.
include(${CMAKE_CURRENT_LIST_DIR}/cmake/DownloadProject.cmake)
include(FetchContent)
if (CMAKE_VERSION VERSION_LESS 3.2)
set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
else()
@ -19,6 +20,13 @@ endif()
##Create directory for the external libraries
file(MAKE_DIRECTORY ${EXTERNAL_DEPS_DIR})
set(DRMSimulationModelDir "/home/iason/Coding/Libraries/DRMSimulationModel")
message("drm dir:" ${DRMSimulationModelDir})
add_subdirectory(${DRMSimulationModelDir} "/home/iason/Coding/build/DRMSimulationModel")
target_link_libraries(${PROJECT_NAME} PUBLIC DRMSimulationModel)
target_include_directories(${PROJECT_NAME} PUBLIC ${DRMSimulationModelDir})
#target_sources(${PROJECT_NAME} PUBLIC ${DRMSimulationModelDir}/simulationmodel.hpp)
##dlib
#set(DLIB_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR}/dlib)
#download_project(PROJ DLIB
@ -36,56 +44,77 @@ file(MAKE_DIRECTORY ${EXTERNAL_DEPS_DIR})
#endif()
#add_compile_definitions(DLIB_DEFINED)
if(${USE_POLYSCOPE})
set(POLYSCOPE_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/polyscope)
download_project(PROJ POLYSCOPE
## polyscope
FetchContent_Declare(polyscope
GIT_REPOSITORY https://github.com/nmwsharp/polyscope.git
GIT_TAG master
PREFIX ${EXTERNAL_DEPS_DIR}
BINARY_DIR ${POLYSCOPE_BINARY_DIR}
${UPDATE_DISCONNECTED_IF_AVAILABLE}
)
add_subdirectory(${POLYSCOPE_SOURCE_DIR} ${POLYSCOPE_BINARY_DIR})
add_compile_definitions(POLYSCOPE_DEFINED)
target_sources(${PROJECT_NAME} PUBLIC ${POLYSCOPE_SOURCE_DIR}/deps/imgui/imgui/misc/cpp/imgui_stdlib.cpp)
GIT_TAG master
)
FetchContent_MakeAvailable(polyscope)
target_include_directories(${PROJECT_NAME} PUBLIC ${polyscope_SOURCE_DIR}/include)
target_link_libraries(${PROJECT_NAME} PUBLIC polyscope)
#if(NOT TARGET polyscope AND ${USE_POLYSCOPE})
# set(POLYSCOPE_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/polyscope)
# download_project(PROJ POLYSCOPE
# GIT_REPOSITORY https://github.com/nmwsharp/polyscope.git
# GIT_TAG master
# PREFIX ${EXTERNAL_DEPS_DIR}
# BINARY_DIR ${POLYSCOPE_BINARY_DIR}
# ${UPDATE_DISCONNECTED_IF_AVAILABLE}
# )
# add_subdirectory(${POLYSCOPE_SOURCE_DIR} ${POLYSCOPE_BINARY_DIR})
# add_compile_definitions(POLYSCOPE_DEFINED)
# target_sources(${PROJECT_NAME} PUBLIC ${POLYSCOPE_SOURCE_DIR}/deps/imgui/imgui/misc/cpp/imgui_stdlib.cpp)
message("Using polyscope..")
target_include_directories(${PROJECT_NAME} PUBLIC ${POLYSCOPE_SOURCE_DIR}/include)
target_link_libraries(${PROJECT_NAME} PUBLIC polyscope)
endif()
# message("Using polyscope..")
# target_include_directories(${PROJECT_NAME} PUBLIC ${POLYSCOPE_SOURCE_DIR}/include)
# target_link_libraries(${PROJECT_NAME} PUBLIC polyscope)
#endif()
##vcglib devel branch
download_project(PROJ vcglib_devel
FetchContent_Declare(vcglib
GIT_REPOSITORY https://gitea-s2i2s.isti.cnr.it/manolas/vcglib.git
GIT_TAG devel
PREFIX ${EXTERNAL_DEPS_DIR}
${UPDATE_DISCONNECTED_IF_AVAILABLE}
)
add_subdirectory(${vcglib_devel_SOURCE_DIR} ${vcglib_devel_BINARY_DIR})
target_sources(${PROJECT_NAME} PUBLIC ${vcglib_devel_SOURCE_DIR}/wrap/ply/plylib.cpp)
)
FetchContent_MakeAvailable(vcglib)
target_include_directories(${PROJECT_NAME} PUBLIC ${vcglib_SOURCE_DIR})
target_sources(${PROJECT_NAME} PUBLIC ${vcglib_SOURCE_DIR}/wrap/ply/plylib.cpp)
##matplot++ lib
set(MATPLOTPLUSPLUS_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/matplot)
download_project(PROJ MATPLOTPLUSPLUS
###matplot++ lib
FetchContent_Declare(matplot
GIT_REPOSITORY https://github.com/alandefreitas/matplotplusplus
GIT_TAG master
BINARY_DIR ${MATPLOTPLUSPLUS_BINARY_DIR}
PREFIX ${EXTERNAL_DEPS_DIR}
${UPDATE_DISCONNECTED_IF_AVAILABLE}
)
add_subdirectory(${MATPLOTPLUSPLUS_SOURCE_DIR} ${MATPLOTPLUSPLUS_BINARY_DIR})
GIT_TAG master
)
FetchContent_MakeAvailable(matplot)
target_include_directories(${PROJECT_NAME} PUBLIC ${matplot_SOURCE_DIR})
#set(MATPLOTPLUSPLUS_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/matplot)
#download_project(PROJ MATPLOTPLUSPLUS
# GIT_TAG master
# BINARY_DIR ${MATPLOTPLUSPLUS_BINARY_DIR}
# PREFIX ${EXTERNAL_DEPS_DIR}
# ${UPDATE_DISCONNECTED_IF_AVAILABLE}
# )
#add_subdirectory(${MATPLOTPLUSPLUS_SOURCE_DIR} ${MATPLOTPLUSPLUS_BINARY_DIR})
##threed-beam-fea
set(threed-beam-fea_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/threed-beam-fea)
download_project(PROJ threed-beam-fea
# GIT_REPOSITORY https://github.com/IasonManolas/threed-beam-fea.git
if(NOT TARGET ThreedBeamFEA)
FetchContent_Declare(threed-beam-fea
GIT_REPOSITORY https://gitea-s2i2s.isti.cnr.it/manolas/threed-beam-fea.git
GIT_TAG master
BINARY_DIR ${threed-beam-fea_BINARY_DIR}
PREFIX ${EXTERNAL_DEPS_DIR}
${UPDATE_DISCONNECTED_IF_AVAILABLE}
)
add_subdirectory(${threed-beam-fea_SOURCE_DIR} ${threed-beam-fea_BINARY_DIR})
GIT_TAG master
)
FetchContent_MakeAvailable(threed-beam-fea)
endif()
target_link_libraries(${PROJECT_NAME} PUBLIC ThreedBeamFEA)
target_include_directories(${PROJECT_NAME} PUBLIC ${threed-beam-fea_SOURCE_DIR})
#set(threed-beam-fea_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/threed-beam-fea)
#download_project(PROJ threed-beam-fea
## GIT_REPOSITORY https://github.com/IasonManolas/threed-beam-fea.git
# GIT_TAG master
# BINARY_DIR ${threed-beam-fea_BINARY_DIR}
# PREFIX ${EXTERNAL_DEPS_DIR}
# ${UPDATE_DISCONNECTED_IF_AVAILABLE}
#)
#add_subdirectory(${threed-beam-fea_SOURCE_DIR} ${threed-beam-fea_BINARY_DIR})
find_package(Eigen3 3.4 REQUIRED)
if(MSVC)
@ -97,17 +126,16 @@ if(${MYSOURCES_STATIC_LINK})
endif()
if(${MYSOURCES_STATIC_LINK})
message("Linking statically here")
target_link_libraries(${PROJECT_NAME} PUBLIC -static Eigen3::Eigen matplot ThreedBeamFEA pthread gfortran quadmath)
target_link_libraries(${PROJECT_NAME} PUBLIC -static Eigen3::Eigen matplot pthread gfortran quadmath)
else()
target_link_libraries(${PROJECT_NAME} PUBLIC Eigen3::Eigen matplot ThreedBeamFEA tbb pthread)
target_link_libraries(${PROJECT_NAME} PUBLIC Eigen3::Eigen matplot tbb pthread)
endif()
target_link_directories(MySources PUBLIC ${CMAKE_CURRENT_LIST_DIR}/boost_graph/libs)
target_include_directories(${PROJECT_NAME}
PUBLIC ${CMAKE_CURRENT_LIST_DIR}/boost_graph
PUBLIC ${vcglib_devel_SOURCE_DIR}
PUBLIC ${threed-beam-fea_SOURCE_DIR}
PUBLIC ${MATPLOTPLUSPLUS_SOURCE_DIR}/source
# PUBLIC ${threed-beam-fea_SOURCE_DIR}
)
@ -118,13 +146,32 @@ add_subdirectory(${ARMADILLO_SOURCE_DIR} ${ARMADILLO_BIN_DIR})
target_include_directories(${PROJECT_NAME} PUBLIC ${ARMADILLO_SOURCE_DIR}/include)
add_compile_definitions(ARMA_DONT_USE_WRAPPER)
target_link_libraries(${PROJECT_NAME} PUBLIC "/home/iason/Coding/Libraries/armadillo/build/libarmadillo.a" #[[blas lapack]])
find_package(Armadillo REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC ${ARMADILLO_LIBRARIES})
#if(NOT TARGET ThreedBeamFEA)
#FetchContent_Declare(armadillo
# GIT_REPOSITORY https://gitlab.com/conradsnicta/armadillo-code.git
# GIT_TAG "11.2.x"
# )
#FetchContent_MakeAvailable(armadillo)
#find_package(Armadillo REQUIRED)
#target_link_libraries(${PROJECT_NAME} PUBLIC ${ARMADILLO_LIBRARIES})
#endif()
#target_link_libraries(${PROJECT_NAME} PUBLIC "/home/iason/Coding/build/FormFInder/Debug/_deps/armadillo-build/libarmadillo.a")
###ENSMALLEN
#FetchContent_Declare(ensmallen
# GIT_REPOSITORY https://github.com/mlpack/ensmallen.git
# GIT_TAG master
# )
#FetchContent_MakeAvailable(ensmallen)
#target_link_libraries(${PROJECT_NAME} PRIVATE ensmallen)
#target_include_directories(${PROJECT_NAME}
#PUBLIC ${ensmallen_SOURCE_DIR}/include)
#add_compile_definitions(USE_ENSMALLEN)
##ENSMALLEN
set(ENSMALLEN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/ensmallen)
download_project(PROJ ENSMALLEN
GIT_REPOSITORY https://github.com/mlpack/ensmallen.git
GIT_REPOSITORY https://github.com/mlpack/ensmallen.git
GIT_TAG master
BINARY_DIR ${ENSMALLEN_BINARY_DIR}
PREFIX ${EXTERNAL_DEPS_DIR}
@ -190,16 +237,21 @@ target_link_libraries(${PROJECT_NAME} PUBLIC "/home/iason/Coding/Libraries/chron
##TBB
if(NOT TARGET tbb_static AND NOT TARGET tbb)
set(TBB_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/tbb)
download_project(PROJ TBB
GIT_REPOSITORY https://github.com/wjakob/tbb.git
GIT_TAG master
PREFIX ${EXTERNAL_DEPS_DIR}
BINARY_DIR ${TBB_BINARY_DIR}
${UPDATE_DISCONNECTED_IF_AVAILABLE}
)
option(TBB_BUILD_TESTS "Build TBB tests and enable testing infrastructure" OFF)
add_subdirectory(${TBB_SOURCE_DIR} ${TBB_BINARY_DIR})
link_directories(${TBB_BINARY_DIR})
target_link_libraries(${PROJECT_NAME} PRIVATE tbb_static)
# set(TBB_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/tbb)
# download_project(PROJ TBB
# GIT_REPOSITORY https://github.com/wjakob/tbb.git
# GIT_TAG master
# PREFIX ${EXTERNAL_DEPS_DIR}
# BINARY_DIR ${TBB_BINARY_DIR}
# ${UPDATE_DISCONNECTED_IF_AVAILABLE}
# )
# option(TBB_BUILD_TESTS "Build TBB tests and enable testing infrastructure" OFF)
# add_subdirectory(${TBB_SOURCE_DIR} ${TBB_BINARY_DIR})
#link_directories(${TBB_BINARY_DIR})
FetchContent_Declare(tbb
GIT_REPOSITORY https://github.com/wjakob/tbb.git
GIT_TAG master
)
FetchContent_MakeAvailable(tbb)
target_link_libraries(${PROJECT_NAME} PRIVATE tbb_static)
endif()

157
beam.hpp
View File

@ -1,157 +0,0 @@
#ifndef BEAM_HPP
#define BEAM_HPP
#include "nlohmann/json.hpp"
#include <assert.h>
#include <cmath>
#include <iostream>
#include <string>
struct BeamDimensions
{
double dim1, dim2;
double A{0}; // cross sectional area
struct MomentsOfInertia
{
double I2{0}; // second moment of inertia
double I3{0}; // third moment of inertia
double J{0}; // torsional constant (polar moment of inertia)
} inertia;
public:
double getDim1() const;
double getDim2() const;
virtual void to_json(nlohmann::json &j, const BeamDimensions &beamDim) const;
virtual void from_json(const nlohmann::json &j, BeamDimensions &beamDim) const;
virtual double getDrawingRadius() const = 0;
};
inline double BeamDimensions::getDim2() const
{
return dim2;
}
inline void BeamDimensions::to_json(nlohmann::json &j, const BeamDimensions &beamDim) const
{
j = nlohmann::json{{"BeamDimension.dim1", beamDim.dim1}, {"BeamDimension.dim2", beamDim.dim2}};
}
inline void BeamDimensions::from_json(const nlohmann::json &j, BeamDimensions &beamDim) const
{
const std::string jsonKey_dim1 = "BeamDimension.dim1";
const std::string jsonKey_dim2 = "BeamDimension.dim2";
if (j.contains(jsonKey_dim1)) {
j.at(jsonKey_dim1).get_to(beamDim.dim1);
}
if (j.contains(jsonKey_dim2)) {
j.at(jsonKey_dim2).get_to(beamDim.dim2);
}
}
inline double BeamDimensions::getDim1() const
{
return dim1;
}
struct RectangularBeamDimensions : public BeamDimensions
{
inline static std::string name{"Rectangular"};
inline const static double defaultSize = 0.002;
RectangularBeamDimensions(const double &width, const double &height)
{
assert(width > 0 && height > 0);
dim1 = width;
dim2 = height;
updateProperties();
}
RectangularBeamDimensions()
{
dim1 = defaultSize;
dim2 = defaultSize;
updateProperties();
}
std::string toString() const
{
return std::string("w=") + std::to_string(dim1) + std::string(" h=") + std::to_string(dim2);
}
void updateProperties()
{
A = dim1 * dim2;
inertia.I2 = dim1 * std::pow(dim2, 3) / 12;
inertia.I3 = dim2 * std::pow(dim1, 3) / 12;
inertia.J = inertia.I2 + inertia.I3;
}
static void computeMomentsOfInertia(const RectangularBeamDimensions &dimensions,
MomentsOfInertia &inertia);
double getWidth() const { return dim1; }
double getHeight() const { return dim2; }
double getDrawingRadius() const override;
};
inline double RectangularBeamDimensions::getDrawingRadius() const
{
return getWidth() / std::sqrt(2);
}
struct CylindricalBeamDimensions : public BeamDimensions
{
inline static std::string name{"Cylindrical"};
// https://www.engineeringtoolbox.com/area-moment-inertia-d_1328.html
CylindricalBeamDimensions(const double &outsideDiameter, const double &insideDiameter)
{
assert(outsideDiameter > 0 && insideDiameter > 0 && outsideDiameter > insideDiameter);
dim1 = insideDiameter;
dim2 = outsideDiameter;
updateProperties();
}
CylindricalBeamDimensions()
{
dim1 = 0.026;
dim2 = 0.03;
updateProperties();
}
void updateProperties()
{
A = M_PI * (std::pow(getOutterDiameter(), 2) - std::pow(getInnerDiameter(), 2)) / 4;
inertia.I2 = M_PI * (std::pow(getOutterDiameter(), 4) - std::pow(getInnerDiameter(), 4))
/ 64;
inertia.I3 = inertia.I2;
inertia.J = inertia.I2 + inertia.I3;
}
double getInnerDiameter() const { return dim1; }
double getOutterDiameter() const { return dim2; }
double getDrawingRadius() const override;
};
inline double CylindricalBeamDimensions::getDrawingRadius() const
{
return getOutterDiameter();
}
struct ElementMaterial
{
double poissonsRatio;
double youngsModulus;
double G;
ElementMaterial(const double &poissonsRatio, const double &youngsModulus)
: poissonsRatio(poissonsRatio), youngsModulus(youngsModulus)
{
assert(poissonsRatio <= 0.5 && poissonsRatio >= -1);
updateShearModulus();
}
ElementMaterial() : poissonsRatio(0.3), youngsModulus(1e9) { updateShearModulus(); }
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);
}
void updateShearModulus() { G = youngsModulus / (2 * (1 + poissonsRatio)); }
};
#endif // BEAM_HPP

File diff suppressed because it is too large Load Diff

View File

@ -1,304 +0,0 @@
#ifndef BEAMFORMFINDER_HPP
#define BEAMFORMFINDER_HPP
//#ifdef USE_MATPLOT
//#include "matplot.h"
#include <matplot/matplot.h>
//#endif
#include "simulationmesh.hpp"
#include "simulationmodel.hpp"
#include <Eigen/Dense>
#include <filesystem>
#include <unordered_set>
#ifdef USE_ENSMALLEN
#include <armadillo>
#include <ensmallen.hpp>
#endif
class SimulationJob;
enum DoF { Ux = 0, Uy, Uz, Nx, Ny, Nr, NumDoF };
using DoFType = int;
using EdgeType = VCGEdgeMesh::EdgeType;
using VertexType = VCGEdgeMesh::VertexType;
struct DifferentiateWithRespectTo {
const VertexType &v;
const DoFType &dofi;
};
class DRMSimulationModel : public SimulationModel
{
public:
struct Settings
{
bool useTranslationalKineticEnergyForKineticDamping{true};
bool useTotalRotationalKineticEnergyForKineticDamping{false};
bool shouldDraw{false};
bool beVerbose{false};
bool shouldCreatePlots{false};
// int drawingStep{0};
// double residualForcesMovingAverageDerivativeNormThreshold{1e-8};
// double residualForcesMovingAverageNormThreshold{1e-8};
double Dtini{0.1};
double xi{0.9969};
std::optional<double> translationalKineticEnergyThreshold;
int gradualForcedDisplacementSteps{50};
// int desiredGradualExternalLoadsSteps{1};
double gamma{0.8};
std::optional<double> totalResidualForcesNormThreshold;
double totalExternalForcesNormPercentageTermination{1e-5};
std::optional<int> maxDRMIterations;
std::optional<int> debugModeStep;
std::optional<double> totalTranslationalKineticEnergyThreshold;
std::optional<double> averageResidualForcesCriterionThreshold;
std::optional<double> linearGuessForceScaleFactor;
// std::optional<int> intermediateResultsSaveStep;
std::optional<bool> saveIntermediateBestStates;
std::optional<double> viscousDampingFactor;
Settings() {}
void save(const std::filesystem::path &jsonFilePath) const;
bool load(const std::filesystem::path &filePath);
struct JsonLabels
{
const std::string shouldDraw{"shouldDraw"};
const std::string beVerbose{"beVerbose"};
const std::string shouldCreatePlots{"shouldCreatePlots"};
const std::string Dtini{"DtIni"};
const std::string xi{"xi"};
const std::string gamma{"gamma"};
const std::string totalResidualForcesNormThreshold;
const std::string maxDRMIterations{"maxIterations"};
const std::string debugModeStep{"debugModeStep"};
const std::string totalTranslationalKineticEnergyThreshold{
"totalTranslationaKineticEnergyThreshold"};
const std::string averageResidualForcesCriterionThreshold{
"averageResidualForcesThreshold"};
const std::string linearGuessForceScaleFactor{"linearGuessForceScaleFactor"};
const std::string viscousDampingFactor{"viscousDampingFactor"};
};
static JsonLabels jsonLabels;
};
inline const static std::string label{"DRM"};
private:
Settings mSettings;
double Dt{mSettings.Dtini};
bool checkedForMaximumMoment{false};
bool shouldTemporarilyDampForces{false};
bool shouldTemporarilyAmplifyForces{true};
double externalMomentsNorm{0};
size_t mCurrentSimulationStep{0};
matplot::line_handle plotHandle;
std::vector<double> plotYValues;
size_t numOfDampings{0};
int externalLoadStep{1};
std::vector<bool> isVertexConstrained;
std::vector<bool> isRigidSupport;
double minTotalResidualForcesNorm{std::numeric_limits<double>::max()};
const std::string meshPolyscopeLabel{"Simulation mesh"};
std::unique_ptr<SimulationMesh> pMesh;
std::unordered_map<VertexIndex, std::unordered_set<DoFType>> constrainedVertices;
SimulationHistory history;
// Eigen::Tensor<double, 4> theta3Derivatives;
// std::unordered_map<MyKeyType, double, key_hash> theta3Derivatives;
bool shouldApplyInitialDistortion{false};
//#ifdef USE_ENSMALLEN
// std::shared_ptr<SimulationJob> pJob;
//#endif
void reset(const std::shared_ptr<SimulationJob> &pJob, const Settings &settings);
void updateNodalInternalForces(
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>> &fixedVertices);
void updateNodalExternalForces(
const std::unordered_map<VertexIndex, Vector6d> &nodalForces,
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>> &fixedVertices);
void updateResidualForces();
void updateRotationalDisplacements();
void updateElementalLengths();
void updateNodalMasses();
void updateNodalAccelerations();
void updateNodalVelocities();
void updateNodalDisplacements();
void applyForcedDisplacements(
const std::unordered_map<VertexIndex, Eigen::Vector3d> &nodalForcedDisplacements);
Vector6d computeElementTorsionalForce(const Element &element,
const Vector6d &displacementDifference,
const std::unordered_set<DoFType> &constrainedDof);
// BeamFormFinder::Vector6d computeElementFirstBendingForce(
// const Element &element, const Vector6d &displacementDifference,
// const std::unordered_set<gsl::index> &constrainedDof);
// BeamFormFinder::Vector6d computeElementSecondBendingForce(
// const Element &element, const Vector6d &displacementDifference,
// const std::unordered_set<gsl::index> &constrainedDof);
void updateKineticEnergy();
void resetVelocities();
SimulationResults computeResults(const std::shared_ptr<SimulationJob> &pJob);
void updateNodePosition(
VertexType &v,
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>> &fixedVertices);
void applyDisplacements(
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>> &fixedVertices);
#ifdef POLYSCOPE_DEFINED
void draw(const std::shared_ptr<SimulationJob> &pJob, const std::string &screenshotsFolder = {});
#endif
void
updateNodalInternalForce(Vector6d &nodalInternalForce,
const Vector6d &elementInternalForce,
const std::unordered_set<DoFType> &nodalFixedDof);
Vector6d computeElementInternalForce(
const Element &elem, const Node &n0, const Node &n1,
const std::unordered_set<DoFType> &n0ConstrainedDof,
const std::unordered_set<DoFType> &n1ConstrainedDof);
Vector6d computeElementAxialForce(const ::EdgeType &e) const;
VectorType computeDisplacementDifferenceDerivative(
const EdgeType &e, const DifferentiateWithRespectTo &dui) const;
double
computeDerivativeElementLength(const EdgeType &e,
const DifferentiateWithRespectTo &dui) const;
VectorType computeDerivativeT1(const EdgeType &e,
const DifferentiateWithRespectTo &dui) const;
VectorType
computeDerivativeOfNormal(const VertexType &v,
const DifferentiateWithRespectTo &dui) const;
VectorType computeDerivativeT3(const EdgeType &e,
const DifferentiateWithRespectTo &dui) const;
VectorType computeDerivativeT2(const EdgeType &e,
const DifferentiateWithRespectTo &dui) const;
double computeDerivativeTheta2(const EdgeType &e, const VertexIndex &evi,
const VertexIndex &dwrt_evi,
const DoFType &dwrt_dofi) const;
void updateElementalFrames();
VectorType computeDerivativeOfR(const EdgeType &e,
const DifferentiateWithRespectTo &dui) const;
static double computeDerivativeOfNorm(const VectorType &x,
const VectorType &derivativeOfX);
static VectorType computeDerivativeOfCrossProduct(
const VectorType &a, const VectorType &derivativeOfA, const VectorType &b,
const VectorType &derivativeOfB);
double computeTheta3(const EdgeType &e, const VertexType &v);
double computeDerivativeTheta3(const EdgeType &e, const VertexType &v,
const DifferentiateWithRespectTo &dui) const;
double computeTotalPotentialEnergy();
void computeRigidSupports();
void updateNormalDerivatives();
void updateT1Derivatives();
void updateT2Derivatives();
void updateT3Derivatives();
void updateRDerivatives();
double computeDerivativeTheta1(const EdgeType &e, const VertexIndex &evi,
const VertexIndex &dwrt_evi,
const DoFType &dwrt_dofi) const;
// void updatePositionsOnTheFly(
// const std::unordered_map<VertexIndex,
// std::unordered_set<gsl::index>>
// &fixedVertices);
void updateResidualForcesOnTheFly(
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>>
&fixedVertices);
void updatePositionsOnTheFly(
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>>
&fixedVertices);
void updateNodeNormal(
VertexType &v,
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>>
&fixedVertices);
void applyForcedNormals(
const std::unordered_map<VertexIndex, VectorType> nodalForcedRotations);
void printCurrentState() const;
void printDebugInfo() const;
double computeTotalInternalPotentialEnergy();
void applySolutionGuess(const SimulationResults &solutionGuess,
const std::shared_ptr<SimulationJob> &pJob);
void updateNodeNr(VertexType &v);
void applyKineticDamping(const std::shared_ptr<SimulationJob> &pJob);
virtual SimulationResults executeSimulation(const std::shared_ptr<SimulationJob> &pJob) override;
void setStructure(const std::shared_ptr<SimulationMesh> &pMesh) override;
void reset(const std::shared_ptr<SimulationJob> &pJob);
std::vector<std::array<Vector6d, 4>> computeInternalForces(
const std::unordered_map<VertexIndex, std::unordered_set<DoFType>> &fixedVertices);
void updateDerivatives();
public:
DRMSimulationModel();
SimulationResults executeSimulation(const std::shared_ptr<SimulationJob> &pJob,
const Settings &settings,
const SimulationResults &solutionGuess = SimulationResults());
//#ifdef USE_ENSMALLEN
// std::shared_ptr<SimulationJob> pJob;
// double EvaluateWithGradient(const arma::mat &x, arma::mat &g);
// void setJob(const std::shared_ptr<SimulationJob> &pJob);
// SimulationMesh *getDeformedMesh(const arma::mat &x, const std::shared_ptr<SimulationJob> &pJob);
// double Evaluate(const arma::mat &x);
//#endif
static void runUnitTests();
};
inline DRMSimulationModel::Settings::JsonLabels DRMSimulationModel::Settings::jsonLabels;
template <typename PointType> PointType Cross(PointType p1, PointType p2) {
return p1 ^ p2;
}
inline size_t currentStep{0};
inline bool TriggerBreakpoint(const VertexIndex &vi, const EdgeIndex &ei,
const DoFType &dofi) {
const size_t numberOfVertices = 10;
const VertexIndex middleNodeIndex = numberOfVertices / 2;
// return vi == middleNodeIndex && dofi == 1;
return dofi == 1 && ((vi == 1 && ei == 0) || (vi == 9 && ei == 9));
}
inline bool TriggerBreakpoint(const VertexIndex &vi, const EdgeIndex &ei) {
const size_t numberOfVertices = 10;
const VertexIndex middleNodeIndex = numberOfVertices / 2;
return (vi == middleNodeIndex);
// return (vi == 0 || vi == numberOfVertices - 1) && currentStep == 1;
return (vi == 1 && ei == 0) || (vi == 9 && ei == 9);
}
#endif // BEAMFORMFINDER_HPP

View File

@ -1,608 +0,0 @@
#include "edgemesh.hpp"
#include "vcg/simplex/face/topology.h"
#include <wrap/io_trimesh/import.h>
//#include <wrap/nanoply/include/nanoplyWrapper.hpp>
#include <wrap/io_trimesh/export.h>
#include <wrap/io_trimesh/import.h>
Eigen::MatrixX2i VCGEdgeMesh::getEigenEdges() const { return eigenEdges; }
std::vector<vcg::Point2i> VCGEdgeMesh::computeEdges()
{
computeEdges(eigenEdges);
std::vector<vcg::Point2i> edges(eigenEdges.rows());
for (int ei = 0; ei < eigenEdges.rows(); ei++) {
edges[ei] = vcg::Point2i(eigenEdges(ei, 0), eigenEdges(ei, 1));
}
return edges;
}
Eigen::MatrixX3d VCGEdgeMesh::getEigenVertices() const
{
// getVertices(eigenVertices);
return eigenVertices;
}
Eigen::MatrixX3d VCGEdgeMesh::getEigenEdgeNormals() const {
return eigenEdgeNormals;
}
bool VCGEdgeMesh::save(const std::filesystem::path &meshFilePath)
{
std::string filename = meshFilePath;
if (filename.empty()) {
filename = std::filesystem::current_path().append(getLabel() + ".ply").string();
} else if (std::filesystem::is_directory(std::filesystem::path(meshFilePath))) {
filename = std::filesystem::path(meshFilePath).append(getLabel() + ".ply").string();
}
assert(std::filesystem::path(filename).extension().string() == ".ply");
unsigned int mask = 0;
mask |= vcg::tri::io::Mask::IOM_VERTCOORD;
mask |= vcg::tri::io::Mask::IOM_EDGEINDEX;
mask |= vcg::tri::io::Mask::IOM_VERTNORMAL;
mask |= vcg::tri::io::Mask::IOM_VERTCOLOR;
// if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::SaveModel(filename.c_str(), *this, mask, false) != 0) {
if (std::filesystem::is_directory(meshFilePath.parent_path())) {
std::filesystem::create_directories(meshFilePath.parent_path());
}
if (vcg::tri::io::Exporter<VCGEdgeMesh>::Save(*this, filename.c_str(), mask) != 0) {
return false;
}
return true;
}
void VCGEdgeMesh::GeneratedRegularSquaredPattern(
const double angleDeg, std::vector<std::vector<vcg::Point2d>> &pattern,
const size_t &desiredNumberOfSamples) {
static const size_t piSamples = 10;
// generate a pattern in a 1x1 quad
const vcg::Point2d offset(0, 0);
const size_t samplesNo = desiredNumberOfSamples;
// std::max(desiredNumberOfSamples, size_t(piSamples * (angleDeg /
// 180)));
const double angle = vcg::math::ToRad(angleDeg);
pattern.clear();
// first arm
std::vector<vcg::Point2d> arm;
{
for (int k = 0; k <= samplesNo; k++) {
const double t = double(k) / samplesNo;
const double a = (1 - t) * angle;
// const double r = vcg::math::Sin(t*M_PI_2) /*(1-((1-t)*(1-t)))*/ *
// 0.5;
const double r = t * 0.5; // linear
vcg::Point2d p(vcg::math::Cos(a), vcg::math::Sin(a));
arm.push_back((p * r));
}
pattern.push_back(arm);
}
// other arms
for (int i = 0; i < 3; i++) {
for (vcg::Point2d &p : arm) {
p = vcg::Point2d(-p.Y(), p.X());
}
pattern.push_back(arm);
}
assert(pattern.size() == 4);
// offset all
for (auto &arm : pattern) {
for (vcg::Point2d &p : arm) {
p += offset;
}
}
}
void VCGEdgeMesh::createSpiral(const float &degreesOfArm,
const size_t &numberOfSamples) {
std::vector<std::vector<vcg::Point2d>> spiralPoints;
GeneratedRegularSquaredPattern(degreesOfArm, spiralPoints, numberOfSamples);
for (size_t armIndex = 0; armIndex < spiralPoints.size(); armIndex++) {
for (size_t pointIndex = 0; pointIndex < spiralPoints[armIndex].size() - 1;
pointIndex++) {
const vcg::Point2d p0 = spiralPoints[armIndex][pointIndex];
const vcg::Point2d p1 = spiralPoints[armIndex][pointIndex + 1];
CoordType n(0, 0, 1);
auto ei = vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(
*this, VCGEdgeMesh::CoordType(p0.X(), p0.Y(), 0),
VCGEdgeMesh::CoordType(p1.X(), p1.Y(), 0));
ei->cV(0)->N() = n;
ei->cV(1)->N() = n;
}
}
// setDefaultAttributes();
}
bool VCGEdgeMesh::createSpanGrid(const size_t squareGridDimension) {
return createSpanGrid(squareGridDimension, squareGridDimension);
}
bool VCGEdgeMesh::createSpanGrid(const size_t desiredWidth,
const size_t desiredHeight) {
std::cout << "Grid of dimensions:" << desiredWidth << "," << desiredHeight
<< std::endl;
const VCGEdgeMesh::CoordType n(0, 0, 1);
int x = 0;
int y = 0;
// for (size_t vi = 0; vi < numberOfVertices; vi++) {
while (y <= desiredHeight) {
// std::cout << x << " " << y << std::endl;
auto p = VCGEdgeMesh::CoordType(x, y, 0);
vcg::tri::Allocator<VCGEdgeMesh>::AddVertex(*this, p, n);
x++;
if (x > desiredWidth) {
x = 0;
y++;
}
}
for (size_t vi = 0; vi < VN(); vi++) {
int x = vi % (desiredWidth + 1);
int y = vi / (desiredWidth + 1);
const bool isCornerNode = (y == 0 && x == 0) ||
(y == 0 && x == desiredWidth) ||
(y == desiredHeight && x == 0) ||
(y == desiredHeight && x == desiredWidth);
if (isCornerNode) {
continue;
}
if (y == 0) { // row 0.Connect with node above
vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi,
vi + desiredWidth + 1);
continue;
} else if (x == 0) { // col 0.Connect with node to the right
vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi, vi + 1);
continue;
} else if (y == desiredHeight) { // row 0.Connect with node below
// vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi,
// vi - (desiredWidth +
// 1));
continue;
} else if (x == desiredWidth) { // row 0.Connect with node to the left
// vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi, vi - 1);
continue;
}
vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi, vi + desiredWidth + 1);
vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi, vi + 1);
// vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi,
// vi - (desiredWidth + 1));
// vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, vi, vi - 1);
}
vcg::tri::Allocator<VCGEdgeMesh>::DeleteVertex(*this, vert[0]);
vcg::tri::Allocator<VCGEdgeMesh>::DeleteVertex(*this, vert[desiredWidth]);
vcg::tri::Allocator<VCGEdgeMesh>::DeleteVertex(
*this, vert[desiredHeight * (desiredWidth + 1)]);
vcg::tri::Allocator<VCGEdgeMesh>::DeleteVertex(
*this, vert[(desiredHeight + 1) * (desiredWidth + 1) - 1]);
vcg::tri::Allocator<VCGEdgeMesh>::CompactVertexVector(*this);
computeEdges(eigenEdges);
computeVertices(eigenVertices);
// vcg::tri::Allocator<VCGEdgeMesh>::CompactEdgeVector(*this);
// const size_t numberOfEdges =
// desiredHeight * (desiredWidth - 1) + desiredWidth * (desiredHeight -
// 1);
// handleBeamDimensions._handle->data.resize(
// numberOfEdges, CylindricalElementDimensions(0.03, 0.026));
// handleBeamMaterial._handle->data.resize(numberOfEdges,
// ElementMaterial(0.3, 200));
return true;
}
bool VCGEdgeMesh::load(const std::filesystem::path &meshFilePath)
{
std::string usedPath = meshFilePath;
if (std::filesystem::path(meshFilePath).is_relative()) {
usedPath = std::filesystem::absolute(meshFilePath).string();
}
assert(std::filesystem::exists(usedPath));
Clear();
// if (!loadUsingNanoply(usedPath)) {
// std::cerr << "Error: Unable to open " + usedPath << std::endl;
// return false;
// }
if (!loadUsingDefaultLoader(usedPath)) {
std::cerr << "Error: Unable to open " + usedPath << std::endl;
return false;
}
computeEdges(eigenEdges);
computeVertices(eigenVertices);
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
label = std::filesystem::path(meshFilePath).stem().string();
const bool printDebugInfo = false;
if (printDebugInfo) {
std::cout << meshFilePath << " was loaded successfuly." << std::endl;
std::cout << "Mesh has " << EN() << " edges." << std::endl;
}
label = std::filesystem::path(meshFilePath).stem().string();
return true;
}
//bool VCGEdgeMesh::loadUsingNanoply(const std::string &plyFilename) {
// this->Clear();
// // assert(plyFileHasAllRequiredFields(plyFilename));
// // Load the ply file
// unsigned int mask = 0;
// mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOORD;
// mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTNORMAL;
// mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOLOR;
// mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEINDEX;
// if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::LoadModel(plyFilename.c_str(),
// *this, mask) != 0) {
// return false;
// }
// return true;
//}
bool VCGEdgeMesh::loadUsingDefaultLoader(const std::string &plyFilePath)
{
Clear();
// assert(plyFileHasAllRequiredFields(plyFilename));
// Load the ply file
int mask = 0;
mask |= vcg::tri::io::Mask::IOM_VERTCOORD;
mask |= vcg::tri::io::Mask::IOM_VERTNORMAL;
mask |= vcg::tri::io::Mask::IOM_VERTCOLOR;
mask |= vcg::tri::io::Mask::IOM_EDGEINDEX;
// if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::LoadModel(plyFilename.c_str(),
// *this, mask) != 0) {
const int loadErrorCode = vcg::tri::io::Importer<VCGEdgeMesh>::Open(*this,
plyFilePath.c_str(),
mask);
if (loadErrorCode != 0) {
std::cerr << vcg::tri::io::Importer<VCGEdgeMesh>::ErrorMsg(loadErrorCode) << std::endl;
return false;
}
return true;
}
// bool VCGEdgeMesh::plyFileHasAllRequiredFields(const std::string
// &plyFilename)
// {
// const nanoply::Info info(plyFilename);
// const std::vector<nanoply::PlyElement>::const_iterator edgeElemIt =
// std::find_if(info.elemVec.begin(), info.elemVec.end(),
// [&](const nanoply::PlyElement &plyElem) {
// return plyElem.plyElem == nanoply::NNP_EDGE_ELEM;
// });
// if (edgeElemIt == info.elemVec.end()) {
// std::cerr << "Ply file is missing edge elements." << std::endl;
// return false;
// }
// const std::vector<nanoply::PlyProperty> &edgePropertyVector =
// edgeElemIt->propVec;
// return hasPlyEdgeProperty(plyFilename, edgePropertyVector,
// plyPropertyBeamDimensionsID) &&
// hasPlyEdgeProperty(plyFilename, edgePropertyVector,
// plyPropertyBeamMaterialID);
//}
Eigen::MatrixX3d VCGEdgeMesh::getNormals() const {
Eigen::MatrixX3d vertexNormals;
vertexNormals.resize(VN(), 3);
for (int vertexIndex = 0; vertexIndex < VN(); vertexIndex++) {
VCGEdgeMesh::CoordType vertexNormal =
vert[static_cast<size_t>(vertexIndex)].cN();
vertexNormals.row(vertexIndex) =
vertexNormal.ToEigenVector<Eigen::Vector3d>();
}
return vertexNormals;
}
void VCGEdgeMesh::computeEdges(Eigen::MatrixX3d &edgeStartingPoints,
Eigen::MatrixX3d &edgeEndingPoints) const
{
edgeStartingPoints.resize(EN(), 3);
edgeEndingPoints.resize(EN(), 3);
for (int edgeIndex = 0; edgeIndex < EN(); edgeIndex++) {
const VCGEdgeMesh::EdgeType &edge = this->edge[edgeIndex];
edgeStartingPoints.row(edgeIndex) = edge.cP(0).ToEigenVector<Eigen::Vector3d>();
edgeEndingPoints.row(edgeIndex) = edge.cP(1).ToEigenVector<Eigen::Vector3d>();
}
}
VCGEdgeMesh::VCGEdgeMesh() {}
void VCGEdgeMesh::updateEigenEdgeAndVertices() {
#ifdef POLYSCOPE_DEFINED
computeEdges(eigenEdges);
computeVertices(eigenVertices);
#endif
}
bool VCGEdgeMesh::copy(const VCGEdgeMesh &mesh)
{
vcg::tri::Append<VCGEdgeMesh, VCGEdgeMesh>::MeshCopyConst(*this, mesh);
label = mesh.getLabel();
eigenEdges = mesh.getEigenEdges();
// assert(eigenEdges.rows() != 0);
// if (eigenEdges.rows() == 0) {
// getEdges(eigenEdges);
// }
eigenVertices = mesh.getEigenVertices();
// assert(eigenVertices.rows() != 0);
// if (eigenVertices.rows() == 0) {
// getVertices(eigenVertices);
// }
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
return true;
}
void VCGEdgeMesh::set(const std::vector<double> &vertexPositions, const std::vector<int> &edges)
{
Clear();
for (int ei = 0; ei < edges.size(); ei += 2) {
const int vi0 = edges[ei];
const int vi1 = edges[ei + 1];
const int vi0_offset = 3 * vi0;
const int vi1_offset = 3 * vi1;
const CoordType p0(vertexPositions[vi0_offset],
vertexPositions[vi0_offset + 1],
vertexPositions[vi0_offset + 2]);
const CoordType p1(vertexPositions[vi1_offset],
vertexPositions[vi1_offset + 1],
vertexPositions[vi1_offset + 2]);
auto eIt = vcg::tri::Allocator<VCGEdgeMesh>::AddEdge(*this, p0, p1);
CoordType n(0, 0, 1);
eIt->cV(0)->N() = n;
eIt->cV(1)->N() = n;
}
// removeDuplicateVertices();
updateEigenEdgeAndVertices();
}
void VCGEdgeMesh::removeDuplicateVertices()
{
vcg::tri::Clean<VCGEdgeMesh>::MergeCloseVertex(*this, 0.000061524);
vcg::tri::Allocator<VCGEdgeMesh>::CompactVertexVector(*this);
vcg::tri::Allocator<VCGEdgeMesh>::CompactEdgeVector(*this);
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
}
void VCGEdgeMesh::removeDuplicateVertices(
vcg::tri::Allocator<VCGEdgeMesh>::PointerUpdater<VertexPointer> &pu_vertices,
vcg::tri::Allocator<VCGEdgeMesh>::PointerUpdater<EdgePointer> &pu_edges)
{
vcg::tri::Clean<VCGEdgeMesh>::MergeCloseVertex(*this, 0.000061524);
vcg::tri::Allocator<VCGEdgeMesh>::CompactVertexVector(*this, pu_vertices);
vcg::tri::Allocator<VCGEdgeMesh>::CompactEdgeVector(*this, pu_edges);
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
}
std::string VCGEdgeMesh::ToSvgText(const VCGEdgeMesh &edgeMesh)
{
// Measures in mm (?)
constexpr double ImgPadding = 5;
//constexpr double PatternSize = 200 * 19.230769231;
//constexpr double PatternSize = 200 * 17.913461538;
constexpr double PatternSize = 1000;
constexpr double ImgSize = 2 * ImgPadding + PatternSize;
constexpr double StrokeWidth = 5;
VCGEdgeMesh edgeMeshCopy;
edgeMeshCopy.copy(edgeMesh);
vcg::tri::UpdateBounding<VCGEdgeMesh>::Box(edgeMeshCopy);
const double maxDim = edgeMeshCopy.bbox.Dim()[edgeMeshCopy.bbox.MaxDim()];
vcg::tri::UpdatePosition<VCGEdgeMesh>::Translate(
edgeMeshCopy,
VCGEdgeMesh::CoordType(maxDim / 2.0 - edgeMeshCopy.bbox.Center().X(),
maxDim / 2.0 - edgeMeshCopy.bbox.Center().Y(),
0));
vcg::tri::UpdatePosition<VCGEdgeMesh>::Scale(edgeMeshCopy, PatternSize / maxDim);
// debug
// vcg::tri::UpdateBounding<EdgeMesh>::Box(em);
// std::cout << "pattern size "
// << em.bbox.min.X() << " "
// << em.bbox.max.X() << " "
// << em.bbox.min.Y() << " "
// << em.bbox.max.Y() << " " << std::endl;
std::stringstream ss;
// svg header
ss << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl
<< "<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' "
"'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>"
<< std::endl;
// size & background
ss << "<svg height=\"" << ImgSize << "\" width=\"" << ImgSize
<< "\" xmlns=\"http://www.w3.org/2000/svg\" "
"xmlns:xlink=\"http://www.w3.org/1999/xlink\">"
<< std::endl;
ss << "<rect width=\"100%\" height=\"100%\" fill=\"white\"/>" << std::endl;
for (const auto &e : edgeMeshCopy.edge) {
const auto &p0 = e.cP(0);
const auto &p1 = e.cP(1);
ss << "<line "
<< "x1=\"" << ImgPadding + p0.X() << "\" y1=\"" << -ImgPadding - p0.Y() + ImgSize
<< "\" "
<< "x2=\"" << ImgPadding + p1.X() << "\" y2=\"" << -ImgPadding - p1.Y() + ImgSize
<< "\" "
<< "style=\"stroke:rgb(67,160,232);stroke-width:" << StrokeWidth
<< "\" stroke-linecap=\"round\"/>" << std::endl;
}
ss << "</svg>" << std::endl;
return ss.str();
}
void VCGEdgeMesh::writeToSvg(const std::filesystem::path &writeToPath) const
{
// retrieve filepath for saving svg
const std::filesystem::path svgPath = [=]() {
if (writeToPath.empty()) {
return std::filesystem::current_path().append(getLabel()).concat(".svg");
}
return std::filesystem::absolute(writeToPath);
}();
// save to svg file
std::cout << "saving to " << svgPath << std::endl;
std::ofstream ofs(svgPath);
if (!ofs.is_open()) {
std::cout << "unable to save to " << svgPath << std::endl;
assert(false);
return;
}
ofs << ToSvgText(*this);
ofs.close();
}
void VCGEdgeMesh::deleteDanglingVertices() {
vcg::tri::Allocator<VCGEdgeMesh>::PointerUpdater<VertexPointer> pu;
deleteDanglingVertices(pu);
}
void VCGEdgeMesh::deleteDanglingVertices(vcg::tri::Allocator<VCGEdgeMesh>::PointerUpdater<TriMesh::VertexPointer> &pu) {
for (VertexType &v : vert) {
std::vector<VCGEdgeMesh::EdgePointer> incidentElements;
vcg::edge::VEStarVE(&v, incidentElements);
if (incidentElements.size() == 0 && !v.IsD()) {
vcg::tri::Allocator<VCGEdgeMesh>::DeleteVertex(*this, v);
}
}
vcg::tri::Allocator<VCGEdgeMesh>::CompactVertexVector(*this, pu);
vcg::tri::Allocator<VCGEdgeMesh>::CompactEdgeVector(*this);
updateEigenEdgeAndVertices();
}
void VCGEdgeMesh::computeVertices(Eigen::MatrixX3d &vertices)
{
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<size_t>(vi)].cP();
vertices.row(vi) = vertexCoordinates.ToEigenVector<Eigen::Vector3d>();
}
}
void VCGEdgeMesh::computeEdges(Eigen::MatrixX2i &edges)
{
edges = Eigen::MatrixX2i();
edges.resize(EN(), 2);
for (int edgeIndex = 0; edgeIndex < EN(); edgeIndex++) {
const VCGEdgeMesh::EdgeType &edge = this->edge[edgeIndex];
assert(!edge.IsD());
auto vp0 = edge.cV(0);
auto vp1 = edge.cV(1);
assert(vcg::tri::IsValidPointer(*this, vp0) && vcg::tri::IsValidPointer(*this, vp1));
const size_t vi0 = vcg::tri::Index<VCGEdgeMesh>(*this, vp0);
const size_t vi1 = vcg::tri::Index<VCGEdgeMesh>(*this, vp1);
assert(vi0 != -1 && vi1 != -1);
edges.row(edgeIndex) = Eigen::Vector2i(vi0, vi1);
}
}
void VCGEdgeMesh::printVertexCoordinates(const size_t &vi) const
{
std::cout << "vi:" << vi << " " << vert[vi].cP()[0] << " " << vert[vi].cP()[1] << " "
<< vert[vi].cP()[2] << std::endl;
}
#ifdef POLYSCOPE_DEFINED
void VCGEdgeMesh::markVertices(const std::vector<size_t> &vertsToMark)
{
if (vertsToMark.empty()) {
return;
}
std::vector<std::array<double, 3>> nodeColors(VN(), {0, 0, 0});
for (const size_t vi : vertsToMark) {
nodeColors[vi] = {1, 0, 0};
}
polyscope::getCurveNetwork(getLabel())
->addNodeColorQuantity("Marked vertices" + getLabel(), nodeColors)
->setEnabled(true);
}
//TODO: make const getEigenVertices is not
polyscope::CurveNetwork *VCGEdgeMesh::registerForDrawing(
const std::optional<std::array<double, 3>> &desiredColor,
const double &desiredRadius,
const bool &shouldEnable)
{
PolyscopeInterface::init();
const double drawingRadius = desiredRadius;
updateEigenEdgeAndVertices();
polyscope::CurveNetwork *polyscopeHandle_edgeMesh
= polyscope::registerCurveNetwork(label, getEigenVertices(), getEigenEdges());
// std::cout << "EDGES:" << polyscopeHandle_edgeMesh->nEdges() << std::endl;
assert(polyscopeHandle_edgeMesh->nEdges() == getEigenEdges().rows()
&& polyscopeHandle_edgeMesh->nNodes() == getEigenVertices().rows());
polyscopeHandle_edgeMesh->setEnabled(shouldEnable);
polyscopeHandle_edgeMesh->setRadius(drawingRadius, false);
if (desiredColor.has_value()) {
const glm::vec3 desiredColor_glm(desiredColor.value()[0],
desiredColor.value()[1],
desiredColor.value()[2]);
polyscopeHandle_edgeMesh->setColor(/*glm::normalize(*/ desiredColor_glm /*)*/);
}
return polyscopeHandle_edgeMesh;
}
void VCGEdgeMesh::unregister() const {
if (!polyscope::hasCurveNetwork(label)) {
std::cerr << "No curve network registered with a name: " << getLabel()
<< std::endl;
std::cerr << "Nothing to remove." << std::endl;
return;
}
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

View File

@ -1,131 +0,0 @@
#ifndef EDGEMESH_HPP
#define EDGEMESH_HPP
#include "beam.hpp"
#include "mesh.hpp"
#include "utilities.hpp"
#include <array>
#include <optional>
#include <vcg/complex/complex.h>
#include <vector>
#ifdef POLYSCOPE_DEFINED
#include <polyscope/curve_network.h>
#endif
using EdgeIndex = size_t;
class VCGEdgeMeshEdgeType;
class VCGEdgeMeshVertexType;
struct VCGEdgeMeshUsedTypes
: public vcg::UsedTypes<vcg::Use<VCGEdgeMeshVertexType>::AsVertexType,
vcg::Use<VCGEdgeMeshEdgeType>::AsEdgeType> {};
class VCGEdgeMeshVertexType
: public vcg::Vertex<VCGEdgeMeshUsedTypes, vcg::vertex::Coord3d,
vcg::vertex::Normal3d, vcg::vertex::BitFlags,
vcg::vertex::Color4b, vcg::vertex::VEAdj> {};
class VCGEdgeMeshEdgeType
: public vcg::Edge<VCGEdgeMeshUsedTypes, vcg::edge::VertexRef,
vcg::edge::BitFlags, vcg::edge::EEAdj,
vcg::edge::VEAdj> {};
class VCGEdgeMesh
: public vcg::tri::TriMesh<std::vector<VCGEdgeMeshVertexType>, std::vector<VCGEdgeMeshEdgeType>>,
public Mesh
{
protected:
Eigen::MatrixX2i eigenEdges;
Eigen::MatrixX3d eigenVertices;
Eigen::MatrixX3d eigenEdgeNormals;
void computeEdges(Eigen::MatrixX2i &edges);
void computeVertices(Eigen::MatrixX3d &vertices);
public:
VCGEdgeMesh();
template<typename MeshElement>
size_t getIndex(const MeshElement &meshElement)
{
return vcg::tri::Index<VCGEdgeMesh>(*this, meshElement);
}
void updateEigenEdgeAndVertices();
/*
* 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(const VCGEdgeMesh &mesh);
void set(const std::vector<double> &vertexPositions, const std::vector<int> &edges);
void removeDuplicateVertices();
virtual void deleteDanglingVertices();
virtual void deleteDanglingVertices(
vcg::tri::Allocator<VCGEdgeMesh>::PointerUpdater<VertexPointer> &pu);
void computeEdges(Eigen::MatrixX3d &edgeStartingPoints, Eigen::MatrixX3d &edgeEndingPoints) const;
Eigen::MatrixX3d getNormals() const;
bool plyFileHasAllRequiredFields(const std::string &plyFilename);
// bool loadUsingNanoply(const std::string &plyFilename);
bool load(const std::filesystem::path &meshFilePath) override;
bool save(const std::filesystem::path &meshFilePath = std::filesystem::path()) override;
bool createSpanGrid(const size_t squareGridDimension);
bool createSpanGrid(const size_t desiredWidth, const size_t desiredHeight);
void createSpiral(const float &degreesOfArm, const size_t &numberOfSamples);
Eigen::MatrixX2i getEigenEdges() const;
std::vector<vcg::Point2i> computeEdges();
Eigen::MatrixX3d getEigenVertices() const;
Eigen::MatrixX3d getEigenEdgeNormals() const;
void printVertexCoordinates(const size_t &vi) const;
#ifdef POLYSCOPE_DEFINED
polyscope::CurveNetwork *registerForDrawing(
const std::optional<std::array<double, 3>> &desiredColor = std::nullopt,
const double &desiredRadius = 0.001,
const bool &shouldEnable = true);
void unregister() const;
void drawInitialFrames(polyscope::CurveNetwork *polyscopeHandle_initialMesh) const;
#endif
void removeDuplicateVertices(
vcg::tri::Allocator<VCGEdgeMesh>::PointerUpdater<VertexPointer> &pu_vertices,
vcg::tri::Allocator<VCGEdgeMesh>::PointerUpdater<EdgePointer> &pu_edges);
void centerMesh()
{
CoordType centerOfMass(0, 0, 0);
for (const auto &v : vert) {
centerOfMass = centerOfMass + v.cP();
}
centerOfMass = centerOfMass / VN();
for (auto &v : vert) {
v.P() = v.cP() - centerOfMass;
}
}
static std::string ToSvgText(const VCGEdgeMesh &edgeMesh);
void writeToSvg(const std::filesystem::path &writeToPath = std::filesystem::path()) const;
void markVertices(const std::vector<size_t> &vertsToMark);
private:
void GeneratedRegularSquaredPattern(const double angleDeg,
std::vector<std::vector<vcg::Point2d>> &pattern,
const size_t &desiredNumberOfSamples);
bool loadUsingDefaultLoader(const std::string &plyFilePath);
};
using VectorType = VCGEdgeMesh::CoordType;
using CoordType = VCGEdgeMesh::CoordType;
using VertexPointer = VCGEdgeMesh::VertexPointer;
using EdgePointer = VCGEdgeMesh::EdgePointer;
using ConstVCGEdgeMesh = VCGEdgeMesh;
#endif // EDGEMESH_HPP

View File

@ -1,274 +0,0 @@
#include "linearsimulationmodel.hpp"
//#include "gsl/gsl"
std::vector<fea::Elem> LinearSimulationModel::getFeaElements(
const std::shared_ptr<SimulationMesh> &pMesh)
{
const int numberOfEdges = pMesh->EN();
std::vector<fea::Elem> elements(numberOfEdges);
for (int edgeIndex = 0; edgeIndex < numberOfEdges; edgeIndex++) {
const SimulationMesh::CoordType &evn0 = pMesh->edge[edgeIndex].cV(0)->cN();
const SimulationMesh::CoordType &evn1 = pMesh->edge[edgeIndex].cV(1)->cN();
const std::vector<double> nAverageVector{(evn0[0] + evn1[0]) / 2,
(evn0[1] + evn1[1]) / 2,
(evn0[2] + evn1[2]) / 2};
const Element &element = pMesh->elements[edgeIndex];
const double E = element.material.youngsModulus;
fea::Props feaProperties(E * element.dimensions.A,
E * element.dimensions.inertia.I3,
E * element.dimensions.inertia.I2,
element.material.G * element.dimensions.inertia.J,
nAverageVector);
const int vi0 = pMesh->getIndex(pMesh->edge[edgeIndex].cV(0));
const int vi1 = pMesh->getIndex(pMesh->edge[edgeIndex].cV(1));
elements[edgeIndex] = fea::Elem(vi0, vi1, feaProperties);
}
return elements;
}
std::vector<fea::Node> LinearSimulationModel::getFeaNodes(
const std::shared_ptr<SimulationMesh> &pMesh)
{
const int numberOfNodes = pMesh->VN();
std::vector<fea::Node> feaNodes(numberOfNodes);
for (int vi = 0; vi < numberOfNodes; vi++) {
const CoordType &p = pMesh->vert[vi].cP();
feaNodes[vi] = fea::Node(p[0], p[1], p[2]);
}
return feaNodes;
}
std::vector<fea::BC> LinearSimulationModel::getFeaFixedNodes(
const std::shared_ptr<SimulationJob> &job)
{
std::vector<fea::BC> 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<fea::DOF>(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<fea::DOF>(dofIndex), forcedDisplacement.second[dofIndex]));
}
}
return boundaryConditions;
}
std::vector<fea::Force> LinearSimulationModel::getFeaNodalForces(
const std::shared_ptr<SimulationJob> &job)
{
std::vector<fea::Force> 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;
}
fea::Force f(nodalForce.first, dofIndex, nodalForce.second[dofIndex]);
nodalForces.emplace_back(f);
}
}
return nodalForces;
}
SimulationResults LinearSimulationModel::getResults(
const fea::Summary &feaSummary, const std::shared_ptr<SimulationJob> &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<double> q_nx;
q_nx = Eigen::AngleAxis<double>(nodalDisplacement[3], Eigen::Vector3d(1, 0, 0));
Eigen::Quaternion<double> q_ny;
q_ny = Eigen::AngleAxis<double>(nodalDisplacement[4], Eigen::Vector3d(0, 1, 0));
Eigen::Quaternion<double> q_nz;
q_nz = Eigen::AngleAxis<double>(nodalDisplacement[5], Eigen::Vector3d(0, 0, 1));
results.rotationalDisplacementQuaternion[vi] = q_nx * q_ny * q_nz;
}
results.setLabelPrefix("Linear");
// // 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<Eigen::VectorXd>(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<double> &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.dimensions.A);
const double elementPotentialEnergy_axial_v1 = std::pow(elementForces[6], 2)
* element.initialLength
/ (4 * element.material.youngsModulus
* element.dimensions.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.dimensions.A
* element.material.G);
const double elementPotentialEnergy_shearZ_v0 = std::pow(elementForces[2], 2)
* element.initialLength
/ (4 * element.dimensions.A
* element.material.G);
const double elementPotentialEnergy_shearY_v1 = std::pow(elementForces[7], 2)
* element.initialLength
/ (4 * element.dimensions.A
* element.material.G);
const double elementPotentialEnergy_shearZ_v1 = std::pow(elementForces[8], 2)
* element.initialLength
/ (4 * element.dimensions.A
* element.material.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.dimensions.inertia.I2);
const double elementPotentialEnergy_bendingZ_v0 = std::pow(elementForces[5], 2)
* element.initialLength
/ (4 * element.material.youngsModulus
* element.dimensions.inertia.I3);
const double elementPotentialEnergy_bendingY_v1 = std::pow(elementForces[10], 2)
* element.initialLength
/ (4 * element.material.youngsModulus
* element.dimensions.inertia.I2);
const double elementPotentialEnergy_bendingZ_v1 = std::pow(elementForces[11], 2)
* element.initialLength
/ (4 * element.material.youngsModulus
* element.dimensions.inertia.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.dimensions.inertia.J
* element.material.G);
const double elementPotentialEnergy_torsion_v1 = std::pow(elementForces[9], 2)
* element.initialLength
/ (4 * element.dimensions.inertia.J
* element.material.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;
}
results.pJob = simulationJob;
return results;
}
SimulationResults LinearSimulationModel::executeSimulation(
const std::shared_ptr<SimulationJob> &pSimulationJob)
{
assert(pSimulationJob->pMesh->VN() != 0);
const bool wasInitializedWithDifferentStructure = pSimulationJob->pMesh->EN()
!= simulator.structure.elems.size()
|| pSimulationJob->pMesh->VN()
!= simulator.structure.nodes.size();
if (!simulator.wasInitialized() || wasInitializedWithDifferentStructure) {
setStructure(pSimulationJob->pMesh);
}
// 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<fea::Tie> ties;
// also create an empty list of equations
std::vector<fea::Equation> equations;
auto fixedVertices = getFeaFixedNodes(pSimulationJob);
auto nodalForces = getFeaNodalForces(pSimulationJob);
fea::Summary feaResults = simulator.solve(fixedVertices, nodalForces, ties, equations, opts);
SimulationResults results = getResults(feaResults, pSimulationJob);
results.pJob = pSimulationJob;
return results;
}
void LinearSimulationModel::setStructure(const std::shared_ptr<SimulationMesh> &pMesh)
{
fea::BeamStructure structure(getFeaNodes(pMesh), getFeaElements(pMesh));
simulator.initialize(structure);
}
void LinearSimulationModel::printInfo(const fea::BeamStructure &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;
}
}

View File

@ -1,32 +0,0 @@
#ifndef LINEARSIMULATIONMODEL_HPP
#define LINEARSIMULATIONMODEL_HPP
#include "simulationmodel.hpp"
#include "threed_beam_fea.h"
#include <filesystem>
#include <vector>
class LinearSimulationModel : public SimulationModel
{
public:
LinearSimulationModel(){
}
static std::vector<fea::Elem> getFeaElements(const std::shared_ptr<SimulationMesh> &pMesh);
static std::vector<fea::Node> getFeaNodes(const std::shared_ptr<SimulationMesh> &pMesh);
static std::vector<fea::BC> getFeaFixedNodes(const std::shared_ptr<SimulationJob> &job);
static std::vector<fea::Force> getFeaNodalForces(const std::shared_ptr<SimulationJob> &job);
static SimulationResults getResults(const fea::Summary &feaSummary,
const std::shared_ptr<SimulationJob> &simulationJob);
SimulationResults executeSimulation(const std::shared_ptr<SimulationJob> &simulationJob);
void setStructure(const std::shared_ptr<SimulationMesh> &pMesh);
private:
fea::ThreedBeamFEA simulator;
static void printInfo(const fea::BeamStructure &job);
};
#endif // LINEARSIMULATIONMODEL_HPP

View File

@ -1,37 +0,0 @@
#ifndef MESH_HPP
#define MESH_HPP
#include <filesystem>
#include <string>
class Mesh
{
protected:
std::string label{"empty_label"};
public:
virtual ~Mesh() = default;
virtual bool load(const std::filesystem::path &meshFilePath) { return false; }
virtual bool save(const std::filesystem::path &meshFilePath) { return false; }
std::string getLabel() const;
void setLabel(const std::string &newLabel);
void prependToLabel(const std::string &text);
void appendToLabel(const std::string &text);
};
inline std::string Mesh::getLabel() const { return label; }
inline void Mesh::setLabel(const std::string &newLabel) { label = newLabel; }
inline void Mesh::prependToLabel(const std::string &text)
{
label = text + label;
}
inline void Mesh::appendToLabel(const std::string &text)
{
label = label + text;
}
#endif // MESH_HPP

File diff suppressed because it is too large Load Diff

View File

@ -1,176 +0,0 @@
#ifndef SIMULATIONHISTORYPLOTTER_HPP
#define SIMULATIONHISTORYPLOTTER_HPP
#include "simulation_structs.hpp"
#include "simulationmesh.hpp"
#include "utilities.hpp"
#include <algorithm>
#include <matplot/matplot.h>
struct SimulationResultsReporter {
using VertexType = VCGEdgeMesh::VertexType;
using CoordType = VCGEdgeMesh::CoordType;
SimulationResultsReporter() {}
void writeStatistics(const SimulationResults &results,
const std::string &reportFolderPath) {
std::ofstream file;
file.open(std::filesystem::path(reportFolderPath).append("results.txt").string());
const size_t numberOfSteps = results.history.numberOfSteps;
file << "Number of steps " << numberOfSteps << "\n";
// file << "Force threshold used " << 1000 << "\n";
// assert(numberOfSteps == results.history.potentialEnergy.size() &&
// numberOfSteps == results.history.residualForces.size());
// Write kinetic energies
const SimulationHistory &history = results.history;
if (!history.kineticEnergy.empty()) {
file << "Kinetic energies"
<< "\n";
for (size_t step = 0; step < numberOfSteps; step++) {
file << history.kineticEnergy[step] << "\n";
}
file << "\n";
}
if (!history.logResidualForces.empty()) {
file << "Residual forces"
<< "\n";
for (size_t step = 0; step < numberOfSteps; step++) {
file << history.logResidualForces[step] << "\n";
}
file << "\n";
}
if (!history.potentialEnergies.empty()) {
file << "Potential energies"
<< "\n";
for (size_t step = 0; step < numberOfSteps; step++) {
file << history.potentialEnergies[step] << "\n";
}
file << "\n";
}
file.close();
}
void reportHistory(const SimulationHistory &history,
const std::string &reportFolderPath,
const std::string &graphSuffix = std::string())
{
const auto simulationResultPath = std::filesystem::path(reportFolderPath).append(history.label);
std::filesystem::create_directories(simulationResultPath);
createPlots(history, simulationResultPath.string(), graphSuffix);
}
void reportResults(const std::vector<SimulationResults> &results,
const std::string &reportFolderPath,
const std::string &graphSuffix = std::string()) {
if (results.empty()) {
return;
}
// std::filesystem::remove_all(debuggerFolder);
std::filesystem::create_directory(reportFolderPath);
for (const SimulationResults &simulationResult : results) {
const auto simulationResultPath =
std::filesystem::path(reportFolderPath)
.append(simulationResult.getLabel());
std::filesystem::create_directory(simulationResultPath.string());
createPlots(simulationResult.history, simulationResultPath.string(),
graphSuffix);
writeStatistics(simulationResult, simulationResultPath.string());
}
}
static void createPlot(const std::string &xLabel,
const std::string &yLabel,
const std::vector<double> &YvaluesToPlot,
const std::string &saveTo = {},
const std::vector<size_t> &markPoints = {})
{
if (YvaluesToPlot.size() < 2) {
return;
}
std::vector<double> colors(YvaluesToPlot.size(), 0.5);
std::vector<double> markerSizes(YvaluesToPlot.size(), 5);
if (!markPoints.empty()) {
for (const auto pointIndex : markPoints) {
colors[pointIndex] = 0.9;
markerSizes[pointIndex] = 14;
}
}
std::vector<double> x = matplot::linspace(0, YvaluesToPlot.size() - 1, YvaluesToPlot.size());
Utilities::createPlot(xLabel, yLabel, x, YvaluesToPlot, markerSizes, colors, saveTo);
}
void createPlots(const SimulationHistory &history,
const std::string &reportFolderPath,
const std::string &graphSuffix)
{
const auto graphsFolder = std::filesystem::path(reportFolderPath).append("Graphs");
std::filesystem::remove_all(graphsFolder);
std::filesystem::create_directory(graphsFolder.string());
if (!history.kineticEnergy.empty()) {
createPlot("Number of Iterations",
"Log of Kinetic Energy log",
history.kineticEnergy,
std::filesystem::path(graphsFolder)
.append("KineticEnergyLog_" + graphSuffix + ".png")
.string(),
history.redMarks);
}
if (!history.logResidualForces.empty()) {
createPlot("Number of Iterations",
"Residual Forces norm log",
history.logResidualForces,
std::filesystem::path(graphsFolder)
.append("ResidualForcesLog_" + graphSuffix + ".png")
.string(),
history.redMarks);
}
if (!history.potentialEnergies.empty()) {
createPlot("Number of Iterations",
"Potential energy",
history.potentialEnergies,
std::filesystem::path(graphsFolder)
.append("PotentialEnergy_" + graphSuffix + ".png")
.string(),
history.redMarks);
}
if (!history.residualForcesMovingAverage.empty()) {
createPlot("Number of Iterations",
"Residual forces moving average",
history.residualForcesMovingAverage,
std::filesystem::path(graphsFolder)
.append("ResidualForcesMovingAverage_" + graphSuffix + ".png")
.string(),
history.redMarks);
}
// if (!history.residualForcesMovingAverageDerivativesLog.empty()) {
// createPlot("Number of Iterations",
// "Residual forces moving average derivative log",
// history.residualForcesMovingAverageDerivativesLog,
// std::filesystem::path(graphsFolder)
// .append("ResidualForcesMovingAverageDerivativesLog_" + graphSuffix + ".png")
// .string());
// }
if (!history.perVertexAverageNormalizedDisplacementNorm.empty()) {
createPlot("Number of Iterations",
"Sum of normalized displacement norms",
history.perVertexAverageNormalizedDisplacementNorm,
std::filesystem::path(graphsFolder)
.append("SumOfNormalizedDisplacementNorms_" + graphSuffix + ".png")
.string(),
history.redMarks);
}
}
};
#endif // SIMULATIONHISTORYPLOTTER_HPP

View File

@ -1,528 +0,0 @@
#include "simulationmesh.hpp"
//#include <wrap/nanoply/include/nanoplyWrapper.hpp>
SimulationMesh::SimulationMesh() {
elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(
*this, std::string("Elements"));
nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(
*this, std::string("Nodes"));
}
SimulationMesh::SimulationMesh(VCGEdgeMesh &mesh) {
vcg::tri::MeshAssert<VCGEdgeMesh>::VertexNormalNormalized(mesh);
VCGEdgeMesh::copy(mesh);
elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(
*this, std::string("Elements"));
nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(*this,
std::string("Nodes"));
initializeNodes();
initializeElements();
}
SimulationMesh::~SimulationMesh() {
vcg::tri::Allocator<SimulationMesh>::DeletePerEdgeAttribute<Element>(*this,
elements);
vcg::tri::Allocator<SimulationMesh>::DeletePerVertexAttribute<Node>(*this,
nodes);
}
SimulationMesh::SimulationMesh(PatternGeometry &pattern) {
vcg::tri::MeshAssert<PatternGeometry>::VertexNormalNormalized(pattern);
VCGEdgeMesh::copy(pattern);
elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(
*this, std::string("Elements"));
nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(
*this, std::string("Nodes"));
initializeNodes();
initializeElements();
}
SimulationMesh::SimulationMesh(SimulationMesh &m)
{
vcg::tri::MeshAssert<SimulationMesh>::VertexNormalNormalized(m);
VCGEdgeMesh::copy(m);
elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(*this,
std::string(
"Elements"));
nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(*this,
std::string("Nodes"));
initializeNodes();
for (size_t ei = 0; ei < EN(); ei++) {
elements[ei] = m.elements[ei];
}
reset();
}
SimulationMesh::SimulationMesh(VCGTriMesh &triMesh)
{
vcg::tri::Append<VCGEdgeMesh, VCGTriMesh>::MeshCopy(*this, triMesh);
label = triMesh.getLabel();
// eigenEdges = triMesh.getEigenEdges();
// if (eigenEdges.rows() == 0) {
computeEdges(eigenEdges);
// }
// eigenVertices = triMesh.getEigenVertices();
// if (eigenVertices.rows() == 0) {
computeVertices(eigenVertices);
// }
vcg::tri::UpdateTopology<VCGEdgeMesh>::VertexEdge(*this);
elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(*this,
std::string(
"Elements"));
nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(*this,
std::string("Nodes"));
initializeNodes();
initializeElements();
reset();
}
void SimulationMesh::computeElementalProperties() {
const std::vector<CrossSectionType> elementalDimensions = getBeamDimensions();
const std::vector<ElementMaterial> elementalMaterials = getBeamMaterial();
assert(EN() == elementalDimensions.size() &&
elementalDimensions.size() == elementalMaterials.size());
for (const EdgeType &e : edge) {
const EdgeIndex ei = getIndex(e);
elements[e].setDimensions(elementalDimensions[ei]);
elements[e].setMaterial(elementalMaterials[ei]);
}
}
void SimulationMesh::initializeNodes() {
// set initial and previous locations,
for (const VertexType &v : vert) {
const VertexIndex vi = getIndex(v);
Node &node = nodes[v];
node.vi = vi;
node.initialLocation = v.cP();
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
// normal
// y component.
// Initialize incident elements
std::vector<VCGEdgeMesh::EdgePointer> incidentElements;
vcg::edge::VEStarVE(&v, incidentElements);
// assert(
// vcg::tri::IsValidPointer<SimulationMesh>(*this, incidentElements[0]) &&
// incidentElements.size() > 0);
if (incidentElements.size() != 0) {
nodes[v].incidentElements = incidentElements;
node.referenceElement = getReferenceElement(v);
// std::vector<int> incidentElementsIndices(node.incidentElements.size());
// if (drawGlobal && vi == 5) {
// std::vector<glm::vec3> edgeColors(EN(), glm::vec3(0, 1, 0));
// std::vector<glm::vec3> 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 = *node.referenceElement;
const VectorType t01 = computeT1Vector(referenceElement.cP(0), referenceElement.cP(1));
const VectorType f01 = (t01 - (v.cN() * (t01.dot(v.cN())))).Normalize();
node.alphaAngles.reserve(incidentElements.size());
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.emplace_back(std::make_pair(ei, alphaAngle));
}
}
}
}
void SimulationMesh::reset() {
for (const EdgeType &e : edge) {
Element &element = elements[e];
element.ei = getIndex(e);
const VCGEdgeMesh::CoordType p0 = e.cP(0);
const VCGEdgeMesh::CoordType p1 = e.cP(1);
const vcg::Segment3<double> s(p0, p1);
element.initialLength = s.Length();
element.length = element.initialLength;
element.updateRigidity();
}
for (const VertexType &v : vert) {
Node &node = nodes[v];
node.vi = getIndex(v);
node.initialLocation = v.cP();
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
// normal
// y component.
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();
node.alphaAngles.clear();
node.alphaAngles.reserve(node.incidentElements.size());
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.emplace_back(std::make_pair(ei, alphaAngle));
}
}
}
void SimulationMesh::initializeElements() {
for (const EdgeType &e : edge) {
Element &element = elements[e];
element.ei = getIndex(e);
// Initialize dimensions
element.dimensions = CrossSectionType();
// Initialize material
element.material = ElementMaterial();
// Initialize lengths
const VCGEdgeMesh::CoordType p0 = e.cP(0);
const VCGEdgeMesh::CoordType p1 = e.cP(1);
const vcg::Segment3<double> s(p0, p1);
element.initialLength = s.Length();
element.length = element.initialLength;
// Initialize const factors
element.updateRigidity();
element.derivativeT1.resize(
2, std::vector<VectorType>(6, VectorType(0, 0, 0)));
element.derivativeT2.resize(
2, std::vector<VectorType>(6, VectorType(0, 0, 0)));
element.derivativeT3.resize(
2, std::vector<VectorType>(6, VectorType(0, 0, 0)));
element.derivativeT1_jplus1.resize(6, VectorType(0, 0, 0));
element.derivativeT1_j.resize(6, VectorType(0, 0, 0));
element.derivativeT1_jplus1.resize(6, VectorType(0, 0, 0));
element.derivativeT2_j.resize(6, VectorType(0, 0, 0));
element.derivativeT2_jplus1.resize(6, VectorType(0, 0, 0));
element.derivativeT3_j.resize(6, VectorType(0, 0, 0));
element.derivativeT3_jplus1.resize(6, VectorType(0, 0, 0));
element.derivativeR_j.resize(6, VectorType(0, 0, 0));
element.derivativeR_jplus1.resize(6, VectorType(0, 0, 0));
}
updateElementalFrames();
}
void SimulationMesh::updateElementalLengths() {
for (const EdgeType &e : edge) {
const EdgeIndex ei = getIndex(e);
const VertexIndex vi0 = getIndex(e.cV(0));
const VCGEdgeMesh::CoordType p0 = e.cP(0);
const VertexIndex vi1 = getIndex(e.cV(1));
const VCGEdgeMesh::CoordType p1 = e.cP(1);
const vcg::Segment3<double> s(p0, p1);
const double elementLength = s.Length();
elements[e].length = elementLength;
}
}
void SimulationMesh::updateElementalFrames() {
for (const EdgeType &e : edge) {
const VectorType elementNormal =
(e.cV(0)->cN() + e.cV(1)->cN()).Normalize();
elements[e].frame = computeElementFrame(e.cP(0), e.cP(1), elementNormal);
}
}
#ifdef POLYSCOPE_DEFINED
polyscope::CurveNetwork *SimulationMesh::registerForDrawing(
const std::optional<std::array<double, 3>> &desiredColor, const bool &shouldEnable)
{
const double drawingRadius = getBeamDimensions()[0].getDrawingRadius();
// std::cout << __FUNCTION__ << " revert this" << std::endl;
return VCGEdgeMesh::registerForDrawing(desiredColor, /*0.08*/ drawingRadius, shouldEnable);
}
void SimulationMesh::unregister() const
{
VCGEdgeMesh::unregister();
}
#endif
void SimulationMesh::setBeamCrossSection(const CrossSectionType &beamDimensions)
{
for (size_t ei = 0; ei < EN(); ei++) {
elements[ei].dimensions = beamDimensions;
elements[ei].updateRigidity();
}
}
void SimulationMesh::setBeamMaterial(const double &pr, const double &ym) {
for (size_t ei = 0; ei < EN(); ei++) {
elements[ei].setMaterial(ElementMaterial{pr, ym});
elements[ei].updateRigidity();
}
}
std::vector<CrossSectionType> SimulationMesh::getBeamDimensions()
{
std::vector<CrossSectionType> beamDimensions(EN());
for (size_t ei = 0; ei < EN(); ei++) {
beamDimensions[ei] = elements[ei].dimensions;
}
return beamDimensions;
}
std::vector<ElementMaterial> SimulationMesh::getBeamMaterial()
{
std::vector<ElementMaterial> beamMaterial(EN());
for (size_t ei = 0; ei < EN(); ei++) {
beamMaterial[ei] = elements[ei].material;
}
return beamMaterial;
}
bool SimulationMesh::load(const std::string &plyFilename)
{
this->Clear();
// assert(plyFileHasAllRequiredFields(plyFilename));
// Load the ply file
// VCGEdgeMesh::PerEdgeAttributeHandle<CrossSectionType> handleBeamDimensions =
// vcg::tri::Allocator<SimulationMesh>::AddPerEdgeAttribute<
// CrossSectionType>(*this, plyPropertyBeamDimensionsID);
// VCGEdgeMesh::PerEdgeAttributeHandle<ElementMaterial> handleBeamMaterial =
// vcg::tri::Allocator<SimulationMesh>::AddPerEdgeAttribute<ElementMaterial>(
// *this, plyPropertyBeamMaterialID);
// nanoply::NanoPlyWrapper<SimulationMesh>::CustomAttributeDescriptor
// customAttrib;
// customAttrib.GetMeshAttrib(plyFilename);
// customAttrib.AddEdgeAttribDescriptor<CrossSectionType, double, 2>(
// plyPropertyBeamDimensionsID, nanoply::NNP_LIST_INT8_FLOAT64, nullptr);
// /*FIXME: Since I allow CrossSectionType to take two types I should export the
// * type as well such that that when loaded the correct type of cross section
// * is used.
// */
// customAttrib.AddEdgeAttribDescriptor<vcg::Point2d, double, 2>(
// plyPropertyBeamMaterialID, nanoply::NNP_LIST_INT8_FLOAT64, nullptr);
// VCGEdgeMesh::PerEdgeAttributeHandle<std::array<double, 6>>
// handleBeamProperties =
// vcg::tri::Allocator<SimulationMesh>::AddPerEdgeAttribute<
// std::array<double, 6>>(*this, plyPropertyBeamProperties);
// customAttrib.AddEdgeAttribDescriptor<std::array<double, 6>, double, 6>(
// plyPropertyBeamProperties, nanoply::NNP_LIST_INT8_FLOAT64, nullptr);
// VCGEdgeMesh::PerEdgeAttributeHandle<ElementMaterial>
// handleBeamRigidityContants;
// customAttrib.AddEdgeAttribDescriptor<vcg::Point4f, float, 4>(
// plyPropertyBeamRigidityConstantsID, nanoply::NNP_LIST_INT8_FLOAT32,
// nullptr);
// unsigned int mask = 0;
// mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOORD;
// mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTNORMAL;
// mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEINDEX;
// mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEATTRIB;
// mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_MESHATTRIB;
if (!VCGEdgeMesh::load(plyFilename)) {
return false;
}
// elements = vcg::tri::Allocator<SimulationMesh>::GetPerEdgeAttribute<Element>(
// *this, std::string("Elements"));
// nodes = vcg::tri::Allocator<SimulationMesh>::GetPerVertexAttribute<Node>(
// *this, std::string("Nodes"));
vcg::tri::UpdateTopology<SimulationMesh>::VertexEdge(*this);
initializeNodes();
initializeElements();
setBeamMaterial(0.3, 1 * 1e9);
updateEigenEdgeAndVertices();
// if (!handleBeamProperties._handle->data.empty()) {
// for (size_t ei = 0; ei < EN(); ei++) {
// elements[ei] =
// Element::Properties(handleBeamProperties[ei]);
// elements[ei].updateRigidity();
// }
// }
// for (size_t ei = 0; ei < EN(); ei++) {
// elements[ei].setDimensions(handleBeamDimensions[ei]);
// elements[ei].setMaterial(handleBeamMaterial[ei]);
// elements[ei].updateRigidity();
// }
bool normalsAreAbsent = vert[0].cN().Norm() < 0.000001;
if (normalsAreAbsent) {
CoordType normalVector(0, 0, 1);
std::cout << "Warning: Normals are missing from " << plyFilename
<< ". Added normal vector:" << toString(normalVector) << std::endl;
for (auto &v : vert) {
v.N() = normalVector;
}
}
return true;
}
bool SimulationMesh::save(const std::string &plyFilename)
{
std::string filename = plyFilename;
if (filename.empty()) {
filename = std::filesystem::current_path().append(getLabel() + ".ply").string();
}
// nanoply::NanoPlyWrapper<VCGEdgeMesh>::CustomAttributeDescriptor customAttrib;
// customAttrib.GetMeshAttrib(filename);
// std::vector<CrossSectionType> dimensions = getBeamDimensions();
// customAttrib.AddEdgeAttribDescriptor<CrossSectionType, double, 2>(plyPropertyBeamDimensionsID,
// nanoply::NNP_LIST_INT8_FLOAT64,
// dimensions.data());
// std::vector<ElementMaterial> material = getBeamMaterial();
// customAttrib.AddEdgeAttribDescriptor<vcg::Point2d, double, 2>(plyPropertyBeamMaterialID,
// nanoply::NNP_LIST_INT8_FLOAT64,
// material.data());
// unsigned int mask = 0;
// mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTCOORD;
// mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEINDEX;
// mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_EDGEATTRIB;
// mask |= nanoply::NanoPlyWrapper<VCGEdgeMesh>::IO_VERTNORMAL;
// if (nanoply::NanoPlyWrapper<VCGEdgeMesh>::SaveModel(filename.c_str(),
// *this,
// mask,
// customAttrib,
// false)
// != 1) {
// return false;
// }
if (!VCGEdgeMesh::save(plyFilename)) {
return false;
}
return true;
}
SimulationMesh::EdgePointer
SimulationMesh::getReferenceElement(const VCGEdgeMesh::VertexType &v) {
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 CoordType &p0, const CoordType &p1) {
const VectorType t1 = (p1 - p0).Normalize();
return t1;
}
Element::LocalFrame computeElementFrame(const CoordType &p0,
const CoordType &p1,
const VectorType &elementNormal) {
const VectorType t1 = computeT1Vector(p0, p1);
const VectorType t2 = (elementNormal ^ t1).Normalize();
const VectorType t3 = (t1 ^ t2).Normalize();
return Element::LocalFrame{t1, t2, t3};
}
double computeAngle(const VectorType &vector0, const VectorType &vector1,
const VectorType &normalVector) {
double cosAngle = vector0.dot(vector1);
const double epsilon = std::pow(10, -6);
if (abs(cosAngle) > 1 && abs(cosAngle) < 1 + epsilon) {
if (cosAngle > 0) {
cosAngle = 1;
} else {
cosAngle = -1;
}
}
assert(abs(cosAngle) <= 1);
const double angle =
acos(cosAngle); // NOTE: I compute the alpha angle not between
// two consecutive elements but rather between
// the first and the ith. Is this correct?
assert(!std::isnan(angle));
const VectorType cp = vector0 ^ vector1;
if (cp.dot(normalVector) < 0) {
return -angle;
}
return angle;
}
//void Element::computeMaterialProperties(const ElementMaterial &material) {
// G = material.youngsModulus / (2 * (1 + material.poissonsRatio));
//}
//void Element::computeCrossSectionArea(const RectangularBeamDimensions &dimensions, double &A)
//{
// A = dimensions.b * dimensions.h;
//}
//void Element::computeDimensionsProperties(
// const RectangularBeamDimensions &dimensions) {
// assert(typeid(CrossSectionType) == typeid(RectangularBeamDimensions));
// computeCrossSectionArea(dimensions, A);
// computeMomentsOfInertia(dimensions, dimensions.inertia);
//}
//void Element::computeDimensionsProperties(
// const CylindricalBeamDimensions &dimensions) {
// assert(typeid(CrossSectionType) == typeid(CylindricalBeamDimensions));
// A = M_PI * (std::pow(dimensions.od / 2, 2) - std::pow(dimensions.id / 2, 2));
// dimensions.inertia.I2 = M_PI * (std::pow(dimensions.od, 4) - std::pow(dimensions.id, 4)) / 64;
// dimensions.inertia.I3 = dimensions.inertia.I2;
// dimensions.inertia.J = dimensions.inertia.I2 + dimensions.inertia.I3;
//}
void Element::setDimensions(const CrossSectionType &dimensions) {
this->dimensions = dimensions;
assert(this->dimensions.A == dimensions.A);
// computeDimensionsProperties(dimensions);
updateRigidity();
}
void Element::setMaterial(const ElementMaterial &material)
{
this->material = material;
// computeMaterialProperties(material);
updateRigidity();
}
double Element::getMass(const double &materialDensity)
{
const double beamVolume = dimensions.A * length;
return beamVolume * materialDensity;
}
void Element::updateRigidity() {
// assert(initialLength != 0);
rigidity.axial = material.youngsModulus * dimensions.A / initialLength;
// assert(rigidity.axial != 0);
rigidity.torsional = material.G * dimensions.inertia.J / initialLength;
// assert(rigidity.torsional != 0);
rigidity.firstBending = 2 * material.youngsModulus * dimensions.inertia.I2 / initialLength;
// assert(rigidity.firstBending != 0);
rigidity.secondBending = 2 * material.youngsModulus * dimensions.inertia.I3 / initialLength;
// assert(rigidity.secondBending != 0);
}

View File

@ -1,198 +0,0 @@
#ifndef SIMULATIONMESH_HPP
#define SIMULATIONMESH_HPP
#include "edgemesh.hpp"
#include "trianglepatterngeometry.hpp"
#include <Eigen/Dense>
//extern bool drawGlobal;
struct Element;
struct Node;
//using CrossSectionType = RectangularBeamDimensions;
using CrossSectionType = CylindricalBeamDimensions;
class SimulationMesh : public VCGEdgeMesh {
private:
void computeElementalProperties();
void initializeElements();
void initializeNodes();
EdgePointer getReferenceElement(const VertexType &v);
const std::string plyPropertyBeamDimensionsID{"beam_dimensions"};
const std::string plyPropertyBeamMaterialID{"beam_material"};
public:
PerEdgeAttributeHandle<Element> elements;
PerVertexAttributeHandle<Node> nodes;
~SimulationMesh();
SimulationMesh(PatternGeometry &pattern);
SimulationMesh(ConstVCGEdgeMesh &edgeMesh);
SimulationMesh(SimulationMesh &elementalMesh);
SimulationMesh(VCGTriMesh &triMesh);
void updateElementalLengths();
void updateIncidentElements();
std::vector<VCGEdgeMesh::EdgePointer>
getIncidentElements(const VCGEdgeMesh::VertexType &v);
virtual bool load(const std::string &plyFilename);
std::vector<CrossSectionType> getBeamDimensions();
std::vector<ElementMaterial> getBeamMaterial();
double pre_previousTotalKineticEnergy{0};
double pre_previousTotalTranslationalKineticEnergy{0};
double pre_previousTotalRotationalKineticEnergy{0};
double previousTotalKineticEnergy{0};
double previousTotalTranslationalKineticEnergy{0};
double previousTotalRotationalKineticEnergy{0};
double previousTotalResidualForcesNorm{0};
double currentTotalKineticEnergy{0};
double currentTotalTranslationalKineticEnergy{0};
double currentTotalRotationalKineticEnergy{0};
double totalResidualForcesNorm{0};
double totalExternalForcesNorm{0};
double averageResidualForcesNorm{0};
double currentTotalPotentialEnergykN{0};
double previousTotalPotentialEnergykN{0};
double residualForcesMovingAverageDerivativeNorm{0};
double residualForcesMovingAverage{0};
double perVertexAverageNormalizedDisplacementNorm{0};
bool save(const std::string &plyFilename = std::string());
void setBeamCrossSection(const CrossSectionType &beamDimensions);
void setBeamMaterial(const double &pr, const double &ym);
void reset();
SimulationMesh();
void updateElementalFrames();
#ifdef POLYSCOPE_DEFINED
polyscope::CurveNetwork *registerForDrawing(
const std::optional<std::array<double, 3>> &desiredColor = std::nullopt,
const bool &shouldEnable = true);
void unregister() const;
#endif
};
struct Element
{
CrossSectionType dimensions;
ElementMaterial material;
void computeMaterialProperties(const ElementMaterial &material);
// void computeDimensionsProperties(const RectangularBeamDimensions &dimensions);
// void computeDimensionsProperties(const CylindricalBeamDimensions &dimensions);
void setDimensions(const CrossSectionType &dimensions);
void setMaterial(const ElementMaterial &material);
double getMass(const double &matrialDensity);
struct LocalFrame
{
VectorType t1;
VectorType t2;
VectorType t3;
};
EdgeIndex ei;
double length{0};
double initialLength;
LocalFrame frame;
struct Rigidity
{
double axial;
double torsional;
double firstBending;
double secondBending;
std::string toString() const
{
return std::string("Rigidity:") + std::string("\nAxial=") + std::to_string(axial)
+ std::string("\nTorsional=") + std::to_string(torsional)
+ std::string("\nFirstBending=") + std::to_string(firstBending)
+ std::string("\nSecondBending=") + std::to_string(secondBending);
}
};
Rigidity rigidity;
void updateRigidity();
VectorType f1_j;
VectorType f1_jplus1;
VectorType f2_j;
VectorType f2_jplus1;
VectorType f3_j;
VectorType f3_jplus1;
double cosRotationAngle_j;
double cosRotationAngle_jplus1;
double sinRotationAngle_j;
double sinRotationAngle_jplus1;
std::vector<std::vector<VectorType>> derivativeT1;
std::vector<std::vector<VectorType>> derivativeT2;
std::vector<std::vector<VectorType>> derivativeT3;
std::vector<VectorType> derivativeT1_j;
std::vector<VectorType> derivativeT1_jplus1;
std::vector<VectorType> derivativeT2_j;
std::vector<VectorType> derivativeT2_jplus1;
std::vector<VectorType> derivativeT3_j;
std::vector<VectorType> derivativeT3_jplus1;
std::vector<VectorType> derivativeR_j;
std::vector<VectorType> derivativeR_jplus1;
struct RotationalDisplacements
{
double theta1{0}, theta2{0}, theta3{0};
};
RotationalDisplacements rotationalDisplacements_j;
RotationalDisplacements rotationalDisplacements_jplus1;
static void computeCrossSectionArea(const RectangularBeamDimensions &dimensions, double &A);
};
struct Node {
struct Forces {
Vector6d external{0};
Vector6d internal{0};
Vector6d residual{0};
Vector6d internalAxial{0};
Vector6d internalTorsion{0};
Vector6d internalFirstBending{0};
Vector6d internalSecondBending{0};
bool hasExternalForce() const { return external.isZero(); }
};
struct Mass {
double translational;
double rotationalI2;
double rotationalI3;
double rotationalJ;
};
Mass mass;
Vector6d mass_6d;
Vector6d damping_6d;
VertexIndex vi;
CoordType initialLocation;
CoordType initialNormal;
Vector6d acceleration{0};
Forces force;
Vector6d previousVelocity{0};
Vector6d velocity{0};
double kineticEnergy{0};
Vector6d displacements{0};
double nR{0};
// std::unordered_map<EdgeIndex, double>
// alphaAngles; // contains the initial angles between the first star element
// // incident to this node and the other elements of the star
// // has size equal to the valence of the vertex
std::vector<std::pair<EdgeIndex, double>> alphaAngles;
std::vector<VCGEdgeMesh::EdgePointer> incidentElements;
std::vector<VectorType> derivativeOfNormal;
SimulationMesh::EdgePointer referenceElement;
};
Element::LocalFrame computeElementFrame(const CoordType &p0,
const CoordType &p1,
const VectorType &elementNormal);
VectorType computeT1Vector(const SimulationMesh::EdgeType &e);
VectorType computeT1Vector(const CoordType &p0, const CoordType &p1);
double computeAngle(const VectorType &vector0, const VectorType &vector1,
const VectorType &normalVector);
#endif // ELEMENTALMESH_HPP

View File

@ -1,15 +0,0 @@
#ifndef SIMULATIONMODEL_HPP
#define SIMULATIONMODEL_HPP
#include "simulation_structs.hpp"
class SimulationModel
{
public:
virtual SimulationResults executeSimulation(const std::shared_ptr<SimulationJob> &simulationJob)
= 0;
virtual void setStructure(const std::shared_ptr<SimulationMesh> &pMesh) = 0;
virtual ~SimulationModel() = default;
};
#endif // SIMULATIONMODEL_HPP

View File

@ -1,108 +0,0 @@
#include "utilities.hpp"
#include "matplot/matplot.h"
void Utilities::createPlot(const std::string &xLabel,
const std::string &yLabel,
const std::vector<double> &x,
const std::vector<double> &y,
const std::vector<double> &markerSizes,
const std::vector<double> &c,
const std::string &saveTo)
{
// matplot::figure(true);
matplot::xlabel(xLabel);
matplot::ylabel(yLabel);
matplot::grid(matplot::on);
matplot::scatter(x, y, markerSizes, c)->marker_face(true);
if (!saveTo.empty()) {
matplot::save(saveTo);
}
}
#ifdef POLYSCOPE_DEFINED
#include "polyscope/curve_network.h"
#include "polyscope/pick.h"
#include "polyscope/polyscope.h"
#include <functional>
void PolyscopeInterface::mainCallback()
{
ImGui::PushItemWidth(100); // Make ui elements 100 pixels wide,
// instead of full width. Must have
// matching PopItemWidth() below.
for (std::function<void()> &userCallback : globalPolyscopeData.userCallbacks) {
userCallback();
}
ImGui::PopItemWidth();
}
void PolyscopeInterface::addUserCallback(const std::function<void()> &userCallback)
{
globalPolyscopeData.userCallbacks.push_back(userCallback);
}
void PolyscopeInterface::deinitPolyscope()
{
if (!polyscope::state::initialized) {
return;
}
polyscope::render::engine->shutdownImGui();
}
void PolyscopeInterface::init()
{
if (polyscope::state::initialized) {
return;
}
polyscope::init();
polyscope::options::groundPlaneEnabled = false;
polyscope::view::upDir = polyscope::view::UpDir::ZUp;
polyscope::state::userCallback = &mainCallback;
polyscope::options::autocenterStructures = false;
polyscope::options::autoscaleStructures = false;
}
std::pair<PolyscopeInterface::PolyscopeLabel, size_t> PolyscopeInterface::getSelection()
{
std::pair<polyscope::Structure *, size_t> selection = polyscope::pick::getSelection();
if (selection.first == nullptr) {
return std::make_pair(std::string(), 0);
}
return std::make_pair(selection.first->name, selection.second);
}
void PolyscopeInterface::registerWorldAxes()
{
PolyscopeInterface::init();
Eigen::MatrixX3d axesPositions(4, 3);
axesPositions.row(0) = Eigen::Vector3d(0, 0, 0);
// axesPositions.row(1) = Eigen::Vector3d(polyscope::state::lengthScale, 0, 0);
// axesPositions.row(2) = Eigen::Vector3d(0, polyscope::state::lengthScale, 0);
// axesPositions.row(3) = Eigen::Vector3d(0, 0, polyscope::state::lengthScale);
axesPositions.row(1) = Eigen::Vector3d(1, 0, 0);
axesPositions.row(2) = Eigen::Vector3d(0, 1, 0);
axesPositions.row(3) = Eigen::Vector3d(0, 0, 1);
Eigen::MatrixX2i axesEdges(3, 2);
axesEdges.row(0) = Eigen::Vector2i(0, 1);
axesEdges.row(1) = Eigen::Vector2i(0, 2);
axesEdges.row(2) = Eigen::Vector2i(0, 3);
Eigen::MatrixX3d axesColors(3, 3);
axesColors.row(0) = Eigen::Vector3d(1, 0, 0);
axesColors.row(1) = Eigen::Vector3d(0, 1, 0);
axesColors.row(2) = Eigen::Vector3d(0, 0, 1);
const std::string worldAxesName = "World Axes";
polyscope::registerCurveNetwork(worldAxesName, axesPositions, axesEdges);
polyscope::getCurveNetwork(worldAxesName)->setRadius(0.0001, false);
const std::string worldAxesColorName = worldAxesName + " Color";
polyscope::getCurveNetwork(worldAxesName)
->addEdgeColorQuantity(worldAxesColorName, axesColors)
->setEnabled(true);
}
#endif

View File

@ -1,409 +0,0 @@
#ifndef UTILITIES_H
#define UTILITIES_H
#include <Eigen/Dense>
#include <algorithm>
#include <array>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iterator>
#include <numeric>
#include <regex>
#include <string_view>
#define GET_VARIABLE_NAME(Variable) (#Variable)
struct Vector6d : public std::array<double, 6> {
Vector6d() {
for (size_t i = 0; i < 6; i++) {
this->operator[](i) = 0;
}
}
Vector6d(const std::vector<double> &v) {
assert(v.size() == 6);
std::copy(v.begin(), v.end(), this->begin());
}
Vector6d(const double &d) {
for (size_t i = 0; i < 6; i++) {
this->operator[](i) = d;
}
}
Vector6d(const std::array<double, 6> &arr) : std::array<double, 6>(arr) {}
Vector6d(const std::initializer_list<double> &initList) {
std::copy(initList.begin(), initList.end(), std::begin(*this));
}
Vector6d operator*(const double &d) const {
Vector6d result;
for (size_t i = 0; i < 6; i++) {
result[i] = this->operator[](i) * d;
}
return result;
}
Vector6d operator*(const Vector6d &v) const {
Vector6d result;
for (size_t i = 0; i < 6; i++) {
result[i] = this->operator[](i) * v[i];
}
return result;
}
Vector6d operator/(const double &d) const {
Vector6d result;
for (size_t i = 0; i < 6; i++) {
result[i] = this->operator[](i) / d;
}
return result;
}
Vector6d operator/(const Vector6d &v) const
{
Vector6d result;
for (size_t i = 0; i < 6; i++) {
result[i] = this->operator[](i) / v[i];
}
return result;
}
Vector6d operator+(const Vector6d &v) const {
Vector6d result;
for (size_t i = 0; i < 6; i++) {
result[i] = this->operator[](i) + v[i];
}
return result;
}
Vector6d operator-(const Vector6d &v) const {
Vector6d result;
for (size_t i = 0; i < 6; i++) {
result[i] = this->operator[](i) - v[i];
}
return result;
}
Vector6d inverted() const {
Vector6d result;
for (size_t i = 0; i < 6; i++) {
assert(this->operator[](i) != 0);
result[i] = 1 / this->operator[](i);
}
return result;
}
bool isZero() const {
for (size_t i = 0; i < 6; i++) {
if (this->operator[](i) != 0)
return false;
}
return true;
}
double squaredNorm() const {
double squaredNorm = 0;
std::for_each(this->begin(), std::end(*this),
[&](const double &v) { squaredNorm += pow(v, 2); });
return squaredNorm;
}
double norm() const { return sqrt(squaredNorm()); }
bool isFinite() const {
return std::any_of(std::begin(*this), std::end(*this), [](const double &v) {
if (!std::isfinite(v)) {
return false;
}
return true;
});
}
Eigen::Vector3d getTranslation() const {
return Eigen::Vector3d(this->operator[](0), this->operator[](1),
this->operator[](2));
}
Eigen::Vector3d getRotation() const
{
return Eigen::Vector3d(this->operator[](3), this->operator[](4), this->operator[](5));
}
std::string toString() const
{
std::string s;
for (int i = 0; i < 6; i++) {
s.append(std::to_string(this->operator[](i)) + ",");
}
s.pop_back();
return s;
}
};
namespace Utilities {
template<typename T>
std::string to_string_with_precision(const T a_value, const int n = 2)
{
std::ostringstream out;
out.precision(n);
out << std::fixed << a_value;
return out.str();
}
inline bool compareNat(const std::string &a, const std::string &b)
{
if (a.empty())
return true;
if (b.empty())
return false;
if (std::isdigit(a[0]) && !std::isdigit(b[0]))
return true;
if (!std::isdigit(a[0]) && std::isdigit(b[0]))
return false;
if (!std::isdigit(a[0]) && !std::isdigit(b[0])) {
if (std::toupper(a[0]) == std::toupper(b[0]))
return compareNat(a.substr(1), b.substr(1));
return (std::toupper(a[0]) < std::toupper(b[0]));
}
// Both strings begin with digit --> parse both numbers
std::istringstream issa(a);
std::istringstream issb(b);
int ia, ib;
issa >> ia;
issb >> ib;
if (ia != ib)
return ia < ib;
// Numbers are the same --> remove numbers and recurse
std::string anew, bnew;
std::getline(issa, anew);
std::getline(issb, bnew);
return (compareNat(anew, bnew));
}
inline std::string_view leftTrimSpaces(const std::string_view& str)
{
std::string_view trimmedString=str;
const auto pos(str.find_first_not_of(" \t\n\r\f\v"));
trimmedString.remove_prefix(std::min(pos, trimmedString.length()));
return trimmedString;
}
inline std::string_view rightTrimSpaces(const std::string_view& str)
{
std::string_view trimmedString=str;
const auto pos(trimmedString.find_last_not_of(" \t\n\r\f\v"));
trimmedString.remove_suffix(std::min(trimmedString.length() - pos - 1,trimmedString.length()));
return trimmedString;
}
inline std::string_view trimLeftAndRightSpaces(std::string_view str)
{
std::string_view trimmedString=str;
trimmedString = leftTrimSpaces(trimmedString);
trimmedString = rightTrimSpaces(trimmedString);
return trimmedString;
}
template<typename InputIt>
inline void normalize(InputIt itBegin, InputIt itEnd)
{
const auto squaredSumOfElements = std::accumulate(itBegin,
itEnd,
0.0,
[](const auto &sum, const auto &el) {
return sum + el * el;
});
assert(squaredSumOfElements != 0);
std::transform(itBegin, itEnd, itBegin, [&](auto &element) {
return element / std::sqrt(squaredSumOfElements);
});
}
inline std::vector<std::string> split(const std::string& text, std::string delim)
{
std::vector<std::string> vec;
size_t pos = 0, prevPos = 0;
while (1) {
pos = text.find(delim, prevPos);
if (pos == std::string::npos) {
vec.push_back(text.substr(prevPos));
return vec;
}
vec.push_back(text.substr(prevPos, pos - prevPos));
prevPos = pos + delim.length();
}
}
inline std::string toString(const std::vector<std::vector<int>> &vv)
{
std::string s;
s.append("{");
for (const std::vector<int> &v : vv) {
s.append("{");
for (const int &i : v) {
s.append(std::to_string(i) + ",");
}
s.pop_back();
s.append("}");
}
s.append("}");
return s;
}
inline void parseIntegers(const std::string &str, std::vector<size_t> &result)
{
typedef std::regex_iterator<std::string::const_iterator> re_iterator;
typedef re_iterator::value_type re_iterated;
std::regex re("(\\d+)");
re_iterator rit(str.begin(), str.end(), re);
re_iterator rend;
std::transform(rit, rend, std::back_inserter(result), [](const re_iterated &it) {
return std::stoi(it[1]);
});
}
inline Eigen::MatrixXd toEigenMatrix(const std::vector<Vector6d> &v) {
Eigen::MatrixXd m(v.size(), 6);
for (size_t vi = 0; vi < v.size(); vi++) {
const Vector6d &vec = v[vi];
for (size_t i = 0; i < 6; i++) {
m(vi, i) = vec[i];
}
}
return m;
}
inline std::vector<Vector6d> fromEigenMatrix(const Eigen::MatrixXd &m)
{
std::vector<Vector6d> v(m.rows());
for (size_t vi = 0; vi < m.rows(); vi++) {
const Eigen::RowVectorXd &row = m.row(vi);
for (size_t i = 0; i < 6; i++) {
v[vi][i] = row(i);
}
}
return v;
}
// std::string convertToLowercase(const std::string &s) {
// std::string lowercase;
// std::transform(s.begin(), s.end(), lowercase.begin(),
// [](unsigned char c) { return std::tolower(c); });
// return lowercase;
//}
// bool hasExtension(const std::string &filename, const std::string &extension)
// {
// const std::filesystem::path path(filename);
// if (!path.has_extension()) {
// std::cerr << "Error: No file extension found in " << filename <<
// std::endl; return false;
// }
// const std::string detectedExtension = path.extension().string();
// if (convertToLowercase(detectedExtension) != convertToLowercase(extension))
// {
// std::cerr << "Error: detected extension is " + detectedExtension +
// " and not " + extension
// << std::endl;
// return false;
// }
// return true;
//}
inline std::filesystem::path getFilepathWithExtension(const std::filesystem::path &folderPath,
const std::string &extension)
{
assert(std::filesystem::exists(folderPath));
for (const std::filesystem::directory_entry &dirEntry :
std::filesystem::directory_iterator(folderPath)) {
if (dirEntry.is_regular_file() && std::filesystem::path(dirEntry).extension() == extension) {
return std::filesystem::path(dirEntry);
}
}
return "";
}
void createPlot(const std::string &xLabel,
const std::string &yLabel,
const std::vector<double> &x,
const std::vector<double> &y,
const std::vector<double> &markerSizes,
const std::vector<double> &c,
const std::string &saveTo = {});
} // namespace Utilities
#ifdef POLYSCOPE_DEFINED
namespace PolyscopeInterface {
inline struct GlobalPolyscopeData
{
std::vector<std::function<void()>> userCallbacks;
} globalPolyscopeData;
void mainCallback();
void addUserCallback(const std::function<void()> &userCallback);
void deinitPolyscope();
void init();
using PolyscopeLabel = std::string;
std::pair<PolyscopeLabel, size_t> getSelection();
void registerWorldAxes();
} // namespace PolyscopeInterface
#endif
// namespace ConfigurationFile {
//}
//} // namespace ConfigurationFile
template <typename T1, typename T2>
void constructInverseMap(const T1 &map, T2 &oppositeMap) {
assert(!map.empty());
oppositeMap.clear();
for (const auto &mapIt : map) {
oppositeMap[mapIt.second] = mapIt.first;
}
}
template <typename T> std::string toString(const T &v) {
return "(" + std::to_string(v[0]) + "," + std::to_string(v[1]) + "," +
std::to_string(v[2]) + ")";
}
template<typename T>
size_t computeHashUnordered(const std::vector<T> &v)
{
size_t hash = 0;
for (const auto &el : v) {
hash += std::hash<T>{}(el);
}
return hash;
}
inline size_t computeHashOrdered(const std::vector<int> &v)
{
std::string elementsString;
for (const auto &el : v) {
elementsString += std::to_string(el);
}
return std::hash<std::string>{}(elementsString);
}
#endif // UTILITIES_H