| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright Contributors to the OpenVDB Project | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | // | ||
| 4 | /// @file PoissonSolver.h | ||
| 5 | /// | ||
| 6 | /// @authors D.J. Hill, Peter Cucka | ||
| 7 | /// | ||
| 8 | /// @brief Solve Poisson's equation ∇<sup><small>2</small></sup><i>x</i> = <i>b</i> | ||
| 9 | /// for <i>x</i>, where @e b is a vector comprising the values of all of the active voxels | ||
| 10 | /// in a grid. | ||
| 11 | /// | ||
| 12 | /// @par Example: | ||
| 13 | /// Solve for the pressure in a cubic tank of liquid, assuming uniform boundary conditions: | ||
| 14 | /// @code | ||
| 15 | /// FloatTree source(/*background=*/0.0f); | ||
| 16 | /// // Activate voxels to indicate that they contain liquid. | ||
| 17 | /// source.fill(CoordBBox(Coord(0, -10, 0), Coord(10, 0, 10)), /*value=*/0.0f); | ||
| 18 | /// | ||
| 19 | /// math::pcg::State state = math::pcg::terminationDefaults<float>(); | ||
| 20 | /// FloatTree::Ptr solution = tools::poisson::solve(source, state); | ||
| 21 | /// @endcode | ||
| 22 | /// | ||
| 23 | /// @par Example: | ||
| 24 | /// Solve for the pressure, <i>P</i>, in a cubic tank of liquid that is open at the top. | ||
| 25 | /// Boundary conditions are <i>P</i> = 0 at the top, | ||
| 26 | /// ∂<i>P</i>/∂<i>y</i> = −1 at the bottom | ||
| 27 | /// and ∂<i>P</i>/∂<i>x</i> = 0 at the sides: | ||
| 28 | /// <pre> | ||
| 29 | /// P = 0 | ||
| 30 | /// +--------+ (N,0,N) | ||
| 31 | /// /| /| | ||
| 32 | /// (0,0,0) +--------+ | | ||
| 33 | /// | | | | dP/dx = 0 | ||
| 34 | /// dP/dx = 0 | +------|-+ | ||
| 35 | /// |/ |/ | ||
| 36 | /// (0,-N,0) +--------+ (N,-N,0) | ||
| 37 | /// dP/dy = -1 | ||
| 38 | /// </pre> | ||
| 39 | /// @code | ||
| 40 | /// const int N = 10; | ||
| 41 | /// DoubleTree source(/*background=*/0.0); | ||
| 42 | /// // Activate voxels to indicate that they contain liquid. | ||
| 43 | /// source.fill(CoordBBox(Coord(0, -N, 0), Coord(N, 0, N)), /*value=*/0.0); | ||
| 44 | /// | ||
| 45 | /// auto boundary = [](const openvdb::Coord& ijk, const openvdb::Coord& neighbor, | ||
| 46 | /// double& source, double& diagonal) | ||
| 47 | /// { | ||
| 48 | /// if (neighbor.x() == ijk.x() && neighbor.z() == ijk.z()) { | ||
| 49 | /// if (neighbor.y() < ijk.y()) source -= 1.0; | ||
| 50 | /// else diagonal -= 1.0; | ||
| 51 | /// } | ||
| 52 | /// }; | ||
| 53 | /// | ||
| 54 | /// math::pcg::State state = math::pcg::terminationDefaults<double>(); | ||
| 55 | /// util::NullInterrupter interrupter; | ||
| 56 | /// | ||
| 57 | /// DoubleTree::Ptr solution = tools::poisson::solveWithBoundaryConditions( | ||
| 58 | /// source, boundary, state, interrupter); | ||
| 59 | /// @endcode | ||
| 60 | |||
| 61 | #ifndef OPENVDB_TOOLS_POISSONSOLVER_HAS_BEEN_INCLUDED | ||
| 62 | #define OPENVDB_TOOLS_POISSONSOLVER_HAS_BEEN_INCLUDED | ||
| 63 | |||
| 64 | #include <openvdb/Types.h> | ||
| 65 | #include <openvdb/math/ConjGradient.h> | ||
| 66 | #include <openvdb/tree/LeafManager.h> | ||
| 67 | #include <openvdb/tree/Tree.h> | ||
| 68 | #include <openvdb/util/NullInterrupter.h> | ||
| 69 | #include "Morphology.h" // for erodeActiveValues | ||
| 70 | #include <openvdb/openvdb.h> | ||
| 71 | |||
| 72 | |||
| 73 | namespace openvdb { | ||
| 74 | OPENVDB_USE_VERSION_NAMESPACE | ||
| 75 | namespace OPENVDB_VERSION_NAME { | ||
| 76 | namespace tools { | ||
| 77 | namespace poisson { | ||
| 78 | |||
| 79 | // This type should be at least as wide as math::pcg::SizeType. | ||
| 80 | using VIndex = Int32; | ||
| 81 | |||
| 82 | /// The type of a matrix used to represent a three-dimensional %Laplacian operator | ||
| 83 | using LaplacianMatrix = math::pcg::SparseStencilMatrix<double, 7>; | ||
| 84 | |||
| 85 | |||
| 86 | //@{ | ||
| 87 | /// @brief Solve ∇<sup><small>2</small></sup><i>x</i> = <i>b</i> for <i>x</i>, | ||
| 88 | /// where @e b is a vector comprising the values of all of the active voxels | ||
| 89 | /// in the input tree. | ||
| 90 | /// @return a new tree, with the same active voxel topology as the input tree, | ||
| 91 | /// whose voxel values are the elements of the solution vector <i>x</i>. | ||
| 92 | /// @details On input, the State object should specify convergence criteria | ||
| 93 | /// (minimum error and maximum number of iterations); on output, it gives | ||
| 94 | /// the actual termination conditions. | ||
| 95 | /// @details The solution is computed using the conjugate gradient method | ||
| 96 | /// with (where possible) incomplete Cholesky preconditioning, falling back | ||
| 97 | /// to Jacobi preconditioning. | ||
| 98 | /// @sa solveWithBoundaryConditions | ||
| 99 | template<typename TreeType> | ||
| 100 | typename TreeType::Ptr | ||
| 101 | solve(const TreeType&, math::pcg::State&, bool staggered = false); | ||
| 102 | |||
| 103 | template<typename TreeType, typename Interrupter> | ||
| 104 | typename TreeType::Ptr | ||
| 105 | solve(const TreeType&, math::pcg::State&, Interrupter&, bool staggered = false); | ||
| 106 | //@} | ||
| 107 | |||
| 108 | |||
| 109 | //@{ | ||
| 110 | /// @brief Solve ∇<sup><small>2</small></sup><i>x</i> = <i>b</i> for <i>x</i> | ||
| 111 | /// with user-specified boundary conditions, where @e b is a vector comprising | ||
| 112 | /// the values of all of the active voxels in the input tree or domain mask if provided | ||
| 113 | /// @return a new tree, with the same active voxel topology as the input tree, | ||
| 114 | /// whose voxel values are the elements of the solution vector <i>x</i>. | ||
| 115 | /// @details On input, the State object should specify convergence criteria | ||
| 116 | /// (minimum error and maximum number of iterations); on output, it gives | ||
| 117 | /// the actual termination conditions. | ||
| 118 | /// @details The solution is computed using the conjugate gradient method with | ||
| 119 | /// the specified type of preconditioner (default: incomplete Cholesky), | ||
| 120 | /// falling back to Jacobi preconditioning if necessary. | ||
| 121 | /// @details Each thread gets its own copy of the BoundaryOp, which should be | ||
| 122 | /// a functor of the form | ||
| 123 | /// @code | ||
| 124 | /// struct BoundaryOp { | ||
| 125 | /// using ValueType = LaplacianMatrix::ValueType; | ||
| 126 | /// void operator()( | ||
| 127 | /// const Coord& ijk, // coordinates of a boundary voxel | ||
| 128 | /// const Coord& ijkNeighbor, // coordinates of an exterior neighbor of ijk | ||
| 129 | /// ValueType& source, // element of b corresponding to ijk | ||
| 130 | /// ValueType& diagonal // element of Laplacian matrix corresponding to ijk | ||
| 131 | /// ) const; | ||
| 132 | /// }; | ||
| 133 | /// @endcode | ||
| 134 | /// The functor is called for each of the exterior neighbors of each boundary voxel @ijk, | ||
| 135 | /// and it must specify a boundary condition for @ijk by modifying one or both of two | ||
| 136 | /// provided values: the entry in the source vector @e b corresponding to @ijk and | ||
| 137 | /// the weighting coefficient for @ijk in the Laplacian operator matrix. | ||
| 138 | /// | ||
| 139 | /// @sa solve | ||
| 140 | template<typename TreeType, typename BoundaryOp, typename Interrupter> | ||
| 141 | typename TreeType::Ptr | ||
| 142 | solveWithBoundaryConditions( | ||
| 143 | const TreeType&, | ||
| 144 | const BoundaryOp&, | ||
| 145 | math::pcg::State&, | ||
| 146 | Interrupter&, | ||
| 147 | bool staggered = false); | ||
| 148 | |||
| 149 | template< | ||
| 150 | typename PreconditionerType, | ||
| 151 | typename TreeType, | ||
| 152 | typename BoundaryOp, | ||
| 153 | typename Interrupter> | ||
| 154 | typename TreeType::Ptr | ||
| 155 | solveWithBoundaryConditionsAndPreconditioner( | ||
| 156 | const TreeType&, | ||
| 157 | const BoundaryOp&, | ||
| 158 | math::pcg::State&, | ||
| 159 | Interrupter&, | ||
| 160 | bool staggered = false); | ||
| 161 | |||
| 162 | template< | ||
| 163 | typename PreconditionerType, | ||
| 164 | typename TreeType, | ||
| 165 | typename DomainTreeType, | ||
| 166 | typename BoundaryOp, | ||
| 167 | typename Interrupter> | ||
| 168 | typename TreeType::Ptr | ||
| 169 | solveWithBoundaryConditionsAndPreconditioner( | ||
| 170 | const TreeType&, | ||
| 171 | const DomainTreeType&, | ||
| 172 | const BoundaryOp&, | ||
| 173 | math::pcg::State&, | ||
| 174 | Interrupter&, | ||
| 175 | bool staggered = false); | ||
| 176 | //@} | ||
| 177 | |||
| 178 | |||
| 179 | /// @name Low-level functions | ||
| 180 | //@{ | ||
| 181 | // The following are low-level routines that can be used to assemble custom solvers. | ||
| 182 | |||
| 183 | /// @brief Overwrite each active voxel in the given scalar tree | ||
| 184 | /// with a sequential index, starting from zero. | ||
| 185 | template<typename VIndexTreeType> | ||
| 186 | void populateIndexTree(VIndexTreeType&); | ||
| 187 | |||
| 188 | /// @brief Iterate over the active voxels of the input tree and for each one | ||
| 189 | /// assign its index in the iteration sequence to the corresponding voxel | ||
| 190 | /// of an integer-valued output tree. | ||
| 191 | template<typename TreeType> | ||
| 192 | typename TreeType::template ValueConverter<VIndex>::Type::Ptr | ||
| 193 | createIndexTree(const TreeType&); | ||
| 194 | |||
| 195 | |||
| 196 | /// @brief Return a vector of the active voxel values of the scalar-valued @a source tree. | ||
| 197 | /// @details The <i>n</i>th element of the vector corresponds to the voxel whose value | ||
| 198 | /// in the @a index tree is @e n. | ||
| 199 | /// @param source a tree with a scalar value type | ||
| 200 | /// @param index a tree of the same configuration as @a source but with | ||
| 201 | /// value type VIndex that maps voxels to elements of the output vector | ||
| 202 | template<typename VectorValueType, typename SourceTreeType> | ||
| 203 | typename math::pcg::Vector<VectorValueType>::Ptr | ||
| 204 | createVectorFromTree( | ||
| 205 | const SourceTreeType& source, | ||
| 206 | const typename SourceTreeType::template ValueConverter<VIndex>::Type& index); | ||
| 207 | |||
| 208 | |||
| 209 | /// @brief Return a tree with the same active voxel topology as the @a index tree | ||
| 210 | /// but whose voxel values are taken from the the given vector. | ||
| 211 | /// @details The voxel whose value in the @a index tree is @e n gets assigned | ||
| 212 | /// the <i>n</i>th element of the vector. | ||
| 213 | /// @param index a tree with value type VIndex that maps voxels to elements of @a values | ||
| 214 | /// @param values a vector of values with which to populate the active voxels of the output tree | ||
| 215 | /// @param background the value for the inactive voxels of the output tree | ||
| 216 | template<typename TreeValueType, typename VIndexTreeType, typename VectorValueType> | ||
| 217 | typename VIndexTreeType::template ValueConverter<TreeValueType>::Type::Ptr | ||
| 218 | createTreeFromVector( | ||
| 219 | const math::pcg::Vector<VectorValueType>& values, | ||
| 220 | const VIndexTreeType& index, | ||
| 221 | const TreeValueType& background); | ||
| 222 | |||
| 223 | |||
| 224 | /// @brief Generate a sparse matrix of the index-space (Δ<i>x</i> = 1) %Laplacian operator | ||
| 225 | /// using second-order finite differences. | ||
| 226 | /// @details This construction assumes homogeneous Dirichlet boundary conditions | ||
| 227 | /// (exterior grid points are zero). | ||
| 228 | template<typename BoolTreeType> | ||
| 229 | LaplacianMatrix::Ptr | ||
| 230 | createISLaplacian( | ||
| 231 | const typename BoolTreeType::template ValueConverter<VIndex>::Type& vectorIndexTree, | ||
| 232 | const BoolTreeType& interiorMask, | ||
| 233 | bool staggered = false); | ||
| 234 | |||
| 235 | |||
| 236 | /// @brief Generate a sparse matrix of the index-space (Δ<i>x</i> = 1) %Laplacian operator | ||
| 237 | /// with user-specified boundary conditions using second-order finite differences. | ||
| 238 | /// @details Each thread gets its own copy of @a boundaryOp, which should be | ||
| 239 | /// a functor of the form | ||
| 240 | /// @code | ||
| 241 | /// struct BoundaryOp { | ||
| 242 | /// using ValueType = LaplacianMatrix::ValueType; | ||
| 243 | /// void operator()( | ||
| 244 | /// const Coord& ijk, // coordinates of a boundary voxel | ||
| 245 | /// const Coord& ijkNeighbor, // coordinates of an exterior neighbor of ijk | ||
| 246 | /// ValueType& source, // element of source vector corresponding to ijk | ||
| 247 | /// ValueType& diagonal // element of Laplacian matrix corresponding to ijk | ||
| 248 | /// ) const; | ||
| 249 | /// }; | ||
| 250 | /// @endcode | ||
| 251 | /// The functor is called for each of the exterior neighbors of each boundary voxel @ijk, | ||
| 252 | /// and it must specify a boundary condition for @ijk by modifying one or both of two | ||
| 253 | /// provided values: an entry in the given @a source vector corresponding to @ijk and | ||
| 254 | /// the weighting coefficient for @ijk in the %Laplacian matrix. | ||
| 255 | template<typename BoolTreeType, typename BoundaryOp> | ||
| 256 | LaplacianMatrix::Ptr | ||
| 257 | createISLaplacianWithBoundaryConditions( | ||
| 258 | const typename BoolTreeType::template ValueConverter<VIndex>::Type& vectorIndexTree, | ||
| 259 | const BoolTreeType& interiorMask, | ||
| 260 | const BoundaryOp& boundaryOp, | ||
| 261 | typename math::pcg::Vector<LaplacianMatrix::ValueType>& source, | ||
| 262 | bool staggered = false); | ||
| 263 | |||
| 264 | |||
| 265 | /// @brief Dirichlet boundary condition functor | ||
| 266 | /// @details This is useful in describing fluid/air interfaces, where the pressure | ||
| 267 | /// of the air is assumed to be zero. | ||
| 268 | template<typename ValueType> | ||
| 269 | struct DirichletBoundaryOp { | ||
| 270 | inline void operator()(const Coord&, const Coord&, ValueType&, ValueType& diag) const { | ||
| 271 | // Exterior neighbors are empty, so decrement the weighting coefficient | ||
| 272 | // as for interior neighbors but leave the source vector unchanged. | ||
| 273 | 2460 | diag -= 1; | |
| 274 | 60156 | } | |
| 275 | }; | ||
| 276 | |||
| 277 | //@} | ||
| 278 | |||
| 279 | |||
| 280 | //////////////////////////////////////// | ||
| 281 | |||
| 282 | /// @cond OPENVDB_DOCS_INTERNAL | ||
| 283 | |||
| 284 | namespace internal { | ||
| 285 | |||
| 286 | /// @brief Functor for use with LeafManager::foreach() to populate an array | ||
| 287 | /// with per-leaf active voxel counts | ||
| 288 | template<typename LeafType> | ||
| 289 | struct LeafCountOp | ||
| 290 | { | ||
| 291 | VIndex* count; | ||
| 292 | 10 | LeafCountOp(VIndex* count_): count(count_) {} | |
| 293 | void operator()(const LeafType& leaf, size_t leafIdx) const { | ||
| 294 | 9306 | count[leafIdx] = static_cast<VIndex>(leaf.onVoxelCount()); | |
| 295 | } | ||
| 296 | }; | ||
| 297 | |||
| 298 | |||
| 299 | /// @brief Functor for use with LeafManager::foreach() to populate | ||
| 300 | /// active leaf voxels with sequential indices | ||
| 301 | template<typename LeafType> | ||
| 302 | struct LeafIndexOp | ||
| 303 | { | ||
| 304 | const VIndex* count; | ||
| 305 | 10 | LeafIndexOp(const VIndex* count_): count(count_) {} | |
| 306 | 9306 | void operator()(LeafType& leaf, size_t leafIdx) const { | |
| 307 |
2/2✓ Branch 0 taken 9296 times.
✓ Branch 1 taken 10 times.
|
9306 | VIndex idx = (leafIdx == 0) ? 0 : count[leafIdx - 1]; |
| 308 |
2/2✓ Branch 0 taken 3852431 times.
✓ Branch 1 taken 9306 times.
|
3861737 | for (typename LeafType::ValueOnIter it = leaf.beginValueOn(); it; ++it) { |
| 309 | 3852431 | it.setValue(idx++); | |
| 310 | } | ||
| 311 | 9306 | } | |
| 312 | }; | ||
| 313 | |||
| 314 | } // namespace internal | ||
| 315 | |||
| 316 | /// @endcond | ||
| 317 | |||
| 318 | template<typename VIndexTreeType> | ||
| 319 | inline void | ||
| 320 | 10 | populateIndexTree(VIndexTreeType& result) | |
| 321 | { | ||
| 322 | using LeafT = typename VIndexTreeType::LeafNodeType; | ||
| 323 | using LeafMgrT = typename tree::LeafManager<VIndexTreeType>; | ||
| 324 | |||
| 325 | // Linearize the tree. | ||
| 326 | 20 | LeafMgrT leafManager(result); | |
| 327 | const size_t leafCount = leafManager.leafCount(); | ||
| 328 | |||
| 329 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (leafCount == 0) return; |
| 330 | |||
| 331 | // Count the number of active voxels in each leaf node. | ||
| 332 |
2/4✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
|
10 | std::unique_ptr<VIndex[]> perLeafCount(new VIndex[leafCount]); |
| 333 | VIndex* perLeafCountPtr = perLeafCount.get(); | ||
| 334 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | leafManager.foreach(internal::LeafCountOp<LeafT>(perLeafCountPtr)); |
| 335 | |||
| 336 | // The starting index for each leaf node is the total number | ||
| 337 | // of active voxels in all preceding leaf nodes. | ||
| 338 |
2/2✓ Branch 0 taken 9296 times.
✓ Branch 1 taken 10 times.
|
9306 | for (size_t i = 1; i < leafCount; ++i) { |
| 339 | 9296 | perLeafCount[i] += perLeafCount[i - 1]; | |
| 340 | } | ||
| 341 | |||
| 342 | // The last accumulated value should be the total of all active voxels. | ||
| 343 |
2/4✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
|
10 | assert(Index64(perLeafCount[leafCount-1]) == result.activeVoxelCount()); |
| 344 | |||
| 345 | // Parallelize over the leaf nodes of the tree, storing a unique index | ||
| 346 | // in each active voxel. | ||
| 347 |
2/6✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
10 | leafManager.foreach(internal::LeafIndexOp<LeafT>(perLeafCountPtr)); |
| 348 | } | ||
| 349 | |||
| 350 | |||
| 351 | template<typename TreeType> | ||
| 352 | inline typename TreeType::template ValueConverter<VIndex>::Type::Ptr | ||
| 353 | 20 | createIndexTree(const TreeType& tree) | |
| 354 | { | ||
| 355 | using VIdxTreeT = typename TreeType::template ValueConverter<VIndex>::Type; | ||
| 356 | |||
| 357 | // Construct an output tree with the same active voxel topology as the input tree. | ||
| 358 | 20 | const VIndex invalidIdx = -1; | |
| 359 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | typename VIdxTreeT::Ptr result( |
| 360 |
2/4✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
|
20 | new VIdxTreeT(tree, /*background=*/invalidIdx, TopologyCopy())); |
| 361 | |||
| 362 | // All active voxels are degrees of freedom, including voxels contained in active tiles. | ||
| 363 | result->voxelizeActiveTiles(); | ||
| 364 | |||
| 365 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | populateIndexTree(*result); |
| 366 | |||
| 367 | 20 | return result; | |
| 368 | } | ||
| 369 | |||
| 370 | |||
| 371 | //////////////////////////////////////// | ||
| 372 | |||
| 373 | /// @cond OPENVDB_DOCS_INTERNAL | ||
| 374 | |||
| 375 | namespace internal { | ||
| 376 | |||
| 377 | /// @brief Functor for use with LeafManager::foreach() to populate a vector | ||
| 378 | /// with the values of a tree's active voxels | ||
| 379 | template<typename VectorValueType, typename SourceTreeType> | ||
| 380 | struct CopyToVecOp | ||
| 381 | { | ||
| 382 | using VIdxTreeT = typename SourceTreeType::template ValueConverter<VIndex>::Type; | ||
| 383 | using VIdxLeafT = typename VIdxTreeT::LeafNodeType; | ||
| 384 | using LeafT = typename SourceTreeType::LeafNodeType; | ||
| 385 | using TreeValueT = typename SourceTreeType::ValueType; | ||
| 386 | using VectorT = typename math::pcg::Vector<VectorValueType>; | ||
| 387 | |||
| 388 | const SourceTreeType* tree; | ||
| 389 | VectorT* vector; | ||
| 390 | |||
| 391 | 10 | CopyToVecOp(const SourceTreeType& t, VectorT& v): tree(&t), vector(&v) {} | |
| 392 | |||
| 393 | 18612 | void operator()(const VIdxLeafT& idxLeaf, size_t /*leafIdx*/) const | |
| 394 | { | ||
| 395 | 18612 | VectorT& vec = *vector; | |
| 396 |
2/2✓ Branch 1 taken 8705 times.
✓ Branch 2 taken 601 times.
|
18612 | if (const LeafT* leaf = tree->probeLeaf(idxLeaf.origin())) { |
| 397 | // If a corresponding leaf node exists in the source tree, | ||
| 398 | // copy voxel values from the source node to the output vector. | ||
| 399 |
2/2✓ Branch 0 taken 3552399 times.
✓ Branch 1 taken 8705 times.
|
7122208 | for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { |
| 400 | 11637664 | vec[*it] = leaf->getValue(it.pos()); | |
| 401 | } | ||
| 402 | } else { | ||
| 403 | // If no corresponding leaf exists in the source tree, | ||
| 404 | // fill the vector with a uniform value. | ||
| 405 | 1202 | const TreeValueT& value = tree->getValue(idxLeaf.origin()); | |
| 406 |
2/2✓ Branch 0 taken 300032 times.
✓ Branch 1 taken 601 times.
|
601266 | for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { |
| 407 | 600064 | vec[*it] = value; | |
| 408 | } | ||
| 409 | } | ||
| 410 | 18612 | } | |
| 411 | }; | ||
| 412 | |||
| 413 | } // namespace internal | ||
| 414 | |||
| 415 | /// @endcond | ||
| 416 | |||
| 417 | template<typename VectorValueType, typename SourceTreeType> | ||
| 418 | inline typename math::pcg::Vector<VectorValueType>::Ptr | ||
| 419 | 20 | createVectorFromTree(const SourceTreeType& tree, | |
| 420 | const typename SourceTreeType::template ValueConverter<VIndex>::Type& idxTree) | ||
| 421 | { | ||
| 422 | using VIdxTreeT = typename SourceTreeType::template ValueConverter<VIndex>::Type; | ||
| 423 | using VIdxLeafMgrT = tree::LeafManager<const VIdxTreeT>; | ||
| 424 | using VectorT = typename math::pcg::Vector<VectorValueType>; | ||
| 425 | |||
| 426 | // Allocate the vector. | ||
| 427 | 20 | const size_t numVoxels = idxTree.activeVoxelCount(); | |
| 428 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
20 | typename VectorT::Ptr result(new VectorT(static_cast<math::pcg::SizeType>(numVoxels))); |
| 429 | |||
| 430 | // Parallelize over the leaf nodes of the index tree, filling the output vector | ||
| 431 | // with values from corresponding voxels of the source tree. | ||
| 432 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | VIdxLeafMgrT leafManager(idxTree); |
| 433 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | leafManager.foreach(internal::CopyToVecOp<VectorValueType, SourceTreeType>(tree, *result)); |
| 434 | |||
| 435 | 20 | return result; | |
| 436 | } | ||
| 437 | |||
| 438 | |||
| 439 | //////////////////////////////////////// | ||
| 440 | |||
| 441 | /// @cond OPENVDB_DOCS_INTERNAL | ||
| 442 | |||
| 443 | namespace internal { | ||
| 444 | |||
| 445 | /// @brief Functor for use with LeafManager::foreach() to populate a tree | ||
| 446 | /// with values from a vector | ||
| 447 | template<typename TreeValueType, typename VIndexTreeType, typename VectorValueType> | ||
| 448 | struct CopyFromVecOp | ||
| 449 | { | ||
| 450 | using OutTreeT = typename VIndexTreeType::template ValueConverter<TreeValueType>::Type; | ||
| 451 | using OutLeafT = typename OutTreeT::LeafNodeType; | ||
| 452 | using VIdxLeafT = typename VIndexTreeType::LeafNodeType; | ||
| 453 | using VectorT = typename math::pcg::Vector<VectorValueType>; | ||
| 454 | |||
| 455 | const VectorT* vector; | ||
| 456 | OutTreeT* tree; | ||
| 457 | |||
| 458 | 8 | CopyFromVecOp(const VectorT& v, OutTreeT& t): vector(&v), tree(&t) {} | |
| 459 | |||
| 460 | 18556 | void operator()(const VIdxLeafT& idxLeaf, size_t /*leafIdx*/) const | |
| 461 | { | ||
| 462 | 18556 | const VectorT& vec = *vector; | |
| 463 | 18556 | OutLeafT* leaf = tree->probeLeaf(idxLeaf.origin()); | |
| 464 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9278 times.
|
18556 | assert(leaf != nullptr); |
| 465 |
2/2✓ Branch 0 taken 3845229 times.
✓ Branch 1 taken 9278 times.
|
7709014 | for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { |
| 466 | 7690458 | leaf->setValueOnly(it.pos(), static_cast<TreeValueType>(vec[*it])); | |
| 467 | } | ||
| 468 | 18556 | } | |
| 469 | }; | ||
| 470 | |||
| 471 | } // namespace internal | ||
| 472 | |||
| 473 | /// @endcond | ||
| 474 | |||
| 475 | template<typename TreeValueType, typename VIndexTreeType, typename VectorValueType> | ||
| 476 | inline typename VIndexTreeType::template ValueConverter<TreeValueType>::Type::Ptr | ||
| 477 | 16 | createTreeFromVector( | |
| 478 | const math::pcg::Vector<VectorValueType>& vector, | ||
| 479 | const VIndexTreeType& idxTree, | ||
| 480 | const TreeValueType& background) | ||
| 481 | { | ||
| 482 | using OutTreeT = typename VIndexTreeType::template ValueConverter<TreeValueType>::Type; | ||
| 483 | using VIdxLeafMgrT = typename tree::LeafManager<const VIndexTreeType>; | ||
| 484 | |||
| 485 | // Construct an output tree with the same active voxel topology as the index tree. | ||
| 486 |
3/6✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
32 | typename OutTreeT::Ptr result(new OutTreeT(idxTree, background, TopologyCopy())); |
| 487 | OutTreeT& tree = *result; | ||
| 488 | |||
| 489 | // Parallelize over the leaf nodes of the index tree, populating voxels | ||
| 490 | // of the output tree with values from the input vector. | ||
| 491 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
16 | VIdxLeafMgrT leafManager(idxTree); |
| 492 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
16 | leafManager.foreach( |
| 493 | internal::CopyFromVecOp<TreeValueType, VIndexTreeType, VectorValueType>(vector, tree)); | ||
| 494 | |||
| 495 | 16 | return result; | |
| 496 | } | ||
| 497 | |||
| 498 | |||
| 499 | //////////////////////////////////////// | ||
| 500 | |||
| 501 | /// @cond OPENVDB_DOCS_INTERNAL | ||
| 502 | |||
| 503 | namespace internal { | ||
| 504 | |||
| 505 | /// Functor for use with LeafManager::foreach() to populate a sparse %Laplacian matrix | ||
| 506 | template<typename BoolTreeType, typename BoundaryOp> | ||
| 507 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
4 | struct ISStaggeredLaplacianOp |
| 508 | { | ||
| 509 | using VIdxTreeT = typename BoolTreeType::template ValueConverter<VIndex>::Type; | ||
| 510 | using VIdxLeafT = typename VIdxTreeT::LeafNodeType; | ||
| 511 | using ValueT = LaplacianMatrix::ValueType; | ||
| 512 | using VectorT = typename math::pcg::Vector<ValueT>; | ||
| 513 | |||
| 514 | LaplacianMatrix* laplacian; | ||
| 515 | const VIdxTreeT* idxTree; | ||
| 516 | const BoolTreeType* interiorMask; | ||
| 517 | const BoundaryOp boundaryOp; | ||
| 518 | VectorT* source; | ||
| 519 | |||
| 520 | 8 | ISStaggeredLaplacianOp(LaplacianMatrix& m, const VIdxTreeT& idx, | |
| 521 | const BoolTreeType& mask, const BoundaryOp& op, VectorT& src): | ||
| 522 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
4 | laplacian(&m), idxTree(&idx), interiorMask(&mask), boundaryOp(op), source(&src) {} |
| 523 | |||
| 524 | 15792 | void operator()(const VIdxLeafT& idxLeaf, size_t /*leafIdx*/) const | |
| 525 | { | ||
| 526 | // Local accessors | ||
| 527 | 15792 | typename tree::ValueAccessor<const BoolTreeType> interior(*interiorMask); | |
| 528 |
1/2✓ Branch 1 taken 7896 times.
✗ Branch 2 not taken.
|
15792 | typename tree::ValueAccessor<const VIdxTreeT> vectorIdx(*idxTree); |
| 529 | |||
| 530 | Coord ijk; | ||
| 531 | VIndex column; | ||
| 532 | 15792 | const ValueT diagonal = -6.f, offDiagonal = 1.f; | |
| 533 | |||
| 534 | // Loop over active voxels in this leaf. | ||
| 535 |
2/2✓ Branch 0 taken 3316969 times.
✓ Branch 1 taken 7896 times.
|
6649730 | for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { |
| 536 |
2/4✓ Branch 1 taken 3316969 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3316969 times.
|
6633938 | assert(it.getValue() > -1); |
| 537 |
1/2✓ Branch 1 taken 3316969 times.
✗ Branch 2 not taken.
|
6633938 | const math::pcg::SizeType rowNum = static_cast<math::pcg::SizeType>(it.getValue()); |
| 538 | |||
| 539 | 6633938 | LaplacianMatrix::RowEditor row = laplacian->getRowEditor(rowNum); | |
| 540 | |||
| 541 |
1/2✓ Branch 1 taken 3316969 times.
✗ Branch 2 not taken.
|
6633938 | ijk = it.getCoord(); |
| 542 |
2/2✓ Branch 1 taken 3148529 times.
✓ Branch 2 taken 168440 times.
|
6633938 | if (interior.isValueOn(ijk)) { |
| 543 | // The current voxel is an interior voxel. | ||
| 544 | // All of its neighbors are in the solution domain. | ||
| 545 | |||
| 546 | // -x direction | ||
| 547 |
2/4✓ Branch 1 taken 3148529 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3148529 times.
✗ Branch 6 not taken.
|
6297058 | row.setValue(vectorIdx.getValue(ijk.offsetBy(-1, 0, 0)), offDiagonal); |
| 548 | // -y direction | ||
| 549 |
2/4✓ Branch 1 taken 3148529 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3148529 times.
✗ Branch 6 not taken.
|
6297058 | row.setValue(vectorIdx.getValue(ijk.offsetBy(0, -1, 0)), offDiagonal); |
| 550 | // -z direction | ||
| 551 |
1/2✓ Branch 1 taken 3148529 times.
✗ Branch 2 not taken.
|
6297058 | row.setValue(vectorIdx.getValue(ijk.offsetBy(0, 0, -1)), offDiagonal); |
| 552 | // diagonal | ||
| 553 | 6297058 | row.setValue(rowNum, diagonal); | |
| 554 | // +z direction | ||
| 555 |
2/4✓ Branch 1 taken 3148529 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3148529 times.
✗ Branch 6 not taken.
|
6297058 | row.setValue(vectorIdx.getValue(ijk.offsetBy(0, 0, 1)), offDiagonal); |
| 556 | // +y direction | ||
| 557 |
2/4✓ Branch 1 taken 3148529 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3148529 times.
✗ Branch 6 not taken.
|
6297058 | row.setValue(vectorIdx.getValue(ijk.offsetBy(0, 1, 0)), offDiagonal); |
| 558 | // +x direction | ||
| 559 |
1/2✓ Branch 1 taken 3148529 times.
✗ Branch 2 not taken.
|
6297058 | row.setValue(vectorIdx.getValue(ijk.offsetBy(1, 0, 0)), offDiagonal); |
| 560 | |||
| 561 | } else { | ||
| 562 | // The current voxel is a boundary voxel. | ||
| 563 | // At least one of its neighbors is outside the solution domain. | ||
| 564 | |||
| 565 |
1/2✓ Branch 1 taken 168440 times.
✗ Branch 2 not taken.
|
336880 | ValueT modifiedDiagonal = 0.f; |
| 566 | |||
| 567 | // -x direction | ||
| 568 |
3/4✓ Branch 1 taken 168440 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 131537 times.
✓ Branch 4 taken 36903 times.
|
336880 | if (vectorIdx.probeValue(ijk.offsetBy(-1, 0, 0), column)) { |
| 569 | 263074 | row.setValue(column, offDiagonal); | |
| 570 | 263074 | modifiedDiagonal -= 1; | |
| 571 | } else { | ||
| 572 |
1/2✓ Branch 1 taken 36293 times.
✗ Branch 2 not taken.
|
72586 | boundaryOp(ijk, ijk.offsetBy(-1, 0, 0), source->at(rowNum), modifiedDiagonal); |
| 573 | } | ||
| 574 | // -y direction | ||
| 575 |
3/4✓ Branch 1 taken 168440 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 131537 times.
✓ Branch 4 taken 36903 times.
|
336880 | if (vectorIdx.probeValue(ijk.offsetBy(0, -1, 0), column)) { |
| 576 | 263074 | row.setValue(column, offDiagonal); | |
| 577 | 263074 | modifiedDiagonal -= 1; | |
| 578 | } else { | ||
| 579 |
1/2✓ Branch 1 taken 36293 times.
✗ Branch 2 not taken.
|
72986 | boundaryOp(ijk, ijk.offsetBy(0, -1, 0), source->at(rowNum), modifiedDiagonal); |
| 580 | } | ||
| 581 | // -z direction | ||
| 582 |
3/4✓ Branch 1 taken 168440 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 131072 times.
✓ Branch 4 taken 37368 times.
|
336880 | if (vectorIdx.probeValue(ijk.offsetBy(0, 0, -1), column)) { |
| 583 | 262144 | row.setValue(column, offDiagonal); | |
| 584 | 262144 | modifiedDiagonal -= 1; | |
| 585 | } else { | ||
| 586 |
1/2✓ Branch 1 taken 36758 times.
✗ Branch 2 not taken.
|
73516 | boundaryOp(ijk, ijk.offsetBy(0, 0, -1), source->at(rowNum), modifiedDiagonal); |
| 587 | } | ||
| 588 | // +z direction | ||
| 589 |
3/4✓ Branch 1 taken 168440 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 131072 times.
✓ Branch 4 taken 37368 times.
|
336880 | if (vectorIdx.probeValue(ijk.offsetBy(0, 0, 1), column)) { |
| 590 | 262144 | row.setValue(column, offDiagonal); | |
| 591 | 262144 | modifiedDiagonal -= 1; | |
| 592 | } else { | ||
| 593 |
1/2✓ Branch 1 taken 36758 times.
✗ Branch 2 not taken.
|
73516 | boundaryOp(ijk, ijk.offsetBy(0, 0, 1), source->at(rowNum), modifiedDiagonal); |
| 594 | } | ||
| 595 | // +y direction | ||
| 596 |
3/4✓ Branch 1 taken 168440 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 131537 times.
✓ Branch 4 taken 36903 times.
|
336880 | if (vectorIdx.probeValue(ijk.offsetBy(0, 1, 0), column)) { |
| 597 | 263074 | row.setValue(column, offDiagonal); | |
| 598 | 263074 | modifiedDiagonal -= 1; | |
| 599 | } else { | ||
| 600 |
1/2✓ Branch 1 taken 36293 times.
✗ Branch 2 not taken.
|
72586 | boundaryOp(ijk, ijk.offsetBy(0, 1, 0), source->at(rowNum), modifiedDiagonal); |
| 601 | } | ||
| 602 | // +x direction | ||
| 603 |
3/6✓ Branch 1 taken 168440 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 131537 times.
✓ Branch 4 taken 36903 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
336880 | if (vectorIdx.probeValue(ijk.offsetBy(1, 0, 0), column)) { |
| 604 | 263074 | row.setValue(column, offDiagonal); | |
| 605 | 263074 | modifiedDiagonal -= 1; | |
| 606 | } else { | ||
| 607 |
1/4✓ Branch 1 taken 36293 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
72586 | boundaryOp(ijk, ijk.offsetBy(1, 0, 0), source->at(rowNum), modifiedDiagonal); |
| 608 | } | ||
| 609 | // diagonal | ||
| 610 | 336880 | row.setValue(rowNum, modifiedDiagonal); | |
| 611 | } | ||
| 612 | } // end loop over voxels | ||
| 613 | 15792 | } | |
| 614 | }; | ||
| 615 | |||
| 616 | |||
| 617 | // Stencil 1 is the correct stencil, but stencil 2 requires | ||
| 618 | // half as many comparisons and produces smoother results at boundaries. | ||
| 619 | //#define OPENVDB_TOOLS_POISSON_LAPLACIAN_STENCIL 1 | ||
| 620 | #define OPENVDB_TOOLS_POISSON_LAPLACIAN_STENCIL 2 | ||
| 621 | |||
| 622 | /// Functor for use with LeafManager::foreach() to populate a sparse %Laplacian matrix | ||
| 623 | template<typename VIdxTreeT, typename BoundaryOp> | ||
| 624 | ✗ | struct ISLaplacianOp | |
| 625 | { | ||
| 626 | using VIdxLeafT = typename VIdxTreeT::LeafNodeType; | ||
| 627 | using ValueT = LaplacianMatrix::ValueType; | ||
| 628 | using VectorT = typename math::pcg::Vector<ValueT>; | ||
| 629 | |||
| 630 | LaplacianMatrix* laplacian; | ||
| 631 | const VIdxTreeT* idxTree; | ||
| 632 | const BoundaryOp boundaryOp; | ||
| 633 | VectorT* source; | ||
| 634 | |||
| 635 | 1 | ISLaplacianOp(LaplacianMatrix& m, const VIdxTreeT& idx, const BoundaryOp& op, VectorT& src): | |
| 636 | ✗ | laplacian(&m), idxTree(&idx), boundaryOp(op), source(&src) {} | |
| 637 | |||
| 638 | 1410 | void operator()(const VIdxLeafT& idxLeaf, size_t /*leafIdx*/) const | |
| 639 | { | ||
| 640 | 1410 | typename tree::ValueAccessor<const VIdxTreeT> vectorIdx(*idxTree); | |
| 641 | |||
| 642 | const int kNumOffsets = 6; | ||
| 643 | const Coord ijkOffset[kNumOffsets] = { | ||
| 644 | #if OPENVDB_TOOLS_POISSON_LAPLACIAN_STENCIL == 1 | ||
| 645 | Coord(-1,0,0), Coord(1,0,0), Coord(0,-1,0), Coord(0,1,0), Coord(0,0,-1), Coord(0,0,1) | ||
| 646 | #else | ||
| 647 | Coord(-2,0,0), Coord(2,0,0), Coord(0,-2,0), Coord(0,2,0), Coord(0,0,-2), Coord(0,0,2) | ||
| 648 | #endif | ||
| 649 | }; | ||
| 650 | |||
| 651 | // For each active voxel in this leaf... | ||
| 652 |
2/2✓ Branch 0 taken 267731 times.
✓ Branch 1 taken 705 times.
|
536872 | for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { |
| 653 |
2/4✓ Branch 1 taken 267731 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 267731 times.
|
535462 | assert(it.getValue() > -1); |
| 654 | |||
| 655 |
1/2✓ Branch 1 taken 267731 times.
✗ Branch 2 not taken.
|
535462 | const Coord ijk = it.getCoord(); |
| 656 |
1/2✓ Branch 1 taken 267731 times.
✗ Branch 2 not taken.
|
535462 | const math::pcg::SizeType rowNum = static_cast<math::pcg::SizeType>(it.getValue()); |
| 657 | |||
| 658 | 535462 | LaplacianMatrix::RowEditor row = laplacian->getRowEditor(rowNum); | |
| 659 | |||
| 660 | 535462 | ValueT modifiedDiagonal = 0.f; | |
| 661 | |||
| 662 | // For each of the neighbors of the voxel at (i,j,k)... | ||
| 663 |
2/2✓ Branch 0 taken 1606386 times.
✓ Branch 1 taken 267731 times.
|
3748234 | for (int dir = 0; dir < kNumOffsets; ++dir) { |
| 664 |
1/2✓ Branch 1 taken 1606386 times.
✗ Branch 2 not taken.
|
3212772 | const Coord neighbor = ijk + ijkOffset[dir]; |
| 665 | VIndex column; | ||
| 666 | // For collocated vector grids, the central differencing stencil requires | ||
| 667 | // access to neighbors at a distance of two voxels in each direction | ||
| 668 | // (-x, +x, -y, +y, -z, +z). | ||
| 669 | #if OPENVDB_TOOLS_POISSON_LAPLACIAN_STENCIL == 1 | ||
| 670 | const bool ijkIsInterior = (vectorIdx.probeValue(neighbor + ijkOffset[dir], column) | ||
| 671 | && vectorIdx.isValueOn(neighbor)); | ||
| 672 | #else | ||
| 673 |
1/2✓ Branch 1 taken 1606386 times.
✗ Branch 2 not taken.
|
3212772 | const bool ijkIsInterior = vectorIdx.probeValue(neighbor, column); |
| 674 | #endif | ||
| 675 |
2/2✓ Branch 0 taken 1546230 times.
✓ Branch 1 taken 60156 times.
|
3212772 | if (ijkIsInterior) { |
| 676 | // If (i,j,k) is sufficiently far away from the exterior, | ||
| 677 | // set its weight to one and adjust the center weight accordingly. | ||
| 678 | 3092460 | row.setValue(column, 1.f); | |
| 679 | 3092460 | modifiedDiagonal -= 1.f; | |
| 680 | } else { | ||
| 681 | // If (i,j,k) is adjacent to or one voxel away from the exterior, | ||
| 682 | // invoke the boundary condition functor. | ||
| 683 | ✗ | boundaryOp(ijk, neighbor, source->at(rowNum), modifiedDiagonal); | |
| 684 | } | ||
| 685 | } | ||
| 686 | // Set the (possibly modified) weight for the voxel at (i,j,k). | ||
| 687 | 535462 | row.setValue(rowNum, modifiedDiagonal); | |
| 688 | } | ||
| 689 | } | ||
| 690 | }; | ||
| 691 | |||
| 692 | } // namespace internal | ||
| 693 | |||
| 694 | /// @endcond | ||
| 695 | |||
| 696 | |||
| 697 | template<typename BoolTreeType> | ||
| 698 | inline LaplacianMatrix::Ptr | ||
| 699 | 2 | createISLaplacian(const typename BoolTreeType::template ValueConverter<VIndex>::Type& idxTree, | |
| 700 | const BoolTreeType& interiorMask, bool staggered) | ||
| 701 | { | ||
| 702 | using ValueT = LaplacianMatrix::ValueType; | ||
| 703 | 2 | math::pcg::Vector<ValueT> unused( | |
| 704 | 2 | static_cast<math::pcg::SizeType>(idxTree.activeVoxelCount())); | |
| 705 | DirichletBoundaryOp<ValueT> op; | ||
| 706 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
4 | return createISLaplacianWithBoundaryConditions(idxTree, interiorMask, op, unused, staggered); |
| 707 | } | ||
| 708 | |||
| 709 | |||
| 710 | template<typename BoolTreeType, typename BoundaryOp> | ||
| 711 | inline LaplacianMatrix::Ptr | ||
| 712 | 18 | createISLaplacianWithBoundaryConditions( | |
| 713 | const typename BoolTreeType::template ValueConverter<VIndex>::Type& idxTree, | ||
| 714 | const BoolTreeType& interiorMask, | ||
| 715 | const BoundaryOp& boundaryOp, | ||
| 716 | typename math::pcg::Vector<LaplacianMatrix::ValueType>& source, | ||
| 717 | bool staggered) | ||
| 718 | { | ||
| 719 | using VIdxTreeT = typename BoolTreeType::template ValueConverter<VIndex>::Type; | ||
| 720 | using VIdxLeafMgrT = typename tree::LeafManager<const VIdxTreeT>; | ||
| 721 | |||
| 722 | // The number of active voxels is the number of degrees of freedom. | ||
| 723 | 18 | const Index64 numDoF = idxTree.activeVoxelCount(); | |
| 724 | |||
| 725 | // Construct the matrix. | ||
| 726 | LaplacianMatrix::Ptr laplacianPtr( | ||
| 727 |
1/2✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
|
18 | new LaplacianMatrix(static_cast<math::pcg::SizeType>(numDoF))); |
| 728 | LaplacianMatrix& laplacian = *laplacianPtr; | ||
| 729 | |||
| 730 | // Populate the matrix using a second-order, 7-point CD stencil. | ||
| 731 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
36 | VIdxLeafMgrT idxLeafManager(idxTree); |
| 732 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
|
18 | if (staggered) { |
| 733 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
16 | idxLeafManager.foreach(internal::ISStaggeredLaplacianOp<BoolTreeType, BoundaryOp>( |
| 734 | laplacian, idxTree, interiorMask, boundaryOp, source)); | ||
| 735 | } else { | ||
| 736 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | idxLeafManager.foreach(internal::ISLaplacianOp<VIdxTreeT, BoundaryOp>( |
| 737 | laplacian, idxTree, boundaryOp, source)); | ||
| 738 | } | ||
| 739 | |||
| 740 | 18 | return laplacianPtr; | |
| 741 | } | ||
| 742 | |||
| 743 | |||
| 744 | //////////////////////////////////////// | ||
| 745 | |||
| 746 | |||
| 747 | template<typename TreeType> | ||
| 748 | typename TreeType::Ptr | ||
| 749 | 1 | solve(const TreeType& inTree, math::pcg::State& state, bool staggered) | |
| 750 | { | ||
| 751 | 1 | util::NullInterrupter interrupter; | |
| 752 | 1 | return solve(inTree, state, interrupter, staggered); | |
| 753 | } | ||
| 754 | |||
| 755 | |||
| 756 | template<typename TreeType, typename Interrupter> | ||
| 757 | typename TreeType::Ptr | ||
| 758 | 1 | solve(const TreeType& inTree, math::pcg::State& state, Interrupter& interrupter, bool staggered) | |
| 759 | { | ||
| 760 | DirichletBoundaryOp<LaplacianMatrix::ValueType> boundaryOp; | ||
| 761 | 1 | return solveWithBoundaryConditions(inTree, boundaryOp, state, interrupter, staggered); | |
| 762 | } | ||
| 763 | |||
| 764 | |||
| 765 | template<typename TreeType, typename BoundaryOp, typename Interrupter> | ||
| 766 | typename TreeType::Ptr | ||
| 767 | 12 | solveWithBoundaryConditions(const TreeType& inTree, const BoundaryOp& boundaryOp, | |
| 768 | math::pcg::State& state, Interrupter& interrupter, bool staggered) | ||
| 769 | { | ||
| 770 | using DefaultPrecondT = math::pcg::IncompleteCholeskyPreconditioner<LaplacianMatrix>; | ||
| 771 | return solveWithBoundaryConditionsAndPreconditioner<DefaultPrecondT>( | ||
| 772 | 12 | inTree, boundaryOp, state, interrupter, staggered); | |
| 773 | } | ||
| 774 | |||
| 775 | |||
| 776 | template< | ||
| 777 | typename PreconditionerType, | ||
| 778 | typename TreeType, | ||
| 779 | typename BoundaryOp, | ||
| 780 | typename Interrupter> | ||
| 781 | typename TreeType::Ptr | ||
| 782 | 12 | solveWithBoundaryConditionsAndPreconditioner( | |
| 783 | const TreeType& inTree, | ||
| 784 | const BoundaryOp& boundaryOp, | ||
| 785 | math::pcg::State& state, | ||
| 786 | Interrupter& interrupter, | ||
| 787 | bool staggered) | ||
| 788 | { | ||
| 789 | return solveWithBoundaryConditionsAndPreconditioner<PreconditionerType>( | ||
| 790 | 12 | /*source=*/inTree, /*domain mask=*/inTree, boundaryOp, state, interrupter, staggered); | |
| 791 | } | ||
| 792 | |||
| 793 | template< | ||
| 794 | typename PreconditionerType, | ||
| 795 | typename TreeType, | ||
| 796 | typename DomainTreeType, | ||
| 797 | typename BoundaryOp, | ||
| 798 | typename Interrupter> | ||
| 799 | typename TreeType::Ptr | ||
| 800 | 7 | solveWithBoundaryConditionsAndPreconditioner( | |
| 801 | const TreeType& inTree, | ||
| 802 | const DomainTreeType& domainMask, | ||
| 803 | const BoundaryOp& boundaryOp, | ||
| 804 | math::pcg::State& state, | ||
| 805 | Interrupter& interrupter, | ||
| 806 | bool staggered) | ||
| 807 | { | ||
| 808 | using TreeValueT = typename TreeType::ValueType; | ||
| 809 | using VecValueT = LaplacianMatrix::ValueType; | ||
| 810 | using VectorT = typename math::pcg::Vector<VecValueT>; | ||
| 811 | using VIdxTreeT = typename TreeType::template ValueConverter<VIndex>::Type; | ||
| 812 | using MaskTreeT = typename TreeType::template ValueConverter<bool>::Type; | ||
| 813 | |||
| 814 | // 1. Create a mapping from active voxels of the input tree to elements of a vector. | ||
| 815 | 7 | typename VIdxTreeT::ConstPtr idxTree = createIndexTree(domainMask); | |
| 816 | |||
| 817 | // 2. Populate a vector with values from the input tree. | ||
| 818 | 7 | typename VectorT::Ptr b = createVectorFromTree<VecValueT>(inTree, *idxTree); | |
| 819 | |||
| 820 | // 3. Create a mask of the interior voxels of the input tree (from the densified index tree). | ||
| 821 | /// @todo Is this really needed? | ||
| 822 | 7 | typename MaskTreeT::Ptr interiorMask( | |
| 823 | 7 | new MaskTreeT(*idxTree, /*background=*/false, TopologyCopy())); | |
| 824 | 7 | tools::erodeActiveValues(*interiorMask, /*iterations=*/1, tools::NN_FACE, tools::IGNORE_TILES); | |
| 825 | |||
| 826 | // 4. Create the Laplacian matrix. | ||
| 827 | 7 | LaplacianMatrix::Ptr laplacian = createISLaplacianWithBoundaryConditions( | |
| 828 | *idxTree, *interiorMask, boundaryOp, *b, staggered); | ||
| 829 | |||
| 830 | // 5. Solve the Poisson equation. | ||
| 831 | laplacian->scale(-1.0); // matrix is negative-definite; solve -M x = -b | ||
| 832 | b->scale(-1.0); | ||
| 833 | 14 | typename VectorT::Ptr x(new VectorT(b->size(), zeroVal<VecValueT>())); | |
| 834 | typename math::pcg::Preconditioner<VecValueT>::Ptr precond( | ||
| 835 | 7 | new PreconditionerType(*laplacian)); | |
| 836 | 7 | if (!precond->isValid()) { | |
| 837 | ✗ | precond.reset(new math::pcg::JacobiPreconditioner<LaplacianMatrix>(*laplacian)); | |
| 838 | } | ||
| 839 | |||
| 840 | 7 | state = math::pcg::solve(*laplacian, *b, *x, *precond, interrupter, state); | |
| 841 | |||
| 842 | // 6. Populate the output tree with values from the solution vector. | ||
| 843 | /// @todo if (state.success) ... ? | ||
| 844 | 14 | return createTreeFromVector<TreeValueT>(*x, *idxTree, /*background=*/zeroVal<TreeValueT>()); | |
| 845 | } | ||
| 846 | |||
| 847 | |||
| 848 | //////////////////////////////////////// | ||
| 849 | |||
| 850 | |||
| 851 | // Explicit Template Instantiation | ||
| 852 | |||
| 853 | #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
| 854 | |||
| 855 | #ifdef OPENVDB_INSTANTIATE_POISSONSOLVER | ||
| 856 | #include <openvdb/util/ExplicitInstantiation.h> | ||
| 857 | #endif | ||
| 858 | |||
| 859 | #define _FUNCTION(TreeT) \ | ||
| 860 | TreeT::Ptr solveWithBoundaryConditionsAndPreconditioner< \ | ||
| 861 | math::pcg::IncompleteCholeskyPreconditioner<LaplacianMatrix>>( \ | ||
| 862 | const TreeT&, const TreeT&, const DirichletBoundaryOp<LaplacianMatrix::ValueType>&, \ | ||
| 863 | math::pcg::State&, util::NullInterrupter&, bool) | ||
| 864 | OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) | ||
| 865 | #undef _FUNCTION | ||
| 866 | |||
| 867 | #define _FUNCTION(TreeT) \ | ||
| 868 | TreeT::Ptr solveWithBoundaryConditionsAndPreconditioner< \ | ||
| 869 | math::pcg::IncompleteCholeskyPreconditioner<LaplacianMatrix>>( \ | ||
| 870 | const TreeT&, const BoolTree&, const DirichletBoundaryOp<LaplacianMatrix::ValueType>&, \ | ||
| 871 | math::pcg::State&, util::NullInterrupter&, bool) | ||
| 872 | OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) | ||
| 873 | #undef _FUNCTION | ||
| 874 | |||
| 875 | #define _FUNCTION(TreeT) \ | ||
| 876 | TreeT::Ptr solveWithBoundaryConditionsAndPreconditioner< \ | ||
| 877 | math::pcg::JacobiPreconditioner<LaplacianMatrix>>( \ | ||
| 878 | const TreeT&, const TreeT&, const DirichletBoundaryOp<LaplacianMatrix::ValueType>&, \ | ||
| 879 | math::pcg::State&, util::NullInterrupter&, bool) | ||
| 880 | OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) | ||
| 881 | #undef _FUNCTION | ||
| 882 | |||
| 883 | #define _FUNCTION(TreeT) \ | ||
| 884 | TreeT::Ptr solveWithBoundaryConditionsAndPreconditioner< \ | ||
| 885 | math::pcg::JacobiPreconditioner<LaplacianMatrix>>( \ | ||
| 886 | const TreeT&, const BoolTree&, const DirichletBoundaryOp<LaplacianMatrix::ValueType>&, \ | ||
| 887 | math::pcg::State&, util::NullInterrupter&, bool) | ||
| 888 | OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) | ||
| 889 | #undef _FUNCTION | ||
| 890 | |||
| 891 | #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
| 892 | |||
| 893 | |||
| 894 | } // namespace poisson | ||
| 895 | } // namespace tools | ||
| 896 | } // namespace OPENVDB_VERSION_NAME | ||
| 897 | } // namespace openvdb | ||
| 898 | |||
| 899 | #endif // OPENVDB_TOOLS_POISSONSOLVER_HAS_BEEN_INCLUDED | ||
| 900 |