#ifndef REDUCEDMODELOPTIMIZER_STRUCTS_HPP #define REDUCEDMODELOPTIMIZER_STRUCTS_HPP #include "csvfile.hpp" #include "drmsimulationmodel.hpp" #include "linearsimulationmodel.hpp" #include "simulation_structs.hpp" #include "unordered_map" #include namespace ReducedPatternOptimization { enum BaseSimulationScenario { Axial, Shear, Bending, Dome, Saddle, NumberOfBaseSimulationScenarios }; inline static std::vector baseSimulationScenarioNames{"Axial", "Shear", "Bending", "Dome", "Saddle"}; struct Colors { inline static std::array fullInitial{0.416666, 0.109804, 0.890196}; inline static std::array fullDeformed{0.583333, 0.890196, 0.109804}; inline static std::array reducedInitial{0.890196, 0.109804, 0.193138}; inline static std::array reducedDeformed{0.109804, 0.890196, 0.806863}; }; struct xRange{ std::string label; double min; double max; std::string toString() const { return label + "=[" + std::to_string(min) + "," + std::to_string(max) + "]"; } }; struct Settings { enum NormalizationStrategy { NonNormalized, Epsilon, MaxDisplacement, EqualDisplacements }; inline static vector normalizationStrategyStrings{ "NonNormalized", "Epsilon", "MaxDsiplacement", "EqualDisplacements"}; std::vector xRanges; int numberOfFunctionCalls{100}; double solutionAccuracy{1e-2}; NormalizationStrategy normalizationStrategy{Epsilon}; double normalizationParameter{0.003}; std::string toString() const { std::string settingsString; if (!xRanges.empty()) { std::string xRangesString; for (const xRange &range : xRanges) { xRangesString += range.toString() + " "; } settingsString += xRangesString; } settingsString += "FuncCalls=" + std::to_string(numberOfFunctionCalls) + " Accuracy=" + std::to_string(solutionAccuracy) + " Norm=" + normalizationStrategyStrings[normalizationStrategy]; return settingsString; } void writeHeaderTo(csvFile &os) const { if (!xRanges.empty()) { for (const xRange &range : xRanges) { os << range.label + " max"; os << range.label + " min"; } } os << "Function Calls"; os << "Solution Accuracy"; os << "Normalization strategy"; // os << std::endl; } void writeSettingsTo(csvFile &os) const { if (!xRanges.empty()) { for (const xRange &range : xRanges) { os << range.max; os << range.min; } } os << numberOfFunctionCalls; os << solutionAccuracy; os << normalizationStrategyStrings[normalizationStrategy] + "_" + std::to_string(normalizationParameter); } }; inline void updateMeshWithOptimalVariables(const std::vector &x, SimulationMesh &m) { const double E = x[0]; const double A = x[1]; const double beamWidth = std::sqrt(A); const double beamHeight = beamWidth; const double J = x[2]; const double I2 = x[3]; const double I3 = x[4]; for (EdgeIndex ei = 0; ei < m.EN(); ei++) { Element &e = m.elements[ei]; e.setDimensions(RectangularBeamDimensions(beamWidth, beamHeight)); e.setMaterial(ElementMaterial(e.material.poissonsRatio, E)); e.J = J; e.I2 = I2; e.I3 = I3; } CoordType center_barycentric(1, 0, 0); CoordType interfaceEdgeMiddle_barycentric(0, 0.5, 0.5); CoordType movableVertex_barycentric((center_barycentric + interfaceEdgeMiddle_barycentric) * x[x.size() - 2]); CoordType patternCoord0 = CoordType(0, 0, 0); double bottomEdgeHalfSize = 0.03 / std::tan(M_PI / 3); CoordType interfaceNodePosition(0, -0.03, 0); CoordType patternBottomRight = interfaceNodePosition + CoordType(bottomEdgeHalfSize, 0, 0); CoordType patternBottomLeft = interfaceNodePosition - CoordType(bottomEdgeHalfSize, 0, 0); vcg::Triangle3 baseTriangle(patternCoord0, patternBottomLeft, patternBottomRight); CoordType baseTriangleMovableVertexPosition = baseTriangle.cP(0) * movableVertex_barycentric[0] + baseTriangle.cP(1) * movableVertex_barycentric[1] + baseTriangle.cP(2) * movableVertex_barycentric[2]; VectorType patternPlaneNormal(0, 0, 1); baseTriangleMovableVertexPosition = vcg::RotationMatrix(patternPlaneNormal, vcg::math::ToRad(x[x.size() - 1])) * baseTriangleMovableVertexPosition; const int fanSize = 6; for (int rotationCounter = 0; rotationCounter < fanSize; rotationCounter++) { m.vert[2 * rotationCounter + 1].P() = vcg::RotationMatrix(patternPlaneNormal, vcg::math::ToRad( 60.0 * rotationCounter)) * baseTriangleMovableVertexPosition; } m.reset(); } struct Results { double time{-1}; int numberOfSimulationCrashes{0}; struct ObjectiveValues { double totalRaw; double total; std::vector perSimulationScenario_rawTranslational; std::vector perSimulationScenario_rawRotational; std::vector perSimulationScenario_translational; std::vector perSimulationScenario_rotational; std::vector perSimulationScenario_total; } objectiveValue; // std::vector> optimalXNameValuePairs; std::vector> optimalXNameValuePairs; std::vector> fullPatternSimulationJobs; std::vector> reducedPatternSimulationJobs; vcg::Triangle3 baseTriangle; struct JsonKeys { inline static std::string filename{"OptimizationResults.json"}; inline static std::string optimizationVariables{"OptimizationVariables"}; inline static std::string baseTriangle{"BaseTriangle"}; }; void save(const string &saveToPath) const { assert(std::filesystem::is_directory(saveToPath)); //clear directory for (const auto &entry : std::filesystem::directory_iterator(saveToPath)) std::filesystem::remove_all(entry.path()); //Save optimal X nlohmann::json json_optimizationResults; std::string jsonValue_optimizationVariables; for (const auto &optimalXNameValuePair : optimalXNameValuePairs) { jsonValue_optimizationVariables.append(optimalXNameValuePair.first + ","); } jsonValue_optimizationVariables.pop_back(); // for deleting the last comma json_optimizationResults[JsonKeys::optimizationVariables] = jsonValue_optimizationVariables; for (const auto &optimalXNameValuePair : optimalXNameValuePairs) { json_optimizationResults[optimalXNameValuePair.first] = optimalXNameValuePair.second; } //base triangle json_optimizationResults[JsonKeys::baseTriangle] = std::vector{baseTriangle.cP0(0)[0], baseTriangle.cP0(0)[1], baseTriangle.cP0(0)[2], baseTriangle.cP1(0)[0], baseTriangle.cP1(0)[1], baseTriangle.cP1(0)[2], baseTriangle.cP2(0)[0], baseTriangle.cP2(0)[1], baseTriangle.cP2(0)[2]}; ////Save to json file std::filesystem::path jsonFilePath( std::filesystem::path(saveToPath).append(JsonKeys::filename)); std::ofstream jsonFile_optimizationResults(jsonFilePath.string()); jsonFile_optimizationResults << json_optimizationResults; /*TODO: Refactor since the meshes are saved for each simulation scenario although they do not change*/ //Save jobs and meshes for each simulation scenario //Save the reduced and full patterns const int numberOfSimulationJobs = fullPatternSimulationJobs.size(); for (int simulationScenarioIndex = 0; simulationScenarioIndex < numberOfSimulationJobs; simulationScenarioIndex++) { const std::shared_ptr &pFullPatternSimulationJob = fullPatternSimulationJobs[simulationScenarioIndex]; std::filesystem::path simulationJobFolderPath( std::filesystem::path(saveToPath).append(pFullPatternSimulationJob->label)); std::filesystem::create_directory(simulationJobFolderPath); const auto fullPatternDirectoryPath = std::filesystem::path(simulationJobFolderPath) .append("Full"); std::filesystem::create_directory(fullPatternDirectoryPath); pFullPatternSimulationJob->save(fullPatternDirectoryPath.string()); const std::shared_ptr &pReducedPatternSimulationJob = reducedPatternSimulationJobs[simulationScenarioIndex]; const auto reducedPatternDirectoryPath = std::filesystem::path(simulationJobFolderPath).append("Reduced"); if (!std::filesystem::exists(reducedPatternDirectoryPath)) { std::filesystem::create_directory(reducedPatternDirectoryPath); } pReducedPatternSimulationJob->save(reducedPatternDirectoryPath.string()); } std::cout << "Saved optimization results to:" << saveToPath << std::endl; } template static void applyOptimizationResults_innerHexagon( const ReducedPatternOptimization::Results &reducedPattern_optimizationResults, const vcg::Triangle3 &patternBaseTriangle, MeshType &reducedPattern) { std::unordered_map optimalXVariables(reducedPattern_optimizationResults.optimalXNameValuePairs.begin(), reducedPattern_optimizationResults.optimalXNameValuePairs.end()); assert(optimalXVariables.contains("HexSize") && optimalXVariables.contains("HexAngle")); //Set optimal geometrical params of the reduced pattern CoordType center_barycentric(1, 0, 0); CoordType interfaceEdgeMiddle_barycentric(0, 0.5, 0.5); CoordType movableVertex_barycentric((center_barycentric + interfaceEdgeMiddle_barycentric) * optimalXVariables.at("HexSize")); reducedPattern.vert[1].P() = patternBaseTriangle.cP(0) * movableVertex_barycentric[0] + patternBaseTriangle.cP(1) * movableVertex_barycentric[1] + patternBaseTriangle.cP(2) * movableVertex_barycentric[2]; reducedPattern.vert[1].P() = vcg::RotationMatrix(CoordType{0, 0, 1}, vcg::math::ToRad( optimalXVariables.at("HexAngle"))) * reducedPattern.vert[1].cP(); } static void applyOptimizationResults_elements( const ReducedPatternOptimization::Results &reducedPattern_optimizationResults, const shared_ptr &pTiledReducedPattern_simulationMesh) { // Set reduced parameters fron the optimization results std::unordered_map optimalXVariables(reducedPattern_optimizationResults.optimalXNameValuePairs.begin(), reducedPattern_optimizationResults.optimalXNameValuePairs.end()); const std::string ALabel = "A"; assert(optimalXVariables.contains(ALabel)); const double A = optimalXVariables.at(ALabel); const double beamWidth = std::sqrt(A); const double beamHeight = beamWidth; RectangularBeamDimensions elementDimensions(beamWidth, beamHeight); const double poissonsRatio = 0.3; const std::string ymLabel = "E"; assert(optimalXVariables.contains(ymLabel)); const double E = optimalXVariables.at(ymLabel); const ElementMaterial elementMaterial(poissonsRatio, E); const std::string JLabel = "J"; assert(optimalXVariables.contains(JLabel)); const double J = optimalXVariables.at(JLabel); const std::string I2Label = "I2"; assert(optimalXVariables.contains(I2Label)); const double I2 = optimalXVariables.at(I2Label); const std::string I3Label = "I3"; assert(optimalXVariables.contains(I3Label)); const double I3 = optimalXVariables.at(I3Label); for (int ei = 0; ei < pTiledReducedPattern_simulationMesh->EN(); ei++) { Element &e = pTiledReducedPattern_simulationMesh->elements[ei]; e.setDimensions(elementDimensions); e.setMaterial(elementMaterial); e.J = J; e.I2 = I2; e.I3 = I3; } pTiledReducedPattern_simulationMesh->reset(); } void load(const string &loadFromPath) { assert(std::filesystem::is_directory(loadFromPath)); //Load optimal X nlohmann::json json_optimizationResults; std::ifstream ifs(std::filesystem::path(loadFromPath).append(JsonKeys::filename)); ifs >> json_optimizationResults; std::string optimizationVariablesString = *json_optimizationResults.find( JsonKeys::optimizationVariables); std::string optimizationVariablesDelimeter = ","; size_t pos = 0; std::vector optimizationVariablesNames; while ((pos = optimizationVariablesString.find(optimizationVariablesDelimeter)) != std::string::npos) { const std::string variableName = optimizationVariablesString.substr(0, pos); optimizationVariablesNames.push_back(variableName); optimizationVariablesString.erase(0, pos + optimizationVariablesDelimeter.length()); } optimizationVariablesNames.push_back(optimizationVariablesString); //add last variable name optimalXNameValuePairs.resize(optimizationVariablesNames.size()); for (int xVariable_index = 0; xVariable_index < optimizationVariablesNames.size(); xVariable_index++) { const std::string xVariable_name = optimizationVariablesNames[xVariable_index]; const double xVariable_value = *json_optimizationResults.find(xVariable_name); optimalXNameValuePairs[xVariable_index] = std::make_pair(xVariable_name, xVariable_value); } std::vector baseTriangleVertices = json_optimizationResults.at( JsonKeys::baseTriangle); baseTriangle.P0(0) = CoordType(baseTriangleVertices[0], baseTriangleVertices[1], baseTriangleVertices[2]); baseTriangle.P1(0) = CoordType(baseTriangleVertices[3], baseTriangleVertices[4], baseTriangleVertices[5]); baseTriangle.P2(0) = CoordType(baseTriangleVertices[6], baseTriangleVertices[7], baseTriangleVertices[8]); for (const auto &directoryEntry : filesystem::directory_iterator(loadFromPath)) { const auto simulationScenarioPath = directoryEntry.path(); if (!std::filesystem::is_directory(simulationScenarioPath)) { continue; } // Load reduced pattern files for (const auto &fileEntry : filesystem::directory_iterator( std::filesystem::path(simulationScenarioPath).append("Full"))) { const auto filepath = fileEntry.path(); if (filepath.extension() == ".json") { SimulationJob job; job.load(filepath.string()); fullPatternSimulationJobs.push_back(std::make_shared(job)); } } // Load full pattern files for (const auto &fileEntry : filesystem::directory_iterator( std::filesystem::path(simulationScenarioPath).append("Reduced"))) { const auto filepath = fileEntry.path(); if (filepath.extension() == ".json") { SimulationJob job; job.load(filepath.string()); applyOptimizationResults_innerHexagon(*this, baseTriangle, *job.pMesh); applyOptimizationResults_elements(*this, job.pMesh); reducedPatternSimulationJobs.push_back(std::make_shared(job)); } } } } #if POLYSCOPE_DEFINED void draw() const { initPolyscope(); DRMSimulationModel drmSimulator; LinearSimulationModel linearSimulator; assert(fullPatternSimulationJobs.size() == reducedPatternSimulationJobs.size()); fullPatternSimulationJobs[0]->pMesh->registerForDrawing( Colors::fullInitial); reducedPatternSimulationJobs[0]->pMesh->registerForDrawing( Colors::reducedInitial, false); const int numberOfSimulationJobs = fullPatternSimulationJobs.size(); for (int simulationJobIndex = 0; simulationJobIndex < numberOfSimulationJobs; simulationJobIndex++) { // Drawing of full pattern results const std::shared_ptr &pFullPatternSimulationJob = fullPatternSimulationJobs[simulationJobIndex]; pFullPatternSimulationJob->registerForDrawing( fullPatternSimulationJobs[0]->pMesh->getLabel()); SimulationResults fullModelResults = drmSimulator.executeSimulation( pFullPatternSimulationJob); fullModelResults.registerForDrawing(Colors::fullDeformed, true, true); // SimulationResults fullModelLinearResults = // linearSimulator.executeSimulation(pFullPatternSimulationJob); // fullModelLinearResults.setLabelPrefix("linear"); // fullModelLinearResults.registerForDrawing(Colors::fullDeformed,false); // Drawing of reduced pattern results const std::shared_ptr &pReducedPatternSimulationJob = reducedPatternSimulationJobs[simulationJobIndex]; // SimulationResults reducedModelResults = drmSimulator.executeSimulation( // pReducedPatternSimulationJob); // reducedModelResults.registerForDrawing(Colors::reducedDeformed, false); SimulationResults reducedModelLinearResults = linearSimulator.executeSimulation(pReducedPatternSimulationJob); reducedModelLinearResults.setLabelPrefix("linear"); reducedModelLinearResults.registerForDrawing(Colors::reducedDeformed, true, true); polyscope::options::programName = fullPatternSimulationJobs[0]->pMesh->getLabel(); polyscope::view::resetCameraToHomeView(); polyscope::show(); // Save a screensh const std::string screenshotFilename = "/home/iason/Coding/Projects/Approximating shapes with flat " "patterns/RodModelOptimizationForPatterns/Results/Images/" + fullPatternSimulationJobs[0]->pMesh->getLabel() + "_" + pFullPatternSimulationJob->getLabel(); polyscope::screenshot(screenshotFilename, false); fullModelResults.unregister(); // reducedModelResults.unregister(); reducedModelLinearResults.unregister(); // fullModelLinearResults.unregister(); // double error = computeError( // reducedModelResults.displacements,fullModelResults.displacements, // ); // std::cout << "Error of simulation scenario " // << // simula simulationScenarioStrings[simulationScenarioIndex] // << " is " // << error << std::endl; } } #endif // POLYSCOPE_DEFINED void saveMeshFiles() const { const int numberOfSimulationJobs = fullPatternSimulationJobs.size(); assert(numberOfSimulationJobs != 0 && fullPatternSimulationJobs.size() == reducedPatternSimulationJobs.size()); fullPatternSimulationJobs[0]->pMesh->save( "undeformed " + fullPatternSimulationJobs[0]->pMesh->getLabel() + ".ply"); reducedPatternSimulationJobs[0]->pMesh->save( "undeformed " + reducedPatternSimulationJobs[0]->pMesh->getLabel() + ".ply"); DRMSimulationModel simulator_drm; LinearSimulationModel simulator_linear; for (int simulationJobIndex = 0; simulationJobIndex < numberOfSimulationJobs; simulationJobIndex++) { // Drawing of full pattern results const std::shared_ptr &pFullPatternSimulationJob = fullPatternSimulationJobs[simulationJobIndex]; SimulationResults fullModelResults = simulator_drm.executeSimulation( pFullPatternSimulationJob); fullModelResults.saveDeformedModel(); // Drawing of reduced pattern results const std::shared_ptr &pReducedPatternSimulationJob = reducedPatternSimulationJobs[simulationJobIndex]; SimulationResults reducedModelResults = simulator_linear.executeSimulation( pReducedPatternSimulationJob); reducedModelResults.saveDeformedModel(); } } void writeHeaderTo(csvFile &os) { os << "Total raw Obj value"; os << "Total Obj value"; for (int simulationScenarioIndex = 0; simulationScenarioIndex < fullPatternSimulationJobs.size(); simulationScenarioIndex++) { const std::string simulationScenarioName = fullPatternSimulationJobs[simulationScenarioIndex]->getLabel(); os << "Obj value Trans " + simulationScenarioName; os << "Obj value Rot " + simulationScenarioName; os << "Obj value Total " + simulationScenarioName; } for (const auto &nameValuePair : optimalXNameValuePairs) { os << nameValuePair.first; } os << "Time(s)"; os << "#Crashes"; } void writeResultsTo(const Settings &settings_optimization, csvFile &os) const { os << objectiveValue.totalRaw; os << objectiveValue.total; for (int simulationScenarioIndex = 0; simulationScenarioIndex < fullPatternSimulationJobs.size(); simulationScenarioIndex++) { os << objectiveValue.perSimulationScenario_translational[simulationScenarioIndex]; os << objectiveValue.perSimulationScenario_rotational[simulationScenarioIndex]; os << objectiveValue.perSimulationScenario_total[simulationScenarioIndex]; } for (const auto &optimalXNameValuePair : optimalXNameValuePairs) { os << optimalXNameValuePair.second; } os << time; if (numberOfSimulationCrashes == 0) { os << "-"; } else { os << numberOfSimulationCrashes; } } }; } // namespace ReducedPatternOptimization #endif // REDUCEDMODELOPTIMIZER_STRUCTS_HPP