diff --git a/Examples/ComputeGraph/Benchmarking/CMakeLists.txt b/Examples/ComputeGraph/Benchmarking/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c52359351e19f0e6d0986cf3181aa22b198ae7f4 --- /dev/null +++ b/Examples/ComputeGraph/Benchmarking/CMakeLists.txt @@ -0,0 +1,34 @@ +########################################################################### +# +# Copyright (c) Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +########################################################################### + +project(Example-ComputeGraphBenchmark) + +#----------------------------------------------------------------------------- +# Create executable +#----------------------------------------------------------------------------- +imstk_add_executable(${PROJECT_NAME} computeGraphBenchmarkExample.cpp) + +#----------------------------------------------------------------------------- +# Add the target to Examples folder +#----------------------------------------------------------------------------- +SET_TARGET_PROPERTIES (${PROJECT_NAME} PROPERTIES FOLDER Examples/ComputeGraph) + +#----------------------------------------------------------------------------- +# Link libraries to executable +#----------------------------------------------------------------------------- +target_link_libraries(${PROJECT_NAME} SimulationManager apiUtilities) diff --git a/Examples/ComputeGraph/Benchmarking/computeGraphBenchmarkExample.cpp b/Examples/ComputeGraph/Benchmarking/computeGraphBenchmarkExample.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8aeeca5ed7163839a2887c5e84034b93416a3f79 --- /dev/null +++ b/Examples/ComputeGraph/Benchmarking/computeGraphBenchmarkExample.cpp @@ -0,0 +1,159 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#include "imstkAPIUtilities.h" +#include "imstkCamera.h" +#include "imstkComputeGraphVizWriter.h" +#include "imstkLineMesh.h" +#include "imstkPbdModel.h" +#include "imstkPbdObject.h" +#include "imstkScene.h" +#include "imstkSceneManager.h" +#include "imstkSimulationManager.h" + +using namespace imstk; + +/// +/// \brief This examples uses the benchmarking features of the computational graph +/// This allows one to see the elapsed time of every step of iMSTK as well as +/// export the computational graph and show information such as the critical path +/// +int +main() +{ + auto simManager = std::make_shared<SimulationManager>(); + auto scene = simManager->createNewScene("PBDString"); + scene->getConfig()->benchmarkingEnabled = true; + + // Setup N separate string simulations with varying bend stiffnesses + const unsigned int numStrings = 8; + const unsigned int numVerts = 30; + const double stringSpacing = 2.0; // How far each string is apart + const double stringLength = 10.0; // Total length of string + const Color startColor = Color::Red; // Color of first string + const Color endColor = Color::Green; // Color of last string + + struct PbdSim + { + std::shared_ptr<LineMesh> geometry; + std::shared_ptr<PbdObject> object; + std::shared_ptr<PbdModel> model; + std::shared_ptr<PBDModelConfig> params; + std::shared_ptr<VisualModel> visualModel; + }; + std::vector<PbdSim> sims(numStrings); + + const double size = stringSpacing * (numStrings - 1); + const double vertexSpacing = stringLength / numVerts; + for (unsigned int i = 0; i < numStrings; i++) + { + // Setup the line mesh + sims[i].geometry = std::make_shared<LineMesh>(); + StdVectorOfVec3d vertList; + vertList.resize(numVerts); + for (size_t j = 0; j < numVerts; j++) + { + vertList[j] = Vec3d( + static_cast<double>(i) * stringSpacing - size * 0.5, + stringLength * 0.5 - static_cast<double>(j) * vertexSpacing, 0.0); + } + sims[i].geometry->setInitialVertexPositions(vertList); + sims[i].geometry->setVertexPositions(vertList); + + // Add connectivity data + std::vector<LineMesh::LineArray> segments; + for (size_t j = 0; j < numVerts - 1; j++) + { + LineMesh::LineArray seg = { j, j + 1 }; + segments.push_back(seg); + } + + sims[i].geometry->setLinesVertices(segments); + + sims[i].object = std::make_shared<PbdObject>("String " + std::to_string(i)); + sims[i].model = std::make_shared<PbdModel>(); + sims[i].model->setModelGeometry(sims[i].geometry); + + // Configure the parameters with bend stiffnesses varying from 0.001 to ~0.1 + sims[i].params = std::make_shared<PBDModelConfig>(); + sims[i].params->enableConstraint(PbdConstraint::Type::Distance, 0.001); + sims[i].params->enableConstraint(PbdConstraint::Type::Bend, static_cast<double>(i) * 0.1 / numStrings + 0.001); + sims[i].params->m_fixedNodeIds = { 0 }; + sims[i].params->m_uniformMassValue = 5.0; + sims[i].params->m_gravity = Vec3d(0, -9.8, 0); + sims[i].params->m_defaultDt = 0.0005; + sims[i].params->m_iterations = 5; + + // Set the parameters + sims[i].model->configure(sims[i].params); + sims[i].object->setDynamicalModel(sims[i].model); + sims[i].object->setPhysicsGeometry(sims[i].geometry); + + sims[i].visualModel = std::make_shared<VisualModel>(sims[i].geometry); + std::shared_ptr<RenderMaterial> material = std::make_shared<RenderMaterial>(); + material->setDisplayMode(RenderMaterial::DisplayMode::Wireframe); + material->setDebugColor(Color::lerpRgb(startColor, endColor, static_cast<double>(i) / (numStrings - 1))); + material->setLineWidth(2.0f); + sims[i].visualModel->setRenderMaterial(material); + sims[i].object->addVisualModel(sims[i].visualModel); + + // Add in scene + scene->addSceneObject(sims[i].object); + } + + // Adjust the camera + scene->getCamera()->setFocalPoint(0.0, 0.0, 0.0); + scene->getCamera()->setPosition(0.0, 0.0, 15.0); + + // Move the points every frame + double t = 0.0; + const double dt = 0.0005; + const double radius = 1.5; + auto movePoints = + [&sims, &t, dt, radius](Module* module) + { + for (unsigned int i = 0; i < sims.size(); i++) + { + Vec3d pos = sims[i].model->getCurrentState()->getVertexPosition(0); + // Move in circle, derivatives of parametric eq of circle + sims[i].model->getCurrentState()->setVertexPosition(0, imstk::Vec3d( + pos.x() + -std::sin(t) * radius * dt, + pos.y(), + pos.z() + std::cos(t) * radius * dt)); + } + t += dt; + }; + simManager->getSceneManager(scene)->setPostUpdateCallback(movePoints); + + // Start + simManager->setActiveScene(scene); + simManager->start(); + + // Write the graph, highlighting the critical path and putting the completion time in the name + ComputeGraphVizWriter writer; + writer.setInput(scene->getComputeGraph()); + writer.setFileName("computeGraphBenchmarkExample.svg"); + writer.setHighlightCriticalPath(true); + writer.setWriteTimes(true); + writer.write(); + + return 0; +} diff --git a/Examples/ComputeGraph/CMakeLists.txt b/Examples/ComputeGraph/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..964a9fe46555e89800ccc1ca30875fc8ac896dab --- /dev/null +++ b/Examples/ComputeGraph/CMakeLists.txt @@ -0,0 +1,21 @@ +#----------------------------------------------------------------------------- +# Add Example subdirectories +#----------------------------------------------------------------------------- +macro(listOfSubDir result curdir) + file(GLOB children RELATIVE ${curdir} ${curdir}/*) + set(dirlist "") + foreach(child ${children}) + if(IS_DIRECTORY ${curdir}/${child}) + list(APPEND dirlist ${child}) + endif() + endforeach() + set(${result} ${dirlist}) +endmacro() + +listOfSubDir(subDirs ${CMAKE_CURRENT_SOURCE_DIR}) + +foreach(subdir ${subDirs}) + add_subdirectory(${subdir}) +endforeach() + + diff --git a/Examples/ComputeGraph/Configuration/CMakeLists.txt b/Examples/ComputeGraph/Configuration/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..3ecf6a10ca4526d0f2fdff3122cefa3017f16692 --- /dev/null +++ b/Examples/ComputeGraph/Configuration/CMakeLists.txt @@ -0,0 +1,34 @@ +########################################################################### +# +# Copyright (c) Kitware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +########################################################################### + +project(Example-ComputeGraphConfigure) + +#----------------------------------------------------------------------------- +# Create executable +#----------------------------------------------------------------------------- +imstk_add_executable(${PROJECT_NAME} computeGraphConfigureExample.cpp) + +#----------------------------------------------------------------------------- +# Add the target to Examples folder +#----------------------------------------------------------------------------- +SET_TARGET_PROPERTIES (${PROJECT_NAME} PROPERTIES FOLDER Examples/ComputeGraph) + +#----------------------------------------------------------------------------- +# Link libraries to executable +#----------------------------------------------------------------------------- +target_link_libraries(${PROJECT_NAME} SimulationManager apiUtilities) diff --git a/Examples/ComputeGraph/Configuration/computeGraphConfigureExample.cpp b/Examples/ComputeGraph/Configuration/computeGraphConfigureExample.cpp new file mode 100644 index 0000000000000000000000000000000000000000..65e03f79d75b152d024e615ba5556b7b22611aad --- /dev/null +++ b/Examples/ComputeGraph/Configuration/computeGraphConfigureExample.cpp @@ -0,0 +1,212 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkAPIUtilities.h" +#include "imstkCamera.h" +#include "imstkComputeGraph.h" +#include "imstkComputeGraphVizWriter.h" +#include "imstkLight.h" +#include "imstkParallelReduce.h" +#include "imstkPbdModel.h" +#include "imstkPbdObject.h" +#include "imstkScene.h" +#include "imstkSceneManager.h" +#include "imstkSimulationManager.h" +#include "imstkSurfaceMesh.h" + +#include "imstkVTKRenderer.h" +#include "VTKRenderer.h" +#include "vtkRenderWindowInteractor.h" + +using namespace imstk; + +static std::unique_ptr<SurfaceMesh> makeCloth( + const double width, const double height, const int nRows, const int nCols) +{ + // Create surface mesh + std::unique_ptr<SurfaceMesh> clothMesh = std::make_unique<SurfaceMesh>(); + StdVectorOfVec3d vertList; + + vertList.resize(nRows * nCols); + const double dy = width / (double)(nCols - 1); + const double dx = height / (double)(nRows - 1); + for (int i = 0; i < nRows; ++i) + { + for (int j = 0; j < nCols; j++) + { + vertList[i * nCols + j] = Vec3d((double)dx * i, 1.0, (double)dy * j); + } + } + clothMesh->setInitialVertexPositions(vertList); + clothMesh->setVertexPositions(vertList); + + // Add connectivity data + std::vector<SurfaceMesh::TriangleArray> triangles; + for (std::size_t i = 0; i < nRows - 1; ++i) + { + for (std::size_t j = 0; j < nCols - 1; j++) + { + SurfaceMesh::TriangleArray tri[2]; + const size_t index1 = i * nCols + j; + const size_t index2 = index1 + nCols; + const size_t index3 = index1 + 1; + const size_t index4 = index2 + 1; + + // Interleave [/][\] + if (i % 2 ^ j % 2) + { + tri[0] = { { index1, index2, index3 } }; + tri[1] = { { index4, index3, index2 } }; + } + else + { + tri[0] = { { index2, index4, index1 } }; + tri[1] = { { index4, index3, index1 } }; + } + triangles.push_back(tri[0]); + triangles.push_back(tri[1]); + } + } + + clothMesh->setTrianglesVertices(triangles); + + return clothMesh; +} + +static std::shared_ptr<PbdObject> makeClothObj(const std::string& name, double width, double height, int nRows, int nCols) +{ + auto clothObj = std::make_shared<PbdObject>(name); + + std::shared_ptr<SurfaceMesh> clothMesh(std::move(makeCloth(width, height, nRows, nCols))); + + // Setup the Parameters + auto pbdParams = std::make_shared<PBDModelConfig>(); + pbdParams->m_uniformMassValue = 1.0; + pbdParams->m_gravity = Vec3d(0, -9.8, 0); + pbdParams->m_defaultDt = 0.005; + pbdParams->m_iterations = 5; + pbdParams->m_viscousDampingCoeff = 0.05; + pbdParams->enableConstraint(PbdConstraint::Type::Distance, 0.1); + pbdParams->enableConstraint(PbdConstraint::Type::Dihedral, 0.001); + std::vector<size_t> fixedNodes(nCols); + for (size_t i = 0; i < fixedNodes.size(); i++) + { + fixedNodes[i] = i; + } + pbdParams->m_fixedNodeIds = fixedNodes; + + // Setup the Model + auto pbdModel = std::make_shared<PbdModel>(); + pbdModel->setModelGeometry(clothMesh); + pbdModel->configure(pbdParams); + + // Setup the VisualModel + auto material = std::make_shared<RenderMaterial>(); + material->setBackFaceCulling(false); + material->setColor(Color::LightGray); + material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface); + + auto clothVisualModel = std::make_shared<VisualModel>(clothMesh); + clothVisualModel->setRenderMaterial(material); + + // Setup the Object + clothObj->addVisualModel(clothVisualModel); + clothObj->setPhysicsGeometry(clothMesh); + clothObj->setDynamicalModel(pbdModel); + + return clothObj; +} + +/// +/// \brief This example demonstrates how to modify the computational graph, post initialization +/// +int +main() +{ + auto simManager = std::make_shared<SimulationManager>(); + auto scene = simManager->createNewScene("PBDCloth"); + + const double width = 10.0; + const double height = 10.0; + const int nRows = 16; + const int nCols = 16; + std::shared_ptr<PbdObject> clothObj = makeClothObj("Cloth", width, height, nRows, nCols); + scene->addSceneObject(clothObj); + + // Light (white) + auto whiteLight = std::make_shared<DirectionalLight>("whiteLight"); + whiteLight->setFocalPoint(Vec3d(5, -8, -5)); + whiteLight->setIntensity(7); + scene->addLight(whiteLight); + + // Light (red) + auto colorLight = std::make_shared<SpotLight>("colorLight"); + colorLight->setPosition(Vec3d(-5, -3, 5)); + colorLight->setFocalPoint(Vec3d(0, -5, 5)); + colorLight->setIntensity(100); + colorLight->setColor(Color::Red); + colorLight->setSpotAngle(30); + scene->addLight(colorLight); + + // Adjust camera + scene->getCamera()->setFocalPoint(0, -5, 5); + scene->getCamera()->setPosition(-15., -5.0, 15.0); + + // Adds a custom physics step to print out intermediate velocities + { + std::shared_ptr<PbdModel> pbdModel = clothObj->getPbdModel(); + scene->setComputeGraphConfigureCallback([&](Scene* scene) + { + // Get the graph + std::shared_ptr<ComputeGraph> graph = scene->getComputeGraph(); + + // First write the graph before we make modifications, just to show the changes + ComputeGraphVizWriter writer; + writer.setInput(graph); + writer.setFileName("computeGraphConfigureExampleOld.svg"); + writer.write(); + + std::shared_ptr<ComputeNode> printVelocities = std::make_shared<ComputeNode>([&]() + { + // Make the timestep a function of max velocity + const StdVectorOfVec3d& velocities = *pbdModel->getCurrentState()->getVelocities(); + for (size_t i = 0; i < velocities.size(); i++) + { + printf("Velocity: %f, %f, %f\n", velocities[i].x(), velocities[i].y(), velocities[i].z()); + } + + }, "PrintVelocities"); + + // After IntegratePosition + graph->insertAfter(pbdModel->getIntegratePositionNode(), printVelocities); + + // Write the modified graph + writer.setFileName("computeGraphConfigureExampleNew.svg"); + writer.write(); + }); + } + + // Start + simManager->setActiveScene(scene); + simManager->start(SimulationStatus::Paused); + + return 0; +} diff --git a/Source/Common/ComputeGraph/imstkComputeGraph.cpp b/Source/Common/ComputeGraph/imstkComputeGraph.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8957da00501ccc1401be678a17830ac7ed4e2816 --- /dev/null +++ b/Source/Common/ComputeGraph/imstkComputeGraph.cpp @@ -0,0 +1,830 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#include "imstkComputeGraph.h" +#include "imstkComputeNode.h" +#include "imstkLogger.h" +#include <queue> +#include <stack> +#include <set> + +namespace imstk +{ +ComputeGraph::ComputeGraph(std::string sourceName, std::string sinkName) : + m_source(std::make_shared<ComputeNode>()), + m_sink(std::make_shared<ComputeNode>()) +{ + m_source->m_name = sourceName; + m_sink->m_name = sinkName; + addNode(m_source); + addNode(m_sink); +} + +ComputeNodeVector::iterator +ComputeGraph::findNode(std::string name) +{ + return std::find_if(m_nodes.begin(), m_nodes.end(), + [&name](const std::shared_ptr<ComputeNode>& x) { return x->m_name == name; }); +} + +ComputeNodeVector::const_iterator +ComputeGraph::findNode(std::string name) const +{ + return std::find_if(m_nodes.begin(), m_nodes.end(), + [&name](const std::shared_ptr<ComputeNode>& x) { return x->m_name == name; }); +} + +ComputeNodeVector::iterator +ComputeGraph::findNode(std::shared_ptr<ComputeNode> node) +{ + return std::find(m_nodes.begin(), m_nodes.end(), node); +} + +ComputeNodeVector::const_iterator +ComputeGraph::findNode(std::shared_ptr<ComputeNode> node) const +{ + return std::find(m_nodes.begin(), m_nodes.end(), node); +} + +bool +ComputeGraph::containsNode(std::shared_ptr<ComputeNode> node) const +{ + return findNode(node) != nodesEnd(); +} + +bool +ComputeGraph::containsEdge(std::shared_ptr<ComputeNode> srcNode, std::shared_ptr<ComputeNode> destNode) +{ + return (m_adjList.count(srcNode) != 0 && m_adjList[srcNode].count(destNode) != 0); +} + +void +ComputeGraph::addEdge(std::shared_ptr<ComputeNode> srcNode, std::shared_ptr<ComputeNode> destNode) +{ + m_adjList[srcNode].insert(destNode); + m_invAdjList[destNode].insert(srcNode); +} + +void +ComputeGraph::nestGraph(std::shared_ptr<ComputeGraph> subgraph, std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink) +{ + // Ensure source and sink are in the graph + if (findNode(source) == nodesEnd()) + { + LOG(WARNING) << "Tried to nest a graph using source, but source does not exist in this"; + return; + } + if (findNode(sink) == nodesEnd()) + { + LOG(WARNING) << "Tried to nest a graph using sink, but sink does not exist in this"; + return; + } + + // Copy all of the nodes into this graph (check duplicates) + for (ComputeNodeVector::iterator it = subgraph->getNodes().begin(); it != subgraph->getNodes().end(); it++) + { + addNode(*it); + } + // Copy all the edges into the graph (no need to check for duplicates) + const ComputeNodeAdjList& adjList = subgraph->getAdjList(); + for (ComputeNodeAdjList::const_iterator it = adjList.begin(); it != adjList.end(); it++) + { + const ComputeNodeSet& outputNodes = it->second; + for (ComputeNodeSet::const_iterator jt = outputNodes.begin(); jt != outputNodes.end(); jt++) + { + addEdge(it->first, *jt); + } + } + // Add source and sink edges + addEdge(source, subgraph->getSource()); + addEdge(subgraph->getSink(), sink); +} + +void +ComputeGraph::removeEdge(std::shared_ptr<ComputeNode> srcNode, std::shared_ptr<ComputeNode> destNode) +{ + if (m_adjList.count(srcNode) == 0) + { + return; + } + if (m_adjList[srcNode].count(destNode) == 0) + { + return; + } + m_adjList[srcNode].erase(destNode); + m_invAdjList[destNode].erase(srcNode); + if (m_adjList[srcNode].size() == 0) + { + m_adjList.erase(srcNode); + } + if (m_invAdjList[destNode].size() == 0) + { + m_invAdjList.erase(destNode); + } +} + +bool +ComputeGraph::addNode(std::shared_ptr<ComputeNode> node) +{ + // If the node already exists return false + if (!containsNode(node)) + { + // Put it in this graph + m_nodes.push_back(node); + return true; + } + else + { + return false; + } +} + +std::shared_ptr<ComputeNode> +ComputeGraph::addFunction(std::string name, std::function<void()> func) +{ + std::shared_ptr<ComputeNode> node = std::make_shared<ComputeNode>(func, name); + m_nodes.push_back(node); + return node; +} + +bool +ComputeGraph::removeNode(std::shared_ptr<ComputeNode> node) +{ + if (!containsNode(node)) + { + LOG(INFO) << "Tried to remove node " + node->m_name + " from graph but it doesn't contain the node."; + return false; + } + + // Erase edges + ComputeNodeSet inputs = m_invAdjList[node]; + ComputeNodeSet outputs = m_adjList[node]; + for (ComputeNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++) + { + removeEdge(*i, node); + } + for (ComputeNodeSet::iterator i = outputs.begin(); i != outputs.end(); i++) + { + removeEdge(node, *i); + } + + // Finally remove the node from the graph + ComputeNodeVector::iterator it = findNode(node); + if (it != nodesEnd()) + { + m_nodes.erase(it); + } + return true; +} + +bool +ComputeGraph::removeNodeAndFix(std::shared_ptr<ComputeNode> node) +{ + if (!containsNode(node)) + { + LOG(INFO) << "Tried to remove node " + node->m_name + " from graph but it doesn't contain the node."; + return false; + } + + // Erase edges + ComputeNodeSet inputs = m_invAdjList[node]; + ComputeNodeSet outputs = m_adjList[node]; + for (ComputeNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++) + { + removeEdge(*i, node); + } + for (ComputeNodeSet::iterator i = outputs.begin(); i != outputs.end(); i++) + { + removeEdge(node, *i); + } + + // Fix the edges + for (ComputeNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++) + { + for (ComputeNodeSet::iterator j = outputs.begin(); j != outputs.end(); j++) + { + addEdge(*i, *j); + } + } + + // Finally remove the node from the graph + ComputeNodeVector::iterator it = findNode(node); + if (it != nodesEnd()) + { + m_nodes.erase(it); + } + + return true; +} + +void +ComputeGraph::insertAfter(std::shared_ptr<ComputeNode> refNode, std::shared_ptr<ComputeNode> newNode) +{ + // Try to add to graph, if already exists, exit + if (!addNode(newNode)) + { + return; + } + + // Remove output edges + ComputeNodeSet outputs = m_adjList[refNode]; // Copy (since we are modifying) + for (ComputeNodeSet::iterator i = outputs.begin(); i != outputs.end(); i++) + { + removeEdge(refNode, *i); + } + + // Add refNode->newNode + addEdge(refNode, newNode); + + // Add newNode->outputs[i] + for (ComputeNodeSet::iterator i = outputs.begin(); i != outputs.end(); i++) + { + addEdge(newNode, *i); + } +} + +void +ComputeGraph::insertBefore(std::shared_ptr<ComputeNode> refNode, std::shared_ptr<ComputeNode> newNode) +{ + // Try to add to graph, if already exists, exit + if (!addNode(newNode)) + { + return; + } + + // Remove input edges + ComputeNodeSet inputs = m_invAdjList[refNode]; // Copy (since we are modifying) + for (ComputeNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++) + { + removeEdge(*i, refNode); + } + + // inputs[i]->newNode + for (ComputeNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++) + { + addEdge(*i, newNode); + } + + // refNode->newNode + addEdge(newNode, refNode); +} + +bool +ComputeGraph::reaches(std::shared_ptr<ComputeNode> srcNode, std::shared_ptr<ComputeNode> destNode) +{ + const ComputeNodeAdjList& adjList = getAdjList(); + + // Simple BFS + std::unordered_set<std::shared_ptr<ComputeNode>> visitedNodes; + + // It inserts itself as well + std::queue<std::shared_ptr<ComputeNode>> nodeStack; + nodeStack.push(srcNode); + while (!nodeStack.empty()) + { + std::shared_ptr<ComputeNode> currNode = nodeStack.front(); + nodeStack.pop(); + + // If we've arrived at the desired node + if (destNode == currNode) + { + return true; + } + + // Add children to stack if not yet visited + if (adjList.count(currNode) != 0) + { + const ComputeNodeSet& outputNodes = adjList.at(currNode); + for (ComputeNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++) + { + std::shared_ptr<ComputeNode> childNode = *j; + if (visitedNodes.count(childNode) == 0) + { + visitedNodes.insert(childNode); + nodeStack.push(childNode); + } + } + } + } + return false; +} + +void +ComputeGraph::clear() +{ + m_nodes.clear(); + clearEdges(); + addNode(m_source); + addNode(m_sink); +} + +std::shared_ptr<ComputeGraph> +ComputeGraph::sum(std::shared_ptr<ComputeGraph> graphA, std::shared_ptr<ComputeGraph> graphB) +{ + std::shared_ptr<ComputeGraph> results = std::make_shared<ComputeGraph>(); + // Get rid of the results source/sink + results->m_source = nullptr; + results->m_sink = nullptr; + results->m_nodes.clear(); + + // Sum the nodes + ComputeNodeVector& nodesA = graphA->getNodes(); + for (size_t i = 0; i < nodesA.size(); i++) + { + results->addNode(nodesA[i]); + } + ComputeNodeVector& nodesB = graphB->getNodes(); + for (size_t i = 0; i < nodesB.size(); i++) + { + results->addNode(nodesB[i]); + } + + // Now sum the edges + const ComputeNodeAdjList& adjListA = graphA->getAdjList(); + for (ComputeNodeAdjList::const_iterator it = adjListA.begin(); it != adjListA.end(); it++) + { + const ComputeNodeSet& outputNodes = it->second; + for (ComputeNodeSet::const_iterator jt = outputNodes.begin(); jt != outputNodes.end(); jt++) + { + results->addEdge(it->first, *jt); + } + } + const ComputeNodeAdjList& adjListB = graphB->getAdjList(); + for (ComputeNodeAdjList::const_iterator it = adjListB.begin(); it != adjListB.end(); it++) + { + const ComputeNodeSet& outputNodes = it->second; + for (ComputeNodeSet::const_iterator jt = outputNodes.begin(); jt != outputNodes.end(); jt++) + { + results->addEdge(it->first, *jt); + } + } + + return results; +} + +std::shared_ptr<ComputeNodeList> +ComputeGraph::topologicalSort(std::shared_ptr<ComputeGraph> graph) +{ + // Compute the number of inputs to each node (we will remove these as we go) + std::unordered_map<std::shared_ptr<ComputeNode>, size_t> numInputs; + + const ComputeNodeAdjList& adjList = graph->getAdjList(); + const ComputeNodeAdjList& invAdjList = graph->getInvAdjList(); + + for (ComputeNodeAdjList::const_iterator i = invAdjList.begin(); i != invAdjList.end(); i++) + { + numInputs[i->first] = invAdjList.size(); + } + + // Create an edge blacklist for edge removal during algorithm + std::unordered_map<std::shared_ptr<ComputeNode>, std::shared_ptr<ComputeNode>> removedEdges; + + auto edgeHasBeenRemoved = [&](const std::shared_ptr<ComputeNode>& node1, const std::shared_ptr<ComputeNode>& node2) + { + return (removedEdges.count(node1) != 0) && (removedEdges[node1] == node2); + }; + + // Kahns algorithm (BFS/queue) + // iterate through all nodes (BFS or DFS) removing edges + // nodes are accepted when all input edges have been removed + std::queue<std::shared_ptr<ComputeNode>> sources; + sources.push(graph->m_source); + std::shared_ptr<ComputeNodeList> results = std::make_shared<ComputeNodeList>(); + + while (!sources.empty()) + { + // Remove a node + std::shared_ptr<ComputeNode> node = sources.front(); + sources.pop(); + + results->push_back(node); + + // As long as there are children + if (adjList.count(node) != 0) + { + const ComputeNodeSet& outputNodes = adjList.at(node); + for (ComputeNodeSet::const_iterator it = outputNodes.begin(); it != outputNodes.end(); it++) + { + std::shared_ptr<ComputeNode> childNode = *it; + // If edge is present + if (!edgeHasBeenRemoved(node, childNode)) + { + // Mark edge as removed + removedEdges[node] = childNode; + + // Decrement amount of inputs + numInputs[childNode]--; + if (numInputs[childNode] <= 0) + { + sources.push(childNode); + } + } + } + } + } + return results; +} + +std::shared_ptr<ComputeGraph> +ComputeGraph::resolveCriticalNodes(std::shared_ptr<ComputeGraph> graph) +{ + std::shared_ptr<ComputeGraph> results = std::make_shared<ComputeGraph>(*graph); + + const ComputeNodeAdjList& adjList = graph->getAdjList(); + const ComputeNodeVector& nodes = graph->getNodes(); + + // Compute the levels of each node via DFS + std::unordered_map<std::shared_ptr<ComputeNode>, int> depths; + { + std::unordered_set<std::shared_ptr<ComputeNode>> visitedNodes; + + // DFS for the dependencies + std::stack<std::shared_ptr<ComputeNode>> nodeStack; + depths[graph->getSource()] = 0; + nodeStack.push(graph->getSource()); + while (!nodeStack.empty()) + { + std::shared_ptr<ComputeNode> currNode = nodeStack.top(); + int currLevel = depths[currNode]; + nodeStack.pop(); + + // Add children to stack if not yet visited + if (adjList.count(currNode) != 0) + { + const ComputeNodeSet& outputNodes = adjList.at(currNode); + for (ComputeNodeSet::const_iterator i = outputNodes.begin(); i != outputNodes.end(); i++) + { + std::shared_ptr<ComputeNode> childNode = *i; + if (visitedNodes.count(childNode) == 0) + { + visitedNodes.insert(childNode); + depths[childNode] = currLevel + 1; + nodeStack.push(childNode); + } + } + } + } + } + + // Identify the set of critical nodes + ComputeNodeVector critNodes; + for (size_t i = 0; i < nodes.size(); i++) + { + if (nodes[i]->m_critical) + critNodes.push_back(nodes[i]); + } + + // Compute the critical adjacency list + ComputeNodeAdjList critAdjList; + for (size_t i = 0; i < critNodes.size(); i++) + { + std::unordered_set<std::shared_ptr<ComputeNode>> visitedNodes; + + // DFS for the dependencies + std::stack<std::shared_ptr<ComputeNode>> nodeStack; + nodeStack.push(critNodes[i]); + while (!nodeStack.empty()) + { + std::shared_ptr<ComputeNode> currNode = nodeStack.top(); + nodeStack.pop(); + + // If you can reach one critical node from the other then they are adjacent + if (currNode->m_critical) + { + critAdjList[critNodes[i]].insert(currNode); + } + + // Add children to stack if not yet visited + if (adjList.count(currNode) != 0) + { + const ComputeNodeSet& outputNodes = adjList.at(currNode); + for (ComputeNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++) + { + std::shared_ptr<ComputeNode> childNode = *j; + if (visitedNodes.count(childNode) == 0) + { + visitedNodes.insert(childNode); + nodeStack.push(childNode); + } + } + } + } + } + + // Now we know which critical nodes depend on each other (we are interested in those that aren't) + // For every critical pair + for (size_t i = 0; i < critNodes.size(); i++) + { + std::shared_ptr<ComputeNode> srcNode = critNodes[i]; + for (size_t j = i + 1; j < critNodes.size(); j++) + { + std::shared_ptr<ComputeNode> destNode = critNodes[j]; + // If the edge doesn't exist, either way + if ((critAdjList.count(srcNode) == 0 || critAdjList.at(srcNode).count(destNode) == 0) && + (critAdjList.count(destNode) == 0 || critAdjList.at(destNode).count(srcNode) == 0)) + { + // Add an edge between the critical nodes in the direction of levels + int leveli = depths[srcNode]; + int levelj = depths[destNode]; + // If i is below j, then j->i + if (leveli > levelj) + { + results->addEdge(destNode, srcNode); + } + else + { + results->addEdge(srcNode, destNode); + } + } + } + } + + return results; +} + +std::shared_ptr<ComputeGraph> +ComputeGraph::transitiveReduce(std::shared_ptr<ComputeGraph> graph) +{ + // It's a bad idea to do this method if the graph is cyclic + if (isCyclic(graph)) + return nullptr; + + std::shared_ptr<ComputeGraph> results = std::make_shared<ComputeGraph>(*graph); + + // Copy the adjList + const ComputeNodeAdjList adjList = results->getAdjList(); + + // For every edge in the graph + for (ComputeNodeAdjList::const_iterator i = adjList.begin(); i != adjList.end(); i++) + { + std::shared_ptr<ComputeNode> inputNode = i->first; + const ComputeNodeSet& outputNodes = i->second; + for (ComputeNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++) + { + std::shared_ptr<ComputeNode> outputNode = *j; + // Edge inputNode->outputNode + + // Remove the edge and see if it still reaches + results->removeEdge(inputNode, outputNode); + + // If there still exists a path inputNode->outputNode, leave it removed + if (!results->reaches(inputNode, outputNode)) + { + results->addEdge(inputNode, outputNode); + } + } + } + + return results; +} + +std::shared_ptr<ComputeGraph> +ComputeGraph::nonFunctionalPrune(std::shared_ptr<ComputeGraph> graph) +{ + std::shared_ptr<ComputeGraph> results = std::make_shared<ComputeGraph>(*graph); + + const ComputeNodeAdjList& adjList = results->getAdjList(); + const ComputeNodeAdjList& invAdjList = results->getInvAdjList(); + ComputeNodeVector& nodes = results->getNodes(); + + for (size_t i = 0; i < nodes.size(); i++) + { + std::shared_ptr<ComputeNode> node = nodes[i]; + + // These can't be removed (following code would break as they have no inputs/outputs) + if (node == graph->getSource() || node == graph->getSink()) + { + continue; + } + + // If node not functional and has single input/output it is removeable + if (!node->isFunctional()) + { + // Get copies of the inputs and outputs of the node + ComputeNodeSet inputs = invAdjList.at(node); + ComputeNodeSet outputs = adjList.at(node); + if (inputs.size() == 1 && outputs.size() == 1) + { + // Remove inputs/outputs of node + for (ComputeNodeSet::iterator j = inputs.begin(); j != inputs.end(); j++) + { + results->removeEdge(*j, node); + } + for (ComputeNodeSet::iterator j = outputs.begin(); j != outputs.end(); j++) + { + results->removeEdge(node, *j); + } + + // Fix the edges + for (ComputeNodeSet::iterator j = inputs.begin(); j != inputs.end(); j++) + { + for (ComputeNodeSet::iterator k = outputs.begin(); k != outputs.end(); k++) + { + results->addEdge(*j, *k); + } + } + + // Finally remove the node from the graph + nodes.erase(nodes.begin() + i); + + // If node was removed, don't advance + i--; + } + } + } + return results; +} + +bool +ComputeGraph::isCyclic(std::shared_ptr<ComputeGraph> graph) +{ + // Brute force, DFS every node to find it again + const ComputeNodeAdjList& adjList = graph->getAdjList(); + const ComputeNodeVector& nodes = graph->getNodes(); + for (size_t i = 0; i < nodes.size(); i++) + { + std::unordered_set<std::shared_ptr<ComputeNode>> visitedNodes; + + // DFS for the dependencies (start at children instead of itself) + std::stack<std::shared_ptr<ComputeNode>> nodeStack; + // Add children to stack if not yet visited + if (adjList.count(nodes[i]) != 0) + { + const ComputeNodeSet& outputNodes = adjList.at(nodes[i]); + for (ComputeNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++) + { + std::shared_ptr<ComputeNode> childNode = *j; + if (visitedNodes.count(childNode) == 0) + { + visitedNodes.insert(childNode); + nodeStack.push(childNode); + } + } + } + while (!nodeStack.empty()) + { + std::shared_ptr<ComputeNode> currNode = nodeStack.top(); + nodeStack.pop(); + + // If we revisit a node then it must be cyclic + if (nodes[i] == currNode) + { + return true; + } + + // Add children to stack if not yet visited + if (adjList.count(currNode) != 0) + { + const ComputeNodeSet& outputNodes = adjList.at(currNode); + for (ComputeNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++) + { + std::shared_ptr<ComputeNode> childNode = *j; + if (visitedNodes.count(childNode) == 0) + { + visitedNodes.insert(childNode); + nodeStack.push(childNode); + } + } + } + } + } + + return false; +} + +std::unordered_map<std::shared_ptr<ComputeNode>, std::string> +ComputeGraph::getUniqueNames(std::shared_ptr<ComputeGraph> graph, bool apply) +{ + // Produce non colliding names + std::unordered_map<std::shared_ptr<ComputeNode>, std::string> nodeNames; + std::unordered_map<std::string, int> names; + const ComputeNodeVector& nodes = graph->getNodes(); + for (size_t i = 0; i < nodes.size(); i++) + { + nodeNames[nodes[i]] = nodes[i]->m_name; + names[nodes[i]->m_name]++; + } + // Adjust names + for (std::unordered_map<std::shared_ptr<ComputeNode>, std::string>::iterator it = nodeNames.begin(); + it != nodeNames.end(); it++) + { + int nameIter = 0; + std::string currName = it->second; + // If we can find a node with this name, increment name counter and try again + while (names[currName] > 1) + { + names[currName]--; + currName = it->second + std::to_string(nameIter); + names[currName]++; + nameIter++; + } + nodeNames[it->first] = currName; + } + if (apply) + { + for (std::shared_ptr<ComputeNode> node : nodes) + { + node->m_name = nodeNames[node]; + } + } + return nodeNames; +} + +std::unordered_map<std::shared_ptr<ComputeNode>, double> +ComputeGraph::getTimes(std::shared_ptr<ComputeGraph> graph) +{ + const ComputeNodeAdjList& adjList = graph->getAdjList(); + + // Setup a map for total elapsed times at each node + std::unordered_map<std::shared_ptr<ComputeNode>, double> times; + + { + std::unordered_set<std::shared_ptr<ComputeNode>> visitedNodes; + + // BFS down the tree computing total times + std::stack<std::shared_ptr<ComputeNode>> nodeStack; + times[graph->getSource()] = 0.0; + nodeStack.push(graph->getSource()); + while (!nodeStack.empty()) + { + std::shared_ptr<ComputeNode> currNode = nodeStack.top(); + nodeStack.pop(); + + // Add children to stack if not yet visited + if (adjList.count(currNode) != 0) + { + const ComputeNodeSet& outputNodes = adjList.at(currNode); + for (ComputeNodeSet::const_iterator i = outputNodes.begin(); i != outputNodes.end(); i++) + { + std::shared_ptr<ComputeNode> childNode = *i; + if (visitedNodes.count(childNode) == 0) + { + visitedNodes.insert(childNode); + times[childNode] = times[currNode] + childNode->m_elapsedTime; + nodeStack.push(childNode); + } + } + } + } + } + return times; +} + +ComputeNodeList +ComputeGraph::getCriticalPath(std::shared_ptr<ComputeGraph> graph) +{ + std::unordered_map<std::shared_ptr<ComputeNode>, double> times = getTimes(graph); + + // Now backtrack to acquire the path of longest duration + const ComputeNodeAdjList& invAdjList = graph->getInvAdjList(); + std::list<std::shared_ptr<ComputeNode>> results; + { + std::shared_ptr<ComputeNode> currNode = graph->getSink(); + // Starting from the sink, choose the input with the longest total elapsed time + while (currNode != graph->getSource()) + { + results.push_front(currNode); + std::shared_ptr<ComputeNode> longestNode = nullptr; + double maxTime = 0.0; + + // For every parent + if (invAdjList.count(currNode) != 0) + { + const ComputeNodeSet& inputNodes = invAdjList.at(currNode); + for (ComputeNodeSet::const_iterator i = inputNodes.begin(); i != inputNodes.end(); i++) + { + std::shared_ptr<ComputeNode> parentNode = *i; + if (times[parentNode] >= maxTime) + { + maxTime = times[parentNode]; + longestNode = parentNode; + } + } + } + + // Move to the node with the longest time + currNode = longestNode; + } + } + results.push_front(graph->getSource()); + return results; +} +} \ No newline at end of file diff --git a/Source/Common/ComputeGraph/imstkComputeGraph.h b/Source/Common/ComputeGraph/imstkComputeGraph.h new file mode 100644 index 0000000000000000000000000000000000000000..ae2422f16a821681c605afac7783040e014a6da4 --- /dev/null +++ b/Source/Common/ComputeGraph/imstkComputeGraph.h @@ -0,0 +1,242 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include "imstkComputeNode.h" + +#include <list> +#include <memory> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +namespace imstk +{ +using ComputeNodeVector = std::vector<std::shared_ptr<ComputeNode>>; +using ComputeNodeList = std::list<std::shared_ptr<ComputeNode>>; +using ComputeNodeSet = std::unordered_set<std::shared_ptr<ComputeNode>>; +using ComputeNodeAdjList = std::unordered_map<std::shared_ptr<ComputeNode>, ComputeNodeSet>; + +/// +/// \brief Base class for compute graphs, a collection of computational nodes +/// edges can exist between graphs. Maintains nodes, adjList, and invAdjList +/// TODO: move algorithms out of the container like STL +/// +class ComputeGraph +{ +public: + ComputeGraph(std::string sourceName = "Source", std::string sinkName = "Sink"); + virtual ~ComputeGraph() = default; + +public: + std::shared_ptr<ComputeNode> getSource() { return m_source; } + std::shared_ptr<ComputeNode> getSink() { return m_sink; } + + /// + /// \brief Get the nodes belonging to this graph + /// + ComputeNodeVector& getNodes() { return m_nodes; } + + /// + /// \brief Get the edges belonging to this graph + /// + ComputeNodeAdjList& getAdjList() { return m_adjList; } + + /// + /// \brief Get the inverse edges belonging to this graph + /// + ComputeNodeAdjList& getInvAdjList() { return m_invAdjList; } + + // Node operations +public: + /// + /// \brief Linear search for node by name within this graph + /// + ComputeNodeVector::iterator findNode(std::string name); + ComputeNodeVector::const_iterator findNode(std::string name) const; + /// + /// \brief Linear search for node within this graph + /// + ComputeNodeVector::iterator findNode(std::shared_ptr<ComputeNode> node); + ComputeNodeVector::const_iterator findNode(std::shared_ptr<ComputeNode> node) const; + + /// + /// \brief Check if node exists in this graph + /// + bool containsNode(std::shared_ptr<ComputeNode> node) const; + + /// + /// \brief Returns the start of the node container + /// + ComputeNodeVector::iterator nodesEnd() { return m_nodes.end(); } + ComputeNodeVector::const_iterator nodesEnd() const { return m_nodes.end(); } + /// + /// \brief Returns the start of the node container + /// + ComputeNodeVector::iterator nodesBegin() { return m_nodes.begin(); } + ComputeNodeVector::const_iterator nodesBegin() const { return m_nodes.begin(); } + + /// + /// \brief Adds a node to the graph, returns if successful. + /// Returns false and fails if node already exists in graph + /// + bool addNode(std::shared_ptr<ComputeNode> node); + + /// + /// \brief Creates a node for the function, adds it to the graph + /// + std::shared_ptr<ComputeNode> addFunction(std::string name, std::function<void()> func); + + /// + /// \brief Removes node from the graph and all relevant edges + /// Returns false and fails if node is not present in graph + /// "Expensive" operation as no inverse adjacency list is maintained + /// full search done for relevant edges + /// + bool removeNode(std::shared_ptr<ComputeNode> node); + + /// + /// \brief Removes node from the graph and all relevant edges + /// any incoming edges to the node are redirected to all its outputs + /// + bool removeNodeAndFix(std::shared_ptr<ComputeNode> node); + + /// + /// \brief newNode gets placed after refNode & added to the graph. newNode takes on refNode's outputs + /// + void insertAfter(std::shared_ptr<ComputeNode> refNode, std::shared_ptr<ComputeNode> newNode); + + /// + /// \brief newNode gets placed before refNode & added to the graph. newNode takes on refNode's inputs + /// + void insertBefore(std::shared_ptr<ComputeNode> refNode, std::shared_ptr<ComputeNode> newNode); + + // Edge operations +public: + /// + /// \brief Returns whether or not this graph contains the given directed edge + /// + bool containsEdge(std::shared_ptr<ComputeNode> srcNode, std::shared_ptr<ComputeNode> destNode); + + /// + /// \brief Adds a directed edge to the graph (doesn't check if nodes are in graph) + /// + void addEdge(std::shared_ptr<ComputeNode> srcNode, std::shared_ptr<ComputeNode> destNode); + + /// + /// \brief Attachs another ComputeGraph as a subgraph (copies nodes and edges, then connects source->subgraph::source, subgraph::sink->sink), + /// source and sink must exist in this graph. Also serves as a graph sum between this and subgraph + /// + void nestGraph(std::shared_ptr<ComputeGraph> subgraph, std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink); + + /// + /// \brief Removes an edge from the graph (removes from adjList and invAdjList, cleans) + /// + void removeEdge(std::shared_ptr<ComputeNode> srcNode, std::shared_ptr<ComputeNode> destNode); + + /// + /// \brief Returns true if srcNode reaches destNode + /// + bool reaches(std::shared_ptr<ComputeNode> srcNode, std::shared_ptr<ComputeNode> destNode); + +public: + /// + /// \brief Removes all nodes & edges from the graph. Source and sink nodes maintained. + /// + void clear(); + + /// + /// \brief Removes all edges from the graph + /// + void clearEdges() + { + m_adjList.clear(); + m_invAdjList.clear(); + } + + // Graph algorithms, todo: Move into filtering module +public: + /// + /// \brief Graph sum, shared references are considered identical nodes, source/sink of results invalidated/nullptr + /// + static std::shared_ptr<ComputeGraph> sum(std::shared_ptr<ComputeGraph> graphA, std::shared_ptr<ComputeGraph> graphB); + + /// + /// \brief Topological sort of all nodes within graph + /// + static std::shared_ptr<ComputeNodeList> topologicalSort(std::shared_ptr<ComputeGraph> graph); + + /// + /// \brief Makes sure no two *critical nodes* run at the same time by establishing an edge between them. + /// + static std::shared_ptr<ComputeGraph> resolveCriticalNodes(std::shared_ptr<ComputeGraph> graph); + + /// + /// \brief Remove redudant edges. Removal is such that all vertices are still reachable and graph goes from source->sink + /// returns nullptr if failed. Only fails if graph is cyclic. + /// + static std::shared_ptr<ComputeGraph> transitiveReduce(std::shared_ptr<ComputeGraph> graph); + + /// + /// \brief Removes nullptr/nonfunctional ComputeNodes that don't split/join + /// + static std::shared_ptr<ComputeGraph> nonFunctionalPrune(std::shared_ptr<ComputeGraph> graph); + + /// + /// \brief Simplifies the graph in a way that retains functionality + /// Performs Nonfunctional Prune and Transitive Reduction removing redundant edges and non functional nodes + /// + static std::shared_ptr<ComputeGraph> reduce(std::shared_ptr<ComputeGraph> graph) + { + return nonFunctionalPrune(transitiveReduce(graph)); + } + + /// + /// \brief Returns if Graph is cyclic or not + /// + static bool isCyclic(std::shared_ptr<ComputeGraph> graph); + + /// + /// \brief Iterates the names with numeric postfix to generate uniques + /// + static std::unordered_map<std::shared_ptr<ComputeNode>, std::string> getUniqueNames(std::shared_ptr<ComputeGraph> graph, bool apply = false); + + /// + /// \brief Gets the completion times of each node (source = ~0s) + /// + static std::unordered_map<std::shared_ptr<ComputeNode>, double> getTimes(std::shared_ptr<ComputeGraph> graph); + + /// + /// \brief Computes the critical path + /// + static ComputeNodeList getCriticalPath(std::shared_ptr<ComputeGraph> graph); + +protected: + ComputeNodeVector m_nodes; + ComputeNodeAdjList m_adjList; ///> This gives the outputs of every node + ComputeNodeAdjList m_invAdjList; ///> This gives the inputs of every node + // inv adjlist? + + std::shared_ptr<ComputeNode> m_source = nullptr; + std::shared_ptr<ComputeNode> m_sink = nullptr; +}; +} \ No newline at end of file diff --git a/Source/Common/ComputeGraph/imstkComputeGraphController.h b/Source/Common/ComputeGraph/imstkComputeGraphController.h new file mode 100644 index 0000000000000000000000000000000000000000..bc1aab113556c6ef8c6a7aad5fcafd9d4b563922 --- /dev/null +++ b/Source/Common/ComputeGraph/imstkComputeGraphController.h @@ -0,0 +1,58 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once +#include <memory> + +namespace imstk +{ +class ComputeGraph; + +/// +/// \brief Base class for compute graph controllers which are responsible +/// for executing the compute graph +/// +class ComputeGraphController +{ +public: + ComputeGraphController() = default; + virtual ~ComputeGraphController() = default; + +public: + virtual void setComputeGraph(std::shared_ptr<ComputeGraph> graph) { this->m_graph = graph; } + + std::shared_ptr<ComputeGraph> getComputeGraph() const { return m_graph; } + + /// + /// \brief Initialization of the ComputeGraphController, good for anything the controller might need + /// to do before execution + /// + virtual void init() { }; + + /// + /// \brief Executes the ComputeGraph + /// + virtual void execute() = 0; + +protected: + std::shared_ptr<ComputeGraph> m_graph = nullptr; +}; +} \ No newline at end of file diff --git a/Source/Common/ComputeGraph/imstkComputeGraphVizWriter.cpp b/Source/Common/ComputeGraph/imstkComputeGraphVizWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..895416cda7d96344ef68cbacf830c213dbd1b903 --- /dev/null +++ b/Source/Common/ComputeGraph/imstkComputeGraphVizWriter.cpp @@ -0,0 +1,104 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#include "imstkComputeGraphVizWriter.h" +#include "imstkComputeGraph.h" +#include "imstkLogger.h" +#include <fstream> + +namespace imstk +{ +void +ComputeGraphVizWriter::write() +{ + if (m_inputGraph == nullptr) + { + LOG(WARNING) << "No input set, unable to write ComputeGraph"; + return; + } + + // This little writer fails when nodes are named the same, some more advanced + // method would have to be used to rename nodes that are the same with consistent + // numbering down the tree + std::ofstream file; + file.open(m_fileName); + + if (!file.is_open() || file.fail()) + { + return; + } + + file << + "digraph imstkDependency\n" + "{\n" + "style=filled;\n" + "color=lightgrey;\n" + "node [style=filled,color=cornflowerblue];\n" + "edge[arrowhead=vee, arrowtail=inv, arrowsize=.7, color=grey20]\n"; + + // Ensure unique names + std::unordered_map<std::shared_ptr<ComputeNode>, std::string> nodeNames = ComputeGraph::getUniqueNames(m_inputGraph); + + // Compute the critical path (ie: longest path in duration) + ComputeNodeList critPath; + if (m_highlightCriticalPath) + { + critPath = ComputeGraph::getCriticalPath(m_inputGraph); + } + auto edgeExists = [&](const std::shared_ptr<ComputeNode>& a, const std::shared_ptr<ComputeNode>& b) + { + ComputeNodeList::iterator srcNode = std::find(critPath.begin(), critPath.end(), a); + // If srcNode was found and the next node is b + return (srcNode != critPath.end() && *std::next(srcNode) == b); + }; + + // Compute completion times of each node + if (m_writeTimes) + { + std::unordered_map<std::shared_ptr<ComputeNode>, double> times = ComputeGraph::getTimes(m_inputGraph); + for (auto i : nodeNames) + { + nodeNames[i.first] = i.second + "(" + std::to_string(times[i.first]) + "ms)"; + } + } + + // Write out all the edges + const ComputeNodeAdjList& adjList = m_inputGraph->getAdjList(); + for (ComputeNodeAdjList::const_iterator it = adjList.begin(); it != adjList.end(); it++) + { + std::shared_ptr<ComputeNode> srcNode = it->first; + const ComputeNodeSet& outputNodes = it->second; + for (ComputeNodeSet::const_iterator jt = outputNodes.begin(); jt != outputNodes.end(); jt++) + { + std::shared_ptr<ComputeNode> destNode = *jt; + file << '\"' << nodeNames[srcNode] << '\"' << " -> " << '\"' << nodeNames[destNode] << '\"'; + + if (m_highlightCriticalPath && edgeExists(srcNode, destNode)) + { + file << "[color=red]"; + } + file << std::endl; + } + } + file << "}\n"; + file.close(); +} +} \ No newline at end of file diff --git a/Source/Common/ComputeGraph/imstkComputeGraphVizWriter.h b/Source/Common/ComputeGraph/imstkComputeGraphVizWriter.h new file mode 100644 index 0000000000000000000000000000000000000000..8d07c9692662427667ccbe7973a8c28e0002d32c --- /dev/null +++ b/Source/Common/ComputeGraph/imstkComputeGraphVizWriter.h @@ -0,0 +1,82 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include <memory> +#include <string> + +namespace imstk +{ +class ComputeGraph; + +/// +/// \class ComputeGraph +/// +/// \brief Writes a ComputeGraph to an svg file. Produces unique node names from duplicates with postfix. +/// May also highlight cyclic graph edges. +/// +class ComputeGraphVizWriter +{ +public: + ComputeGraphVizWriter() = default; + virtual ~ComputeGraphVizWriter() = default; + +public: + /// + /// \brief The graph to write + /// + void setInput(std::shared_ptr<ComputeGraph> graph) { this->m_inputGraph = graph; } + + /// + /// \brief The filename and path to write too + /// + void setFileName(std::string fileName) { this->m_fileName = fileName; } + + /// + /// \brief If on, will highlight the critical path in red + /// + void setHighlightCriticalPath(bool highlightCriticalPath) { this->m_highlightCriticalPath = highlightCriticalPath; } + + /// + /// \brief If on, will write the time the node completed in the node name + /// This isn't exact timing (completion times of each node) as it was run in iMSTK, + /// this is timing purely from the timers around the execution of each step + /// + void setWriteTimes(bool writeTimes) { this->m_writeTimes = writeTimes; } + + std::shared_ptr<ComputeGraph> getInput() const { return m_inputGraph; } + const std::string& getFileName() const { return m_fileName; } + bool getHighlightCriticalPath() const { return m_highlightCriticalPath; } + bool getWriteTimes() const { return m_writeTimes; } + + /// + /// \brief Writes the graph to a file given the filename + /// + void write(); + +private: + std::shared_ptr<ComputeGraph> m_inputGraph = nullptr; + std::string m_fileName = ""; + bool m_highlightCriticalPath = false; + bool m_writeTimes = false; +}; +} \ No newline at end of file diff --git a/Source/Common/ComputeGraph/imstkComputeNode.cpp b/Source/Common/ComputeGraph/imstkComputeNode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da5d7777fb2e702ebc9d6f1b103450e2161f9cc7 --- /dev/null +++ b/Source/Common/ComputeGraph/imstkComputeNode.cpp @@ -0,0 +1,50 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#include "imstkComputeNode.h" +#include "imstkTimer.h" + +namespace imstk +{ +void +ComputeNode::execute() +{ + if (m_enabled && m_func != nullptr) + { + if (!m_enableBenchmarking) + { + m_func(); + } + else + { + StopWatch timer; + timer.start(); + m_func(); + m_elapsedTime = timer.getTimeElapsed(); + timer.stop(); + } + } + else + { + m_elapsedTime = 0.0; + } +} +} \ No newline at end of file diff --git a/Source/Common/ComputeGraph/imstkComputeNode.h b/Source/Common/ComputeGraph/imstkComputeNode.h new file mode 100644 index 0000000000000000000000000000000000000000..31c519f869a050ab9f36d971b953df1539f5b50c --- /dev/null +++ b/Source/Common/ComputeGraph/imstkComputeNode.h @@ -0,0 +1,67 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include <functional> +#include <string> + +namespace imstk +{ +/// +/// \class ComputeNode +/// +/// \brief Base class for compute graph nodes +/// +class ComputeNode +{ +public: + ComputeNode() = default; + ComputeNode(std::function<void()> func, std::string name = "none", bool critical = false) : + m_func(func), m_name(name), m_critical(critical), m_elapsedTime(0.0) + { + } + + virtual ~ComputeNode() = default; + +public: + void setFunction(std::function<void()> func) { this->m_func = func; } + void setEnabled(bool enabled) { this->m_enabled = enabled; } + + /// + /// \brief Returns true if function is nullptr + /// + bool isFunctional() const { return m_func != nullptr; } + + // Executes + virtual void execute(); + +public: + std::string m_name = "none"; + bool m_enabled = true; + bool m_critical = false; + double m_elapsedTime = 0.0; + bool m_enableBenchmarking = false; + +protected: + std::function<void()> m_func = nullptr; ///> Don't allow user to call directly (must use execute) +}; +} \ No newline at end of file diff --git a/Source/SceneEntities/Objects/deprecated/imstkPbdRigidObject.cpp b/Source/Common/ComputeGraph/imstkSequentialComputeGraphController.cpp similarity index 68% rename from Source/SceneEntities/Objects/deprecated/imstkPbdRigidObject.cpp rename to Source/Common/ComputeGraph/imstkSequentialComputeGraphController.cpp index a70731e37ffd09107a38272930b3f615c45515e4..c4cd79d91110fcbed0d1b5443cf934d89ce6471d 100644 --- a/Source/SceneEntities/Objects/deprecated/imstkPbdRigidObject.cpp +++ b/Source/Common/ComputeGraph/imstkSequentialComputeGraphController.cpp @@ -19,20 +19,25 @@ =========================================================================*/ -#include <memory> - -#include "imstkPbdRigidObject.h" -#include "imstkGeometry.h" -#include "imstkGeometryMap.h" -#include "imstkPbdModel.h" - -#include <g3log/g3log.hpp> +#include "imstkSequentialComputeGraphController.h" +#include "imstkComputeGraph.h" +#include "imstkComputeNode.h" namespace imstk { void -PbdRigidObject::updatePbdStates() +SequentialComputeGraphController::init() +{ + m_executionOrderedNodes = ComputeGraph::topologicalSort(m_graph); +} + +void +SequentialComputeGraphController::execute() { - m_pbdModel->updatePbdStateFromPhysicsGeometry(); + // Sequential + for (std::shared_ptr<ComputeNode> node : *m_executionOrderedNodes) + { + node->execute(); + } } -} //imstk +} \ No newline at end of file diff --git a/Source/Common/ComputeGraph/imstkSequentialComputeGraphController.h b/Source/Common/ComputeGraph/imstkSequentialComputeGraphController.h new file mode 100644 index 0000000000000000000000000000000000000000..c8e1f2156b32f3787fa1f8b4b9d31aae19062da6 --- /dev/null +++ b/Source/Common/ComputeGraph/imstkSequentialComputeGraphController.h @@ -0,0 +1,50 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once +#include "imstkComputeGraphController.h" +#include <list> + +namespace imstk +{ +class ComputeNode; + +/// +/// \class SequentialComputeGraphController +/// +/// \brief This class executes a ComputeGraph by first topologically sorting them (Khans algorithm) +/// then sequentially running them +/// +class SequentialComputeGraphController : public ComputeGraphController +{ +public: + /// + /// \brief Sorts the computational nodes + /// + void init() override; + + void execute() override; + +private: + // The current nodes to execute, ordered + std::shared_ptr<std::list<std::shared_ptr<ComputeNode>>> m_executionOrderedNodes; +}; +}; \ No newline at end of file diff --git a/Source/Common/ComputeGraph/imstkTbbComputeGraphController.cpp b/Source/Common/ComputeGraph/imstkTbbComputeGraphController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..59bf013fd20714dd6f2a8045c310ed6941cd4a64 --- /dev/null +++ b/Source/Common/ComputeGraph/imstkTbbComputeGraphController.cpp @@ -0,0 +1,102 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "imstkTbbComputeGraphController.h" +#include "imstkComputeGraph.h" +#include <tbb/tbb.h> + +namespace imstk +{ +class ComputeNodeTask : public tbb::task +{ +public: + ComputeNodeTask(std::shared_ptr<ComputeNode> node) : m_node(node) { } + +public: + task* execute() override + { + __TBB_ASSERT(ref_count() == 0, NULL); + + m_node->execute(); + + for (size_t i = 0; i < successors.size(); i++) + { + if (successors[i]->decrement_ref_count() == 0) + { + spawn(*successors[i]); + } + } + return NULL; + } + +public: + std::shared_ptr<ComputeNode> m_node = nullptr; + std::vector<ComputeNodeTask*> successors; +}; + +void +TbbComputeGraphController::execute() +{ + // Create a Task for every node + const ComputeNodeVector& nodes = m_graph->getNodes(); + if (nodes.size() == 0) + { + return; + } + + // Create a task for every node + std::unordered_map<std::shared_ptr<ComputeNode>, ComputeNodeTask*> tasks; + tasks.reserve(nodes.size()); + + for (size_t i = 0; i < nodes.size(); i++) + { + std::shared_ptr<ComputeNode> node = nodes[i]; + tasks[node] = new (tbb::task::allocate_root())ComputeNodeTask(node); + } + // Increment successor reference counts + const ComputeNodeAdjList& adjList = m_graph->getAdjList(); + // For every node in graph + for (size_t i = 0; i < nodes.size(); i++) + { + // If it contains outputs + if (adjList.count(nodes[i]) != 0) + { + // For every output + const ComputeNodeSet& outputNodes = adjList.at(nodes[i]); + for (ComputeNodeSet::const_iterator it = outputNodes.begin(); it != outputNodes.end(); it++) + { + // Lookup the task of the node + ComputeNodeTask* successor = tasks[*it]; + tasks[nodes[i]]->successors.push_back(successor); + successor->increment_ref_count(); + } + } + } + + ComputeNodeTask* startTask = tasks[m_graph->getSource()]; + ComputeNodeTask* finalTask = tasks[m_graph->getSink()]; + // Extra ref count on the final task + finalTask->increment_ref_count(); + finalTask->spawn_and_wait_for_all(*startTask); + finalTask->execute(); // Execute final task explicitly + tbb::task::destroy(*finalTask); +} +} \ No newline at end of file diff --git a/Source/Common/ComputeGraph/imstkTbbComputeGraphController.h b/Source/Common/ComputeGraph/imstkTbbComputeGraphController.h new file mode 100644 index 0000000000000000000000000000000000000000..2847e5e5116cb35743830402ab94efa15a29b6b0 --- /dev/null +++ b/Source/Common/ComputeGraph/imstkTbbComputeGraphController.h @@ -0,0 +1,38 @@ +/*========================================================================= + +Library: iMSTK + +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=========================================================================*/ + +#pragma once + +#include "imstkComputeGraphController.h" + +namespace imstk +{ +/// +/// \class TbbComputeGraphController +/// +/// \brief This class runs an input ComputeGraph in parallel using tbb tasks +/// +class TbbComputeGraphController : public ComputeGraphController +{ +public: + void execute() override; +}; +}; \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.cpp b/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.cpp index 436d8d5e5d1442b235e2dbe883637100b8efd326..bf3e64f14a3c79db2cac34987c425a6e707d4ba2 100644 --- a/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.cpp +++ b/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.cpp @@ -20,10 +20,16 @@ =========================================================================*/ #include "imstkAbstractDynamicalModel.h" +#include "imstkComputeGraph.h" #include "imstkLogger.h" namespace imstk { +AbstractDynamicalModel::AbstractDynamicalModel(DynamicalModelType type) : + m_type(type), m_numDOF(0), m_computeGraph(std::make_shared<ComputeGraph>("AbstractDynamicalModel_Source", "AbstractDynamicalModel_Sink")) +{ +} + bool AbstractDynamicalModel::isGeometryValid(const std::shared_ptr<Geometry> geometry) { @@ -65,4 +71,29 @@ AbstractDynamicalModel::setModelGeometry(std::shared_ptr<Geometry> geometry) LOG(WARNING) << "Invalid geometry for Model"; } } + +void +AbstractDynamicalModel::initGraphEdges() +{ + m_computeGraph->clearEdges(); + initGraphEdges(m_computeGraph->getSource(), m_computeGraph->getSink()); +} + +void +AbstractDynamicalModel::initGraphEdges(std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink) +{ + m_computeGraph->addEdge(source, sink); +} + +std::shared_ptr<ComputeNode> +AbstractDynamicalModel::addFunction(std::string name, std::function<void()> func) +{ + return m_computeGraph->addFunction(name, func); +} + +void +AbstractDynamicalModel::addEdge(std::shared_ptr<ComputeNode> srcNode, std::shared_ptr<ComputeNode> destNode) +{ + m_computeGraph->addEdge(srcNode, destNode); +} } // imstk \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.h b/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.h index 6d7a547292831a79e2f71e79d414da4e3cd3b5cb..2864f39f157de43d011b5d11926f8556caf21ce8 100644 --- a/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.h +++ b/Source/DynamicalModels/ObjectModels/imstkAbstractDynamicalModel.h @@ -26,9 +26,13 @@ #include <set> #include <string> +#include <unordered_map> namespace imstk { +class ComputeGraph; +class ComputeNode; + /// /// \brief Type of the time dependent mathematical model /// @@ -58,7 +62,6 @@ enum class TimeSteppingType class AbstractDynamicalModel { public: - /// /// \brief Type of the update of the state of the body /// @@ -71,17 +74,13 @@ public: None }; -public: - /// - /// \brief Constructor - /// - AbstractDynamicalModel(DynamicalModelType type = DynamicalModelType::None) : m_type(type), m_numDOF(0) {} +protected: + AbstractDynamicalModel(DynamicalModelType type = DynamicalModelType::None); - /// - /// \brief Destructor - /// +public: virtual ~AbstractDynamicalModel() = default; +public: /// /// \brief Reset the current state to the initial state /// @@ -93,6 +92,8 @@ public: std::size_t getNumDegreeOfFreedom() const { return m_numDOF; } void setNumDegreeOfFreedom(const size_t nDof) { m_numDOF = nDof; } + std::shared_ptr<ComputeGraph> getComputeGraph() const { return m_computeGraph; } + /// /// \brief Get the type of the object /// @@ -104,9 +105,9 @@ public: virtual void updateBodyStates(const Vectord& q, const StateUpdateType updateType = StateUpdateType::Displacement) = 0; /// - /// \brief + /// \brief Update the geometry of the model /// - virtual void updatePhysicsGeometry() {} + virtual void updatePhysicsGeometry() { } /// /// \brief Set the time step size @@ -138,20 +139,43 @@ public: /// virtual bool initialize() = 0; + /// + /// \brief Initializes the edges of the graph + /// + void initGraphEdges(); + /// /// \brief Set the type of approach used to update the time step size after every frame /// virtual void setTimeStepSizeType(const TimeSteppingType type) { m_timeStepSizeType = type; } TimeSteppingType getTimeStepSizeType() { return m_timeStepSizeType; } +protected: + /// + /// \brief Adds a function to the computational graph of this model + /// + std::shared_ptr<ComputeNode> addFunction(std::string name, std::function<void()> func); + + /// + /// \brief Adds an edge to the computational graph of this model + /// + void addEdge(std::shared_ptr<ComputeNode> srcNode, std::shared_ptr<ComputeNode> destNode); + + /// + /// \brief Setup connectivity of the compute graph + /// + virtual void initGraphEdges(std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink); + protected: DynamicalModelType m_type; ///> Mathematical model type std::size_t m_numDOF; ///> Total number of degree of freedom - std::shared_ptr<Geometry> m_geometry; ///> Physics geometry of the model + std::shared_ptr<Geometry> m_geometry = nullptr; ///> Physics geometry of the model std::set<Geometry::Type> m_validGeometryTypes; ///> Valid geometry types of this model TimeSteppingType m_timeStepSizeType = TimeSteppingType::Fixed; + + std::shared_ptr<ComputeGraph> m_computeGraph = nullptr; }; } // imstk diff --git a/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.cpp b/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.cpp index fd2f00405633c7fa323f53093af627720964cf38..a88ea318052dbbb62a4d590536ce71cdd62cb815 100644 --- a/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.cpp +++ b/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.cpp @@ -24,17 +24,20 @@ //imstk #include "imstkFEMDeformableBodyModel.h" -#include "imstkStVKForceModel.h" -#include "imstkLinearFEMForceModel.h" +#include "imstkComputeGraph.h" +#include "imstkComputeNode.h" +#include "imstkConjugateGradient.h" #include "imstkCorotationalFEMForceModel.h" +#include "imstkInternalForceModel.h" #include "imstkIsotropicHyperelasticFEMForceModel.h" -#include "imstkVegaMeshIO.h" -#include "imstkVolumetricMesh.h" +#include "imstkLinearFEMForceModel.h" +#include "imstkMath.h" +#include "imstkNewtonSolver.h" +#include "imstkSolverBase.h" +#include "imstkStVKForceModel.h" #include "imstkTimeIntegrator.h" #include "imstkVegaMeshIO.h" -#include "imstkNewtonSolver.h" -#include "imstkInternalForceModel.h" -#include "imstkMath.h" +#include "imstkVolumetricMesh.h" // vega #include "generateMassMatrix.h" @@ -56,6 +59,8 @@ FEMDeformableBodyModel::FEMDeformableBodyModel() : Geometry::Type::TetrahedralMesh, Geometry::Type::HexahedralMesh }; + + m_solveNode = addFunction("FEMModel_Solve", [&]() { getSolver()->solve(); }); } FEMDeformableBodyModel::~FEMDeformableBodyModel() @@ -86,7 +91,6 @@ FEMDeformableBodyModel::configure(const std::string& configFileName) vegaConfigFileOptions.addOptionOptional("deformationCompliance", &m_FEModelConfig->m_deformationCompliance, m_FEModelConfig->m_deformationCompliance); vegaConfigFileOptions.addOptionOptional("compressionResistance", &m_FEModelConfig->m_compressionResistance, m_FEModelConfig->m_compressionResistance); vegaConfigFileOptions.addOptionOptional("inversionThreshold", &m_FEModelConfig->m_inversionThreshold, m_FEModelConfig->m_inversionThreshold); - vegaConfigFileOptions.addOptionOptional("numberOfThreads", &m_FEModelConfig->m_numberOfThreads, m_FEModelConfig->m_numberOfThreads); vegaConfigFileOptions.addOptionOptional("gravity", &m_FEModelConfig->m_gravity, m_FEModelConfig->m_gravity); // Parse the configuration file @@ -153,47 +157,37 @@ FEMDeformableBodyModel::configure(std::shared_ptr<FEMModelConfig> config) m_FEModelConfig = config; } -void -FEMDeformableBodyModel::setForceModelConfiguration(std::shared_ptr<FEMModelConfig> fmConfig) -{ - m_FEModelConfig = fmConfig; -} - -std::shared_ptr<imstk::FEMModelConfig> -FEMDeformableBodyModel::getForceModelConfiguration() const +bool +FEMDeformableBodyModel::initialize() { - return m_FEModelConfig; -} + // prerequisite of for successfully initializing + CHECK(m_geometry != nullptr && m_FEModelConfig != nullptr) << "DeformableBodyModel::initialize: Physics mesh or force model configuration not set yet!"; -void -FEMDeformableBodyModel::setInternalForceModel(std::shared_ptr<InternalForceModel> fm) -{ - m_internalForceModel = fm; -} + // Setup default solver if model doesn't yet have one + if (m_solver == nullptr) + { + // Create a nonlinear system + auto nlSystem = std::make_shared<NonLinearSystem>(getFunction(), getFunctionGradient()); -std::shared_ptr<imstk::InternalForceModel> -FEMDeformableBodyModel::getInternalForceModel() const -{ - return m_internalForceModel; -} + nlSystem->setUnknownVector(getUnknownVec()); + nlSystem->setUpdateFunction(getUpdateFunction()); + nlSystem->setUpdatePreviousStatesFunction(getUpdatePrevStateFunction()); -void -FEMDeformableBodyModel::setTimeIntegrator(std::shared_ptr<TimeIntegrator> timeIntegrator) -{ - m_timeIntegrator = timeIntegrator; -} + // Create a linear solver + auto linSolver = std::make_shared<ConjugateGradient>(); -std::shared_ptr<imstk::TimeIntegrator> -FEMDeformableBodyModel::getTimeIntegrator() const -{ - return m_timeIntegrator; -} + if (linSolver->getType() == imstk::LinearSolver<imstk::SparseMatrixd>::Type::GaussSeidel + && isFixedBCImplemented()) + { + LOG(WARNING) << "The GS solver may not be viable!"; + } -bool -FEMDeformableBodyModel::initialize() -{ - // prerequisite of for successfully initializing - CHECK(m_geometry != nullptr && m_FEModelConfig != nullptr) << "DeformableBodyModel::initialize: Physics mesh or force model configuration not set yet!"; + // Create a non-linear solver and add to the scene + auto nlSolver = std::make_shared<NewtonSolver>(); + nlSolver->setLinearSolver(linSolver); + nlSolver->setSystem(nlSystem); + setSolver(nlSolver); + } auto physicsMesh = std::dynamic_pointer_cast<imstk::VolumetricMesh>(this->getModelGeometry()); m_vegaPhysicsMesh = VegaMeshIO::convertVolumetricMeshToVegaMesh(physicsMesh); @@ -775,12 +769,6 @@ FEMDeformableBodyModel::initializeEigenMatrixFromVegaMatrix(const vega::SparseMa eigenMatrix.makeCompressed(); } -Vectord& -FEMDeformableBodyModel::getContactForce() -{ - return m_Fcontact; -} - void FEMDeformableBodyModel::setFixedSizeTimeStepping() { @@ -799,4 +787,12 @@ FEMDeformableBodyModel::getTimeStep() const { return m_timeIntegrator->getTimestepSize(); }; + +void +FEMDeformableBodyModel::initGraphEdges(std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink) +{ + // Setup graph connectivity + addEdge(source, m_solveNode); + addEdge(m_solveNode, sink); +} } // imstk diff --git a/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.h b/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.h index ce911e5ef0d97f7573ec98b683ecdbd2ec185924..c6c066e621bba9ecd735d0c3a15f0a0bd58f309c 100644 --- a/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.h +++ b/Source/DynamicalModels/ObjectModels/imstkFEMDeformableBodyModel.h @@ -36,10 +36,11 @@ class VolumetricMesh; namespace imstk { +class InternalForceModel; +class NewtonSolver; class TimeIntegrator; +class SolverBase; class VegaMeshIO; -class NewtonSolver; -class InternalForceModel; struct FEMModelConfig { @@ -57,9 +58,6 @@ struct FEMModelConfig double m_compressionResistance = 500.0; double m_inversionThreshold = -std::numeric_limits<double>::max(); double m_gravity = 9.81; - - // \todo remove from here ? - int m_numberOfThreads = 4; }; /// @@ -83,6 +81,7 @@ public: /// ~FEMDeformableBodyModel(); +public: /// /// \brief Configure the force model from external file /// @@ -97,20 +96,20 @@ public: /// /// \brief Set/Get force model configuration /// - void setForceModelConfiguration(std::shared_ptr<FEMModelConfig> fmConfig); - std::shared_ptr<FEMModelConfig> getForceModelConfiguration() const; + void setForceModelConfiguration(std::shared_ptr<FEMModelConfig> fmConfig) { this->m_FEModelConfig = fmConfig; } + std::shared_ptr<FEMModelConfig> getForceModelConfiguration() const { return m_FEModelConfig; } /// /// \brief Set/Get internal force model /// - void setInternalForceModel(std::shared_ptr<InternalForceModel> fm); - std::shared_ptr<InternalForceModel> getInternalForceModel() const; + void setInternalForceModel(std::shared_ptr<InternalForceModel> fm) { m_internalForceModel = fm; } + std::shared_ptr<InternalForceModel> getInternalForceModel() const { return m_internalForceModel; } /// /// \brief Set/Get time integrator /// - void setTimeIntegrator(std::shared_ptr<TimeIntegrator> timeIntegrator); - std::shared_ptr<TimeIntegrator> getTimeIntegrator() const; + void setTimeIntegrator(std::shared_ptr<TimeIntegrator> timeIntegrator) { this->m_timeIntegrator = timeIntegrator; } + std::shared_ptr<TimeIntegrator> getTimeIntegrator() const { return m_timeIntegrator; } /// /// \brief Load the initial conditions of the deformable object @@ -226,7 +225,7 @@ public: /// /// \brief Get the contact force vector /// - Vectord& getContactForce(); + Vectord& getContactForce() { return m_Fcontact; } /// /// \brief Returns the unknown vectors @@ -265,39 +264,54 @@ public: void disableFixedBC() { m_implementFixedBC = false; }; bool isFixedBCImplemented() const { return m_implementFixedBC; }; + std::shared_ptr<ComputeNode> getSolveNode() const { return m_solveNode; } + + std::shared_ptr<SolverBase> getSolver() const { return m_solver; } + void setSolver(std::shared_ptr<SolverBase> solver) { this->m_solver = solver; } + +protected: + /// + /// \brief Setup the computational graph of FEM + /// + void initGraphEdges(std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink) override; + protected: - std::shared_ptr<InternalForceModel> m_internalForceModel; ///> Mathematical model for intenal forces - std::shared_ptr<TimeIntegrator> m_timeIntegrator; ///> Time integrator - std::shared_ptr<NonLinearSystem> m_nonLinearSystem; ///> Nonlinear system resulting from TI and force model + std::shared_ptr<SolverBase> m_solver = nullptr; + std::shared_ptr<InternalForceModel> m_internalForceModel = nullptr; ///> Mathematical model for intenal forces + std::shared_ptr<TimeIntegrator> m_timeIntegrator = nullptr; ///> Time integrator + std::shared_ptr<NonLinearSystem> m_nonLinearSystem = nullptr; ///> Nonlinear system resulting from TI and force model - std::shared_ptr<FEMModelConfig> m_FEModelConfig; + std::shared_ptr<FEMModelConfig> m_FEModelConfig = nullptr; /// Matrices typical to a elastodynamics and 2nd order analogous systems - SparseMatrixd m_M; ///> Mass matrix - SparseMatrixd m_C; ///> Damping coefficient matrix - SparseMatrixd m_K; ///> Tangent (derivative of internal force w.r.t displacements) stiffness matrix - SparseMatrixd m_Keff; ///> Effective stiffness matrix (dependent on internal force model and time integrator) + SparseMatrixd m_M; ///> Mass matrix + SparseMatrixd m_C; ///> Damping coefficient matrix + SparseMatrixd m_K; ///> Tangent (derivative of internal force w.r.t displacements) stiffness matrix + SparseMatrixd m_Keff; ///> Effective stiffness matrix (dependent on internal force model and time integrator) - Vectord m_Finternal; ///> Vector of internal forces - Vectord m_Feff; ///> Vector of effective forces - Vectord m_Fcontact; ///> Vector of contact forces - Vectord m_Fgravity; ///> Vector of gravity forces - Vectord m_FexplicitExternal; ///> Vector of explicitly defined external forces - Vectord m_qSol; ///> Vector to maintain solution at each iteration of nonlinear solver + Vectord m_Finternal; ///> Vector of internal forces + Vectord m_Feff; ///> Vector of effective forces + Vectord m_Fcontact; ///> Vector of contact forces + Vectord m_Fgravity; ///> Vector of gravity forces + Vectord m_FexplicitExternal; ///> Vector of explicitly defined external forces + Vectord m_qSol; ///> Vector to maintain solution at each iteration of nonlinear solver - std::shared_ptr<vega::VolumetricMesh> m_vegaPhysicsMesh; ///> Mesh used for Physics - std::shared_ptr<vega::SparseMatrix> m_vegaMassMatrix; ///> Vega mass matrix - std::shared_ptr<vega::SparseMatrix> m_vegaTangentStiffnessMatrix; ///> Vega Tangent stiffness matrix - std::shared_ptr<vega::SparseMatrix> m_vegaDampingMatrix; ///> Vega Laplacian damping matrix + std::shared_ptr<vega::VolumetricMesh> m_vegaPhysicsMesh = nullptr; ///> Mesh used for Physics + std::shared_ptr<vega::SparseMatrix> m_vegaMassMatrix = nullptr; ///> Vega mass matrix + std::shared_ptr<vega::SparseMatrix> m_vegaTangentStiffnessMatrix = nullptr; ///> Vega Tangent stiffness matrix + std::shared_ptr<vega::SparseMatrix> m_vegaDampingMatrix = nullptr; ///> Vega Laplacian damping matrix - std::vector<std::size_t> m_fixedNodeIds; ///> Nodal IDs of the nodes that are fixed + std::vector<std::size_t> m_fixedNodeIds; ///> Nodal IDs of the nodes that are fixed - StateUpdateType m_updateType = StateUpdateType::DeltaVelocity; ///> Update type of the model + StateUpdateType m_updateType = StateUpdateType::DeltaVelocity; ///> Update type of the model - bool m_damped = false; ///> Viscous or structurally damped system + bool m_damped = false; ///> Viscous or structurally damped system // If this is true, the tangent stiffness and force vector will be modified to // accommodate (the rows and columns will be nullified) the fixed boundary conditions bool m_implementFixedBC = true; + +private: + std::shared_ptr<ComputeNode> m_solveNode = nullptr; }; } // imstk diff --git a/Source/DynamicalModels/ObjectModels/imstkPbdModel.cpp b/Source/DynamicalModels/ObjectModels/imstkPbdModel.cpp index 188949934a7743c086eb10660386b07ba27b1fda..c03caa4e302e36393ef67acb0db581143303448e 100644 --- a/Source/DynamicalModels/ObjectModels/imstkPbdModel.cpp +++ b/Source/DynamicalModels/ObjectModels/imstkPbdModel.cpp @@ -19,28 +19,50 @@ =========================================================================*/ -#include "imstkGraph.h" #include "imstkPbdModel.h" -#include "imstkTetrahedralMesh.h" -#include "imstkSurfaceMesh.h" +#include "imstkComputeGraph.h" +#include "imstkGeometryUtilities.h" +#include "imstkGraph.h" #include "imstkLineMesh.h" -#include "imstkPbdVolumeConstraint.h" -#include "imstkPbdDistanceConstraint.h" -#include "imstkPbdDihedralConstraint.h" #include "imstkPbdAreaConstraint.h" #include "imstkPbdBendConstraint.h" -#include "imstkPbdFETetConstraint.h" -#include "imstkPbdFEHexConstraint.h" #include "imstkPbdConstantDensityConstraint.h" -#include "imstkParallelUtils.h" -#include "imstkGeometryUtilities.h" -#include "imstkMeshIO.h" -//#include "imstkColor.h" +#include "imstkPbdDihedralConstraint.h" +#include "imstkPbdDistanceConstraint.h" +#include "imstkPbdFETetConstraint.h" +#include "imstkPbdSolver.h" +#include "imstkPbdVolumeConstraint.h" +#include "imstkSurfaceMesh.h" +#include "imstkTetrahedralMesh.h" + +#include "imstkTimer.h" #include <unordered_map> namespace imstk { +PbdModel::PbdModel() : DynamicalModel(DynamicalModelType::PositionBasedDynamics), + m_constraints(std::make_shared<PBDConstraintVector>()), + m_partitionedConstraints(std::make_shared<std::vector<PBDConstraintVector>>()), + m_mass(std::make_shared<StdVectorOfReal>()), + m_invMass(std::make_shared<StdVectorOfReal>()), + m_Parameters(std::make_shared<PBDModelConfig>()) +{ + m_validGeometryTypes = { + Geometry::Type::PointSet, + Geometry::Type::LineMesh, + Geometry::Type::SurfaceMesh, + Geometry::Type::TetrahedralMesh, + Geometry::Type::HexahedralMesh + }; + + // Setup PBD compute nodes + m_integrationPositionNode = addFunction("PbdModel_IntegratePosition", std::bind(&PbdModel::integratePosition, this)); + m_updateCollisionGeometryNode = addFunction("PbdModel_UpdateCollisionGeometry", std::bind(&PbdModel::updatePhysicsGeometry, this)); + m_solveConstraintsNode = addFunction("PbdModel_SolveConstraints", [&]() { m_pbdSolver->solve(); }); // Avoids rebinding on solver swap + m_updateVelocityNode = addFunction("PbdModel_UpdateVelocity", std::bind(&PbdModel::updateVelocity, this)); +} + void PBDModelConfig::enableConstraint(PbdConstraint::Type type, double stiffness) { @@ -58,7 +80,7 @@ PBDModelConfig::enableFEMConstraint(PbdConstraint::Type type, PbdFEMConstraint:: } void -PbdModel::configure(const std::shared_ptr<PBDModelConfig>& params) +PbdModel::configure(std::shared_ptr<PBDModelConfig> params) { LOG_IF(FATAL, (!this->getModelGeometry())) << "PbdModel::configure - Set PBD Model geometry before configuration!"; @@ -76,20 +98,14 @@ PbdModel::initialize() m_previousState = std::make_shared<PbdState>(); m_currentState = std::make_shared<PbdState>(); - bool option[3] = { 1, 0, 0 }; - m_initialState->initialize(m_mesh, option); - m_previousState->initialize(m_mesh, option); - - option[1] = option[2] = 1; - m_currentState->initialize(m_mesh, option); - - m_initialState->setPositions(m_mesh->getVertexPositions()); - m_currentState->setPositions(m_mesh->getVertexPositions()); + m_initialState->initialize(m_mesh->getVertexPositions()); + m_previousState->initialize(m_mesh->getVertexPositions()); + m_currentState->initialize(m_mesh->getVertexPositions()); auto numParticles = m_mesh->getNumVertices(); - m_mass.resize(numParticles, 0); - m_invMass.resize(numParticles, 0); + m_mass->resize(numParticles, 0); + m_invMass->resize(numParticles, 0); setUniformMass(m_Parameters->m_uniformMassValue); for (auto i : m_Parameters->m_fixedNodeIds) @@ -99,6 +115,17 @@ PbdModel::initialize() bool bOK = true; // Return immediately if some constraint failed to initialize + // Setup the default pbd solver if none exists + if (m_pbdSolver == nullptr) + { + m_pbdSolver = std::make_shared<PbdSolver>(); + m_pbdSolver->setIterations(m_Parameters->m_iterations); + } + m_pbdSolver->setPositions(getCurrentState()->getPositions()); + m_pbdSolver->setInvMasses(getInvMasses()); + m_pbdSolver->setConstraints(getConstraints()); + m_pbdSolver->setPartitionedConstraints(getPartitionedConstraints()); + // Initialize FEM constraints for (auto& constraint: m_Parameters->m_FEMConstraints) { @@ -155,23 +182,34 @@ PbdModel::initialize() return bOK; } +void +PbdModel::initGraphEdges(std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink) +{ + // Setup graph connectivity + addEdge(source, m_integrationPositionNode); + addEdge(m_integrationPositionNode, m_updateCollisionGeometryNode); + addEdge(m_updateCollisionGeometryNode, m_solveConstraintsNode); + addEdge(m_solveConstraintsNode, m_updateVelocityNode); + addEdge(m_updateVelocityNode, sink); +} + void PbdModel::computeElasticConstants() { - if (std::abs(m_Parameters->m_mu) < MIN_REAL - && std::abs(m_Parameters->m_lambda) < MIN_REAL) + if (std::abs(m_Parameters->femParams->m_mu) < MIN_REAL + && std::abs(m_Parameters->femParams->m_lambda) < MIN_REAL) { - const auto E = m_Parameters->m_YoungModulus; - const auto nu = m_Parameters->m_PoissonRatio; - m_Parameters->m_mu = E / Real(2.0) / (Real(1.0) + nu); - m_Parameters->m_lambda = E * nu / ((Real(1.0) + nu) * (Real(1.0) - Real(2.0) * nu)); + const auto E = m_Parameters->femParams->m_YoungModulus; + const auto nu = m_Parameters->femParams->m_PoissonRatio; + m_Parameters->femParams->m_mu = E / Real(2.0) / (Real(1.0) + nu); + m_Parameters->femParams->m_lambda = E * nu / ((Real(1.0) + nu) * (Real(1.0) - Real(2.0) * nu)); } else { - const auto mu = m_Parameters->m_mu; - const auto lambda = m_Parameters->m_lambda; - m_Parameters->m_YoungModulus = mu * (Real(3.0) * lambda + Real(2.0) * mu) / (lambda + mu); - m_Parameters->m_PoissonRatio = lambda / Real(2.0) / (lambda + mu); + const auto mu = m_Parameters->femParams->m_mu; + const auto lambda = m_Parameters->femParams->m_lambda; + m_Parameters->femParams->m_YoungModulus = mu * (Real(3.0) * lambda + Real(2.0) * mu) / (lambda + mu); + m_Parameters->femParams->m_PoissonRatio = lambda / Real(2.0) / (lambda + mu); } } @@ -192,9 +230,10 @@ PbdModel::initializeFEMConstraints(PbdFEMConstraint::MaterialType type) { auto& tet = elements[k]; auto c = std::make_shared<PbdFEMTetConstraint>(type); - c->initConstraint(*this, tet[0], tet[1], tet[2], tet[3]); + c->initConstraint(*m_initialState->getPositions(), + tet[0], tet[1], tet[2], tet[3], m_Parameters->femParams); lock.lock(); - m_constraints.push_back(std::move(c)); + m_constraints->push_back(std::move(c)); lock.unlock(); }); return true; @@ -216,9 +255,10 @@ PbdModel::initializeVolumeConstraints(const double stiffness) { auto& tet = elements[k]; auto c = std::make_shared<PbdVolumeConstraint>(); - c->initConstraint(*this, tet[0], tet[1], tet[2], tet[3], stiffness); + c->initConstraint(*m_initialState->getPositions(), + tet[0], tet[1], tet[2], tet[3], stiffness); lock.lock(); - m_constraints.push_back(std::move(c)); + m_constraints->push_back(std::move(c)); lock.unlock(); }); return true; @@ -238,8 +278,8 @@ PbdModel::initializeDistanceConstraints(const double stiffness) { E[i1][i2] = 0; auto c = std::make_shared<PbdDistanceConstraint>(); - c->initConstraint(*this, i1, i2, stiffness); - m_constraints.push_back(std::move(c)); + c->initConstraint(*m_initialState->getPositions(), i1, i2, stiffness); + m_constraints->push_back(std::move(c)); } }; @@ -310,9 +350,9 @@ PbdModel::initializeAreaConstraints(const double stiffness) { auto& tri = elements[k]; auto c = std::make_shared<PbdAreaConstraint>(); - c->initConstraint(*this, tri[0], tri[1], tri[2], stiffness); + c->initConstraint(*m_initialState->getPositions(), tri[0], tri[1], tri[2], stiffness); lock.lock(); - m_constraints.push_back(std::move(c)); + m_constraints->push_back(std::move(c)); lock.unlock(); }); return true; @@ -338,8 +378,8 @@ PbdModel::initializeBendConstraints(const double stiffness) } auto c = std::make_shared<PbdBendConstraint>(); - c->initConstraint(*this, i1, i2, i3, k); - m_constraints.push_back(std::move(c)); + c->initConstraint(*m_initialState->getPositions(), i1, i2, i3, k); + m_constraints->push_back(std::move(c)); }; // Create constraints @@ -411,8 +451,8 @@ PbdModel::initializeDihedralConstraints(const double stiffness) } } auto c = std::make_shared<PbdDihedralConstraint>(); - c->initConstraint(*this, tri[2], tri[idx], tri[0], tri[1], stiffness); - m_constraints.push_back(std::move(c)); + c->initConstraint(*m_initialState->getPositions(), tri[2], tri[idx], tri[0], tri[1], stiffness); + m_constraints->push_back(std::move(c)); } } }; @@ -448,8 +488,8 @@ PbdModel::initializeConstantDensityConstraint(const double stiffness) << "Constant constraint should come with a mesh!"; auto c = std::make_shared<PbdConstantDensityConstraint>(); - c->initConstraint(*this, stiffness); - m_constraints.push_back(std::move(c)); + c->initConstraint(*m_initialState->getPositions(), stiffness); + m_constraints->push_back(std::move(c)); return true; } @@ -458,10 +498,11 @@ void PbdModel::partitionConstraints(const bool print) { // Form the map { vertex : list_of_constraints_involve_vertex } + PBDConstraintVector& allConstraints = *m_constraints; std::unordered_map<size_t, std::vector<size_t>> vertexConstraints; - for (size_t constrIdx = 0; constrIdx < m_constraints.size(); ++constrIdx) + for (size_t constrIdx = 0; constrIdx < allConstraints.size(); ++constrIdx) { - const auto& constr = m_constraints[constrIdx]; + const auto& constr = allConstraints[constrIdx]; for (const auto& vIds : constr->getVertexIds()) { vertexConstraints[vIds].push_back(constrIdx); @@ -470,7 +511,7 @@ PbdModel::partitionConstraints(const bool print) // Add edges to the constraint graph // Each edge represent a shared vertex between two constraints - Graph constraintGraph(m_constraints.size()); + Graph constraintGraph(allConstraints.size()); for (const auto& kv : vertexConstraints) { const auto& constraints = kv.second; // the list of constraints for a vertex @@ -488,86 +529,65 @@ PbdModel::partitionConstraints(const bool print) const auto coloring = constraintGraph.doColoring(); const auto& partitionIndices = coloring.first; const auto numPartitions = coloring.second; - assert(partitionIndices.size() == m_constraints.size()); + assert(partitionIndices.size() == allConstraints.size()); - m_partitionedConstraints.resize(0); - m_partitionedConstraints.resize(static_cast<size_t>(numPartitions)); + std::vector<PBDConstraintVector>& partitionedConstraints = *m_partitionedConstraints; + partitionedConstraints.resize(0); + partitionedConstraints.resize(static_cast<size_t>(numPartitions)); for (size_t constrIdx = 0; constrIdx < partitionIndices.size(); ++constrIdx) { const auto partitionIdx = partitionIndices[constrIdx]; - m_partitionedConstraints[partitionIdx].push_back(std::move(m_constraints[constrIdx])); + partitionedConstraints[partitionIdx].push_back(std::move(allConstraints[constrIdx])); } // If a partition has size smaller than the partition threshold, then move its constraints back // These constraints will be processed sequentially // Because small size partitions yield bad performance upon running in parallel - m_constraints.resize(0); - for (const auto& constraints : m_partitionedConstraints) + allConstraints.resize(0); + for (const auto& constraints : partitionedConstraints) { if (constraints.size() < m_partitionThreshold) { for (size_t constrIdx = 0; constrIdx < constraints.size(); ++constrIdx) { - m_constraints.push_back(std::move(constraints[constrIdx])); + allConstraints.push_back(std::move(constraints[constrIdx])); } } } // Remove all empty partitions size_t writeIdx = 0; - for (size_t readIdx = 0; readIdx < m_partitionedConstraints.size(); ++readIdx) + for (size_t readIdx = 0; readIdx < partitionedConstraints.size(); ++readIdx) { - if (m_partitionedConstraints[readIdx].size() >= m_partitionThreshold) + if (partitionedConstraints[readIdx].size() >= m_partitionThreshold) { - m_partitionedConstraints[writeIdx++] = std::move(m_partitionedConstraints[readIdx]); + partitionedConstraints[writeIdx++] = std::move(partitionedConstraints[readIdx]); } } - m_partitionedConstraints.resize(writeIdx); + partitionedConstraints.resize(writeIdx); // Print if (print) { size_t numConstraints = 0; int idx = 0; - for (const auto& constraints : m_partitionedConstraints) + for (const auto& constraints : partitionedConstraints) { std::cout << "Partition # " << idx++ << " | # nodes: " << constraints.size() << std::endl; numConstraints += constraints.size(); } - std::cout << "Sequential processing # nodes: " << m_constraints.size() << std::endl; - numConstraints += m_constraints.size(); + std::cout << "Sequential processing # nodes: " << allConstraints.size() << std::endl; + numConstraints += allConstraints.size(); std::cout << "Total constraints: " << numConstraints << " | Graph size: " << constraintGraph.size() << std::endl; } } -void -PbdModel::projectConstraints() -{ - unsigned int i = 0; - while (++i < m_Parameters->m_maxIter) - { - for (auto c: m_constraints) - { - c->solvePositionConstraint(*this); - } - - for (auto& partitionConstraints : m_partitionedConstraints) - { - ParallelUtils::parallelFor(partitionConstraints.size(), - [&](const size_t idx) - { - partitionConstraints[idx]->solvePositionConstraint(*this); - }); - } - } -} - void PbdModel::updatePhysicsGeometry() { - m_mesh->setVertexPositions(m_currentState->getPositions()); + m_mesh->setVertexPositions(*m_currentState->getPositions()); } void @@ -582,7 +602,7 @@ PbdModel::setTimeStepSizeType(const TimeSteppingType type) m_timeStepSizeType = type; if (type == TimeSteppingType::Fixed) { - m_Parameters->m_dt = m_Parameters->m_DefaultDt; + m_Parameters->m_dt = m_Parameters->m_defaultDt; } } @@ -591,53 +611,51 @@ PbdModel::setUniformMass(const double val) { if (val != 0.0) { - std::fill(m_mass.begin(), m_mass.end(), val); - std::fill(m_invMass.begin(), m_invMass.end(), 1 / val); + std::fill(m_mass->begin(), m_mass->end(), val); + std::fill(m_invMass->begin(), m_invMass->end(), 1.0 / val); } else { - std::fill(m_invMass.begin(), m_invMass.end(), 0.0); - std::fill(m_mass.begin(), m_mass.end(), 0.0); + std::fill(m_invMass->begin(), m_invMass->end(), 0.0); + std::fill(m_mass->begin(), m_mass->end(), 0.0); } } void PbdModel::setParticleMass(const double val, const size_t idx) { + StdVectorOfReal& masses = *m_mass; + StdVectorOfReal& invMasses = *m_invMass; if (idx < m_mesh->getNumVertices()) { - m_mass[idx] = val; - m_invMass[idx] = 1.0 / val; + masses[idx] = val; + invMasses[idx] = 1.0 / val; } } void PbdModel::setFixedPoint(const size_t idx) { + StdVectorOfReal& invMasses = *m_invMass; if (idx < m_mesh->getNumVertices()) { - m_invMass[idx] = 0; + invMasses[idx] = 0.0; } } -double -PbdModel::getInvMass(const size_t idx) const -{ - return m_invMass[idx]; -} - void PbdModel::integratePosition() { - const auto& accn = m_currentState->getAccelerations(); - auto& prevPos = m_previousState->getPositions(); - auto& pos = m_currentState->getPositions(); - auto& vel = m_currentState->getVelocities(); + StdVectorOfVec3d& prevPos = *m_previousState->getPositions(); + StdVectorOfVec3d& pos = *m_currentState->getPositions(); + StdVectorOfVec3d& vel = *m_currentState->getVelocities(); + const StdVectorOfVec3d& accn = *m_currentState->getAccelerations(); + const StdVectorOfReal& invMasses = *m_invMass; ParallelUtils::parallelFor(m_mesh->getNumVertices(), [&](const size_t i) { - if (std::abs(m_invMass[i]) > MIN_REAL) + if (std::abs(invMasses[i]) > MIN_REAL) { vel[i] += (accn[i] + m_Parameters->m_gravity) * m_Parameters->m_dt; prevPos[i] = pos[i]; @@ -649,14 +667,15 @@ PbdModel::integratePosition() void PbdModel::updateVelocity() { - const auto& prevPos = m_previousState->getPositions(); - const auto& pos = m_currentState->getPositions(); - auto& vel = m_currentState->getVelocities(); + const StdVectorOfVec3d& prevPos = *m_previousState->getPositions(); + const StdVectorOfVec3d& pos = *m_currentState->getPositions(); + StdVectorOfVec3d& vel = *m_currentState->getVelocities(); + const StdVectorOfReal& invMasses = *m_invMass; ParallelUtils::parallelFor(m_mesh->getNumVertices(), [&](const size_t i) { - if (std::abs(m_invMass[i]) > MIN_REAL && m_Parameters->m_dt > 0.) + if (std::abs(invMasses[i]) > MIN_REAL && m_Parameters->m_dt > 0.0) { vel[i] = (pos[i] - prevPos[i]) / m_Parameters->m_dt; } diff --git a/Source/DynamicalModels/ObjectModels/imstkPbdModel.h b/Source/DynamicalModels/ObjectModels/imstkPbdModel.h index a2c908755fd43012f1337ce2c4b1ff79d9210a0a..7f34a3c9250ac4c974e381b6bf6ebac1a076f9d7 100644 --- a/Source/DynamicalModels/ObjectModels/imstkPbdModel.h +++ b/Source/DynamicalModels/ObjectModels/imstkPbdModel.h @@ -21,18 +21,15 @@ #pragma once -#include <vector> -#include <Eigen/Dense> - -#include "imstkPbdConstraint.h" -#include "imstkPbdFEMConstraint.h" #include "imstkDynamicalModel.h" +#include "imstkPbdCollisionConstraint.h" +#include "imstkPbdFEMConstraint.h" #include "imstkPbdState.h" -#include "imstkMath.h" namespace imstk { class PointSet; +class PbdSolver; /// /// \class PBDModelConfig @@ -40,28 +37,35 @@ class PointSet; /// struct PBDModelConfig { - double m_uniformMassValue = 1.0; ///> Mass properties - double m_viscousDampingCoeff = 0.01; ///> Viscous damping coefficient [0, 1] - - double m_contactStiffness = 1.0; ///> Contact stiffness for collisions - double m_proximity = 0.1; ///> Proximity for collisions + double m_uniformMassValue = 1.0; ///> Mass properties + double m_viscousDampingCoeff = 0.01; ///> Viscous damping coefficient [0, 1] - unsigned int m_maxIter = 10; ///> Max. pbd iterations - double m_dt = 0.0; ///> Time step size - double m_DefaultDt = 0.01; ///> Default Time step size + std::shared_ptr<PbdCollisionConstraintConfig> collisionParams = + std::make_shared<PbdCollisionConstraintConfig>(PbdCollisionConstraintConfig + { + 0.1, // Proximity + 1.0 // Contact stiffness + }); ///> Info shared between the collision constraints - std::vector<std::size_t> m_fixedNodeIds; ///> Nodal IDs of the nodes that are fixed - Vec3r m_gravity = Vec3r(0, -9.81, 0); ///> Gravity + unsigned int m_iterations = 10; ///> Pbd iterations + unsigned int m_collisionIterations = 5; ///> Pbd collision iterations + double m_dt = 0.0; ///> Time step size + double m_defaultDt = 0.01; ///> Default Time step size - double m_mu = 0; ///> Lame constant, if constraint type is FEM - double m_lambda = 0; ///> Lame constant, if constraint type is FEM + std::vector<std::size_t> m_fixedNodeIds; ///> Nodal IDs of the nodes that are fixed + Vec3r m_gravity = Vec3r(0, -9.81, 0); ///> Gravity - double m_YoungModulus = 1000; ///> FEM parameter, if constraint type is FEM - double m_PoissonRatio = 0.2; ///> FEM parameter, if constraint type is FEM + std::shared_ptr<PbdFEMConstraintConfig> femParams = + std::make_shared<PbdFEMConstraintConfig>(PbdFEMConstraintConfig + { + 0.0, // Lame constant, if constraint type is FEM + 0.0, // Lame constant, if constraint type is FEM + 1000.0, // FEM parameter, if constraint type is FEM + 0.2 // FEM parameter, if constraint type is FEM + }); ///> Info shared between the fem constraints - std::vector<std::pair<PbdConstraint::Type, double>> m_RegularConstraints; ///> Constraints except FEM - std::vector<std::pair<PbdConstraint::Type, - PbdFEMConstraint::MaterialType>> m_FEMConstraints; ///> FEM constraints + std::vector<std::pair<PbdConstraint::Type, double>> m_RegularConstraints; ///> Constraints except FEM + std::vector<std::pair<PbdConstraint::Type, PbdFEMConstraint::MaterialType>> m_FEMConstraints; ///> Constraints except FEM /// /// \brief Enable a regular constraint (constraint that is not FEM constraint) @@ -70,7 +74,7 @@ struct PBDModelConfig void enableConstraint(PbdConstraint::Type type, double stiffness); /// - /// \brief Enable a FEM constraint with a given FEM material + /// \brief Enable a FEM constraint with mu, lambda /// void enableFEMConstraint(PbdConstraint::Type type, PbdFEMConstraint::MaterialType material); }; @@ -86,16 +90,7 @@ public: /// /// \brief Constructor /// - PbdModel() : DynamicalModel(DynamicalModelType::PositionBasedDynamics) - { - m_validGeometryTypes = { - Geometry::Type::PointSet, - Geometry::Type::LineMesh, - Geometry::Type::SurfaceMesh, - Geometry::Type::TetrahedralMesh, - Geometry::Type::HexahedralMesh - }; - } + PbdModel(); /// /// \brief Destructor @@ -105,7 +100,7 @@ public: /// /// \brief Set simulation parameters /// - void configure(const std::shared_ptr<PBDModelConfig>& params); + void configure(std::shared_ptr<PBDModelConfig> params); /// /// \brief Get the simulation parameters @@ -154,11 +149,6 @@ public: /// bool initializeConstantDensityConstraint(const double stiffness); - /// - /// \brief compute delta x (position) and update position - /// - void projectConstraints(); - /// /// \brief Update the model geometry from the newest PBD state /// @@ -172,13 +162,13 @@ public: /// /// \brief Returns true if there is at least one constraint /// - bool hasConstraints() const { return !m_constraints.empty() || !m_partitionedConstraints.empty(); } + bool hasConstraints() const { return !m_constraints->empty() || !m_partitionedConstraints->empty(); } /// /// \brief Set the time step size /// virtual void setTimeStep(const Real timeStep) override { m_Parameters->m_dt = timeStep; } - void setDefaultTimeStep(const Real timeStep) { m_Parameters->m_DefaultDt = static_cast<Real>(timeStep); } + void setDefaultTimeStep(const Real timeStep) { m_Parameters->m_defaultDt = static_cast<Real>(timeStep); } /// /// \brief Set the time step size to fixed size @@ -189,7 +179,17 @@ public: /// \brief Returns the time step size /// virtual double getTimeStep() const override { return m_Parameters->m_dt; } - double getDefaultTimeStep() const { return m_Parameters->m_DefaultDt; } + double getDefaultTimeStep() const { return m_Parameters->m_defaultDt; } + + /// + /// \brief + /// + std::shared_ptr<PBDConstraintVector> getConstraints() { return m_constraints; } + + /// + /// \brief + /// + std::shared_ptr<std::vector<PBDConstraintVector>> getPartitionedConstraints() { return m_partitionedConstraints; } /// /// \brief Set uniform mass to all the nodes @@ -209,7 +209,9 @@ public: /// /// \brief Get the inverse of mass of a certain node /// - double getInvMass(const size_t idx) const; + double getInvMass(const size_t idx) const { return (*m_invMass)[idx]; } + + std::shared_ptr<StdVectorOfReal> getInvMasses() { return m_invMass; } /// /// \brief Time integrate the position @@ -237,22 +239,46 @@ public: /// void setConstraintPartitionThreshold(size_t threshold) { m_partitionThreshold = threshold; } + std::shared_ptr<PbdSolver> getSolver() const { return m_pbdSolver; } + + void setSolver(std::shared_ptr<PbdSolver> solver) { this->m_pbdSolver = solver; } + + std::shared_ptr<ComputeNode> getIntegratePositionNode() const { return m_integrationPositionNode; } + + std::shared_ptr<ComputeNode> getUpdateCollisionGeometryNode() const { return m_updateCollisionGeometryNode; } + + std::shared_ptr<ComputeNode> getSolveNode() const { return m_solveConstraintsNode; } + + std::shared_ptr<ComputeNode> getUpdateVelocityNode() const { return m_updateVelocityNode; } + protected: /// /// \brief Partition constraints for parallelization /// void partitionConstraints(const bool print = false); - size_t m_partitionThreshold = 16; ///> Threshold for constraint partitioning + /// + /// \brief Setup the computational graph of PBD + /// + void initGraphEdges(std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink) override; + +protected: + size_t m_partitionThreshold = 16; ///> Threshold for constraint partitioning - std::shared_ptr<PointSet> m_mesh; ///> PointSet on which the pbd model operates on - std::vector<double> m_mass; ///> Mass of nodes - std::vector<double> m_invMass; ///> Inverse of mass of nodes + std::shared_ptr<PbdSolver> m_pbdSolver = nullptr; ///> PBD solver + std::shared_ptr<PointSet> m_mesh = nullptr; ///> PointSet on which the pbd model operates on + std::shared_ptr<StdVectorOfReal> m_mass = nullptr; ///> Mass of nodes + std::shared_ptr<StdVectorOfReal> m_invMass = nullptr; ///> Inverse of mass of nodes - using PBDConstraintVector = std::vector<std::shared_ptr<PbdConstraint>>; + std::shared_ptr<PBDConstraintVector> m_constraints = nullptr; ///> List of pbd constraints + std::shared_ptr<std::vector<PBDConstraintVector>> m_partitionedConstraints = nullptr; ///> List of pbd constraints + std::shared_ptr<PBDModelConfig> m_Parameters = nullptr; ///> Model parameters, must be set before simulation - PBDConstraintVector m_constraints; ///> List of pbd constraints - std::vector<PBDConstraintVector> m_partitionedConstraints; ///> List of pbd constraints - std::shared_ptr<PBDModelConfig> m_Parameters; ///> Model parameters, must be set before simulation +protected: + // Computational Nodes + std::shared_ptr<ComputeNode> m_integrationPositionNode = nullptr; + std::shared_ptr<ComputeNode> m_updateCollisionGeometryNode = nullptr; + std::shared_ptr<ComputeNode> m_solveConstraintsNode = nullptr; + std::shared_ptr<ComputeNode> m_updateVelocityNode = nullptr; }; } // imstk diff --git a/Source/DynamicalModels/ObjectModels/imstkRigidBodyModel.h b/Source/DynamicalModels/ObjectModels/imstkRigidBodyModel.h index b9d377d7bd34a1ece03e01dfd423c71ada8bbdf4..7c9e020342cfe46e1bfbcbff817a80e89916bcd7 100644 --- a/Source/DynamicalModels/ObjectModels/imstkRigidBodyModel.h +++ b/Source/DynamicalModels/ObjectModels/imstkRigidBodyModel.h @@ -19,8 +19,7 @@ =========================================================================*/ -#ifndef imstkRigidBodyModel_h -#define imstkRigidBodyModel_h +#pragma once #include "imstkDynamicalModel.h" #include "imstkRigidBodyState.h" @@ -175,6 +174,4 @@ private: void createCube(); void createMesh(); }; -} // imstk - -#endif \ No newline at end of file +} // imstk \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectModels/imstkSPHModel.cpp b/Source/DynamicalModels/ObjectModels/imstkSPHModel.cpp index ca91fa161179157371ec49e46d519426f85c2c32..2f0ea2549de46dbafbd77778c954d2a0f0583b13 100644 --- a/Source/DynamicalModels/ObjectModels/imstkSPHModel.cpp +++ b/Source/DynamicalModels/ObjectModels/imstkSPHModel.cpp @@ -1,25 +1,28 @@ /*========================================================================= - Library: iMSTK +Library: iMSTK - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. +Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, +& Imaging in Medicine, Rensselaer Polytechnic Institute. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0.txt +http://www.apache.org/licenses/LICENSE-2.0.txt - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. =========================================================================*/ #include "imstkSPHModel.h" +#include "imstkComputeGraph.h" +#include "imstkComputeNode.h" +#include "imstkLogger.h" #include "imstkParallelUtils.h" #include "imstkPointSet.h" @@ -54,6 +57,34 @@ SPHModelConfig::initialize() m_kernelRadiusSqr = m_kernelRadius * m_kernelRadius; } +SPHModel::SPHModel() : DynamicalModel<SPHKinematicState>(DynamicalModelType::SmoothedParticleHydrodynamics) +{ + m_validGeometryTypes = { Geometry::Type::PointSet }; + + m_findParticleNeighborsNode = addFunction("SPHModel_Partition", std::bind(&SPHModel::findParticleNeighbors, this)); + m_computeDensityNode = addFunction("SPHModel_ComputeDensity", [&]() + { + computeNeighborRelativePositions(); + computeDensity(); + normalizeDensity(); + collectNeighborDensity(); + }); + m_computePressureAccelNode = + addFunction("SPHModel_ComputePressureAccel", std::bind(&SPHModel::computePressureAcceleration, this)); + m_computeSurfaceTensionNode = + addFunction("SPHModel_ComputeSurfaceTensionAccel", std::bind(&SPHModel::computeSurfaceTension, this)); + m_computeTimeSetpSizeNode = + addFunction("SPHModel_ComputeTimestep", std::bind(&SPHModel::computeTimeStepSize, this)); + m_integrateNode = + addFunction("SPHModel_Integrate", [&]() + { + sumAccels(); + updateVelocity(getTimeStep()); + computeViscosity(); + moveParticles(getTimeStep()); + }); +} + bool SPHModel::initialize() { @@ -80,7 +111,10 @@ SPHModel::initialize() // Initialize neighbor searcher m_neighborSearcher = std::make_shared<NeighborSearch>(m_modelParameters->m_NeighborSearchMethod, - m_modelParameters->m_kernelRadius); + m_modelParameters->m_kernelRadius); + + m_pressureAccels = std::make_shared<StdVectorOfVec3d>(getState().getNumParticles()); + m_surfaceTensionAccels = std::make_shared<StdVectorOfVec3d>(getState().getNumParticles()); return true; } @@ -93,19 +127,22 @@ SPHModel::updatePhysicsGeometry() } void -SPHModel::advanceTimeStep() +SPHModel::initGraphEdges(std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink) { - findParticleNeighbors(); - computeNeighborRelativePositions(); - computeDensity(); - normalizeDensity(); - collectNeighborDensity(); - computePressureAcceleration(); - computeSurfaceTension(); - computeTimeStepSize(); - updateVelocity(getTimeStep()); - computeViscosity(); - moveParticles(getTimeStep()); + // Setup graph connectivity + addEdge(source, m_findParticleNeighborsNode); + addEdge(m_findParticleNeighborsNode, m_computeDensityNode); + + // Pressure, Surface Tension, and time step size can be done in parallel + addEdge(m_computeDensityNode, m_computePressureAccelNode); + addEdge(m_computeDensityNode, m_computeSurfaceTensionNode); + addEdge(m_computeDensityNode, m_computeTimeSetpSizeNode); + + addEdge(m_computePressureAccelNode, m_integrateNode); + addEdge(m_computeSurfaceTensionNode, m_integrateNode); + addEdge(m_computeTimeSetpSizeNode, m_integrateNode); + + addEdge(m_integrateNode, sink); } void @@ -282,6 +319,7 @@ SPHModel::computePressureAcceleration() return error > Real(0) ? error : Real(0); }; + StdVectorOfVec3d& pressureAccels = *m_pressureAccels; ParallelUtils::parallelFor(getState().getNumParticles(), [&](const size_t p) { Vec3r accel(0, 0, 0); @@ -307,7 +345,19 @@ SPHModel::computePressureAcceleration() } accel *= m_modelParameters->m_pressureStiffness * m_modelParameters->m_particleMass; - getState().getAccelerations()[p] = accel; + //getState().getAccelerations()[p] = accel; + pressureAccels[p] = accel; + }); +} + +void +SPHModel::sumAccels() +{ + const StdVectorOfVec3d& pressureAccels = *m_pressureAccels; + const StdVectorOfVec3d& surfaceTensionAccels = *m_surfaceTensionAccels; + ParallelUtils::parallelFor(getState().getNumParticles(), + [&](const size_t p) { + getState().getAccelerations()[p] = pressureAccels[p] + surfaceTensionAccels[p]; }); } @@ -366,7 +416,7 @@ SPHModel::computeViscosity() ParallelUtils::parallelFor(getState().getNumParticles(), [&](const size_t p) { getState().getVelocities()[p] += getState().getDiffuseVelocities()[p]; - }); + }); } void @@ -396,6 +446,7 @@ SPHModel::computeSurfaceTension() }); // Second, compute surface tension acceleration + StdVectorOfVec3d& surfaceTensionAccels = *m_surfaceTensionAccels; ParallelUtils::parallelFor(getState().getNumParticles(), [&](const size_t p) { const auto& fluidNeighborList = getState().getFluidNeighborLists()[p]; @@ -436,7 +487,8 @@ SPHModel::computeSurfaceTension() } accel *= m_modelParameters->m_surfaceTensionStiffness; - getState().getAccelerations()[p] += accel; + //getState().getAccelerations()[p] += accel; + surfaceTensionAccels[p] = accel; }); } @@ -446,6 +498,6 @@ SPHModel::moveParticles(Real timestep) ParallelUtils::parallelFor(getState().getNumParticles(), [&](const size_t p) { getState().getPositions()[p] += getState().getVelocities()[p] * timestep; - }); + }); } } // end namespace imstk diff --git a/Source/DynamicalModels/ObjectModels/imstkSPHModel.h b/Source/DynamicalModels/ObjectModels/imstkSPHModel.h index d5cedcf23b8a2eabe9996d56bbaaed58cc5bae2d..d90a77786203287f9fd1c0a78b782e835b6b9cd8 100644 --- a/Source/DynamicalModels/ObjectModels/imstkSPHModel.h +++ b/Source/DynamicalModels/ObjectModels/imstkSPHModel.h @@ -28,6 +28,7 @@ namespace imstk { +class ComputeNode; class PointSet; /// @@ -92,10 +93,7 @@ public: /// /// \brief Constructor /// - SPHModel() : DynamicalModel<SPHKinematicState>(DynamicalModelType::SmoothedParticleHydrodynamics) - { - m_validGeometryTypes = { Geometry::Type::PointSet }; - } + SPHModel(); /// /// \brief Destructor @@ -175,10 +173,11 @@ public: virtual double getTimeStep() const override { return static_cast<double>(m_dt); } +protected: /// - /// \brief Do one time step simulation + /// \brief Setup SPH compute graph connectivity /// - void advanceTimeStep(); + virtual void initGraphEdges(std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink) override; private: /// @@ -222,6 +221,11 @@ private: /// void computePressureAcceleration(); + /// + /// \brief Sum the forces computed in parallel + /// + void sumAccels(); + /// /// \brief Update particle velocities due to pressure /// @@ -244,6 +248,16 @@ private: /// void moveParticles(const Real timestep); +protected: + std::shared_ptr<ComputeNode> m_findParticleNeighborsNode = nullptr; + std::shared_ptr<ComputeNode> m_computeDensityNode = nullptr; + std::shared_ptr<ComputeNode> m_computePressureAccelNode = nullptr; + std::shared_ptr<ComputeNode> m_computeSurfaceTensionNode = nullptr; + std::shared_ptr<ComputeNode> m_computeTimeSetpSizeNode = nullptr; + std::shared_ptr<ComputeNode> m_sumAccelsNode = nullptr; + std::shared_ptr<ComputeNode> m_integrateNode = nullptr; + +private: std::shared_ptr<PointSet> m_pointSetGeometry; SPHSimulationState m_simulationState; @@ -253,5 +267,8 @@ private: SPHSimulationKernels m_kernels; ///> SPH kernels (must be initialized during model initialization) std::shared_ptr<SPHModelConfig> m_modelParameters; ///> SPH Model parameters (must be set before simulation) std::shared_ptr<NeighborSearch> m_neighborSearcher; ///> Neighbor Search (must be initialized during model initialization) + + std::shared_ptr<StdVectorOfVec3d> m_pressureAccels = nullptr; + std::shared_ptr<StdVectorOfVec3d> m_surfaceTensionAccels = nullptr; }; } // end namespace imstk diff --git a/Source/DynamicalModels/ObjectStates/imstkPbdState.cpp b/Source/DynamicalModels/ObjectStates/imstkPbdState.cpp index 4c06dc9f5a2b0df9df9bf4474a7e259c10f30f5a..13c84c079c919daf9bc67d76a1d167534e89e937 100644 --- a/Source/DynamicalModels/ObjectStates/imstkPbdState.cpp +++ b/Source/DynamicalModels/ObjectStates/imstkPbdState.cpp @@ -25,36 +25,25 @@ namespace imstk { void -PbdState::initialize(const size_t numNodes, const bool (& options)[3]) +PbdState::initialize(const size_t numNodes) { - // CHECKBACK : m_pos could actually not be another copy - if (options[0]) - { - m_pos.resize(numNodes, Vec3d(0, 0, 0)); - } - - if (options[1]) - { - m_vel.resize(numNodes, Vec3d(0, 0, 0)); - } - - if (options[2]) - { - m_acc.resize(numNodes, Vec3d(0, 0, 0)); - } + m_pos->resize(numNodes, Vec3d(0.0, 0.0, 0.0)); + m_vel->resize(numNodes, Vec3d(0.0, 0.0, 0.0)); + m_acc->resize(numNodes, Vec3d(0.0, 0.0, 0.0)); } void -PbdState::initialize(const std::shared_ptr<PointSet>& m, const bool (& options)[3]) +PbdState::initialize(const StdVectorOfVec3d& vertices) { - this->initialize(m->getNumVertices(), options); + this->initialize(vertices.size()); + setPositions(vertices); } void PbdState::setState(std::shared_ptr<PbdState> rhs) { - m_pos = rhs->getPositions(); - m_vel = rhs->getVelocities(); - m_acc = rhs->getAccelerations(); + *m_pos = *rhs->getPositions(); + *m_vel = *rhs->getVelocities(); + *m_acc = *rhs->getAccelerations(); } } // imstk \ No newline at end of file diff --git a/Source/DynamicalModels/ObjectStates/imstkPbdState.h b/Source/DynamicalModels/ObjectStates/imstkPbdState.h index bcc2884edf0417daf9cf185002813c922f46a4e4..65f5019e57a89f52c6b0097059741b7a5bdf3223 100644 --- a/Source/DynamicalModels/ObjectStates/imstkPbdState.h +++ b/Source/DynamicalModels/ObjectStates/imstkPbdState.h @@ -43,30 +43,40 @@ public: /// /// \brief Initialize the pbd state /// - void initialize(const size_t numNodes, const bool (& options)[3]); - void initialize(const std::shared_ptr<PointSet>& m, const bool (& options)[3]); + void initialize(const size_t numNodes); + void initialize(const StdVectorOfVec3d& vertices); /// /// \brief Get/Set nodal position given the index /// - void setVertexPosition(const size_t& idx, const Vec3d& pos) { m_pos.at(idx) = pos; } - Vec3d& getVertexPosition(const size_t& idx) { return m_pos.at(idx); } + void setVertexPosition(const size_t& idx, const Vec3d& pos) { m_pos->at(idx) = pos; } + Vec3d& getVertexPosition(const size_t& idx) { return m_pos->at(idx); } /// /// \brief Returns the vector of current nodal positions /// - StdVectorOfVec3d& getPositions() { return m_pos; } - void setPositions(const StdVectorOfVec3d& p) { m_pos = p; } + std::shared_ptr<StdVectorOfVec3d> getPositions() { return m_pos; } + void setPositions(std::shared_ptr<StdVectorOfVec3d> p) + { + m_pos->resize(p->size()); + std::copy(p->begin(), p->end(), m_pos->begin()); + } + + void setPositions(const StdVectorOfVec3d& p) + { + m_pos->resize(p.size()); + std::copy(p.begin(), p.end(), m_pos->begin()); + } /// /// \brief Returns the vector of current nodal velocities /// - StdVectorOfVec3d& getVelocities() { return m_vel; } + std::shared_ptr<StdVectorOfVec3d> getVelocities() { return m_vel; } /// /// \brief Returns the vector of current nodal accelerations /// - StdVectorOfVec3d& getAccelerations() { return m_acc; } + std::shared_ptr<StdVectorOfVec3d> getAccelerations() { return m_acc; } /// /// \brief Set the state to a given one @@ -74,8 +84,8 @@ public: void setState(std::shared_ptr<PbdState> rhs); private: - StdVectorOfVec3d m_pos; ///> Nodal positions - StdVectorOfVec3d m_vel; ///> Nodal velocities - StdVectorOfVec3d m_acc; ///> Nodal acelerations + std::shared_ptr<StdVectorOfVec3d> m_pos = std::make_shared<StdVectorOfVec3d>(); ///> Nodal positions + std::shared_ptr<StdVectorOfVec3d> m_vel = std::make_shared<StdVectorOfVec3d>(); ///> Nodal velocities + std::shared_ptr<StdVectorOfVec3d> m_acc = std::make_shared<StdVectorOfVec3d>(); ///> Nodal acelerations }; } // imstk diff --git a/Source/DynamicalModels/ObjectStates/imstkRigidBodyState.h b/Source/DynamicalModels/ObjectStates/imstkRigidBodyState.h index 2f0764a6d07e0a0016081a0b14f60138a38db3ea..b67ce30c98cce95d020b5a7b7080949d4f3efad8 100644 --- a/Source/DynamicalModels/ObjectStates/imstkRigidBodyState.h +++ b/Source/DynamicalModels/ObjectStates/imstkRigidBodyState.h @@ -19,8 +19,7 @@ =========================================================================*/ -#ifndef imstkRigidBodyState_h -#define imstkRigidBodyState_h +#pragma once #include <Eigen/Dense> #include <memory> @@ -77,6 +76,4 @@ private: //Can add linear velocity and angular velocity too. }; -} // imstk - -#endif // imstkPbdState_h \ No newline at end of file +} // imstk \ No newline at end of file diff --git a/Source/SceneEntities/Objects/deprecated/imstkPbdRigidObject.h b/Source/SceneEntities/Objects/deprecated/imstkPbdRigidObject.h deleted file mode 100644 index e9bfb4de1444facae73261e35b38e69f08dac32b..0000000000000000000000000000000000000000 --- a/Source/SceneEntities/Objects/deprecated/imstkPbdRigidObject.h +++ /dev/null @@ -1,67 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#pragma once - -#include "imstkPbdObject.h" - -namespace imstk -{ -class Geometry; -class GeometryMap; - -/// -/// \class PbdRigidObject -/// -/// \brief -/// -class PbdRigidObject : public PbdObject -{ -public: - /// - /// \brief Constructor - /// - explicit PbdRigidObject(const std::string& name) : PbdObject(name) - { m_type = SceneObject::Type::Pbd; } - - /// - /// \brief Destructor - /// - ~PbdRigidObject() = default; - - /// - /// \brief - /// - virtual void integratePosition() { return; } - - /// - /// \brief - /// - virtual void integrateVelocity() { return; } - - /// - /// \brief - /// - virtual void updatePbdStates(); - -protected: -}; -} // imstk diff --git a/Source/SceneEntities/Objects/imstkCollidingObject.cpp b/Source/SceneEntities/Objects/imstkCollidingObject.cpp index cb07dc45a23d0f5e97954361081d3569e1aefc7a..324215144a0e40ad5da18280047b6fd0616cffaa 100644 --- a/Source/SceneEntities/Objects/imstkCollidingObject.cpp +++ b/Source/SceneEntities/Objects/imstkCollidingObject.cpp @@ -20,6 +20,7 @@ =========================================================================*/ #include "imstkCollidingObject.h" +#include "imstkComputeGraph.h" #include "imstkGeometryMap.h" namespace imstk @@ -27,19 +28,17 @@ namespace imstk bool CollidingObject::initialize() { - if (SceneObject::initialize()) + if (!SceneObject::initialize()) { - if (m_collidingToVisualMap) - { - m_collidingToVisualMap->initialize(); - } - - return true; + return false; } - else + + if (m_collidingToVisualMap) { - return false; + m_collidingToVisualMap->initialize(); } + + return true; } const std::shared_ptr<Geometry>& diff --git a/Source/SceneEntities/Objects/imstkCollidingObject.h b/Source/SceneEntities/Objects/imstkCollidingObject.h index dfe6e5e14c8c4fe4d11706088d08c6a8401a3417..512f1ab55de7aa72f44107a1909a17ba42b332d4 100644 --- a/Source/SceneEntities/Objects/imstkCollidingObject.h +++ b/Source/SceneEntities/Objects/imstkCollidingObject.h @@ -23,6 +23,7 @@ #include "imstkSceneObject.h" #include "imstkMath.h" +#include <unordered_map> namespace imstk { @@ -81,7 +82,6 @@ public: virtual bool initialize() override; protected: - std::shared_ptr<Geometry> m_collidingGeometry; ///> Geometry for collisions std::shared_ptr<GeometryMap> m_collidingToVisualMap; ///> Maps transformations to visual geometry Vec3d m_force = Vec3d::Zero(); diff --git a/Source/SceneEntities/Objects/imstkDeformableObject.cpp b/Source/SceneEntities/Objects/imstkDeformableObject.cpp index cd760dd8a30aaf2e6aac4662701484e53a028a3b..e4d1fb73f7da0a3df888484e3fa1bc0ce7d4ed8b 100644 --- a/Source/SceneEntities/Objects/imstkDeformableObject.cpp +++ b/Source/SceneEntities/Objects/imstkDeformableObject.cpp @@ -25,62 +25,19 @@ namespace imstk { -Vectord& -FeDeformableObject::getContactForce() -{ - CHECK(m_defModel != nullptr) << "deformation model pointer not valid DeformableObject::getContactForce()"; - - return m_defModel->getContactForce(); -} - bool FeDeformableObject::initialize() { - m_defModel = std::dynamic_pointer_cast<FEMDeformableBodyModel>(m_dynamicalModel); - if (m_defModel) - { - return DynamicObject::initialize(); - } - else + m_femModel = std::dynamic_pointer_cast<FEMDeformableBodyModel>(m_dynamicalModel); + if (m_femModel == nullptr) { LOG(FATAL) << "Dynamics pointer cast failure in DeformableObject::initialize()"; return false; } -} - -const Vectord& -FeDeformableObject::getDisplacements() const -{ - return m_defModel->getCurrentState()->getQ(); -} - -const Vectord& -FeDeformableObject::getPrevDisplacements() const -{ - return m_defModel->getPreviousState()->getQ(); -} -const Vectord& -FeDeformableObject::getVelocities() const -{ - return m_defModel->getCurrentState()->getQDot(); -} + DynamicObject::initialize(); + m_femModel->initialize(); -const Vectord& -FeDeformableObject::getPrevVelocities() const -{ - return m_defModel->getPreviousState()->getQDot(); -} - -const Vectord& -FeDeformableObject::getAccelerations() const -{ - return m_defModel->getCurrentState()->getQDotDot(); -} - -const Vectord& -FeDeformableObject::getPrevAccelerations() const -{ - return m_defModel->getPreviousState()->getQDotDot(); + return true; } } // imstk diff --git a/Source/SceneEntities/Objects/imstkDeformableObject.h b/Source/SceneEntities/Objects/imstkDeformableObject.h index 00a76e40aed650cd094cae8dbfdaa49dd9c7feca..690075e4c75d81d177b25fd555daa37a8cefad51 100644 --- a/Source/SceneEntities/Objects/imstkDeformableObject.h +++ b/Source/SceneEntities/Objects/imstkDeformableObject.h @@ -30,8 +30,6 @@ namespace imstk { -class Geometry; -class GeometryMap; class FEMDeformableBodyModel; /// @@ -42,70 +40,20 @@ class FEMDeformableBodyModel; class FeDeformableObject : public DynamicObject { public: - - /// - /// \brief Constructor - /// explicit FeDeformableObject(const std::string& name) : DynamicObject(name) { m_type = Type::FEMDeformable; } FeDeformableObject() = delete; - /// - /// \brief Destructor - /// ~FeDeformableObject() = default; +public: /// /// \brief Initialize the deformable object /// bool initialize() override; - /// - /// \brief Initialize the kinematic state of the body - /// - void initializeState(); - void initializeState(const Vectord& p, const Vectord& v); - - /// - /// \brief Set/Get dynamical model - /// - //void setDynamicalModel(std::shared_ptr<DynamicalModel<VectorizedState>> dynaDefModel) override; - - /// - /// \brief Get the vector that holds the contact forces - /// - Vectord& getContactForce(); - - /// - /// \brief Get the vector of current displacements - /// - const Vectord& getDisplacements() const; - - /// - /// \brief Get the vector of displacements from previous time step - /// - const Vectord& getPrevDisplacements() const; - - /// - /// \brief Get the vector of current velocities - /// - const Vectord& getVelocities() const; - - /// - /// \brief Get the vector of velocities from previous time step - /// - const Vectord& getPrevVelocities() const; - - /// - /// \brief Get the vector of current accelerations - /// - const Vectord& getAccelerations() const; - - /// - /// \brief Get the vector of accelerations from previous time step - /// - const Vectord& getPrevAccelerations() const; + std::shared_ptr<FEMDeformableBodyModel> getFEMModel() const { return m_femModel; } protected: - std::shared_ptr<FEMDeformableBodyModel> m_defModel; + std::shared_ptr<FEMDeformableBodyModel> m_femModel = nullptr; }; } // imstk diff --git a/Source/SceneEntities/Objects/imstkDynamicObject.cpp b/Source/SceneEntities/Objects/imstkDynamicObject.cpp index 50d28d7b2ed4f744a3bcbd752cc506f5242af72a..b56b3ea93cd609e4b0d5046db0c06e65f6cfc341 100644 --- a/Source/SceneEntities/Objects/imstkDynamicObject.cpp +++ b/Source/SceneEntities/Objects/imstkDynamicObject.cpp @@ -22,6 +22,7 @@ #include "imstkDynamicObject.h" #include "imstkGeometryMap.h" #include "imstkAbstractDynamicalModel.h" +#include "imstkComputeGraph.h" #include "imstkLogger.h" namespace imstk @@ -61,6 +62,17 @@ DynamicObject::updateGeometries() } } +void +DynamicObject::updatePhysicsGeometry() +{ + m_dynamicalModel->updatePhysicsGeometry(); + + if (m_physicsToCollidingGeomMap) + { + m_physicsToCollidingGeomMap->apply(); + } +} + bool DynamicObject::initialize() { @@ -84,6 +96,16 @@ DynamicObject::initialize() } } +void +DynamicObject::initGraphEdges(std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink) +{ + // Copy, sum, and connect the model graph to nest within this graph + m_computeGraph->addEdge(source, getUpdateNode()); + m_dynamicalModel->initGraphEdges(); + m_computeGraph->nestGraph(m_dynamicalModel->getComputeGraph(), getUpdateNode(), getUpdateGeometryNode()); + m_computeGraph->addEdge(getUpdateGeometryNode(), sink); +} + void DynamicObject::reset() { diff --git a/Source/SceneEntities/Objects/imstkDynamicObject.h b/Source/SceneEntities/Objects/imstkDynamicObject.h index 0535d059ab62f7389f3b3d3dda621778552d056b..c562afdb12555250fb776a2854d3ab1862eb6f1a 100644 --- a/Source/SceneEntities/Objects/imstkDynamicObject.h +++ b/Source/SceneEntities/Objects/imstkDynamicObject.h @@ -37,7 +37,6 @@ class AbstractDynamicalModel; class DynamicObject : public CollidingObject { public: - /// /// \brief Destructor /// @@ -82,16 +81,27 @@ public: /// void updateGeometries() final; + /// + /// \brief Update only the physics geometry and apply collision map + /// + void updatePhysicsGeometry(); + /// /// \brief Initialize the scene object /// virtual bool initialize() override; /// - /// \brief Reset the dynamic object to its initial state + /// \brief Reset the dynamic object by reseting the respective DynamicalModel and Geometry /// virtual void reset() override; +protected: + /// + /// \brief Setup connectivity of compute graph + /// + virtual void initGraphEdges(std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink) override; + protected: /// @@ -99,12 +109,12 @@ protected: /// explicit DynamicObject(const std::string& name) : CollidingObject(name) {} - std::shared_ptr<AbstractDynamicalModel> m_dynamicalModel; ///> Dynamical model - std::shared_ptr<Geometry> m_physicsGeometry; ///> Geometry used for Physics + std::shared_ptr<AbstractDynamicalModel> m_dynamicalModel = nullptr; ///> Dynamical model + std::shared_ptr<Geometry> m_physicsGeometry = nullptr; ///> Geometry used for Physics - //Maps - std::shared_ptr<GeometryMap> m_physicsToCollidingGeomMap; ///> Maps from Physics to collision geometry - std::shared_ptr<GeometryMap> m_physicsToVisualGeomMap; ///> Maps from Physics to visual geometry + // Maps + std::shared_ptr<GeometryMap> m_physicsToCollidingGeomMap = nullptr; ///> Maps from Physics to collision geometry + std::shared_ptr<GeometryMap> m_physicsToVisualGeomMap = nullptr; ///> Maps from Physics to visual geometry bool m_updateVisualFromPhysicsGeometry = true; ///> Defines if visual is updated from colliding mapping or physics mapping }; } // imstk diff --git a/Source/SceneEntities/Objects/imstkPbdObject.cpp b/Source/SceneEntities/Objects/imstkPbdObject.cpp index ae380e2f71d42a60afbbf080900b0f7d70ad8782..731aa243662478c8d175300cbc08b51910aef344 100644 --- a/Source/SceneEntities/Objects/imstkPbdObject.cpp +++ b/Source/SceneEntities/Objects/imstkPbdObject.cpp @@ -20,53 +20,31 @@ =========================================================================*/ #include "imstkPbdObject.h" -#include "imstkGeometryMap.h" -#include "imstkPbdModel.h" #include "imstkLogger.h" +#include "imstkPbdModel.h" namespace imstk { -bool -PbdObject::initialize() +std::shared_ptr<PbdModel> +PbdObject::getPbdModel() { m_pbdModel = std::dynamic_pointer_cast<PbdModel>(m_dynamicalModel); + return m_pbdModel; +}; - CHECK(m_pbdModel != nullptr) << "Dynamics pointer cast failure in PbdObject::initialize()"; - - return DynamicObject::initialize(); -} - -void -PbdObject::integratePosition() -{ - if (m_pbdModel && m_pbdModel->hasConstraints()) - { - m_pbdModel->integratePosition(); - } -} - -void -PbdObject::updateVelocity() +bool +PbdObject::initialize() { - if (m_pbdModel && m_pbdModel->hasConstraints()) + m_pbdModel = std::dynamic_pointer_cast<PbdModel>(m_dynamicalModel); + if (m_pbdModel == nullptr) { - m_pbdModel->updateVelocity(); + LOG(FATAL) << "Dynamics pointer cast failure in PbdObject::initialize()"; + return false; } -} -void -PbdObject::solveConstraints() -{ - if (m_pbdModel && m_pbdModel->hasConstraints()) - { - m_pbdModel->projectConstraints(); - } -} + DynamicObject::initialize(); + m_pbdModel->initialize(); -void -PbdObject::reset() -{ - DynamicObject::reset(); - this->updateVelocity(); + return true; } } //imstk diff --git a/Source/SceneEntities/Objects/imstkPbdObject.h b/Source/SceneEntities/Objects/imstkPbdObject.h index 17feda4e8ffffc9326316bba6045e9ca91b316bd..0ee18a27ce575a4c5a19b7b5e4d9139fa2964722 100644 --- a/Source/SceneEntities/Objects/imstkPbdObject.h +++ b/Source/SceneEntities/Objects/imstkPbdObject.h @@ -25,22 +25,17 @@ namespace imstk { -class Geometry; -class GeometryMap; class PbdModel; /// /// \class PbdObject /// /// \brief Base class for scene objects that move and/or deform under position -/// based dynamics formulation +/// based dynamics formulation, implements the PbdModel and PbdSolver /// class PbdObject : public DynamicObject { public: - /// - /// \brief Constructor - /// explicit PbdObject(const std::string& name) : DynamicObject(name) { m_type = SceneObject::Type::Pbd; @@ -48,37 +43,20 @@ public: PbdObject() = delete; - /// - /// \brief Destructor - /// ~PbdObject() override = default; +public: /// - /// \brief Initialize the pbd scene object - /// - bool initialize() override; - - /// - /// \brief Update the position based on Verlet time stepping rule - /// - virtual void integratePosition(); - - /// - /// \brief Update the velocity - /// - virtual void updateVelocity(); - - /// - /// \brief Solve the pbd constraints by projection + /// \biref Get the Pbd model of the object /// - virtual void solveConstraints(); + std::shared_ptr<PbdModel> getPbdModel(); /// - /// \brief Reset the PBD object to its initial state + /// \brief Initialize the pbd scene object /// - void reset() override; + bool initialize() override; protected: - std::shared_ptr<PbdModel> m_pbdModel; ///> PBD mathematical model + std::shared_ptr<PbdModel> m_pbdModel = nullptr; ///> PBD mathematical model }; } // imstk diff --git a/Source/SceneEntities/Objects/imstkRigidObject.cpp b/Source/SceneEntities/Objects/imstkRigidObject.cpp index 407520854b7fb416a8c924a1d2b758b6a99bd8c6..fa2ebba781ae404919be4cf353ecb80b111c0cd0 100644 --- a/Source/SceneEntities/Objects/imstkRigidObject.cpp +++ b/Source/SceneEntities/Objects/imstkRigidObject.cpp @@ -18,28 +18,32 @@ limitations under the License. =========================================================================*/ + #include "imstkRigidObject.h" #include "imstkLogger.h" +#include "imstkRigidBodyModel.h" namespace imstk { bool RigidObject::initialize() { - auto m_rigidBodyModel = std::dynamic_pointer_cast<RigidBodyModel>(m_dynamicalModel); - if (m_rigidBodyModel) - { - /*if (!m_rigidBodyModel->getRigidBodyWorld()) - { - LOG(WARNING) << "RigidObject::initialize() - Rigid body world not specified!"; - return false; - }*/ - return DynamicObject::initialize(); - } - else + m_rigidBodyModel = std::dynamic_pointer_cast<RigidBodyModel>(m_dynamicalModel); + if (m_rigidBodyModel == nullptr) { - LOG(WARNING) << "RigidObject::initialize() - Dynamics pointer cast failure"; + LOG(FATAL) << "Dynamics pointer cast failure in RigidObject::initialize()"; return false; } + + DynamicObject::initialize(); + m_rigidBodyModel->initialize(); + + return true; +} + +void +RigidObject::addForce(const Vec3d& force, const Vec3d& pos, bool wakeup) +{ + m_rigidBodyModel->addForce(force, pos, wakeup); } } // imstk diff --git a/Source/SceneEntities/Objects/imstkRigidObject.h b/Source/SceneEntities/Objects/imstkRigidObject.h index 7e0ff3a2810f87506bc782d6108e36521b841abb..eb24b27f24ab84c3a3ec4b9f9703d5ffd29efca8 100644 --- a/Source/SceneEntities/Objects/imstkRigidObject.h +++ b/Source/SceneEntities/Objects/imstkRigidObject.h @@ -21,12 +21,12 @@ #pragma once -// imstk #include "imstkDynamicObject.h" -#include "imstkRigidBodyModel.h" namespace imstk { +class RigidBodyModel; + /// /// \class RigidObject /// @@ -35,20 +35,14 @@ namespace imstk class RigidObject : public DynamicObject { public: - - /// - /// \brief Constructor - /// explicit RigidObject(const std::string& name) : DynamicObject(name) { m_type = Type::Rigid; } - /// - /// \brief Destructor - /// ~RigidObject() = default; +public: /// /// \brief Initialize the rigid scene object /// @@ -57,24 +51,11 @@ public: /// /// \brief Add local force at a position relative to object /// - void addForce(const Vec3d& force, const Vec3d& pos, bool wakeup = true) - { - getRigidBodyModel()->addForce(force, pos, wakeup); - } + void addForce(const Vec3d& force, const Vec3d& pos, bool wakeup = true); - /// - /// \brief Get/Set rigid body model - /// - void setRigidBodyModel(std::shared_ptr<RigidBodyModel> rbModel) { m_dynamicalModel = rbModel; }; - std::shared_ptr<RigidBodyModel> getRigidBodyModel() const { return std::dynamic_pointer_cast<RigidBodyModel>(m_dynamicalModel); }; - - void reset() override - { - m_dynamicalModel->resetToInitialState(); - this->updateGeometries(); - } + std::shared_ptr<RigidBodyModel> getRigidBodyModel() const { return m_rigidBodyModel; } protected: - //std::shared_ptr<RigidBodyModel> m_rigidBodyModel; ///> PBD mathematical model + std::shared_ptr<RigidBodyModel> m_rigidBodyModel; }; } // imstk \ No newline at end of file diff --git a/Source/SceneEntities/Objects/imstkSPHObject.cpp b/Source/SceneEntities/Objects/imstkSPHObject.cpp index f5896db40469425e42833a82ca5ab554a6c1645a..9be7701c33be9051e53d9ece65162713da930191 100644 --- a/Source/SceneEntities/Objects/imstkSPHObject.cpp +++ b/Source/SceneEntities/Objects/imstkSPHObject.cpp @@ -26,15 +26,22 @@ namespace imstk { SPHObject::SPHObject(const std::string& name) : DynamicObject(name) { - this->m_type = SceneObject::Type::SPH; + this->m_type = Type::SPH; } bool SPHObject::initialize() { - m_SPHModel = std::dynamic_pointer_cast<SPHModel>(this->m_dynamicalModel); - LOG_IF(FATAL, (!m_SPHModel)) << "Invalid SPH model"; + m_SPHModel = std::dynamic_pointer_cast<SPHModel>(m_dynamicalModel); + if (m_SPHModel == nullptr) + { + LOG(FATAL) << "Dynamics pointer cast failure in SPHObject::initialize()"; + return false; + } - return m_SPHModel->initialize(); + DynamicObject::initialize(); + m_SPHModel->initialize(); + + return true; } } // end namespace imstk diff --git a/Source/SceneEntities/Objects/imstkSPHObject.h b/Source/SceneEntities/Objects/imstkSPHObject.h index c123cadb9c953c888d27de7b2e78cabf2ec9e593..d6f8936633e1e84e0043e18ae2c87b4cdc604987 100644 --- a/Source/SceneEntities/Objects/imstkSPHObject.h +++ b/Source/SceneEntities/Objects/imstkSPHObject.h @@ -30,22 +30,17 @@ class SPHModel; /// /// \class SPHObject /// -/// \brief Base class for scene objects that move and/or deform under position -/// based dynamics formulation +/// \brief Base class for scene objects that move and/or deform under +/// smooth particle hydrodynamics /// class SPHObject : public DynamicObject { public: - /// - /// \brief Constructor - /// explicit SPHObject(const std::string& name); - /// - /// \brief Destructor - /// virtual ~SPHObject() override = default; +public: /// /// \brief Initialize the SPH scene object /// @@ -54,9 +49,9 @@ public: /// /// \brief Get the SPH model of the object /// - const std::shared_ptr<SPHModel>& getSPHModel() const { assert(m_SPHModel); return m_SPHModel; } + std::shared_ptr<SPHModel> getSPHModel() const { assert(m_SPHModel); return m_SPHModel; } protected: - std::shared_ptr<SPHModel> m_SPHModel; + std::shared_ptr<SPHModel> m_SPHModel = nullptr; }; } // end namespace imstk diff --git a/Source/SceneEntities/Objects/imstkSceneObject.cpp b/Source/SceneEntities/Objects/imstkSceneObject.cpp index 65c82cc3cbef5701b0c6e0177f48a571c0d4f9ba..b28d296c354dfdc74ede2589e18f4bb2dbf8b1e2 100644 --- a/Source/SceneEntities/Objects/imstkSceneObject.cpp +++ b/Source/SceneEntities/Objects/imstkSceneObject.cpp @@ -20,12 +20,17 @@ =========================================================================*/ #include "imstkSceneObject.h" +#include "imstkComputeGraph.h" +#include "imstkComputeNode.h" namespace imstk { -SceneObject::SceneObject(const std::string& name) : m_name(name), SceneEntity() +SceneObject::SceneObject(const std::string& name) : + m_type(Type::Visual), m_name(name), + m_computeGraph(std::make_shared<ComputeGraph>("SceneObject_" + name + "_Source", "SceneObject_" + name + "_Sink")), SceneEntity() { - m_type = Type::Visual; + m_updateNode = m_computeGraph->addFunction("SceneObject_" + name + "_Update", [&]() { this->update(); }); + m_updateGeometryNode = m_computeGraph->addFunction("SceneObject_" + name + "_UpdateGeometry", [&]() { this->updateGeometries(); }); } std::shared_ptr<Geometry> @@ -51,57 +56,18 @@ SceneObject::setVisualGeometry(std::shared_ptr<Geometry> geometry) } } -std::shared_ptr<Geometry> -SceneObject::getMasterGeometry() const -{ - return this->getVisualGeometry(); -} - -std::shared_ptr<VisualModel> -SceneObject::getVisualModel(unsigned int index) -{ - return m_visualModels[index]; -} - void -SceneObject::addVisualModel(std::shared_ptr<VisualModel> visualModel) -{ - m_visualModels.push_back(visualModel); -} - -const std::vector<std::shared_ptr<VisualModel>>& -SceneObject::getVisualModels() -{ - return m_visualModels; -} - -size_t -SceneObject::getNumVisualModels() -{ - return m_visualModels.size(); -} - -const SceneObject::Type& -SceneObject::getType() const -{ - return m_type; -} - -void -SceneObject::setType(SceneObject::Type type) -{ - m_type = type; -} - -const std::string& -SceneObject::getName() const +SceneObject::initGraphEdges() { - return m_name; + m_computeGraph->clearEdges(); + initGraphEdges(m_computeGraph->getSource(), m_computeGraph->getSink()); } void -SceneObject::setName(const std::string& name) +SceneObject::initGraphEdges(std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink) { - m_name = name; + m_computeGraph->addEdge(source, m_updateNode); + m_computeGraph->addEdge(m_updateNode, m_updateGeometryNode); + m_computeGraph->addEdge(m_updateGeometryNode, sink); } } // imstk diff --git a/Source/SceneEntities/Objects/imstkSceneObject.h b/Source/SceneEntities/Objects/imstkSceneObject.h index 6d1cb9eaeca15f764fbb7e01d08be180a6794ea9..df93a23dc23d3ccd2b9111a6714bab13825358d4 100644 --- a/Source/SceneEntities/Objects/imstkSceneObject.h +++ b/Source/SceneEntities/Objects/imstkSceneObject.h @@ -29,8 +29,10 @@ namespace imstk { -class Geometry; +class ComputeNode; +class ComputeGraph; class DeviceClient; +class Geometry; /// /// \class SceneObject @@ -62,16 +64,22 @@ public: /// virtual ~SceneObject() = default; +public: /// /// \brief Get the type of the object /// - const Type& getType() const; + const Type& getType() const { return m_type; } + + /// + /// \brief Get the computational graph + /// + std::shared_ptr<ComputeGraph> getComputeGraph() const { return m_computeGraph; } /// /// \brief Get/Set the custom name of the scene object /// - const std::string& getName() const; - void setName(const std::string& name); + const std::string& getName() const { return m_name; } + void setName(const std::string& name) { m_name = name; } /// /// \brief DEPRECATED: Get/Set geometry used for viewing @@ -84,23 +92,38 @@ public: /// /// \brief Get/add visual model /// - std::shared_ptr<VisualModel> getVisualModel(unsigned int index); - void addVisualModel(std::shared_ptr<VisualModel> visualModel); + std::shared_ptr<VisualModel> getVisualModel(unsigned int index) { return m_visualModels[index]; } + void addVisualModel(std::shared_ptr<VisualModel> visualModel) { m_visualModels.push_back(visualModel); } /// /// \brief Get all visual models /// - const std::vector<std::shared_ptr<VisualModel>>& getVisualModels(); + const std::vector<std::shared_ptr<VisualModel>>& getVisualModels() { return m_visualModels; } /// /// \brief Get number of visual models /// - size_t getNumVisualModels(); + size_t getNumVisualModels() { return m_visualModels.size(); } /// /// \brief Get the master geometry /// - virtual std::shared_ptr<Geometry> getMasterGeometry() const; + virtual std::shared_ptr<Geometry> getMasterGeometry() const { return this->getVisualGeometry(); } + + /// + /// \brief Returns the computational node for updating + /// + std::shared_ptr<ComputeNode> getUpdateNode() const { return m_updateNode; } + + /// + /// \brief Returns the computational node for updating geometry + /// + std::shared_ptr<ComputeNode> getUpdateGeometryNode() const { return m_updateGeometryNode; } + + /// + /// \brief + /// + virtual void update() { } /// /// \brief @@ -112,20 +135,32 @@ public: /// virtual bool initialize() { return true; } + /// + /// \brief Initializes the edges of the SceneObject's computational graph + /// + void initGraphEdges(); + /// /// \brief /// - virtual void reset() {} + virtual void reset() { } protected: /// - /// \brief Assigns the type of the object + /// \brief Setup connectivity of the compute graph /// - void setType(Type type); + virtual void initGraphEdges(std::shared_ptr<ComputeNode> source, std::shared_ptr<ComputeNode> sink); +protected: Type m_type; ///> Type of the scene object std::string m_name; ///> Custom name of the scene object std::vector<std::shared_ptr<VisualModel>> m_visualModels; ///> Visual objects for rendering + std::shared_ptr<ComputeGraph> m_computeGraph = nullptr; ///> Computational Graph + +private: + // Dissallow reassignment of these in subclasses + std::shared_ptr<ComputeNode> m_updateNode = nullptr; + std::shared_ptr<ComputeNode> m_updateGeometryNode = nullptr; }; using VisualObject = SceneObject; diff --git a/Source/Solvers/CMakeLists.txt b/Source/Solvers/CMakeLists.txt index 14b17c8a3dd2ecff8eb109f6dc8d1c4efdcd8a50..9f45108fb8cb52421166d8cfc28e347a680b2927 100644 --- a/Source/Solvers/CMakeLists.txt +++ b/Source/Solvers/CMakeLists.txt @@ -5,7 +5,6 @@ include(imstkAddLibrary) imstk_add_library( Solvers DEPENDS Constraints - SceneEntities ) #----------------------------------------------------------------------------- diff --git a/Source/Solvers/imstkPbdSolver.cpp b/Source/Solvers/imstkPbdSolver.cpp index 83cbc529fbc3b47bff087936aacd81d2a1bd4bd2..16c5eb09e2b8a89a3980aaf54281bc525a23aa3d 100644 --- a/Source/Solvers/imstkPbdSolver.cpp +++ b/Source/Solvers/imstkPbdSolver.cpp @@ -19,39 +19,100 @@ =========================================================================*/ -#include "imstkPbdObject.h" #include "imstkPbdSolver.h" -#include "imstkPbdCollisionConstraint.h" #include "imstkParallelUtils.h" namespace imstk { +PbdSolver::PbdSolver() : + m_partitionedConstraints(std::make_shared<std::vector<PBDConstraintVector>>()), + m_constraints(std::make_shared<PBDConstraintVector>()), + m_positions(std::make_shared<StdVectorOfVec3d>()), + m_invMasses(std::make_shared<StdVectorOfReal>()) +{ +} + void PbdSolver::solve() { - m_pbdObject->integratePosition(); - m_pbdObject->solveConstraints(); - resolveCollisionConstraints(); - m_pbdObject->updateVelocity(); + // Solve the constraints and partitioned constraints + StdVectorOfVec3d& currPositions = *m_positions; + const StdVectorOfReal& invMasses = *m_invMasses; + + const PBDConstraintVector& constraints = *m_constraints; + const std::vector<PBDConstraintVector>& partitionedConstraints = *m_partitionedConstraints; + + unsigned int i = 0; + while (i++ < m_iterations) + { + for (size_t j = 0; j < constraints.size(); j++) + { + constraints[j]->solvePositionConstraint(currPositions, invMasses); + } + + for (size_t j = 0; j < partitionedConstraints.size(); j++) + { + const PBDConstraintVector& constraintPartition = partitionedConstraints[j]; + const size_t numConstraintsInPartition = constraintPartition.size(); + + ParallelUtils::parallelFor(constraintPartition.size(), + [&](const size_t idx) + { + constraintPartition[idx]->solvePositionConstraint(currPositions, invMasses); + }); + // Sequential + /*for (size_t k = 0; k < constraintPartition.size(); k++) + { + constraintPartition[k]->solvePositionConstraint(currPositions, invMasses); + }*/ + } + } +} + + +PbdCollisionSolver::PbdCollisionSolver() : + m_collisionConstraints(std::make_shared<std::list<PBDCollisionConstraintVector*>>()), + m_collisionConstraintsData(std::make_shared<std::list<CollisionConstraintData>>()) +{ +} + +void +PbdCollisionSolver::addCollisionConstraints(PBDCollisionConstraintVector* constraints, + std::shared_ptr<StdVectorOfVec3d> posA, std::shared_ptr<StdVectorOfReal> invMassA, + std::shared_ptr<StdVectorOfVec3d> posB, std::shared_ptr<StdVectorOfReal> invMassB) +{ + m_collisionConstraints->push_back(constraints); + m_collisionConstraintsData->push_back({ posA, invMassA, posB, invMassB }); } void -PbdSolver::resolveCollisionConstraints() +PbdCollisionSolver::solve() { - if (m_PBDConstraints.size() > 0) + // Solve collision constraints + if (m_collisionConstraints->size() > 0) { - uint32_t maxIter = 3u; - uint32_t i = 0; - while (++i < maxIter) + unsigned int i = 0; + while (i++ < m_collisionIterations) { - for (const auto constraintList : m_PBDConstraints) + std::list<CollisionConstraintData>::iterator colDataIter = m_collisionConstraintsData->begin(); + for (auto constraintList : *m_collisionConstraints) { - for (size_t k = 0; k < constraintList->size(); ++k) + CollisionConstraintData colData = *colDataIter; + StdVectorOfVec3d& posA = *colData.m_posA; + const StdVectorOfReal& invMassA = *colData.m_invMassA; + StdVectorOfVec3d& posB = *colData.m_posB; + const StdVectorOfReal& invMassB = *colData.m_invMassB; + const PBDCollisionConstraintVector& constraints = *constraintList; + for (size_t j = 0; j < constraints.size(); j++) { - (*constraintList)[k]->solvePositionConstraint(); + constraints[j]->solvePositionConstraint(posA, posB, invMassA, invMassB); } + colDataIter++; } } + m_collisionConstraints->clear(); + m_collisionConstraintsData->clear(); } } + } // end namespace imstk diff --git a/Source/Solvers/imstkPbdSolver.h b/Source/Solvers/imstkPbdSolver.h index ef20dbdf5ce286c95b4bdf42810f46a5de65713a..87c3cd9124286aec3c691d3a0e4e42cea77222f8 100644 --- a/Source/Solvers/imstkPbdSolver.h +++ b/Source/Solvers/imstkPbdSolver.h @@ -21,67 +21,120 @@ #pragma once +#include "imstkPbdCollisionConstraint.h" +#include "imstkPbdConstraint.h" #include "imstkSolverBase.h" -#include "imstkGraph.h" - -#include <unordered_set> namespace imstk { -class PbdObject; -class PbdCollisionConstraint; +struct CollisionConstraintData +{ + CollisionConstraintData(std::shared_ptr<StdVectorOfVec3d> posA, + std::shared_ptr<StdVectorOfReal> invMassA, + std::shared_ptr<StdVectorOfVec3d> posB, + std::shared_ptr<StdVectorOfReal> invMassB) : + m_posA(posA), m_invMassA(invMassA), m_posB(posB), m_invMassB(invMassB) + { + } + + std::shared_ptr<StdVectorOfVec3d> m_posA = nullptr; + std::shared_ptr<StdVectorOfReal> m_invMassA = nullptr; + std::shared_ptr<StdVectorOfVec3d> m_posB = nullptr; + std::shared_ptr<StdVectorOfReal> m_invMassB = nullptr; +}; + /// /// \class PbdSolver /// /// \brief Position Based Dynamics solver +/// This solver can operate on both partitioned constraints (unordered_set of vector'd constraints) in parallel +/// and sequentially on vector'd constraints. It requires a set of constraints, positions, and invMasses. /// class PbdSolver : public SolverBase { -using PBDConstraintVector = std::vector<PbdCollisionConstraint*>; - public: /// /// \brief Constructors/Destructor /// - PbdSolver() = default; + PbdSolver(); virtual ~PbdSolver() override = default; - PbdSolver(const PbdSolver& other) = delete; - PbdSolver& operator=(const PbdSolver& other) = delete; +public: + /// + /// \brief Set Iterations. The number of nonlinear iterations. + /// + void setIterations(const size_t iterations) { this->m_iterations = iterations; } /// - /// \brief Set MaxIterations. The maximum number of nonlinear iterations. + /// \brief Sets the partioned constraints the solver should solve for + /// These will be solved in parallel /// - void setMaxIterations(const size_t newMaxIterations) { this->m_maxIterations = newMaxIterations; } + void setPartitionedConstraints(std::shared_ptr<std::vector<PBDConstraintVector>> partitionedConstraints) { this->m_partitionedConstraints = partitionedConstraints; } /// - /// \brief Get MaxIterations. Returns current maximum nonlinear iterations. + /// \brief Sets the constraints the solver should solve for + /// These wil be solved sequentially /// - size_t getMaxIterations() const { return this->m_maxIterations; } + void setConstraints(std::shared_ptr<PBDConstraintVector> constraints) { this->m_constraints = constraints; } /// - /// \brief + /// \brief Sets the positions the solver should solve with /// - void setPbdObject(const std::shared_ptr<PbdObject>& pbdObj) { m_pbdObject = pbdObj; } + void setPositions(std::shared_ptr<StdVectorOfVec3d> positions) { this->m_positions = positions; } + + /// + /// \brief Sets the invMasses the solver should solve with + /// + void setInvMasses(std::shared_ptr<StdVectorOfReal> invMasses) { this->m_invMasses = invMasses; } + + /// + /// \brief Get Iterations. Returns current nonlinear iterations. + /// + size_t getIterations() const { return this->m_iterations; } /// /// \brief Solve the non linear system of equations G(x)=0 using Newton's method. /// void solve() override; +private: + size_t m_iterations = 20; ///> Number of NL Gauss-Seidel iterations for regular constraints + + std::shared_ptr<std::vector<PBDConstraintVector>> m_partitionedConstraints = nullptr; ///> Set of vector'd/partitioned pbd constraints + std::shared_ptr<PBDConstraintVector> m_constraints = nullptr; ///> Vector of constraints + + std::shared_ptr<StdVectorOfVec3d> m_positions = nullptr; + std::shared_ptr<StdVectorOfReal> m_invMasses = nullptr; +}; + +class PbdCollisionSolver : SolverBase +{ +public: + PbdCollisionSolver(); + virtual ~PbdCollisionSolver() override = default; + +public: + /// + /// \brief Get CollisionIterations + /// + size_t getCollisionIterations() const { return this->m_collisionIterations; } + /// /// \brief Add the global collision contraints to this solver /// - void addCollisionConstraints(PBDConstraintVector* constraints) { m_PBDConstraints.insert(constraints); } + void addCollisionConstraints(PBDCollisionConstraintVector* constraints, + std::shared_ptr<StdVectorOfVec3d> posA, std::shared_ptr<StdVectorOfReal> invMassA, + std::shared_ptr<StdVectorOfVec3d> posB, std::shared_ptr<StdVectorOfReal> invMassB); /// - /// \brief Solve the global collision contraints charged to this solver + /// \brief Solve the non linear system of equations G(x)=0 using Newton's method. /// - void resolveCollisionConstraints(); + void solve() override; private: - size_t m_maxIterations = 20; ///< Maximum number of NL Gauss-Seidel iterations - std::unordered_set<PBDConstraintVector*> m_PBDConstraints; ///< Collision contraints charged to this solver - std::shared_ptr<PbdObject> m_pbdObject; + size_t m_collisionIterations = 5; ///> Number of NL Gauss-Seidel iterations for collision constraints + + std::shared_ptr<std::list<PBDCollisionConstraintVector*>> m_collisionConstraints = nullptr; ///< Collision contraints charged to this solver + std::shared_ptr<std::list<CollisionConstraintData>> m_collisionConstraintsData = nullptr; }; } // imstk diff --git a/Source/Solvers/imstkSPHSolver.h b/Source/Solvers/imstkSPHSolver.h deleted file mode 100644 index 58f08edf73e468683864cbaebae71588208f4ae7..0000000000000000000000000000000000000000 --- a/Source/Solvers/imstkSPHSolver.h +++ /dev/null @@ -1,62 +0,0 @@ -/*========================================================================= - - Library: iMSTK - - Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, - & Imaging in Medicine, Rensselaer Polytechnic Institute. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#pragma once - -#include "imstkSolverBase.h" -#include "imstkSPHObject.h" -#include "imstkSPHModel.h" - -namespace imstk -{ -/// -/// \class SPHSolver -/// \brief SPH solver -/// -class SPHSolver : public SolverBase -{ -public: - SPHSolver() = default; - virtual ~SPHSolver() override = default; - - SPHSolver(const SPHSolver& other) = delete; - SPHSolver& operator=(const SPHSolver& other) = delete; - - /// - /// \brief Set the simulation object - /// - void setSPHObject(const std::shared_ptr<SPHObject>& obj) - { assert(obj); m_SPHObject = obj; } - - /// - /// \brief Advance one time step - /// - virtual void solve() override - { - const auto& SPHModel = m_SPHObject->getSPHModel(); - LOG_IF(FATAL, (!SPHModel)) << "SPH model has not been initialized"; - SPHModel->advanceTimeStep(); - } - -private: - std::shared_ptr<SPHObject> m_SPHObject; -}; -} // end namespace imstk diff --git a/Source/Solvers/imstkSolverBase.h b/Source/Solvers/imstkSolverBase.h index 4db160b9ad3e75938bad0a36a5e7c82219b21992..6249e0b0c412196e331a2b49fc52188f4d398343 100644 --- a/Source/Solvers/imstkSolverBase.h +++ b/Source/Solvers/imstkSolverBase.h @@ -28,11 +28,10 @@ namespace imstk /// class SolverBase { -public: - /// - /// \brief Default Constructor/Destructor - /// +protected: SolverBase() = default; + +public: virtual ~SolverBase() = default; virtual void solve() = 0;