2074 lines
106 KiB
C++
2074 lines
106 KiB
C++
#include "reducedmodeloptimizer.hpp"
|
|
#include "hexagonremesher.hpp"
|
|
#include "linearsimulationmodel.hpp"
|
|
#include "simulationhistoryplotter.hpp"
|
|
#include "trianglepatterngeometry.hpp"
|
|
#include "trianglepattterntopology.hpp"
|
|
#include <armadillo>
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <execution>
|
|
#include <experimental/numeric>
|
|
#include <functional>
|
|
#include <parallel/parallel.h>
|
|
|
|
//#define USE_SCENARIO_WEIGHTS
|
|
|
|
using namespace ReducedModelOptimization;
|
|
|
|
struct GlobalOptimizationVariables
|
|
{
|
|
std::vector<SimulationResults> fullPatternResults;
|
|
std::vector<double> translationalDisplacementNormalizationValues;
|
|
std::vector<double> rotationalDisplacementNormalizationValues;
|
|
std::vector<std::shared_ptr<SimulationJob>> fullPatternSimulationJobs;
|
|
std::vector<std::shared_ptr<SimulationJob>> reducedPatternSimulationJobs;
|
|
std::unordered_map<ReducedPatternVertexIndex, FullPatternVertexIndex> reducedToFullInterfaceViMap;
|
|
std::vector<std::pair<FullPatternVertexIndex, FullPatternVertexIndex>>
|
|
fullPatternInterfaceViPairs;
|
|
matplot::line_handle gPlotHandle;
|
|
std::vector<size_t> objectiveValueHistory_iteration;
|
|
std::vector<double> objectiveValueHistory;
|
|
std::vector<double> plotColors;
|
|
std::array<double,
|
|
ReducedModelOptimization::OptimizationParameterIndex::NumberOfOptimizationVariables>
|
|
parametersInitialValue;
|
|
std::array<double,
|
|
ReducedModelOptimization::OptimizationParameterIndex::NumberOfOptimizationVariables>
|
|
optimizationInitialValue;
|
|
std::vector<int> simulationScenarioIndices;
|
|
double minY{DBL_MAX};
|
|
std::vector<double> minX;
|
|
int numOfSimulationCrashes{false};
|
|
int numberOfFunctionCalls{0};
|
|
ReducedModelOptimization::Settings optimizationSettings;
|
|
vcg::Triangle3<double> baseTriangle;
|
|
//Variables for finding the full pattern simulation forces
|
|
std::shared_ptr<SimulationMesh> pFullPatternSimulationMesh;
|
|
std::function<void(const double &,
|
|
const std::vector<std::pair<FullPatternVertexIndex, FullPatternVertexIndex>> &,
|
|
SimulationJob &)>
|
|
constructScenarioFunction;
|
|
FullPatternVertexIndex interfaceViForComputingScenarioError;
|
|
double desiredMaxDisplacementValue;
|
|
double desiredMaxRotationAngle;
|
|
std::string currentScenarioName;
|
|
std::array<std::function<void(const double &newValue,
|
|
std::shared_ptr<SimulationMesh> &pReducedPatternSimulationMesh)>,
|
|
7>
|
|
functions_updateReducedPatternParameter;
|
|
std::vector<double> xMin;
|
|
std::vector<double> xMax;
|
|
std::vector<double> scenarioWeights;
|
|
std::vector<ReducedModelOptimization::Settings::ObjectiveWeights> objectiveWeights;
|
|
} global;
|
|
|
|
double ReducedModelOptimizer::computeDisplacementError(
|
|
const std::vector<Vector6d> &fullPatternDisplacements,
|
|
const std::vector<Vector6d> &reducedPatternDisplacements,
|
|
const std::unordered_map<ReducedPatternVertexIndex, FullPatternVertexIndex>
|
|
&reducedToFullInterfaceViMap,
|
|
const double &normalizationFactor)
|
|
{
|
|
const double rawError = computeRawTranslationalError(fullPatternDisplacements,
|
|
reducedPatternDisplacements,
|
|
reducedToFullInterfaceViMap);
|
|
// std::cout << "raw trans error:" << rawError << std::endl;
|
|
// std::cout << "raw trans error:" << normalizationFactor << std::endl;
|
|
return rawError / normalizationFactor;
|
|
}
|
|
|
|
double ReducedModelOptimizer::computeRawTranslationalError(
|
|
const std::vector<Vector6d> &fullPatternDisplacements,
|
|
const std::vector<Vector6d> &reducedPatternDisplacements,
|
|
const std::unordered_map<ReducedPatternVertexIndex, FullPatternVertexIndex>
|
|
&reducedToFullInterfaceViMap)
|
|
{
|
|
double error = 0;
|
|
for (const auto reducedFullViPair : reducedToFullInterfaceViMap) {
|
|
const VertexIndex reducedModelVi = reducedFullViPair.first;
|
|
const VertexIndex fullModelVi = reducedFullViPair.second;
|
|
const Eigen::Vector3d fullPatternVertexDiplacement = fullPatternDisplacements[fullModelVi]
|
|
.getTranslation();
|
|
const Eigen::Vector3d reducedPatternVertexDiplacement
|
|
= reducedPatternDisplacements[reducedModelVi].getTranslation();
|
|
const double vertexError = (fullPatternVertexDiplacement - reducedPatternVertexDiplacement)
|
|
.norm();
|
|
error += vertexError;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
double ReducedModelOptimizer::computeRawRotationalError(
|
|
const std::vector<Eigen::Quaternion<double>> &rotatedQuaternion_fullPattern,
|
|
const std::vector<Eigen::Quaternion<double>> &rotatedQuaternion_reducedPattern,
|
|
const std::unordered_map<ReducedPatternVertexIndex, FullPatternVertexIndex>
|
|
&reducedToFullInterfaceViMap)
|
|
{
|
|
double rawRotationalError = 0;
|
|
for (const auto &reducedToFullInterfaceViPair : reducedToFullInterfaceViMap) {
|
|
const double vertexRotationalError = std::abs(
|
|
rotatedQuaternion_fullPattern[reducedToFullInterfaceViPair.second].angularDistance(
|
|
rotatedQuaternion_reducedPattern[reducedToFullInterfaceViPair.first]));
|
|
rawRotationalError += vertexRotationalError;
|
|
}
|
|
|
|
return rawRotationalError;
|
|
}
|
|
|
|
double ReducedModelOptimizer::computeRotationalError(
|
|
const std::vector<Eigen::Quaternion<double>> &rotatedQuaternion_fullPattern,
|
|
const std::vector<Eigen::Quaternion<double>> &rotatedQuaternion_reducedPattern,
|
|
const std::unordered_map<ReducedPatternVertexIndex, FullPatternVertexIndex>
|
|
&reducedToFullInterfaceViMap,
|
|
const double &normalizationFactor)
|
|
{
|
|
const double rawRotationalError = computeRawRotationalError(rotatedQuaternion_fullPattern,
|
|
rotatedQuaternion_reducedPattern,
|
|
reducedToFullInterfaceViMap);
|
|
return rawRotationalError / normalizationFactor;
|
|
}
|
|
|
|
double ReducedModelOptimizer::computeError(
|
|
const SimulationResults &simulationResults_fullPattern,
|
|
const SimulationResults &simulationResults_reducedPattern,
|
|
const std::unordered_map<ReducedPatternVertexIndex, FullPatternVertexIndex>
|
|
&reducedToFullInterfaceViMap,
|
|
const double &normalizationFactor_translationalDisplacement,
|
|
const double &normalizationFactor_rotationalDisplacement,
|
|
const double &scenarioWeight,
|
|
const Settings::ObjectiveWeights &objectiveWeights)
|
|
{
|
|
const double translationalError
|
|
= computeDisplacementError(simulationResults_fullPattern.displacements,
|
|
simulationResults_reducedPattern.displacements,
|
|
reducedToFullInterfaceViMap,
|
|
normalizationFactor_translationalDisplacement);
|
|
// const double translationalError
|
|
// = computeRawTranslationalError(simulationResults_fullPattern.displacements,
|
|
// simulationResults_reducedPattern.displacements,
|
|
// reducedToFullInterfaceViMap);
|
|
|
|
// std::cout << "normalization factor:" << normalizationFactor_rotationalDisplacement << std::endl;
|
|
// std::cout << "trans error:" << translationalError << std::endl;
|
|
const double rotationalError
|
|
= computeRotationalError(simulationResults_fullPattern.rotationalDisplacementQuaternion,
|
|
simulationResults_reducedPattern.rotationalDisplacementQuaternion,
|
|
reducedToFullInterfaceViMap,
|
|
normalizationFactor_rotationalDisplacement);
|
|
// const double rotationalError
|
|
// = computeRawRotationalError(simulationResults_fullPattern.rotationalDisplacementQuaternion,
|
|
// simulationResults_reducedPattern.rotationalDisplacementQuaternion,
|
|
// reducedToFullInterfaceViMap);
|
|
// std::cout << "rot error:" << rotationalError<< std::endl;
|
|
const double simulationError = objectiveWeights.translational * translationalError
|
|
+ objectiveWeights.rotational * rotationalError;
|
|
assert(!std::isnan(simulationError));
|
|
return simulationError * simulationError * scenarioWeight * scenarioWeight;
|
|
}
|
|
|
|
//double ReducedModelOptimizer::objective(double E,double A,double J,double I2,double I3,
|
|
// double innerHexagonSize,
|
|
// double innerHexagonRotationAngle) {
|
|
// std::vector<double> x{E,A,J,I2,I3, innerHexagonSize, innerHexagonRotationAngle};
|
|
// return ReducedModelOptimizer::objective(x.size(), x.data());
|
|
//}
|
|
|
|
#ifdef USE_ENSMALLEN
|
|
struct EnsmallenOptimizationObjective
|
|
{
|
|
static double Evaluate(const arma::mat &x_arma)
|
|
{
|
|
std::vector<double> x(x_arma.begin(), x_arma.end());
|
|
for (int xi = 0; xi < x.size(); xi++) {
|
|
if (x[xi] > global.xMax[xi]) {
|
|
//#ifdef POLYSCOPE_DEFINED
|
|
// std::cout << "Out of range" << std::endl;
|
|
//#endif
|
|
return std::numeric_limits<double>::max();
|
|
// return 1e10;
|
|
// x[xi] = global.xMax[xi];
|
|
} else if (x[xi] < global.xMin[xi]) {
|
|
//#ifdef POLYSCOPE_DEFINED
|
|
// std::cout << "Out of range" << std::endl;
|
|
//#endif
|
|
return std::numeric_limits<double>::max();
|
|
// return 1e10;
|
|
// x[xi] = global.xMin[xi];
|
|
}
|
|
}
|
|
return ReducedModelOptimizer::objective(x);
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#ifdef DLIB_DEFINED
|
|
double ReducedModelOptimizer::objective(const dlib::matrix<double, 0, 1> &x)
|
|
{
|
|
return objective(std::vector<double>(x.begin(), x.end()));
|
|
}
|
|
#endif
|
|
|
|
double ReducedModelOptimizer::objective(const double &xValue)
|
|
{
|
|
return objective(std::vector<double>({xValue}));
|
|
}
|
|
|
|
double ReducedModelOptimizer::objective(const std::vector<double> &x)
|
|
{
|
|
// std::cout.precision(17);
|
|
// for (int i = 0; i < x.size(); i++) {
|
|
// std::cout << x[i] << " ";
|
|
// }
|
|
// std::cout << std::endl;
|
|
|
|
// std::cout << x(x.size() - 2) << " " << x(x.size() - 1) << std::endl;
|
|
// const Element &e =
|
|
// global.reducedPatternSimulationJobs[0]->pMesh->elements[0]; std::cout <<
|
|
// e.axialConstFactor << " " << e.torsionConstFactor << " "
|
|
// << e.firstBendingConstFactor << " " <<
|
|
// e.secondBendingConstFactor
|
|
// << std::endl;
|
|
// const int n = x.size();
|
|
std::shared_ptr<SimulationMesh> &pReducedPatternSimulationMesh
|
|
= global.reducedPatternSimulationJobs[global.simulationScenarioIndices[0]]->pMesh;
|
|
function_updateReducedPattern(x, pReducedPatternSimulationMesh);
|
|
// global.reducedPatternSimulationJobs[0]->pMesh->registerForDrawing();
|
|
// global.fullPatternSimulationJobs[0]->pMesh->registerForDrawing();
|
|
// polyscope::show();
|
|
// global.reducedPatternSimulationJobs[0]->pMesh->unregister();
|
|
|
|
// run simulations
|
|
std::vector<double> simulationErrorsPerScenario(totalNumberOfSimulationScenarios, 0);
|
|
LinearSimulationModel simulator;
|
|
simulator.setStructure(pReducedPatternSimulationMesh);
|
|
// simulator.initialize();
|
|
// FormFinder simulator;
|
|
|
|
std::for_each(
|
|
std::execution::par_unseq,
|
|
global.simulationScenarioIndices.begin(),
|
|
global.simulationScenarioIndices.end(),
|
|
[&](const int &simulationScenarioIndex) {
|
|
// for (const int simulationScenarioIndex : global.simulationScenarioIndices) {
|
|
const std::shared_ptr<SimulationJob> &reducedJob
|
|
= global.reducedPatternSimulationJobs[simulationScenarioIndex];
|
|
//#ifdef POLYSCOPE_DEFINED
|
|
// std::cout << reducedJob->getLabel() << ":" << std::endl;
|
|
//#endif
|
|
SimulationResults reducedModelResults = simulator.executeSimulation(reducedJob);
|
|
// std::string filename;
|
|
if (!reducedModelResults.converged) {
|
|
simulationErrorsPerScenario[simulationScenarioIndex]
|
|
= std::numeric_limits<double>::max();
|
|
global.numOfSimulationCrashes++;
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << "Failed simulation" << std::endl;
|
|
#endif
|
|
} else {
|
|
// const double simulationScenarioError_PE = std::abs(
|
|
// (reducedModelResults.internalPotentialEnergy
|
|
// - global.fullPatternResults[simulationScenarioIndex].internalPotentialEnergy)
|
|
// / global.fullPatternResults[simulationScenarioIndex].internalPotentialEnergy);
|
|
// else {
|
|
#ifdef POLYSCOPE_DEFINED
|
|
#ifdef USE_SCENARIO_WEIGHTS
|
|
const double scenarioWeight = global.scenarioWeights[simulationScenarioIndex];
|
|
#else
|
|
const double scenarioWeight = 1;
|
|
#endif
|
|
#else
|
|
const double scenarioWeight = 1;
|
|
#endif
|
|
const double simulationScenarioError_displacements = computeError(
|
|
global.fullPatternResults[simulationScenarioIndex],
|
|
reducedModelResults,
|
|
global.reducedToFullInterfaceViMap,
|
|
global.translationalDisplacementNormalizationValues[simulationScenarioIndex],
|
|
global.rotationalDisplacementNormalizationValues[simulationScenarioIndex],
|
|
scenarioWeight,
|
|
global.objectiveWeights[simulationScenarioIndex]);
|
|
|
|
simulationErrorsPerScenario[simulationScenarioIndex]
|
|
= simulationScenarioError_displacements /*+ simulationScenarioError_PE*/;
|
|
// }
|
|
// #ifdef POLYSCOPE_DEFINED
|
|
// reducedJob->pMesh->registerForDrawing(Colors::reducedInitial);
|
|
// reducedModelResults.registerForDrawing(Colors::reducedDeformed);
|
|
// global.pFullPatternSimulationMesh->registerForDrawing(Colors::fullDeformed);
|
|
// global.fullPatternResults[simulationScenarioIndex].registerForDrawing(
|
|
// Colors::fullDeformed);
|
|
// polyscope::show();
|
|
// reducedModelResults.unregister();
|
|
// global.pFullPatternSimulationMesh->unregister();
|
|
// global.fullPatternResults[simulationScenarioIndex].unregister();
|
|
// #endif
|
|
}
|
|
});
|
|
// double totalError = std::accumulate(simulationErrorsPerScenario.begin(),
|
|
// simulationErrorsPerScenario.end(),
|
|
// 0);
|
|
const double totalError = std::reduce(std::execution::par_unseq,
|
|
simulationErrorsPerScenario.begin(),
|
|
simulationErrorsPerScenario.end());
|
|
// std::cout << totalError << std::endl;
|
|
// global.objectiveValueHistory.push_back(totalError);
|
|
// global.plotColors.push_back(10);
|
|
++global.numberOfFunctionCalls;
|
|
if (totalError < global.minY) {
|
|
global.minY = totalError;
|
|
// global.objectiveValueHistory.push_back(totalError);
|
|
// global.objectiveValueHistory_iteration.push_back(global.numberOfFunctionCalls);
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << "New best:" << totalError << std::endl;
|
|
// // global.minX.assign(x.begin(), x.begin() + n);
|
|
std::cout.precision(17);
|
|
for (int i = 0; i < x.size(); i++) {
|
|
std::cout << x[i] << " ";
|
|
}
|
|
std::cout << std::endl;
|
|
#endif
|
|
// global.objectiveValueHistoryY.push_back(std::log(totalError));
|
|
// global.objectiveValueHistoryX.push_back(global.numberOfFunctionCalls);
|
|
// global.plotColors.push_back(0.1);
|
|
// auto xPlot = matplot::linspace(0,
|
|
// global.objectiveValueHistoryY.size(),
|
|
// global.objectiveValueHistoryY.size());
|
|
// global.gPlotHandle = matplot::scatter(global.objectiveValueHistoryX,
|
|
// global.objectiveValueHistoryY,
|
|
// 4,
|
|
// global.plotColors);
|
|
// matplot::show();
|
|
// SimulationResultsReporter::createPlot("Number of Steps",
|
|
// "Objective value",
|
|
// global.objectiveValueHistoryY);
|
|
}
|
|
#ifdef POLYSCOPE_DEFINED
|
|
if (global.optimizationSettings.numberOfFunctionCalls >= 100
|
|
&& global.numberOfFunctionCalls % (global.optimizationSettings.numberOfFunctionCalls / 100)
|
|
== 0) {
|
|
std::cout << "Number of function calls:" << global.numberOfFunctionCalls << std::endl;
|
|
}
|
|
#endif
|
|
// compute error and return it
|
|
return totalError;
|
|
}
|
|
|
|
void ReducedModelOptimizer::createSimulationMeshes(
|
|
PatternGeometry &fullModel,
|
|
PatternGeometry &reducedModel,
|
|
std::shared_ptr<SimulationMesh> &pFullPatternSimulationMesh,
|
|
std::shared_ptr<SimulationMesh> &pReducedPatternSimulationMesh)
|
|
{
|
|
if (typeid(CrossSectionType) != typeid(RectangularBeamDimensions)) {
|
|
std::cerr << "Error: A rectangular cross section is expected." << std::endl;
|
|
std::terminate();
|
|
}
|
|
// Full pattern
|
|
pFullPatternSimulationMesh = std::make_shared<SimulationMesh>(fullModel);
|
|
pFullPatternSimulationMesh->setBeamCrossSection(CrossSectionType{0.002, 0.002});
|
|
pFullPatternSimulationMesh->setBeamMaterial(0.3, youngsModulus);
|
|
pFullPatternSimulationMesh->reset();
|
|
|
|
// Reduced pattern
|
|
pReducedPatternSimulationMesh = std::make_shared<SimulationMesh>(reducedModel);
|
|
pReducedPatternSimulationMesh->setBeamCrossSection(CrossSectionType{0.002, 0.002});
|
|
pReducedPatternSimulationMesh->setBeamMaterial(0.3, youngsModulus);
|
|
pReducedPatternSimulationMesh->reset();
|
|
}
|
|
|
|
void ReducedModelOptimizer::createSimulationMeshes(PatternGeometry &fullModel,
|
|
PatternGeometry &reducedModel)
|
|
{
|
|
ReducedModelOptimizer::createSimulationMeshes(fullModel,
|
|
reducedModel,
|
|
m_pFullPatternSimulationMesh,
|
|
m_pReducedPatternSimulationMesh);
|
|
}
|
|
|
|
void ReducedModelOptimizer::computeMaps(
|
|
const std::unordered_map<size_t, std::unordered_set<size_t>> &slotToNode,
|
|
PatternGeometry &fullPattern,
|
|
PatternGeometry &reducedPattern,
|
|
std::unordered_map<ReducedPatternVertexIndex, FullPatternVertexIndex>
|
|
&reducedToFullInterfaceViMap,
|
|
std::unordered_map<FullPatternVertexIndex, ReducedPatternVertexIndex>
|
|
&fullToReducedInterfaceViMap,
|
|
std::vector<std::pair<FullPatternVertexIndex, ReducedPatternVertexIndex>>
|
|
&fullPatternOppositeInterfaceViPairs)
|
|
{
|
|
// Compute the offset between the interface nodes
|
|
const size_t interfaceSlotIndex = 4; // bottom edge
|
|
assert(slotToNode.find(interfaceSlotIndex) != slotToNode.end()
|
|
&& slotToNode.find(interfaceSlotIndex)->second.size() == 1);
|
|
// Assuming that in the bottom edge there is only one vertex which is also the
|
|
// interface
|
|
const size_t baseTriangleInterfaceVi = *(slotToNode.find(interfaceSlotIndex)->second.begin());
|
|
|
|
vcg::tri::Allocator<VCGEdgeMesh>::PointerUpdater<PatternGeometry::VertexPointer> pu_fullModel;
|
|
fullPattern.deleteDanglingVertices(pu_fullModel);
|
|
const size_t fullModelBaseTriangleInterfaceVi = pu_fullModel.remap.empty()
|
|
? baseTriangleInterfaceVi
|
|
: pu_fullModel.remap[baseTriangleInterfaceVi];
|
|
const size_t fullModelBaseTriangleVN = fullPattern.VN();
|
|
fullPattern.createFan();
|
|
const size_t duplicateVerticesPerFanPair = fullModelBaseTriangleVN - fullPattern.VN() / 6;
|
|
const size_t fullPatternInterfaceVertexOffset = fullModelBaseTriangleVN
|
|
- duplicateVerticesPerFanPair;
|
|
// std::cout << "Dups in fan pair:" << duplicateVerticesPerFanPair <<
|
|
// std::endl;
|
|
|
|
// Save excluded edges TODO:this changes the global object. Should this be a
|
|
// function parameter?
|
|
// global.reducedPatternExludedEdges.clear();
|
|
// const size_t reducedBaseTriangleNumberOfEdges = reducedPattern.EN();
|
|
// for (size_t fanIndex = 0; fanIndex < fanSize; fanIndex++) {
|
|
// for (const size_t ei : reducedModelExcludedEdges) {
|
|
// global.reducedPatternExludedEdges.insert(
|
|
// fanIndex * reducedBaseTriangleNumberOfEdges + ei);
|
|
// }
|
|
// }
|
|
|
|
// Construct reduced->full and full->reduced interface vi map
|
|
reducedToFullInterfaceViMap.clear();
|
|
vcg::tri::Allocator<VCGEdgeMesh>::PointerUpdater<PatternGeometry::VertexPointer> pu_reducedModel;
|
|
reducedPattern.deleteDanglingVertices(pu_reducedModel);
|
|
const size_t reducedModelBaseTriangleInterfaceVi = pu_reducedModel.remap[baseTriangleInterfaceVi];
|
|
const size_t reducedModelInterfaceVertexOffset
|
|
= reducedPattern.VN() /*- 1*/ /*- reducedModelBaseTriangleInterfaceVi*/;
|
|
Results::applyOptimizationResults_innerHexagon(initialHexagonSize,
|
|
0,
|
|
global.baseTriangle,
|
|
reducedPattern);
|
|
reducedPattern.createFan({0}); //TODO: should be an input parameter from main
|
|
for (size_t fanIndex = 0; fanIndex < fanSize; fanIndex++) {
|
|
reducedToFullInterfaceViMap[reducedModelInterfaceVertexOffset * fanIndex
|
|
+ reducedModelBaseTriangleInterfaceVi]
|
|
= fullModelBaseTriangleInterfaceVi + fanIndex * fullPatternInterfaceVertexOffset;
|
|
}
|
|
fullToReducedInterfaceViMap.clear();
|
|
constructInverseMap(reducedToFullInterfaceViMap, fullToReducedInterfaceViMap);
|
|
|
|
// Create opposite vertex map
|
|
fullPatternOppositeInterfaceViPairs.clear();
|
|
fullPatternOppositeInterfaceViPairs.reserve(fanSize / 2);
|
|
// for (int fanIndex = fanSize / 2 - 1; fanIndex >= 0; fanIndex--) {
|
|
for (int fanIndex = 0; fanIndex < fanSize / 2; fanIndex++) {
|
|
const size_t vi0 = fullModelBaseTriangleInterfaceVi
|
|
+ fanIndex * fullPatternInterfaceVertexOffset;
|
|
const size_t vi1 = vi0 + (fanSize / 2) * fullPatternInterfaceVertexOffset;
|
|
assert(vi0 < fullPattern.VN() && vi1 < fullPattern.VN());
|
|
fullPatternOppositeInterfaceViPairs.emplace_back(std::make_pair(vi0, vi1));
|
|
}
|
|
global.fullPatternInterfaceViPairs = fullPatternOppositeInterfaceViPairs;
|
|
|
|
#if POLYSCOPE_DEFINED
|
|
const bool debugMapping = false;
|
|
if (debugMapping) {
|
|
reducedPattern.registerForDrawing();
|
|
|
|
// std::vector<glm::vec3> colors_reducedPatternExcludedEdges(
|
|
// reducedPattern.EN(), glm::vec3(0, 0, 0));
|
|
// for (const size_t ei : global.reducedPatternExludedEdges) {
|
|
// colors_reducedPatternExcludedEdges[ei] = glm::vec3(1, 0, 0);
|
|
// }
|
|
// const std::string label = reducedPattern.getLabel();
|
|
// polyscope::getCurveNetwork(label)
|
|
// ->addEdgeColorQuantity("Excluded edges",
|
|
// colors_reducedPatternExcludedEdges)
|
|
// ->setEnabled(true);
|
|
// polyscope::show();
|
|
|
|
std::vector<glm::vec3> nodeColorsOpposite(fullPattern.VN(), glm::vec3(0, 0, 0));
|
|
for (const std::pair<size_t, size_t> oppositeVerts : fullPatternOppositeInterfaceViPairs) {
|
|
auto color = polyscope::getNextUniqueColor();
|
|
nodeColorsOpposite[oppositeVerts.first] = color;
|
|
nodeColorsOpposite[oppositeVerts.second] = color;
|
|
}
|
|
fullPattern.registerForDrawing();
|
|
polyscope::getCurveNetwork(fullPattern.getLabel())
|
|
->addNodeColorQuantity("oppositeMap", nodeColorsOpposite)
|
|
->setEnabled(true);
|
|
polyscope::show();
|
|
|
|
std::vector<glm::vec3> nodeColorsReducedToFull_reduced(reducedPattern.VN(),
|
|
glm::vec3(0, 0, 0));
|
|
std::vector<glm::vec3> nodeColorsReducedToFull_full(fullPattern.VN(), glm::vec3(0, 0, 0));
|
|
for (size_t vi = 0; vi < reducedPattern.VN(); vi++) {
|
|
if (global.reducedToFullInterfaceViMap.contains(vi)) {
|
|
auto color = polyscope::getNextUniqueColor();
|
|
nodeColorsReducedToFull_reduced[vi] = color;
|
|
nodeColorsReducedToFull_full[global.reducedToFullInterfaceViMap[vi]] = color;
|
|
}
|
|
}
|
|
polyscope::getCurveNetwork(reducedPattern.getLabel())
|
|
->addNodeColorQuantity("reducedToFull_reduced", nodeColorsReducedToFull_reduced)
|
|
->setEnabled(true);
|
|
polyscope::getCurveNetwork(fullPattern.getLabel())
|
|
->addNodeColorQuantity("reducedToFull_full", nodeColorsReducedToFull_full)
|
|
->setEnabled(true);
|
|
polyscope::show();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ReducedModelOptimizer::computeMaps(PatternGeometry &fullPattern,
|
|
PatternGeometry &reducedPattern)
|
|
{
|
|
ReducedModelOptimizer::computeMaps(slotToNode,
|
|
fullPattern,
|
|
reducedPattern,
|
|
global.reducedToFullInterfaceViMap,
|
|
m_fullToReducedInterfaceViMap,
|
|
m_fullPatternOppositeInterfaceViPairs);
|
|
}
|
|
|
|
ReducedModelOptimizer::ReducedModelOptimizer(const std::vector<size_t> &numberOfNodesPerSlot)
|
|
{
|
|
FlatPatternTopology::constructNodeToSlotMap(numberOfNodesPerSlot, nodeToSlot);
|
|
FlatPatternTopology::constructSlotToNodeMap(nodeToSlot, slotToNode);
|
|
interfaceNodeIndex = numberOfNodesPerSlot[0] + numberOfNodesPerSlot[3];
|
|
constructBaseScenarioFunctions.resize(BaseSimulationScenario::NumberOfBaseSimulationScenarios);
|
|
scenarioIsSymmetrical.resize(BaseSimulationScenario::NumberOfBaseSimulationScenarios);
|
|
constructBaseScenarioFunctions[BaseSimulationScenario::Axial] = &constructAxialSimulationScenario;
|
|
scenarioIsSymmetrical[BaseSimulationScenario::Axial] = false;
|
|
constructBaseScenarioFunctions[BaseSimulationScenario::Shear] = &constructShearSimulationScenario;
|
|
scenarioIsSymmetrical[BaseSimulationScenario::Shear] = false;
|
|
constructBaseScenarioFunctions[BaseSimulationScenario::Bending]
|
|
= &constructBendingSimulationScenario;
|
|
scenarioIsSymmetrical[BaseSimulationScenario::Bending] = true;
|
|
constructBaseScenarioFunctions[BaseSimulationScenario::Dome] = &constructDomeSimulationScenario;
|
|
scenarioIsSymmetrical[BaseSimulationScenario::Dome] = true;
|
|
constructBaseScenarioFunctions[BaseSimulationScenario::Saddle]
|
|
= &constructSaddleSimulationScenario;
|
|
scenarioIsSymmetrical[BaseSimulationScenario::Saddle] = true;
|
|
}
|
|
|
|
void ReducedModelOptimizer::initializePatterns(
|
|
PatternGeometry &fullPattern,
|
|
PatternGeometry &reducedPattern,
|
|
const std::array<xRange, NumberOfOptimizationVariables> &optimizationParameters)
|
|
{
|
|
assert(fullPattern.VN() == reducedPattern.VN() && fullPattern.EN() >= reducedPattern.EN());
|
|
|
|
#if POLYSCOPE_DEFINED
|
|
polyscope::removeAllStructures();
|
|
#endif
|
|
// Create copies of the input models
|
|
PatternGeometry copyFullPattern;
|
|
PatternGeometry copyReducedPattern;
|
|
copyFullPattern.copy(fullPattern);
|
|
copyReducedPattern.copy(reducedPattern);
|
|
global.baseTriangle = copyReducedPattern.getBaseTriangle();
|
|
|
|
computeMaps(copyFullPattern, copyReducedPattern);
|
|
createSimulationMeshes(copyFullPattern, copyReducedPattern);
|
|
initializeUpdateReducedPatternFunctions();
|
|
initializeOptimizationParameters(m_pFullPatternSimulationMesh, optimizationParameters);
|
|
}
|
|
|
|
void ReducedModelOptimizer::optimize(ConstPatternGeometry &fullPattern,
|
|
const ReducedModelOptimization::Settings &optimizationSettings,
|
|
ReducedModelOptimization::Results &optimizationResults)
|
|
{
|
|
//scale pattern and reduced model
|
|
fullPattern.scale(optimizationSettings.targetBaseTriangleSize, interfaceNodeIndex);
|
|
ReducedModel reducedModel;
|
|
reducedModel.scale(optimizationSettings.targetBaseTriangleSize, interfaceNodeIndex);
|
|
auto start = std::chrono::system_clock::now();
|
|
optimizationResults.label = fullPattern.getLabel();
|
|
optimizationResults.baseTriangleFullPattern.copy(fullPattern);
|
|
optimizationResults.settings = optimizationSettings;
|
|
initializePatterns(fullPattern, reducedModel, optimizationSettings.variablesRanges);
|
|
optimize(optimizationSettings, optimizationResults);
|
|
auto end = std::chrono::system_clock::now();
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
|
optimizationResults.time = elapsed.count() / 1000.0;
|
|
}
|
|
|
|
void ReducedModelOptimizer::initializeUpdateReducedPatternFunctions()
|
|
{
|
|
global.functions_updateReducedPatternParameter[R] =
|
|
[](const double &newR, std::shared_ptr<SimulationMesh> &pReducedPatternSimulationMesh) {
|
|
const CoordType barycentricCoordinates_hexagonBaseTriangleVertex(1 - newR,
|
|
newR / 2,
|
|
newR / 2);
|
|
const CoordType hexagonBaseTriangleVertexPosition
|
|
= global.baseTriangle.cP(0) * barycentricCoordinates_hexagonBaseTriangleVertex[0]
|
|
+ global.baseTriangle.cP(1) * barycentricCoordinates_hexagonBaseTriangleVertex[1]
|
|
+ global.baseTriangle.cP(2) * barycentricCoordinates_hexagonBaseTriangleVertex[2];
|
|
|
|
for (int rotationCounter = 0; rotationCounter < ReducedModelOptimizer::fanSize;
|
|
rotationCounter++) {
|
|
pReducedPatternSimulationMesh->vert[2 * rotationCounter].P()
|
|
= vcg::RotationMatrix(PatternGeometry::DefaultNormal,
|
|
vcg::math::ToRad(60.0 * rotationCounter))
|
|
* hexagonBaseTriangleVertexPosition;
|
|
}
|
|
};
|
|
|
|
global.functions_updateReducedPatternParameter[Theta] =
|
|
[](const double &newTheta, std::shared_ptr<SimulationMesh> &pReducedPatternSimulationMesh) {
|
|
const CoordType baseTriangleHexagonVertexPosition
|
|
= pReducedPatternSimulationMesh->vert[0].cP();
|
|
|
|
const CoordType thetaRotatedHexagonBaseTriangleVertexPosition
|
|
= vcg::RotationMatrix(PatternGeometry::DefaultNormal, vcg::math::ToRad(newTheta))
|
|
* baseTriangleHexagonVertexPosition;
|
|
|
|
for (int rotationCounter = 0; rotationCounter < ReducedModelOptimizer::fanSize;
|
|
rotationCounter++) {
|
|
pReducedPatternSimulationMesh->vert[2 * rotationCounter].P()
|
|
= vcg::RotationMatrix(PatternGeometry::DefaultNormal,
|
|
vcg::math::ToRad(60.0 * rotationCounter))
|
|
* thetaRotatedHexagonBaseTriangleVertexPosition;
|
|
}
|
|
};
|
|
|
|
global.functions_updateReducedPatternParameter[E] =
|
|
[](const double &newE, std::shared_ptr<SimulationMesh> &pReducedPatternSimulationMesh) {
|
|
for (EdgeIndex ei = 0; ei < pReducedPatternSimulationMesh->EN(); ei++) {
|
|
Element &e = pReducedPatternSimulationMesh->elements[ei];
|
|
e.setMaterial(ElementMaterial(e.material.poissonsRatio, newE));
|
|
}
|
|
};
|
|
global.functions_updateReducedPatternParameter[A] =
|
|
[](const double &newA, std::shared_ptr<SimulationMesh> &pReducedPatternSimulationMesh) {
|
|
assert(pReducedPatternSimulationMesh != nullptr);
|
|
const double beamWidth = std::sqrt(newA);
|
|
const double beamHeight = beamWidth;
|
|
for (EdgeIndex ei = 0; ei < pReducedPatternSimulationMesh->EN(); ei++) {
|
|
Element &e = pReducedPatternSimulationMesh->elements[ei];
|
|
e.setDimensions(RectangularBeamDimensions(beamWidth, beamHeight));
|
|
}
|
|
};
|
|
|
|
global.functions_updateReducedPatternParameter[I2] =
|
|
[](const double &newI2, std::shared_ptr<SimulationMesh> &pReducedPatternSimulationMesh) {
|
|
for (EdgeIndex ei = 0; ei < pReducedPatternSimulationMesh->EN(); ei++) {
|
|
Element &e = pReducedPatternSimulationMesh->elements[ei];
|
|
e.dimensions.inertia.I2 = newI2;
|
|
e.updateRigidity();
|
|
}
|
|
};
|
|
global.functions_updateReducedPatternParameter[I3] =
|
|
[](const double &newI3, std::shared_ptr<SimulationMesh> &pReducedPatternSimulationMesh) {
|
|
for (EdgeIndex ei = 0; ei < pReducedPatternSimulationMesh->EN(); ei++) {
|
|
Element &e = pReducedPatternSimulationMesh->elements[ei];
|
|
e.dimensions.inertia.I3 = newI3;
|
|
e.updateRigidity();
|
|
}
|
|
};
|
|
global.functions_updateReducedPatternParameter[J] =
|
|
[](const double &newJ, std::shared_ptr<SimulationMesh> &pReducedPatternSimulationMesh) {
|
|
for (EdgeIndex ei = 0; ei < pReducedPatternSimulationMesh->EN(); ei++) {
|
|
Element &e = pReducedPatternSimulationMesh->elements[ei];
|
|
e.dimensions.inertia.J = newJ;
|
|
e.updateRigidity();
|
|
}
|
|
};
|
|
}
|
|
|
|
void ReducedModelOptimizer::initializeOptimizationParameters(
|
|
const std::shared_ptr<SimulationMesh> &mesh,
|
|
const std::array<ReducedModelOptimization::xRange,
|
|
ReducedModelOptimization::NumberOfOptimizationVariables> &optimizationParameters)
|
|
{
|
|
for (int optimizationParameterIndex = 0;
|
|
optimizationParameterIndex < optimizationParameters.size();
|
|
optimizationParameterIndex++) {
|
|
for (int optimizationParameterIndex = E;
|
|
optimizationParameterIndex != NumberOfOptimizationVariables;
|
|
optimizationParameterIndex++) {
|
|
// const xRange &xOptimizationParameter = optimizationParameters[optimizationParameterIndex];
|
|
switch (optimizationParameterIndex) {
|
|
case E:
|
|
global.parametersInitialValue[optimizationParameterIndex]
|
|
= mesh->elements[0].material.youngsModulus;
|
|
global.optimizationInitialValue[optimizationParameterIndex] = 1;
|
|
break;
|
|
case A:
|
|
global.parametersInitialValue[optimizationParameterIndex] = mesh->elements[0]
|
|
.dimensions.A;
|
|
global.optimizationInitialValue[optimizationParameterIndex] = 1;
|
|
break;
|
|
case I2:
|
|
global.parametersInitialValue[optimizationParameterIndex]
|
|
= mesh->elements[0].dimensions.inertia.I2;
|
|
global.optimizationInitialValue[optimizationParameterIndex] = 1;
|
|
break;
|
|
case I3:
|
|
global.parametersInitialValue[optimizationParameterIndex]
|
|
= mesh->elements[0].dimensions.inertia.I3;
|
|
global.optimizationInitialValue[optimizationParameterIndex] = 1;
|
|
break;
|
|
case J:
|
|
global.parametersInitialValue[optimizationParameterIndex]
|
|
= mesh->elements[0].dimensions.inertia.J;
|
|
global.optimizationInitialValue[optimizationParameterIndex] = 1;
|
|
break;
|
|
case R:
|
|
global.parametersInitialValue[optimizationParameterIndex] = 0;
|
|
global.optimizationInitialValue[optimizationParameterIndex] = 0.5;
|
|
break;
|
|
case Theta:
|
|
global.parametersInitialValue[optimizationParameterIndex] = 0;
|
|
global.optimizationInitialValue[optimizationParameterIndex] = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReducedModelOptimizer::computeReducedModelSimulationJob(
|
|
const SimulationJob &simulationJobOfFullModel,
|
|
const std::unordered_map<size_t, size_t> &fullToReducedMap,
|
|
SimulationJob &simulationJobOfReducedModel)
|
|
{
|
|
assert(simulationJobOfReducedModel.pMesh->VN() != 0);
|
|
std::unordered_map<VertexIndex, std::unordered_set<DoFType>> reducedModelFixedVertices;
|
|
for (auto fullModelFixedVertex : simulationJobOfFullModel.constrainedVertices) {
|
|
reducedModelFixedVertices[fullToReducedMap.at(fullModelFixedVertex.first)]
|
|
= fullModelFixedVertex.second;
|
|
}
|
|
|
|
std::unordered_map<VertexIndex, Vector6d> reducedModelNodalForces;
|
|
for (auto fullModelNodalForce : simulationJobOfFullModel.nodalExternalForces) {
|
|
reducedModelNodalForces[fullToReducedMap.at(fullModelNodalForce.first)]
|
|
= fullModelNodalForce.second;
|
|
}
|
|
|
|
std::unordered_map<VertexIndex, Eigen::Vector3d> reducedNodalForcedDisplacements;
|
|
for (auto fullForcedDisplacement : simulationJobOfFullModel.nodalForcedDisplacements) {
|
|
reducedNodalForcedDisplacements[fullToReducedMap.at(fullForcedDisplacement.first)]
|
|
= fullForcedDisplacement.second;
|
|
}
|
|
|
|
simulationJobOfReducedModel.constrainedVertices = reducedModelFixedVertices;
|
|
simulationJobOfReducedModel.nodalExternalForces = reducedModelNodalForces;
|
|
simulationJobOfReducedModel.label = simulationJobOfFullModel.getLabel();
|
|
simulationJobOfReducedModel.nodalForcedDisplacements = reducedNodalForcedDisplacements;
|
|
}
|
|
|
|
void ReducedModelOptimizer::computeDesiredReducedModelDisplacements(
|
|
const SimulationResults &fullModelResults,
|
|
const std::unordered_map<size_t, size_t> &displacementsReducedToFullMap,
|
|
Eigen::MatrixX3d &optimalDisplacementsOfReducedModel)
|
|
{
|
|
assert(optimalDisplacementsOfReducedModel.rows() != 0
|
|
&& optimalDisplacementsOfReducedModel.cols() == 3);
|
|
optimalDisplacementsOfReducedModel.setZero(optimalDisplacementsOfReducedModel.rows(),
|
|
optimalDisplacementsOfReducedModel.cols());
|
|
|
|
for (auto reducedFullViPair : displacementsReducedToFullMap) {
|
|
const VertexIndex fullModelVi = reducedFullViPair.second;
|
|
const Vector6d fullModelViDisplacements = fullModelResults.displacements[fullModelVi];
|
|
optimalDisplacementsOfReducedModel.row(reducedFullViPair.first)
|
|
= Eigen::Vector3d(fullModelViDisplacements[0],
|
|
fullModelViDisplacements[1],
|
|
fullModelViDisplacements[2]);
|
|
}
|
|
}
|
|
|
|
void ReducedModelOptimizer::getResults(const FunctionEvaluation &optimalObjective,
|
|
const Settings &settings,
|
|
ReducedModelOptimization::Results &results)
|
|
{
|
|
std::shared_ptr<SimulationMesh> &pReducedPatternSimulationMesh
|
|
= global.reducedPatternSimulationJobs[global.simulationScenarioIndices[0]]->pMesh;
|
|
//Number of crashes
|
|
// results.numberOfSimulationCrashes = global.numOfSimulationCrashes;
|
|
//Value of optimal objective Y
|
|
results.objectiveValue.total = optimalObjective.y;
|
|
// results.objectiveValue.total = 0;
|
|
if (optimalObjective.y != global.minY) {
|
|
std::cout << "Different optimal y:" << optimalObjective.y << " " << global.minY
|
|
<< std::endl;
|
|
}
|
|
//Optimal X values
|
|
results.optimalXNameValuePairs.resize(NumberOfOptimizationVariables);
|
|
for (int optimizationParameterIndex = E;
|
|
optimizationParameterIndex != NumberOfOptimizationVariables;
|
|
optimizationParameterIndex++) {
|
|
if (optimizationParameterIndex != OptimizationParameterIndex::R
|
|
&& optimizationParameterIndex != OptimizationParameterIndex::Theta) {
|
|
results.optimalXNameValuePairs[optimizationParameterIndex]
|
|
= std::make_pair(settings.variablesRanges[optimizationParameterIndex].label,
|
|
optimalObjective.x[optimizationParameterIndex]
|
|
* global.parametersInitialValue[optimizationParameterIndex]);
|
|
} else {
|
|
//Hex size and angle are pure values (not multipliers of the initial values)
|
|
results.optimalXNameValuePairs[optimizationParameterIndex]
|
|
= std::make_pair(settings.variablesRanges[optimizationParameterIndex].label,
|
|
optimalObjective.x[optimizationParameterIndex]);
|
|
}
|
|
|
|
global.functions_updateReducedPatternParameter[optimizationParameterIndex](
|
|
results.optimalXNameValuePairs[optimizationParameterIndex].second,
|
|
pReducedPatternSimulationMesh);
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << results.optimalXNameValuePairs[optimizationParameterIndex].first << ":"
|
|
<< results.optimalXNameValuePairs[optimizationParameterIndex].second << " ";
|
|
#endif
|
|
}
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << std::endl;
|
|
#endif
|
|
pReducedPatternSimulationMesh->reset();
|
|
|
|
results.fullPatternYoungsModulus = youngsModulus;
|
|
// Compute obj value per simulation scenario and the raw objective value
|
|
// updateMeshFunction(optimalX);
|
|
// results.objectiveValue.totalPerSimulationScenario.resize(totalNumberOfSimulationScenarios);
|
|
//TODO:use push_back it will make the code more readable
|
|
LinearSimulationModel simulator;
|
|
results.objectiveValue.totalRaw = 0;
|
|
results.objectiveValue.perSimulationScenario_translational.resize(
|
|
global.simulationScenarioIndices.size());
|
|
results.objectiveValue.perSimulationScenario_rawTranslational.resize(
|
|
global.simulationScenarioIndices.size());
|
|
results.objectiveValue.perSimulationScenario_rotational.resize(
|
|
global.simulationScenarioIndices.size());
|
|
results.objectiveValue.perSimulationScenario_rawRotational.resize(
|
|
global.simulationScenarioIndices.size());
|
|
results.objectiveValue.perSimulationScenario_total.resize(
|
|
global.simulationScenarioIndices.size());
|
|
results.objectiveValue.perSimulationScenario_total_unweighted.resize(
|
|
global.simulationScenarioIndices.size());
|
|
//#ifdef POLYSCOPE_DEFINED
|
|
// global.pFullPatternSimulationMesh->registerForDrawing(Colors::fullDeformed);
|
|
//#endif
|
|
|
|
results.perScenario_fullPatternPotentialEnergy.resize(global.simulationScenarioIndices.size());
|
|
for (int i = 0; i < global.simulationScenarioIndices.size(); i++) {
|
|
const int simulationScenarioIndex = global.simulationScenarioIndices[i];
|
|
SimulationResults reducedModelResults = simulator.executeSimulation(
|
|
global.reducedPatternSimulationJobs[simulationScenarioIndex]);
|
|
|
|
#ifdef POLYSCOPE_DEFINED
|
|
#ifdef USE_SCENARIO_WEIGHTS
|
|
const double scenarioWeight = global.scenarioWeights[simulationScenarioIndex];
|
|
#else
|
|
const double scenarioWeight = 1;
|
|
#endif
|
|
#else
|
|
const double scenarioWeight = 1;
|
|
#endif
|
|
results.objectiveValue.perSimulationScenario_total[i] = computeError(
|
|
global.fullPatternResults[simulationScenarioIndex],
|
|
reducedModelResults,
|
|
global.reducedToFullInterfaceViMap,
|
|
global.translationalDisplacementNormalizationValues[simulationScenarioIndex],
|
|
global.rotationalDisplacementNormalizationValues[simulationScenarioIndex],
|
|
scenarioWeight,
|
|
global.objectiveWeights[simulationScenarioIndex]);
|
|
|
|
results.objectiveValue.perSimulationScenario_total_unweighted[i] = computeError(
|
|
global.fullPatternResults[simulationScenarioIndex],
|
|
reducedModelResults,
|
|
global.reducedToFullInterfaceViMap,
|
|
global.translationalDisplacementNormalizationValues[simulationScenarioIndex],
|
|
global.rotationalDisplacementNormalizationValues[simulationScenarioIndex],
|
|
1,
|
|
global.objectiveWeights[simulationScenarioIndex]);
|
|
|
|
// results.objectiveValue.total += results.objectiveValue.perSimulationScenario_total[i];
|
|
//Raw translational
|
|
const double rawTranslationalError = computeRawTranslationalError(
|
|
global.fullPatternResults[simulationScenarioIndex].displacements,
|
|
reducedModelResults.displacements,
|
|
global.reducedToFullInterfaceViMap);
|
|
results.objectiveValue.perSimulationScenario_rawTranslational[i] = rawTranslationalError;
|
|
//Raw rotational
|
|
const double rawRotationalError = computeRawRotationalError(
|
|
global.fullPatternResults[simulationScenarioIndex].rotationalDisplacementQuaternion,
|
|
reducedModelResults.rotationalDisplacementQuaternion,
|
|
global.reducedToFullInterfaceViMap);
|
|
results.objectiveValue.perSimulationScenario_rawRotational[i] = rawRotationalError;
|
|
|
|
//Normalized translational
|
|
const double normalizedTranslationalError = computeDisplacementError(
|
|
global.fullPatternResults[simulationScenarioIndex].displacements,
|
|
reducedModelResults.displacements,
|
|
global.reducedToFullInterfaceViMap,
|
|
global.translationalDisplacementNormalizationValues[simulationScenarioIndex]);
|
|
results.objectiveValue.perSimulationScenario_translational[i] = normalizedTranslationalError;
|
|
// const double test_normalizedTranslationError = computeDisplacementError(
|
|
// global.fullPatternResults[simulationScenarioIndex].displacements,
|
|
// reducedModelResults.displacements,
|
|
// global.reducedToFullInterfaceViMap,
|
|
// global.translationalDisplacementNormalizationValues[simulationScenarioIndex]);
|
|
//Normalized rotational
|
|
const double normalizedRotationalError = computeRotationalError(
|
|
global.fullPatternResults[simulationScenarioIndex].rotationalDisplacementQuaternion,
|
|
reducedModelResults.rotationalDisplacementQuaternion,
|
|
global.reducedToFullInterfaceViMap,
|
|
global.rotationalDisplacementNormalizationValues[simulationScenarioIndex]);
|
|
results.objectiveValue.perSimulationScenario_rotational[i] = normalizedRotationalError;
|
|
// const double test_normalizedRotationalError = computeRotationalError(
|
|
// global.fullPatternResults[simulationScenarioIndex].rotationalDisplacementQuaternion,
|
|
// reducedModelResults.rotationalDisplacementQuaternion,
|
|
// global.reducedToFullInterfaceViMap,
|
|
// global.rotationalDisplacementNormalizationValues[simulationScenarioIndex]);
|
|
// assert(test_normalizedTranslationError == normalizedTranslationalError);
|
|
// assert(test_normalizedRotationalError == normalizedRotationalError);
|
|
results.objectiveValue.totalRaw += rawTranslationalError + rawRotationalError;
|
|
results.perScenario_fullPatternPotentialEnergy[i]
|
|
= global.fullPatternResults[simulationScenarioIndex].internalPotentialEnergy;
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << "Simulation scenario:"
|
|
<< global.reducedPatternSimulationJobs[simulationScenarioIndex]->getLabel()
|
|
<< std::endl;
|
|
std::cout << "raw translational error:" << rawTranslationalError << std::endl;
|
|
std::cout << "translation normalization value:"
|
|
<< global.translationalDisplacementNormalizationValues[simulationScenarioIndex]
|
|
<< std::endl;
|
|
std::cout << "raw Rotational error:" << rawRotationalError << std::endl;
|
|
std::cout << "rotational normalization value:"
|
|
<< global.rotationalDisplacementNormalizationValues[simulationScenarioIndex]
|
|
<< std::endl;
|
|
std::cout << "Translational error:" << normalizedTranslationalError << std::endl;
|
|
std::cout << "Rotational error:" << normalizedRotationalError << std::endl;
|
|
// results.objectiveValuePerSimulationScenario[simulationScenarioIndex]
|
|
// = normalizedTranslationalError + normalizedRotationalError;
|
|
std::cout << "Total Error value:" << results.objectiveValue.perSimulationScenario_total[i]
|
|
<< std::endl;
|
|
std::cout << std::endl;
|
|
|
|
// reducedModelResults.registerForDrawing(Colors::reducedDeformed);
|
|
// global.fullPatternResults[simulationScenarioIndex].registerForDrawing(Colors::fullDeformed);
|
|
// polyscope::show();
|
|
// reducedModelResults.unregister();
|
|
// global.fullPatternResults[simulationScenarioIndex].unregister();
|
|
|
|
#endif
|
|
}
|
|
|
|
for (int simulationScenarioIndex : global.simulationScenarioIndices) {
|
|
results.fullPatternSimulationJobs.push_back(
|
|
global.fullPatternSimulationJobs[simulationScenarioIndex]);
|
|
results.reducedPatternSimulationJobs.push_back(
|
|
global.reducedPatternSimulationJobs[simulationScenarioIndex]);
|
|
// const std::string temp = global.reducedPatternSimulationJobs[simulationScenarioIndex]
|
|
// ->pMesh->getLabel();
|
|
// global.reducedPatternSimulationJobs[simulationScenarioIndex]->pMesh->setLabel("temp");
|
|
// global.reducedPatternSimulationJobs[simulationScenarioIndex]->pMesh->registerForDrawing();
|
|
// global.reducedPatternSimulationJobs[simulationScenarioIndex]->pMesh->setLabel(temp);
|
|
}
|
|
results.objectiveValueHistory = global.objectiveValueHistory;
|
|
results.objectiveValueHistory_iteration = global.objectiveValueHistory_iteration;
|
|
// results.draw();
|
|
}
|
|
|
|
std::array<double, NumberOfBaseSimulationScenarios>
|
|
ReducedModelOptimizer::computeFullPatternMaxSimulationForces(
|
|
const std::vector<BaseSimulationScenario> &desiredBaseSimulationScenario) const
|
|
{
|
|
std::array<double, NumberOfBaseSimulationScenarios> fullPatternMaxSimulationForces;
|
|
for (const BaseSimulationScenario &scenario : desiredBaseSimulationScenario) {
|
|
const double maxForce = computeFullPatternMaxSimulationForce(scenario);
|
|
fullPatternMaxSimulationForces[scenario] = maxForce;
|
|
}
|
|
|
|
return fullPatternMaxSimulationForces;
|
|
}
|
|
|
|
std::array<double, NumberOfBaseSimulationScenarios>
|
|
ReducedModelOptimizer::getFullPatternMaxSimulationForces(
|
|
const std::vector<BaseSimulationScenario> &desiredBaseSimulationScenarioIndices,
|
|
const std::filesystem::path &intermediateResultsDirectoryPath,
|
|
const bool &recomputeForceMagnitudes)
|
|
{
|
|
std::array<double, NumberOfBaseSimulationScenarios> fullPatternSimulationScenarioMaxMagnitudes;
|
|
//#ifdef POLYSCOPE_DEFINED
|
|
const std::filesystem::path forceMagnitudesDirectoryPath(
|
|
std::filesystem::path(intermediateResultsDirectoryPath).append("ForceMagnitudes"));
|
|
std::filesystem::path patternMaxForceMagnitudesFilePath(
|
|
std::filesystem::path(forceMagnitudesDirectoryPath)
|
|
.append(m_pFullPatternSimulationMesh->getLabel() + ".json"));
|
|
const bool fullPatternScenarioMagnitudesExist = std::filesystem::exists(
|
|
patternMaxForceMagnitudesFilePath);
|
|
if (fullPatternScenarioMagnitudesExist && !recomputeForceMagnitudes) {
|
|
nlohmann::json json;
|
|
std::ifstream ifs(patternMaxForceMagnitudesFilePath.string());
|
|
ifs >> json;
|
|
fullPatternSimulationScenarioMaxMagnitudes
|
|
= static_cast<std::array<double, NumberOfBaseSimulationScenarios>>(json.at("maxMagn"));
|
|
return fullPatternSimulationScenarioMaxMagnitudes;
|
|
}
|
|
|
|
//#endif
|
|
fullPatternSimulationScenarioMaxMagnitudes = computeFullPatternMaxSimulationForces(
|
|
desiredBaseSimulationScenarioIndices);
|
|
|
|
//#ifdef POLYSCOPE_DEFINED
|
|
nlohmann::json json;
|
|
json["maxMagn"] = fullPatternSimulationScenarioMaxMagnitudes;
|
|
|
|
std::filesystem::create_directories(forceMagnitudesDirectoryPath);
|
|
std::ofstream jsonFile(patternMaxForceMagnitudesFilePath.string());
|
|
jsonFile << json;
|
|
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << "Saved base scenario max magnitudes to:" << patternMaxForceMagnitudesFilePath
|
|
<< std::endl;
|
|
#endif
|
|
|
|
//#endif
|
|
assert(fullPatternSimulationScenarioMaxMagnitudes.size()
|
|
== desiredBaseSimulationScenarioIndices.size());
|
|
|
|
return fullPatternSimulationScenarioMaxMagnitudes;
|
|
}
|
|
|
|
void ReducedModelOptimizer::runOptimization(const Settings &settings,
|
|
ReducedModelOptimization::Results &results)
|
|
{
|
|
global.objectiveValueHistory.clear();
|
|
global.objectiveValueHistory_iteration.clear();
|
|
global.objectiveValueHistory.reserve(settings.numberOfFunctionCalls / 100);
|
|
global.objectiveValueHistory_iteration.reserve(settings.numberOfFunctionCalls / 100);
|
|
|
|
#if POLYSCOPE_DEFINED
|
|
// global.plotColors.reserve(settings.numberOfFunctionCalls);
|
|
#endif
|
|
assert(!settings.optimizationStrategy.empty());
|
|
const std::vector<std::vector<OptimizationParameterIndex>> &optimizationParametersGroups
|
|
= settings.optimizationStrategy;
|
|
|
|
#ifndef USE_ENSMALLEN
|
|
const int totalNumberOfOptimizationParameters
|
|
= std::accumulate(optimizationParametersGroups.begin(),
|
|
optimizationParametersGroups.end(),
|
|
0,
|
|
[](const int &sum,
|
|
const std::vector<OptimizationParameterIndex> ¶meterGroup) {
|
|
return sum + parameterGroup.size();
|
|
});
|
|
#endif
|
|
|
|
FunctionEvaluation optimization_optimalResult;
|
|
optimization_optimalResult.x.resize(NumberOfOptimizationVariables, 0);
|
|
for (int optimizationParameterIndex = E;
|
|
optimizationParameterIndex != NumberOfOptimizationVariables;
|
|
optimizationParameterIndex++) {
|
|
optimization_optimalResult.x[optimizationParameterIndex]
|
|
= global.optimizationInitialValue[optimizationParameterIndex];
|
|
}
|
|
for (int parameterGroupIndex=0;parameterGroupIndex<optimizationParametersGroups.size();parameterGroupIndex++) {
|
|
const std::vector<OptimizationParameterIndex> ¶meterGroup =
|
|
optimizationParametersGroups[parameterGroupIndex];
|
|
FunctionEvaluation parameterGroup_optimalResult;
|
|
//Set update function. TODO: Make this function immutable by defining it once and using the global variable to set parameterGroup
|
|
function_updateReducedPattern = [&](const std::vector<double> &x,
|
|
std::shared_ptr<SimulationMesh> &pMesh) {
|
|
for (int xIndex = 0; xIndex < parameterGroup.size(); xIndex++) {
|
|
const OptimizationParameterIndex parameterIndex = parameterGroup[xIndex];
|
|
// const double parameterInitialValue=optimizationSettings.parameterRanges[parameterIndex].initialValue;
|
|
const double parameterNewValue = [&]() {
|
|
if (parameterIndex == R || parameterIndex == Theta) {
|
|
return x[xIndex] /*+ parameterInitialValue*/;
|
|
}
|
|
//and the material parameters exponentially(?).TODO: Check what happens if I make all linear
|
|
const double parameterInitialValue
|
|
= global.parametersInitialValue[parameterIndex];
|
|
return x[xIndex] * parameterInitialValue;
|
|
}();
|
|
// std::cout << "Optimization parameter:" << parameterIndex << std::endl;
|
|
// std::cout << "New value:" << parameterNewValue << std::endl;
|
|
global.functions_updateReducedPatternParameter[parameterIndex](parameterNewValue,
|
|
pMesh);
|
|
}
|
|
pMesh->reset(); //NOTE: I could put this code into each updateParameter function for avoiding unessecary calculations
|
|
};
|
|
|
|
std::vector<double> xMin;
|
|
std::vector<double> xMax;
|
|
xMin.resize(parameterGroup.size());
|
|
xMax.resize(parameterGroup.size());
|
|
for (int xIndex = 0; xIndex < parameterGroup.size(); xIndex++) {
|
|
const OptimizationParameterIndex parameterIndex = parameterGroup[xIndex];
|
|
|
|
xMin[xIndex] = settings.variablesRanges[parameterIndex].min;
|
|
xMax[xIndex] = settings.variablesRanges[parameterIndex].max;
|
|
}
|
|
#ifdef USE_ENSMALLEN
|
|
arma::mat x(parameterGroup.size(), 1);
|
|
for (int xIndex = 0; xIndex < parameterGroup.size(); xIndex++) {
|
|
const OptimizationParameterIndex parameterIndex = parameterGroup[xIndex];
|
|
|
|
x(xIndex, 0) = global.optimizationInitialValue[parameterIndex];
|
|
}
|
|
|
|
global.xMin = xMin;
|
|
global.xMax = xMax;
|
|
|
|
// Create simulated annealing optimizer with default options.
|
|
// The ens::SA<> type can be replaced with any suitable ensmallen optimizer
|
|
// that is able to handle arbitrary functions.
|
|
EnsmallenOptimizationObjective optimizationFunction;
|
|
//Set min max values
|
|
// ens::SA optimizer;
|
|
// ens::CNE optimizer;
|
|
// ens::DE optimizer;
|
|
// ens::SPSA optimizer;
|
|
arma::mat xMin_arma(global.xMin);
|
|
arma::mat xMax_arma(global.xMax);
|
|
// ens::LBestPSO optimizer(64, xMin_arma, xMax_arma, 1000);
|
|
ens::LBestPSO optimizer(100,
|
|
xMin_arma,
|
|
xMax_arma,
|
|
settings.numberOfFunctionCalls,
|
|
500,
|
|
settings.solverAccuracy,
|
|
2.05,
|
|
2.35);
|
|
// ens::LBestPSO optimizer;
|
|
const double minima = optimizer.Optimize(optimizationFunction, x);
|
|
// for (int xIndex = 0; xIndex < parameterGroup.size(); xIndex++) {
|
|
// if (x[xIndex] > global.xMax[xIndex]) {
|
|
// x[xIndex] = global.xMax[xIndex];
|
|
// } else if (x[xIndex] < global.xMin[xIndex]) {
|
|
// x[xIndex] = global.xMin[xIndex];
|
|
// }
|
|
// }
|
|
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << "optimal x:"
|
|
<< "\n"
|
|
<< x << std::endl;
|
|
std::cout << "Minima:" << minima << std::endl;
|
|
std::cout << "optimal objective:" << optimizationFunction.Evaluate(x) << std::endl;
|
|
#endif
|
|
parameterGroup_optimalResult.x.clear();
|
|
parameterGroup_optimalResult.x.resize(parameterGroup.size());
|
|
std::copy(x.begin(), x.end(), parameterGroup_optimalResult.x.begin());
|
|
parameterGroup_optimalResult.y = minima;
|
|
#else
|
|
//Set min max values
|
|
dlib::matrix<double, 0, 1> xMin_dlib(parameterGroup.size());
|
|
dlib::matrix<double, 0, 1> xMax_dlib(parameterGroup.size());
|
|
for (int xIndex = 0; xIndex < parameterGroup.size(); xIndex++) {
|
|
const OptimizationParameterIndex parameterIndex = parameterGroup[xIndex];
|
|
|
|
xMin_dlib(xIndex) = settings.variablesRanges[parameterIndex].min;
|
|
xMax_dlib(xIndex) = settings.variablesRanges[parameterIndex].max;
|
|
}
|
|
const double portionOfTotalFunctionCallsBudget = [&]() {
|
|
if (settings.optimizationVariablesGroupsWeights.has_value()) {
|
|
assert(settings.optimizationVariablesGroupsWeights->size()
|
|
== settings.optimizationStrategy.size());
|
|
return settings.optimizationVariablesGroupsWeights.value()[parameterGroupIndex];
|
|
|
|
} else {
|
|
return static_cast<double>(parameterGroup.size())
|
|
/ totalNumberOfOptimizationParameters;
|
|
}
|
|
}();
|
|
const int optimizationNumberOfFunctionCalls = portionOfTotalFunctionCallsBudget
|
|
* settings.numberOfFunctionCalls;
|
|
const dlib::function_evaluation optimalResult_dlib = [&]() {
|
|
if (parameterGroup.size() == 1) {
|
|
dlib::function_evaluation result;
|
|
double optimalX = global.optimizationInitialValue[parameterGroup[0]];
|
|
double (*singleVariableObjective)(const double &) = &objective;
|
|
try {
|
|
result.y = dlib::find_min_single_variable(singleVariableObjective,
|
|
optimalX,
|
|
xMin_dlib(0),
|
|
xMax_dlib(0),
|
|
1e-5,
|
|
optimizationNumberOfFunctionCalls);
|
|
} catch (const std::exception &e) { // reference to the base of a polymorphic object
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << e.what() << std::endl;
|
|
#endif
|
|
}
|
|
|
|
result.x.set_size(1);
|
|
result.x(0) = optimalX;
|
|
return result;
|
|
}
|
|
double (*objF)(const dlib::matrix<double, 0, 1> &) = &objective;
|
|
return dlib::find_min_global(objF,
|
|
xMin_dlib,
|
|
xMax_dlib,
|
|
dlib::max_function_calls(optimizationNumberOfFunctionCalls),
|
|
std::chrono::hours(24 * 365 * 290),
|
|
settings.solverAccuracy);
|
|
}();
|
|
// constexpr bool useBOBYQA = false;
|
|
// if (useBOBYQA) {
|
|
// const size_t npt = 2 * global.numberOfOptimizationParameters;
|
|
// // ((n + 2) + ((n + 1) * (n + 2) / 2)) / 2;
|
|
// const double rhobeg = 0.1;
|
|
// // const double rhobeg = 10;
|
|
// const double rhoend = rhobeg * 1e-6;
|
|
// // const size_t maxFun = 10 * (x.size() ^ 2);
|
|
// const size_t bobyqa_maxFunctionCalls = 10000;
|
|
// dlib::matrix<double, 1, 0> x;
|
|
// dlib::function_evaluation optimalResult_dlib;
|
|
// optimalResult_dlib.x.set_size(parameterGroup.size());
|
|
// for (int xIndex = 0; xIndex < parameterGroup.size(); xIndex++) {
|
|
// const OptimizationParameterIndex parameterIndex = parameterGroup[xIndex];
|
|
// optimalResult_dlib.x(xIndex) = global.optimizationInitialValue[parameterIndex];
|
|
|
|
// std::cout << "xIndex:" << xIndex << std::endl;
|
|
// std::cout << "xInit:" << optimalResult_dlib.x(xIndex) << std::endl;
|
|
// }
|
|
|
|
// optimalResult_dlib.y = dlib::find_min_bobyqa(objF,
|
|
// optimalResult_dlib.x,
|
|
// npt,
|
|
// xMin,
|
|
// xMax,
|
|
// rhobeg,
|
|
// rhoend,
|
|
// bobyqa_maxFunctionCalls);
|
|
// }
|
|
|
|
parameterGroup_optimalResult.x.clear();
|
|
parameterGroup_optimalResult.x.resize(parameterGroup.size());
|
|
std::copy(optimalResult_dlib.x.begin(),
|
|
optimalResult_dlib.x.end(),
|
|
parameterGroup_optimalResult.x.begin());
|
|
parameterGroup_optimalResult.y = optimalResult_dlib.y;
|
|
|
|
#endif
|
|
|
|
const auto firstNonNullReducedSimulationJobIt
|
|
= std::find_if(global.reducedPatternSimulationJobs.begin(),
|
|
global.reducedPatternSimulationJobs.end(),
|
|
[](const std::shared_ptr<SimulationJob> &pJob) {
|
|
return pJob != nullptr;
|
|
});
|
|
function_updateReducedPattern(
|
|
std::vector<double>(parameterGroup_optimalResult.x.begin(),
|
|
parameterGroup_optimalResult.x.end()),
|
|
(*firstNonNullReducedSimulationJobIt)
|
|
->pMesh); //TODO: Check if its ok to update only the changed parameters
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << "Optimal x:"
|
|
<< "\n";
|
|
for (const auto &x : parameterGroup_optimalResult.x) {
|
|
std::cout << x << std::endl;
|
|
}
|
|
std::cout << "optimal objective:" << objective(parameterGroup_optimalResult.x) << std::endl;
|
|
// std::cout << "Minima:" << minima << std::endl;
|
|
std::cout << "GLOBAL MIN:" << global.minY << std::endl;
|
|
std::cout << "opt res y:" << parameterGroup_optimalResult.y << std::endl;
|
|
#endif
|
|
|
|
optimization_optimalResult.y=parameterGroup_optimalResult.y;
|
|
for (int xIndex = 0; xIndex < parameterGroup.size(); xIndex++) {
|
|
const OptimizationParameterIndex parameterIndex = parameterGroup[xIndex];
|
|
optimization_optimalResult.x[parameterIndex] = parameterGroup_optimalResult.x[xIndex];
|
|
}
|
|
}
|
|
getResults(optimization_optimalResult, settings, results);
|
|
}
|
|
|
|
void ReducedModelOptimizer::constructAxialSimulationScenario(
|
|
const double &forceMagnitude,
|
|
const std::vector<std::pair<FullPatternVertexIndex, FullPatternVertexIndex>>
|
|
&oppositeInterfaceViPairs,
|
|
SimulationJob &job)
|
|
{
|
|
for (auto viPairIt = oppositeInterfaceViPairs.begin();
|
|
viPairIt != oppositeInterfaceViPairs.end();
|
|
viPairIt++) {
|
|
if (viPairIt != oppositeInterfaceViPairs.begin()) {
|
|
CoordType forceDirection(1, 0, 0);
|
|
job.nodalExternalForces[viPairIt->first]
|
|
= Vector6d({forceDirection[0], forceDirection[1], forceDirection[2], 0, 0, 0})
|
|
* forceMagnitude;
|
|
job.constrainedVertices[viPairIt->second] = std::unordered_set<DoFType>{0, 1, 2, 3, 4, 5};
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReducedModelOptimizer::constructShearSimulationScenario(
|
|
const double &forceMagnitude,
|
|
const std::vector<std::pair<FullPatternVertexIndex, FullPatternVertexIndex>>
|
|
&oppositeInterfaceViPairs,
|
|
SimulationJob &job)
|
|
{
|
|
for (auto viPairIt = oppositeInterfaceViPairs.begin();
|
|
viPairIt != oppositeInterfaceViPairs.end();
|
|
viPairIt++) {
|
|
if (viPairIt != oppositeInterfaceViPairs.begin()) {
|
|
CoordType forceDirection(0, 1, 0);
|
|
const auto viPair = *viPairIt;
|
|
job.nodalExternalForces[viPair.first]
|
|
= Vector6d({forceDirection[0], forceDirection[1], forceDirection[2], 0, 0, 0})
|
|
* forceMagnitude;
|
|
job.constrainedVertices[viPair.second] = std::unordered_set<DoFType>{0, 1, 2, 3, 4, 5};
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReducedModelOptimizer::constructBendingSimulationScenario(
|
|
const double &forceMagnitude,
|
|
const std::vector<std::pair<FullPatternVertexIndex, FullPatternVertexIndex>>
|
|
&oppositeInterfaceViPairs,
|
|
SimulationJob &job)
|
|
{
|
|
for (const auto &viPair : oppositeInterfaceViPairs) {
|
|
job.nodalExternalForces[viPair.first] = Vector6d({0, 0, 1, 0, 0, 0}) * forceMagnitude;
|
|
job.constrainedVertices[viPair.second] = std::unordered_set<DoFType>{0, 1, 2, 3, 4, 5};
|
|
}
|
|
}
|
|
|
|
/*NOTE: From the results it seems as if the forced displacements are different in the linear and in the drm
|
|
* */
|
|
void ReducedModelOptimizer::constructDomeSimulationScenario(
|
|
const double &forceMagnitude,
|
|
const std::vector<std::pair<FullPatternVertexIndex, FullPatternVertexIndex>>
|
|
&oppositeInterfaceViPairs,
|
|
SimulationJob &job)
|
|
{
|
|
for (auto viPairIt = oppositeInterfaceViPairs.begin();
|
|
viPairIt != oppositeInterfaceViPairs.end();
|
|
viPairIt++) {
|
|
const auto viPair = *viPairIt;
|
|
CoordType interfaceVector = (job.pMesh->vert[viPair.first].cP()
|
|
- job.pMesh->vert[viPair.second].cP());
|
|
VectorType momentAxis = vcg::RotationMatrix(VectorType(0, 0, 1), vcg::math::ToRad(90.0))
|
|
* interfaceVector.Normalize();
|
|
if (viPairIt == oppositeInterfaceViPairs.begin()) {
|
|
job.nodalForcedDisplacements[viPair.first]
|
|
= Eigen::Vector3d(-interfaceVector[0],
|
|
-interfaceVector[1],
|
|
0)
|
|
* 0.001
|
|
* std::abs(
|
|
forceMagnitude); //NOTE:Should the forced displacement change relatively to the magnitude?
|
|
// * std::abs(forceMagnitude / maxForceMagnitude_dome);
|
|
job.nodalForcedDisplacements[viPair.second] = Eigen::Vector3d(interfaceVector[0],
|
|
interfaceVector[1],
|
|
0)
|
|
* 0.001 * std::abs(forceMagnitude);
|
|
// * std::abs(forceMagnitude / maxForceMagnitude_dome);
|
|
// CoordType v = (pMesh->vert[viPair.first].cP() - pMesh->vert[viPair.second].cP())
|
|
// ^ CoordType(0, 0, -1).Normalize();
|
|
// nodalForces[viPair.first] = Vector6d({0, 0, 0, v[0], v[1], 0}) * forceMagnitude
|
|
// * 0.0001;
|
|
// nodalForces[viPair.second] = Vector6d({0, 0, 0, -v[0], -v[1], 0}) * forceMagnitude
|
|
// * 0.0001;
|
|
} else {
|
|
job.nodalExternalForces[viPair.first]
|
|
= Vector6d({0, 0, 0, momentAxis[0], momentAxis[1], momentAxis[2]}) * forceMagnitude
|
|
/ 3;
|
|
job.nodalExternalForces[viPair.second]
|
|
= Vector6d({0, 0, 0, -momentAxis[0], -momentAxis[1], -momentAxis[2]})
|
|
* forceMagnitude / 3;
|
|
job.constrainedVertices[viPair.first] = std::unordered_set<DoFType>{2};
|
|
job.constrainedVertices[viPair.second] = std::unordered_set<DoFType>{2};
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReducedModelOptimizer::constructSaddleSimulationScenario(
|
|
const double &forceMagnitude,
|
|
const std::vector<std::pair<FullPatternVertexIndex, FullPatternVertexIndex>>
|
|
&oppositeInterfaceViPairs,
|
|
SimulationJob &job)
|
|
{
|
|
for (auto viPairIt = oppositeInterfaceViPairs.begin();
|
|
viPairIt != oppositeInterfaceViPairs.end();
|
|
viPairIt++) {
|
|
const auto &viPair = *viPairIt;
|
|
CoordType v = (job.pMesh->vert[viPair.first].cP() - job.pMesh->vert[viPair.second].cP())
|
|
^ CoordType(0, 0, -1).Normalize();
|
|
if (viPairIt == oppositeInterfaceViPairs.begin()) {
|
|
job.nodalExternalForces[viPair.first] = Vector6d({0, 0, 0, v[0], v[1], 0})
|
|
* forceMagnitude;
|
|
job.nodalExternalForces[viPair.second] = Vector6d({0, 0, 0, -v[0], -v[1], 0})
|
|
* forceMagnitude;
|
|
} else {
|
|
job.constrainedVertices[viPair.first] = std::unordered_set<DoFType>{2};
|
|
job.constrainedVertices[viPair.second] = std::unordered_set<DoFType>{0, 1, 2};
|
|
|
|
job.nodalExternalForces[viPair.first] = Vector6d({0, 0, 0, -v[0], -v[1], 0})
|
|
* forceMagnitude / 2;
|
|
job.nodalExternalForces[viPair.second] = Vector6d({0, 0, 0, v[0], v[1], 0})
|
|
* forceMagnitude / 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
double fullPatternMaxSimulationForceRotationalObjective(const double &forceMagnitude)
|
|
{
|
|
SimulationJob job;
|
|
job.pMesh = global.pFullPatternSimulationMesh;
|
|
global.constructScenarioFunction(forceMagnitude, global.fullPatternInterfaceViPairs, job);
|
|
// ReducedModelOptimizer::axialSimulationScenario(forceMagnitude,
|
|
// global.fullPatternInterfaceViPairs,
|
|
// job);
|
|
|
|
DRMSimulationModel simulator;
|
|
DRMSimulationModel::Settings settings;
|
|
// settings.totalExternalForcesNormPercentageTermination = 1e-2;
|
|
settings.totalTranslationalKineticEnergyThreshold = 1e-10;
|
|
// settings.viscousDampingFactor = 1e-2;
|
|
// settings.useKineticDamping = true;
|
|
settings.shouldDraw = false;
|
|
settings.debugModeStep = 200;
|
|
// settings.averageResidualForcesCriterionThreshold = 1e-5;
|
|
settings.maxDRMIterations = 200000;
|
|
SimulationResults results = simulator.executeSimulation(std::make_shared<SimulationJob>(job),
|
|
settings);
|
|
const double &desiredRotationAngle = global.desiredMaxRotationAngle;
|
|
double error;
|
|
// job.pMesh->setLabel("initial");
|
|
// job.pMesh->registerForDrawing();
|
|
// results.registerForDrawing();
|
|
// polyscope::show();
|
|
std::string saveJobToPath;
|
|
if (!results.converged) {
|
|
// std::cout << "Force used:" << forceMagnitude << std::endl;
|
|
error = std::numeric_limits<double>::max();
|
|
// DRMSimulationModel::Settings debugSimulationSettings;
|
|
// debugSimulationSettings.isDebugMode = true;
|
|
// debugSimulationSettings.debugModeStep = 1000;
|
|
// // debugSimulationSettings.maxDRMIterations = 100000;
|
|
// debugSimulationSettings.shouldDraw = true;
|
|
// debugSimulationSettings.drawingStep = debugSimulationSettings.debugModeStep;
|
|
// debugSimulationSettings.shouldCreatePlots = true;
|
|
// // debugSimulationSettings.Dtini = 0.06;
|
|
// debugSimulationSettings.beVerbose = true;
|
|
// debugSimulationSettings.useAverage = true;
|
|
// // debugSimulationSettings.totalExternalForcesNormPercentageTermination = 1e-3;
|
|
// debugSimulationSettings.shouldUseTranslationalKineticEnergyThreshold = true;
|
|
// auto debugResults = simulator.executeSimulation(std::make_shared<SimulationJob>(job),
|
|
// debugSimulationSettings);
|
|
// std::terminate();
|
|
saveJobToPath = "../nonConvergingJobs";
|
|
} else {
|
|
error = std::abs(
|
|
results.rotationalDisplacementQuaternion[global.interfaceViForComputingScenarioError]
|
|
.angularDistance(Eigen::Quaterniond::Identity())
|
|
- desiredRotationAngle);
|
|
saveJobToPath = "../convergingJobs";
|
|
}
|
|
// std::filesystem::path outputPath(std::filesystem::path(saveJobToPath)
|
|
// .append(job.pMesh->getLabel())
|
|
// .append("mag_" + global.currentScenarioName));
|
|
// std::filesystem::create_directories(outputPath);
|
|
// job.save(outputPath);
|
|
// settings.save(outputPath);
|
|
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << "Force:" << forceMagnitude << " Error is:" << vcg::math::ToDeg(error) << std::endl;
|
|
#endif
|
|
return error;
|
|
}
|
|
|
|
void search(const std::function<double(const double &)> objectiveFunction,
|
|
const double &targetY,
|
|
double &optimalX,
|
|
double &error,
|
|
const double &epsilon,
|
|
const double &maxIterations)
|
|
{
|
|
error = std::numeric_limits<double>::max();
|
|
int iterationIndex = 0;
|
|
double low = optimalX / 1e-3, high = optimalX * 1e3;
|
|
while (error > epsilon && iterationIndex < maxIterations) {
|
|
const double y = objectiveFunction(optimalX);
|
|
error = std::abs(targetY - y);
|
|
if (y > targetY) {
|
|
high = optimalX;
|
|
} else {
|
|
low = optimalX;
|
|
}
|
|
optimalX = low + (high - low) / 2;
|
|
|
|
iterationIndex++;
|
|
}
|
|
|
|
if (iterationIndex == maxIterations) {
|
|
std::cerr << "Max iterations reached." << std::endl;
|
|
}
|
|
}
|
|
|
|
double fullPatternMaxSimulationForceTranslationalObjective(const double &forceMagnitude)
|
|
{
|
|
SimulationJob job;
|
|
job.pMesh = global.pFullPatternSimulationMesh;
|
|
global.constructScenarioFunction(forceMagnitude, global.fullPatternInterfaceViPairs, job);
|
|
// ReducedModelOptimizer::axialSimulationScenario(forceMagnitude,
|
|
// global.fullPatternInterfaceViPairs,
|
|
// job);
|
|
|
|
DRMSimulationModel simulator;
|
|
DRMSimulationModel::Settings settings;
|
|
// settings.totalResidualForcesNormThreshold = 1e-3;
|
|
settings.totalExternalForcesNormPercentageTermination = 1e-2;
|
|
settings.totalTranslationalKineticEnergyThreshold = 1e-8;
|
|
settings.viscousDampingFactor = 5e-3;
|
|
settings.useKineticDamping = true;
|
|
// settings.averageResidualForcesCriterionThreshold = 1e-5;
|
|
// settings.useAverage = true;
|
|
// settings.totalTranslationalKineticEnergyThreshold = 1e-10;
|
|
// settings.shouldUseTranslationalKineticEnergyThreshold = true;
|
|
// settings.shouldDraw = true;
|
|
// settings.isDebugMode = true;
|
|
// settings.drawingStep = 200000;
|
|
// settings.beVerbose = true;
|
|
// settings.debugModeStep = 200;
|
|
settings.maxDRMIterations = 200000;
|
|
SimulationResults results = simulator.executeSimulation(std::make_shared<SimulationJob>(job),
|
|
settings);
|
|
const double &desiredDisplacementValue = global.desiredMaxDisplacementValue;
|
|
double error;
|
|
if (!results.converged) {
|
|
error = std::numeric_limits<double>::max();
|
|
std::filesystem::path outputPath(std::filesystem::path("../nonConvergingJobs")
|
|
.append(job.pMesh->getLabel())
|
|
.append("mag_" + global.currentScenarioName));
|
|
std::filesystem::create_directories(outputPath);
|
|
job.save(outputPath);
|
|
settings.save(outputPath);
|
|
// std::terminate();
|
|
} else {
|
|
error = std::abs(
|
|
results.displacements[global.interfaceViForComputingScenarioError].getTranslation().norm()
|
|
- desiredDisplacementValue);
|
|
}
|
|
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << "Force:" << forceMagnitude << " Error is:" << error << std::endl;
|
|
#endif
|
|
|
|
return error;
|
|
}
|
|
|
|
#ifdef USE_ENSMALLEN
|
|
struct ForceMagnitudeOptimization
|
|
{
|
|
std::function<double(const double &)> objectiveFunction;
|
|
ForceMagnitudeOptimization(std::function<double(const double &)> &f) : objectiveFunction(f) {}
|
|
double Evaluate(const arma::mat &x) { return objectiveFunction(x(0, 0)); }
|
|
};
|
|
#endif
|
|
|
|
double ReducedModelOptimizer::computeFullPatternMaxSimulationForce(
|
|
const BaseSimulationScenario &scenario) const
|
|
{
|
|
double forceMagnitude = 1;
|
|
double minimumError;
|
|
bool wasSuccessful = false;
|
|
global.constructScenarioFunction = constructBaseScenarioFunctions[scenario];
|
|
const double baseTriangleHeight = vcg::Distance(global.baseTriangle.cP(0),
|
|
(global.baseTriangle.cP(1)
|
|
+ global.baseTriangle.cP(2))
|
|
/ 2);
|
|
std::function<double(const double &)> objectiveFunction;
|
|
double translationalOptimizationEpsilon{baseTriangleHeight * 0.001};
|
|
double objectiveEpsilon = translationalOptimizationEpsilon;
|
|
objectiveFunction = &fullPatternMaxSimulationForceTranslationalObjective;
|
|
global.interfaceViForComputingScenarioError = global.fullPatternInterfaceViPairs[1].first;
|
|
global.desiredMaxDisplacementValue = 0.1 * baseTriangleHeight;
|
|
global.currentScenarioName = baseSimulationScenarioNames[scenario];
|
|
double forceMagnitudeEpsilon = 1e-4;
|
|
|
|
switch (scenario) {
|
|
case Axial:
|
|
global.desiredMaxDisplacementValue = 0.04 * baseTriangleHeight;
|
|
break;
|
|
case Shear:
|
|
global.desiredMaxDisplacementValue = 0.04 * baseTriangleHeight;
|
|
break;
|
|
case Bending:
|
|
global.interfaceViForComputingScenarioError = global.fullPatternInterfaceViPairs[0].first;
|
|
global.desiredMaxDisplacementValue = 0.05 * baseTriangleHeight;
|
|
break;
|
|
case Dome:
|
|
global.desiredMaxRotationAngle = vcg::math::ToRad(20.0);
|
|
objectiveFunction = &fullPatternMaxSimulationForceRotationalObjective;
|
|
forceMagnitudeEpsilon *= 1e-2;
|
|
objectiveEpsilon = vcg::math::ToRad(1.0);
|
|
forceMagnitude = 0.6;
|
|
break;
|
|
case Saddle:
|
|
global.interfaceViForComputingScenarioError = global.fullPatternInterfaceViPairs[0].first;
|
|
global.desiredMaxDisplacementValue = 0.05 * baseTriangleHeight;
|
|
break;
|
|
}
|
|
|
|
constexpr int maxIterations = 1000;
|
|
minimumError = dlib::find_min_single_variable(objectiveFunction,
|
|
forceMagnitude,
|
|
1e-4,
|
|
1e4,
|
|
forceMagnitudeEpsilon,
|
|
maxIterations);
|
|
#ifdef DLIB_DEFINED
|
|
#else
|
|
// ens::SA<> optimizer;
|
|
// arma::vec lowerBound("0.00001");
|
|
// arma::vec upperBound("10000");
|
|
// ens::LBestPSO optimizer(64, lowerBound, upperBound, maxIterations, 350, forceMagnitudeEpsilon);
|
|
// ForceMagnitudeOptimization f(objectiveFunction); // Create function to be optimized.
|
|
// arma::mat forceMagnitude_mat({forceMagnitude});
|
|
// minimumError = optimizer.Optimize(f, forceMagnitude_mat);
|
|
// std::cout << ReducedModelOptimization::baseSimulationScenarioNames[scenario] << ": "
|
|
// << optimalObjective << std::endl;
|
|
// forceMagnitude = forceMagnitude_mat(0, 0);
|
|
|
|
// search(objectiveFunction,
|
|
// global.desiredMaxDisplacementValue,
|
|
// forceMagnitude,
|
|
// minimumError,
|
|
// objectiveEpsilon,
|
|
// maxIterations);
|
|
#endif
|
|
|
|
wasSuccessful = minimumError < objectiveEpsilon;
|
|
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << "Max " << ReducedModelOptimization::baseSimulationScenarioNames[scenario]
|
|
<< " magnitude:" << forceMagnitude << std::endl;
|
|
if (!wasSuccessful) {
|
|
SimulationJob job;
|
|
job.pMesh = global.pFullPatternSimulationMesh;
|
|
global.constructScenarioFunction(forceMagnitude, global.fullPatternInterfaceViPairs, job);
|
|
std::cerr << ReducedModelOptimization::baseSimulationScenarioNames[scenario]
|
|
+ " max scenario magnitude was not succefully determined."
|
|
<< std::endl;
|
|
std::filesystem::path outputPath(
|
|
std::filesystem::path("../nonConvergingJobs")
|
|
.append(m_pFullPatternSimulationMesh->getLabel())
|
|
.append("magFinal_"
|
|
+ ReducedModelOptimization::baseSimulationScenarioNames[scenario]));
|
|
std::filesystem::create_directories(outputPath);
|
|
job.save(outputPath);
|
|
std::terminate();
|
|
}
|
|
#endif
|
|
|
|
return forceMagnitude;
|
|
}
|
|
|
|
void ReducedModelOptimizer::computeScenarioWeights(
|
|
const std::vector<BaseSimulationScenario> &baseSimulationScenarios)
|
|
{
|
|
std::array<double, NumberOfBaseSimulationScenarios> baseScenario_equalizationWeights;
|
|
baseScenario_equalizationWeights.fill(0);
|
|
std::array<double, NumberOfBaseSimulationScenarios> baseScenario_userWeights;
|
|
baseScenario_userWeights.fill(0);
|
|
//Fill only used base scenario weights
|
|
for (const BaseSimulationScenario &baseScenario : baseSimulationScenarios) {
|
|
baseScenario_equalizationWeights[baseScenario] = static_cast<double>(
|
|
simulationScenariosResolution[baseScenario]);
|
|
baseScenario_userWeights[baseScenario] = baseScenarioWeights[baseScenario];
|
|
}
|
|
|
|
Utilities::normalize(baseScenario_userWeights.begin(), baseScenario_userWeights.end());
|
|
|
|
Utilities::normalize(baseScenario_equalizationWeights.begin(),
|
|
baseScenario_equalizationWeights.end());
|
|
std::transform(baseScenario_equalizationWeights.begin(),
|
|
baseScenario_equalizationWeights.end(),
|
|
baseScenario_equalizationWeights.begin(),
|
|
[](const double &d) {
|
|
if (d == 0) {
|
|
return d;
|
|
}
|
|
return 1 / d;
|
|
});
|
|
Utilities::normalize(baseScenario_equalizationWeights.begin(),
|
|
baseScenario_equalizationWeights.end());
|
|
|
|
std::array<double, NumberOfBaseSimulationScenarios> baseScenarios_weights;
|
|
baseScenarios_weights.fill(0);
|
|
for (const BaseSimulationScenario &baseScenario : baseSimulationScenarios) {
|
|
baseScenarios_weights[baseScenario] = baseScenario_equalizationWeights[baseScenario]
|
|
+ 2 * baseScenario_userWeights[baseScenario];
|
|
}
|
|
Utilities::normalize(baseScenarios_weights.begin(), baseScenarios_weights.end());
|
|
|
|
global.objectiveWeights.resize(totalNumberOfSimulationScenarios);
|
|
global.scenarioWeights.resize(
|
|
totalNumberOfSimulationScenarios,
|
|
0); //This essentially holds the base scenario weights but I use totalNumberOfSimulationScenarios elements instead in order to have O(1) access time during the optimization
|
|
for (const BaseSimulationScenario &baseScenario : baseSimulationScenarios) {
|
|
const int baseSimulationScenarioIndexOffset
|
|
= std::accumulate(simulationScenariosResolution.begin(),
|
|
simulationScenariosResolution.begin() + baseScenario,
|
|
0);
|
|
for (int simulationScenarioIndex = 0;
|
|
simulationScenarioIndex < simulationScenariosResolution[baseScenario];
|
|
simulationScenarioIndex++) {
|
|
const int globalScenarioIndex = baseSimulationScenarioIndexOffset
|
|
+ simulationScenarioIndex;
|
|
global.scenarioWeights[globalScenarioIndex] = baseScenarios_weights[baseScenario];
|
|
|
|
global.objectiveWeights[globalScenarioIndex]
|
|
= global.optimizationSettings.perBaseScenarioObjectiveWeights[baseScenario];
|
|
}
|
|
}
|
|
|
|
//Dont normalize since we want the base scenarios to be normalized not the "global scenarios"
|
|
// Utilities::normalize(global.scenarioWeights.begin(), global.scenarioWeights.end());
|
|
}
|
|
|
|
std::vector<std::shared_ptr<SimulationJob>> ReducedModelOptimizer::createFullPatternSimulationJobs(
|
|
const std::shared_ptr<SimulationMesh> &pMesh,
|
|
const std::array<double, NumberOfBaseSimulationScenarios> &baseScenarioMaxForceMagnitudes) const
|
|
{
|
|
std::vector<std::shared_ptr<SimulationJob>> scenarios;
|
|
scenarios.resize(totalNumberOfSimulationScenarios);
|
|
|
|
SimulationJob job;
|
|
job.pMesh = pMesh;
|
|
|
|
for (int baseScenario = Axial; baseScenario != NumberOfBaseSimulationScenarios; baseScenario++) {
|
|
const double maxForceMagnitude = baseScenarioMaxForceMagnitudes[baseScenario];
|
|
const int numberOfSimulationScenarios = simulationScenariosResolution[baseScenario];
|
|
const double minForceMagnitude = scenarioIsSymmetrical[baseScenario]
|
|
? maxForceMagnitude / numberOfSimulationScenarios
|
|
: -maxForceMagnitude;
|
|
const double forceMagnitudeStep = numberOfSimulationScenarios == 1
|
|
? maxForceMagnitude
|
|
: (maxForceMagnitude - minForceMagnitude)
|
|
/ (numberOfSimulationScenarios);
|
|
const int baseSimulationScenarioIndexOffset
|
|
= std::accumulate(simulationScenariosResolution.begin(),
|
|
simulationScenariosResolution.begin() + baseScenario,
|
|
0);
|
|
for (int simulationScenarioIndex = 0; simulationScenarioIndex < numberOfSimulationScenarios;
|
|
simulationScenarioIndex++) {
|
|
const int scenarioIndex = baseSimulationScenarioIndexOffset + simulationScenarioIndex;
|
|
if (baseScenarioMaxForceMagnitudes[baseScenario] == -1) {
|
|
scenarios[scenarioIndex] = nullptr;
|
|
continue;
|
|
}
|
|
job.nodalExternalForces.clear();
|
|
job.constrainedVertices.clear();
|
|
job.nodalForcedDisplacements.clear();
|
|
job.label = baseSimulationScenarioNames[baseScenario] + "_"
|
|
+ std::to_string(simulationScenarioIndex);
|
|
|
|
const double forceMagnitude = (forceMagnitudeStep * simulationScenarioIndex
|
|
+ minForceMagnitude);
|
|
constructBaseScenarioFunctions[baseScenario](forceMagnitude,
|
|
m_fullPatternOppositeInterfaceViPairs,
|
|
job);
|
|
|
|
scenarios[scenarioIndex] = std::make_shared<SimulationJob>(job);
|
|
}
|
|
}
|
|
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << "Computed full pattern scenario magnitudes" << std::endl;
|
|
#endif
|
|
return scenarios;
|
|
}
|
|
|
|
void ReducedModelOptimizer::computeObjectiveValueNormalizationFactors()
|
|
{
|
|
// m_pFullPatternSimulationMesh->registerForDrawing();
|
|
// m_pFullPatternSimulationMesh->save(std::filesystem::current_path().append("initial.ply"));
|
|
// Compute the sum of the displacement norms
|
|
std::vector<double> fullPatternTranslationalDisplacementNormSum(
|
|
totalNumberOfSimulationScenarios);
|
|
std::vector<double> fullPatternAngularDistance(totalNumberOfSimulationScenarios);
|
|
for (int simulationScenarioIndex : global.simulationScenarioIndices) {
|
|
double translationalDisplacementNormSum = 0;
|
|
for (auto interfaceViPair : global.reducedToFullInterfaceViMap) {
|
|
const int fullPatternVi = interfaceViPair.second;
|
|
//If the full pattern vertex is translationally constrained dont take it into account
|
|
if (global.fullPatternSimulationJobs[simulationScenarioIndex]
|
|
->constrainedVertices.contains(fullPatternVi)) {
|
|
const std::unordered_set<int> constrainedDof
|
|
= global.fullPatternSimulationJobs[simulationScenarioIndex]
|
|
->constrainedVertices.at(fullPatternVi);
|
|
if (constrainedDof.contains(0) && constrainedDof.contains(1)
|
|
&& constrainedDof.contains(2)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const Vector6d &vertexDisplacement = global.fullPatternResults[simulationScenarioIndex]
|
|
.displacements[fullPatternVi];
|
|
// std::cout << "vi:" << fullPatternVi << std::endl;
|
|
// std::cout << "displacement:" << vertexDisplacement.getTranslation().norm() << std::endl;
|
|
|
|
translationalDisplacementNormSum += vertexDisplacement.getTranslation().norm();
|
|
}
|
|
// global.fullPatternResults[simulationScenarioIndex].saveDeformedModel(
|
|
// std::filesystem::current_path());
|
|
// global.fullPatternResults[simulationScenarioIndex].registerForDrawing();
|
|
// polyscope::show();
|
|
// global.fullPatternResults[simulationScenarioIndex].unregister();
|
|
double angularDistanceSum = 0;
|
|
for (auto interfaceViPair : global.reducedToFullInterfaceViMap) {
|
|
const int fullPatternVi = interfaceViPair.second;
|
|
//If the full pattern vertex is rotationally constrained dont take it into account
|
|
if (global.fullPatternSimulationJobs[simulationScenarioIndex]
|
|
->constrainedVertices.contains(fullPatternVi)) {
|
|
const std::unordered_set<int> constrainedDof
|
|
= global.fullPatternSimulationJobs[simulationScenarioIndex]
|
|
->constrainedVertices.at(fullPatternVi);
|
|
if (constrainedDof.contains(3) && constrainedDof.contains(5)
|
|
&& constrainedDof.contains(4)) {
|
|
continue;
|
|
}
|
|
}
|
|
angularDistanceSum += std::abs(global.fullPatternResults[simulationScenarioIndex]
|
|
.rotationalDisplacementQuaternion[fullPatternVi]
|
|
.angularDistance(Eigen::Quaterniond::Identity()));
|
|
}
|
|
|
|
fullPatternTranslationalDisplacementNormSum[simulationScenarioIndex]
|
|
= translationalDisplacementNormSum;
|
|
fullPatternAngularDistance[simulationScenarioIndex] = angularDistanceSum;
|
|
}
|
|
|
|
for (int simulationScenarioIndex : global.simulationScenarioIndices) {
|
|
if (global.optimizationSettings.normalizationStrategy
|
|
== Settings::NormalizationStrategy::Epsilon) {
|
|
const double epsilon_translationalDisplacement = global.optimizationSettings
|
|
.translationEpsilon;
|
|
global.translationalDisplacementNormalizationValues[simulationScenarioIndex]
|
|
= std::max(fullPatternTranslationalDisplacementNormSum[simulationScenarioIndex],
|
|
epsilon_translationalDisplacement);
|
|
const double epsilon_rotationalDisplacement = global.optimizationSettings
|
|
.angularDistanceEpsilon;
|
|
global.rotationalDisplacementNormalizationValues[simulationScenarioIndex]
|
|
= std::max(fullPatternAngularDistance[simulationScenarioIndex],
|
|
epsilon_rotationalDisplacement);
|
|
} else {
|
|
global.translationalDisplacementNormalizationValues[simulationScenarioIndex] = 1;
|
|
global.rotationalDisplacementNormalizationValues[simulationScenarioIndex] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReducedModelOptimizer::optimize(
|
|
const Settings &optimizationSettings,
|
|
ReducedModelOptimization::Results &results,
|
|
const std::vector<BaseSimulationScenario> &desiredBaseSimulationScenarioIndices)
|
|
{
|
|
assert(!optimizationSettings.optimizationStrategy.empty());
|
|
assert(!optimizationSettings.optimizationVariablesGroupsWeights.has_value()
|
|
|| (optimizationSettings.optimizationStrategy.size()
|
|
== optimizationSettings.optimizationVariablesGroupsWeights->size()
|
|
&& std::accumulate(optimizationSettings.optimizationVariablesGroupsWeights->begin(),
|
|
optimizationSettings.optimizationVariablesGroupsWeights->end(),
|
|
0)));
|
|
|
|
for (int baseSimulationScenarioIndex : desiredBaseSimulationScenarioIndices) {
|
|
//Increase the size of the vector holding the simulation scenario indices
|
|
global.simulationScenarioIndices.resize(
|
|
global.simulationScenarioIndices.size()
|
|
+ simulationScenariosResolution[baseSimulationScenarioIndex]);
|
|
//Add the simulation scenarios indices that correspond to this base simulation scenario
|
|
std::iota(global.simulationScenarioIndices.end()
|
|
- simulationScenariosResolution[baseSimulationScenarioIndex],
|
|
global.simulationScenarioIndices.end(),
|
|
std::accumulate(simulationScenariosResolution.begin(),
|
|
simulationScenariosResolution.begin()
|
|
+ baseSimulationScenarioIndex,
|
|
0));
|
|
}
|
|
|
|
// global.totalNumberOfSimulationScenarios = totalNumberOfSimulationScenarios;
|
|
global.reducedPatternSimulationJobs.resize(totalNumberOfSimulationScenarios);
|
|
global.fullPatternResults.resize(totalNumberOfSimulationScenarios);
|
|
global.translationalDisplacementNormalizationValues.resize(totalNumberOfSimulationScenarios);
|
|
global.rotationalDisplacementNormalizationValues.resize(totalNumberOfSimulationScenarios);
|
|
global.minY = std::numeric_limits<double>::max();
|
|
global.numOfSimulationCrashes = 0;
|
|
global.numberOfFunctionCalls = 0;
|
|
global.optimizationSettings = optimizationSettings;
|
|
global.pFullPatternSimulationMesh = m_pFullPatternSimulationMesh;
|
|
|
|
#ifdef POLYSCOPE_DEFINED
|
|
constexpr bool recomputeMagnitudes = false;
|
|
#else
|
|
constexpr bool recomputeMagnitudes = true;
|
|
#endif
|
|
// std::array<double, NumberOfBaseSimulationScenarios> fullPatternSimulationScenarioMaxMagnitudes
|
|
// = getFullPatternMaxSimulationForces(desiredBaseSimulationScenarioIndices,
|
|
// intermediateResultsDirectoryPath,
|
|
// recomputeMagnitudes);
|
|
std::array<double, NumberOfBaseSimulationScenarios> fullPatternSimulationScenarioMaxMagnitudes
|
|
= optimizationSettings.baseScenarioMagnitudes;
|
|
global.fullPatternSimulationJobs
|
|
= createFullPatternSimulationJobs(m_pFullPatternSimulationMesh,
|
|
fullPatternSimulationScenarioMaxMagnitudes);
|
|
// polyscope::removeAllStructures();
|
|
|
|
// ComputeScenarioWeights({Axial, Shear, Bending, Dome, Saddle},
|
|
computeScenarioWeights(desiredBaseSimulationScenarioIndices);
|
|
|
|
results.baseTriangle = global.baseTriangle;
|
|
|
|
DRMSimulationModel::Settings simulationSettings;
|
|
simulationSettings.totalExternalForcesNormPercentageTermination = 1e-5;
|
|
// simulationSettings.maxDRMIterations = 200000;
|
|
// simulationSettings.totalTranslationalKineticEnergyThreshold = 1e-10;
|
|
// simulationSettings.viscousDampingFactor = 5e-3;
|
|
// simulationSettings.useKineticDamping = true;
|
|
// simulationSettings.save(std::filesystem::path(std::string(__FILE__))
|
|
// .parent_path()
|
|
// .parent_path()
|
|
// .append("DefaultSettings")
|
|
// .append("DRMSettings")
|
|
// .append("defaultDRMSimulationSettings.json"));
|
|
|
|
// simulationSettings.averageResidualForcesCriterionThreshold = 1e-5;
|
|
// simulationSettings.viscousDampingFactor = 1e-3;
|
|
// simulationSettings.beVerbose = true;
|
|
// simulationSettings.shouldDraw = true;
|
|
// simulationSettings.isDebugMode = true;
|
|
// simulationSettings.debugModeStep = 100000;
|
|
#ifdef POLYSCOPE_DEFINED
|
|
constexpr bool drawFullPatternSimulationResults = false;
|
|
if (drawFullPatternSimulationResults) {
|
|
global.fullPatternSimulationJobs[0]->pMesh->registerForDrawing(
|
|
ReducedModelOptimization::Colors::fullInitial);
|
|
}
|
|
#endif
|
|
results.wasSuccessful = true;
|
|
for (int simulationScenarioIndex : global.simulationScenarioIndices) {
|
|
const std::shared_ptr<SimulationJob> &pFullPatternSimulationJob
|
|
= global.fullPatternSimulationJobs[simulationScenarioIndex];
|
|
|
|
std::filesystem::path jobResultsDirectoryPath(
|
|
std::filesystem::path(optimizationSettings.intermediateResultsDirectoryPath)
|
|
.append("FullPatternResults")
|
|
.append(m_pFullPatternSimulationMesh->getLabel())
|
|
.append(pFullPatternSimulationJob->getLabel()));
|
|
// .append(pFullPatternSimulationJob->getLabel() + ".json")
|
|
#ifdef POLYSCOPE_DEFINED
|
|
constexpr bool recomputeFullPatternResults = false;
|
|
#else
|
|
constexpr bool recomputeFullPatternResults = true;
|
|
#endif
|
|
SimulationResults fullPatternResults;
|
|
if (!recomputeFullPatternResults && std::filesystem::exists(jobResultsDirectoryPath)) {
|
|
fullPatternResults.load(std::filesystem::path(jobResultsDirectoryPath).append("Results"),
|
|
pFullPatternSimulationJob);
|
|
} else {
|
|
// const bool fullPatternScenarioMagnitudesExist = std::filesystem::exists(
|
|
// patternMaxForceMagnitudesFilePath);
|
|
// if (fullPatternScenarioMagnitudesExist) {
|
|
// nlohmann::json json;
|
|
// std::ifstream ifs(patternMaxForceMagnitudesFilePath.string());
|
|
// ifs >> json;
|
|
// fullPatternSimulationScenarioMaxMagnitudes
|
|
// = static_cast<std::vector<std::pair<BaseSimulationScenario, double>>>(
|
|
// json.at("maxMagn"));
|
|
// const bool shouldRecompute = fullPatternSimulationScenarioMaxMagnitudes.size()
|
|
// != desiredBaseSimulationScenarioIndices.size();
|
|
// if (!shouldRecompute) {
|
|
// return fullPatternSimulationScenarioMaxMagnitudes;
|
|
// }
|
|
// }
|
|
//#ifdef POLYSCOPE_DEFINED
|
|
// LinearSimulationModel linearSimulator;
|
|
// SimulationResults fullPatternResults = linearSimulator.executeSimulation(
|
|
// pFullPatternSimulationJob);
|
|
|
|
//#else
|
|
fullPatternResults = simulator.executeSimulation(pFullPatternSimulationJob,
|
|
simulationSettings);
|
|
fullPatternResults.save(jobResultsDirectoryPath);
|
|
}
|
|
//#endif
|
|
// if (!fullPatternResults.converged) {
|
|
// DRMSimulationModel::Settings simulationSettings_secondRound;
|
|
// simulationSettings_secondRound.viscousDampingFactor = 2e-3;
|
|
// simulationSettings_secondRound.useKineticDamping = true;
|
|
// simulationSettings.maxDRMIterations = 200000;
|
|
// fullPatternResults = simulator.executeSimulation(pFullPatternSimulationJob,
|
|
// simulationSettings_secondRound);
|
|
#ifdef POLYSCOPE_DEFINED
|
|
// std::cout << "Simulation job " << pFullPatternSimulationJob->getLabel()
|
|
// << " used viscous damping." << std::endl;
|
|
#endif
|
|
|
|
if (!fullPatternResults.converged) {
|
|
results.wasSuccessful = false;
|
|
std::cerr << "Simulation job " << pFullPatternSimulationJob->getLabel()
|
|
<< " did not converge." << std::endl;
|
|
#ifdef POLYSCOPE_DEFINED
|
|
// DRMSimulationModel::Settings debugSimulationSettings;
|
|
// debugSimulationSettings.debugModeStep = 50;
|
|
// // // debugSimulationSettings.maxDRMIterations = 100000;
|
|
// debugSimulationSettings.shouldDraw = true;
|
|
// // debugSimulationSettings.drawingStep = debugSimulationSettings.debugModeStep;
|
|
// debugSimulationSettings.shouldCreatePlots = true;
|
|
// // // debugSimulationSettings.Dtini = 0.06;
|
|
// debugSimulationSettings.beVerbose = true;
|
|
// debugSimulationSettings.averageResidualForcesCriterionThreshold = 1e-5;
|
|
// debugSimulationSettings.maxDRMIterations = 100000;
|
|
// debugSimulationSettings.totalTranslationalKineticEnergyThreshold = 1e-8;
|
|
// debugSimulationSettings.viscousDampingFactor = 1e-2;
|
|
// // // debugSimulationSettings.totalExternalForcesNormPercentageTermination = 1e-3;
|
|
// // // debugSimulationSettings.shouldUseTranslationalKineticEnergyThreshold = true;
|
|
// auto debugResults = simulator.executeSimulation(pFullPatternSimulationJob,
|
|
// debugSimulationSettings);
|
|
// debugResults.setLabelPrefix("debugResults");
|
|
// debugResults.registerForDrawing();
|
|
// polyscope::show();
|
|
// debugResults.unregister();
|
|
std::filesystem::path outputPath(
|
|
std::filesystem::path("../nonConvergingJobs")
|
|
.append(m_pFullPatternSimulationMesh->getLabel())
|
|
.append("final_" + pFullPatternSimulationJob->getLabel()));
|
|
std::filesystem::create_directories(outputPath);
|
|
pFullPatternSimulationJob->save(outputPath);
|
|
simulationSettings.save(outputPath);
|
|
std::terminate();
|
|
return;
|
|
#endif
|
|
// }
|
|
}
|
|
#ifdef POLYSCOPE_DEFINED
|
|
if (drawFullPatternSimulationResults) {
|
|
// SimulationResults fullPatternResults_linear = linearSimulator.executeSimulation(
|
|
// pFullPatternSimulationJob);
|
|
fullPatternResults.registerForDrawing(ReducedModelOptimization::Colors::fullDeformed,
|
|
true);
|
|
// fullPatternResults_linear.labelPrefix += "_linear";
|
|
// fullPatternResults_linear.registerForDrawing(ReducedModelOptimization::Colors::fullDeformed,
|
|
// true,
|
|
// true);
|
|
polyscope::show();
|
|
fullPatternResults.unregister();
|
|
// fullPatternResults_linear.unregister();
|
|
}
|
|
#endif
|
|
global.fullPatternResults[simulationScenarioIndex] = fullPatternResults;
|
|
SimulationJob reducedPatternSimulationJob;
|
|
reducedPatternSimulationJob.pMesh = m_pReducedPatternSimulationMesh;
|
|
computeReducedModelSimulationJob(*pFullPatternSimulationJob,
|
|
m_fullToReducedInterfaceViMap,
|
|
reducedPatternSimulationJob);
|
|
global.reducedPatternSimulationJobs[simulationScenarioIndex]
|
|
= std::make_shared<SimulationJob>(reducedPatternSimulationJob);
|
|
// std::cout << "Ran sim scenario:" << simulationScenarioIndex << std::endl;
|
|
}
|
|
|
|
#ifdef POLYSCOPE_DEFINED
|
|
if (drawFullPatternSimulationResults) {
|
|
global.fullPatternSimulationJobs[0]->pMesh->unregister();
|
|
}
|
|
#endif
|
|
// if (global.optimizationSettings.normalizationStrategy
|
|
// == Settings::NormalizationStrategy::Epsilon) {
|
|
computeObjectiveValueNormalizationFactors();
|
|
// }
|
|
#ifdef POLYSCOPE_DEFINED
|
|
std::cout << "Running reduced model optimization" << std::endl;
|
|
#endif
|
|
runOptimization(optimizationSettings, results);
|
|
results.notes = optimizationNotes;
|
|
}
|