iMSTK
Interactive Medical Simulation Toolkit
imstkPbdConstraintContainer.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkPbdConstraintContainer.h"
8 #include "imstkGraph.h"
9 
10 #include <unordered_map>
11 
12 namespace imstk
13 {
14 void
15 PbdConstraintContainer::addConstraint(std::shared_ptr<PbdConstraint> constraint)
16 {
18  m_constraints.push_back(constraint);
20 }
21 
22 void
23 PbdConstraintContainer::removeConstraint(std::shared_ptr<PbdConstraint> constraint)
24 {
26  iterator i = std::find(m_constraints.begin(), m_constraints.end(), constraint);
27  if (i != m_constraints.end())
28  {
29  m_constraints.erase(i);
30  }
32 }
33 
34 void
35 PbdConstraintContainer::removeConstraints(std::shared_ptr<std::unordered_set<size_t>> vertices, const int bodyId)
36 {
37  // Remove constraints that contain the given vertices
38  auto removeConstraintFunc = [&](std::shared_ptr<PbdConstraint> constraint)
39  {
40  for (const PbdParticleId& pid : constraint->getParticles())
41  {
42  if (pid.first == bodyId && vertices->find(pid.second) != vertices->end())
43  {
44  return true;
45  }
46  }
47  return false;
48  };
49 
51  m_constraints.erase(std::remove_if(m_constraints.begin(), m_constraints.end(), removeConstraintFunc),
52  m_constraints.end());
53 
54  // Also remove partitioned constraints
55  for (auto& pc : m_partitionedConstraints)
56  {
57  pc.erase(std::remove_if(pc.begin(), pc.end(), removeConstraintFunc), pc.end());
58  }
59 
61 }
62 
63 PbdConstraintContainer::iterator
65 {
67  iterator newIter = m_constraints.erase(iter);
69  return newIter;
70 }
71 
72 PbdConstraintContainer::const_iterator
73 PbdConstraintContainer::eraseConstraint(const_iterator iter)
74 {
76  const_iterator newIter = m_constraints.erase(iter);
78  return newIter;
79 }
80 
81 void
82 PbdConstraintContainer::partitionConstraints(const int partitionedThreshold)
83 {
84  // Form the map { vertex : list_of_constraints_involve_vertex }
85  std::vector<std::shared_ptr<PbdConstraint>>& allConstraints = m_constraints;
86 
87  //std::cout << "---------partitionConstraints: " << allConstraints.size() << std::endl;
88 
89  std::unordered_map<size_t, std::vector<size_t>> vertexConstraints;
90  for (size_t constrIdx = 0; constrIdx < allConstraints.size(); ++constrIdx)
91  {
92  const auto& constr = allConstraints[constrIdx];
93  for (const auto& vIds : constr->getParticles())
94  {
95  vertexConstraints[vIds.second].push_back(constrIdx);
96  }
97  }
98 
99  // Add edges to the constraint graph
100  // Each edge represent a shared vertex between two constraints
101  Graph constraintGraph(allConstraints.size());
102  for (const auto& kv : vertexConstraints)
103  {
104  const auto& constraints = kv.second; // the list of constraints for a vertex
105  for (size_t i = 0; i < constraints.size(); ++i)
106  {
107  for (size_t j = i + 1; j < constraints.size(); ++j)
108  {
109  constraintGraph.addEdge(constraints[i], constraints[j]);
110  }
111  }
112  }
113  vertexConstraints.clear();
114 
115  // do graph coloring for the constraint graph
116  const auto coloring = constraintGraph.doColoring(Graph::ColoringMethod::WelshPowell);
117  // const auto coloring = constraintGraph.doColoring(Graph::ColoringMethod::Greedy);
118  const auto& partitionIndices = coloring.first;
119  const auto numPartitions = coloring.second;
120  assert(partitionIndices.size() == allConstraints.size());
121 
122  std::vector<std::vector<std::shared_ptr<PbdConstraint>>>& partitionedConstraints = m_partitionedConstraints;
123  partitionedConstraints.resize(0);
124  partitionedConstraints.resize(static_cast<size_t>(numPartitions));
125 
126  for (size_t constrIdx = 0; constrIdx < partitionIndices.size(); ++constrIdx)
127  {
128  const auto partitionIdx = partitionIndices[constrIdx];
129  partitionedConstraints[partitionIdx].push_back(allConstraints[constrIdx]);
130  }
131 
132  // If a partition has size smaller than the partition threshold, then move its constraints back
133  // These constraints will be processed sequentially
134  // Because small size partitions yield bad performance upon running in parallel
135  allConstraints.resize(0);
136  for (const auto& constraints : partitionedConstraints)
137  {
138  if (constraints.size() < partitionedThreshold)
139  {
140  for (size_t constrIdx = 0; constrIdx < constraints.size(); ++constrIdx)
141  {
142  allConstraints.push_back(std::move(constraints[constrIdx]));
143  }
144  }
145  }
146 
147  // Remove all empty partitions
148  size_t writeIdx = 0;
149  for (size_t readIdx = 0; readIdx < partitionedConstraints.size(); ++readIdx)
150  {
151  if (partitionedConstraints[readIdx].size() >= partitionedThreshold)
152  {
153  if (readIdx != writeIdx)
154  {
155  partitionedConstraints[writeIdx] = std::move(partitionedConstraints[readIdx]);
156  }
157  ++writeIdx;
158  }
159  }
160  partitionedConstraints.resize(writeIdx);
161 
162  // Print
163  /*if (print)
164  {
165  size_t numConstraints = 0;
166  int idx = 0;
167  for (const auto& constraints : partitionedConstraints)
168  {
169  std::cout << "Partition # " << idx++ << " | # nodes: " << constraints.size() << std::endl;
170  numConstraints += constraints.size();
171  }
172  std::cout << "Sequential processing # nodes: " << allConstraints.size() << std::endl;
173  numConstraints += allConstraints.size();
174  std::cout << "Total constraints: " << numConstraints << " | Graph size: "
175  << constraintGraph.size() << std::endl;
176  }*/
177 }
178 } // namespace imstk
void unlock()
End a thread-safe region.
Definition: imstkSpinLock.h:53
std::vector< std::shared_ptr< PbdConstraint > > m_constraints
Not partitioned constraints.
std::pair< int, int > PbdParticleId
Index pair that refers to a particle in a PbdState. Index 0 is the body id, Index 1 is the particle i...
std::vector< std::vector< std::shared_ptr< PbdConstraint > > > m_partitionedConstraints
Partitioned pbd constraints.
virtual iterator eraseConstraint(iterator iter)
Removes a constraint from the system by iterator, thread safe.
Compound Geometry.
ParallelUtils::SpinLock m_constraintLock
Used to deal with concurrent addition/removal of constraints.
void lock()
Start a thread-safe region, where only one thread can execute at a time until a call to the unlock fu...
Definition: imstkSpinLock.h:45
class to represent a graph object
Definition: imstkGraph.h:18
virtual void removeConstraints(std::shared_ptr< std::unordered_set< size_t >> vertices, const int bodyId)
Removes all constraints associated with vertex ids.
virtual void removeConstraint(std::shared_ptr< PbdConstraint > constraint)
Linear searches for and removes a constraint from the system, thread safe.
virtual void addConstraint(std::shared_ptr< PbdConstraint > constraint)
Adds a constraint to the system, thread safe.
void addEdge(const size_t v, const size_t w)
Add edge to the graph.
Definition: imstkGraph.cpp:17
void partitionConstraints(const int partitionThreshold)
Partitions pbd constraints into separate vectors via graph coloring.