Line |
Branch |
Exec |
Source |
1 |
|
|
// Copyright Contributors to the OpenVDB Project |
2 |
|
|
// SPDX-License-Identifier: MPL-2.0 |
3 |
|
|
|
4 |
|
|
/// @author Ken Museth |
5 |
|
|
/// |
6 |
|
|
/// @file tools/LevelSetAdvect.h |
7 |
|
|
/// |
8 |
|
|
/// @brief Hyperbolic advection of narrow-band level sets |
9 |
|
|
|
10 |
|
|
#ifndef OPENVDB_TOOLS_LEVEL_SET_ADVECT_HAS_BEEN_INCLUDED |
11 |
|
|
#define OPENVDB_TOOLS_LEVEL_SET_ADVECT_HAS_BEEN_INCLUDED |
12 |
|
|
|
13 |
|
|
#include <tbb/parallel_for.h> |
14 |
|
|
#include <tbb/parallel_reduce.h> |
15 |
|
|
#include <openvdb/Platform.h> |
16 |
|
|
#include "LevelSetTracker.h" |
17 |
|
|
#include "VelocityFields.h" // for EnrightField |
18 |
|
|
#include <openvdb/math/FiniteDifference.h> |
19 |
|
|
//#include <openvdb/util/CpuTimer.h> |
20 |
|
|
#include <functional> |
21 |
|
|
|
22 |
|
|
|
23 |
|
|
namespace openvdb { |
24 |
|
|
OPENVDB_USE_VERSION_NAMESPACE |
25 |
|
|
namespace OPENVDB_VERSION_NAME { |
26 |
|
|
namespace tools { |
27 |
|
|
|
28 |
|
|
/// @brief Hyperbolic advection of narrow-band level sets in an |
29 |
|
|
/// external velocity field |
30 |
|
|
/// |
31 |
|
|
/// The @c FieldType template argument below refers to any functor |
32 |
|
|
/// with the following interface (see tools/VelocityFields.h |
33 |
|
|
/// for examples): |
34 |
|
|
/// |
35 |
|
|
/// @code |
36 |
|
|
/// class VelocityField { |
37 |
|
|
/// ... |
38 |
|
|
/// public: |
39 |
|
|
/// openvdb::VectorType operator() (const openvdb::Coord& xyz, ValueType time) const; |
40 |
|
|
/// ... |
41 |
|
|
/// }; |
42 |
|
|
/// @endcode |
43 |
|
|
/// |
44 |
|
|
/// @note The functor method returns the velocity field at coordinate |
45 |
|
|
/// position xyz of the advection grid, and for the specified |
46 |
|
|
/// time. Note that since the velocity is returned in the local |
47 |
|
|
/// coordinate space of the grid that is being advected, the functor |
48 |
|
|
/// typically depends on the transformation of that grid. This design |
49 |
|
|
/// is chosen for performance reasons. Finally we will assume that the |
50 |
|
|
/// functor method is NOT threadsafe (typically uses a ValueAccessor) |
51 |
|
|
/// and that its lightweight enough that we can copy it per thread. |
52 |
|
|
/// |
53 |
|
|
/// The @c InterruptType template argument below refers to any class |
54 |
|
|
/// with the following interface: |
55 |
|
|
/// @code |
56 |
|
|
/// class Interrupter { |
57 |
|
|
/// ... |
58 |
|
|
/// public: |
59 |
|
|
/// void start(const char* name = nullptr) // called when computations begin |
60 |
|
|
/// void end() // called when computations end |
61 |
|
|
/// bool wasInterrupted(int percent=-1) // return true to break computation |
62 |
|
|
///}; |
63 |
|
|
/// @endcode |
64 |
|
|
/// |
65 |
|
|
/// @note If no template argument is provided for this InterruptType |
66 |
|
|
/// the util::NullInterrupter is used which implies that all |
67 |
|
|
/// interrupter calls are no-ops (i.e. incurs no computational overhead). |
68 |
|
|
/// |
69 |
|
|
|
70 |
|
|
template<typename GridT, |
71 |
|
|
typename FieldT = EnrightField<typename GridT::ValueType>, |
72 |
|
|
typename InterruptT = util::NullInterrupter> |
73 |
|
|
class LevelSetAdvection |
74 |
|
|
{ |
75 |
|
|
public: |
76 |
|
|
using GridType = GridT; |
77 |
|
|
using TrackerT = LevelSetTracker<GridT, InterruptT>; |
78 |
|
|
using LeafRange = typename TrackerT::LeafRange; |
79 |
|
|
using LeafType = typename TrackerT::LeafType; |
80 |
|
|
using BufferType = typename TrackerT::BufferType; |
81 |
|
|
using ValueType = typename TrackerT::ValueType; |
82 |
|
|
using VectorType = typename FieldT::VectorType; |
83 |
|
|
|
84 |
|
|
/// Main constructor |
85 |
|
✗ |
LevelSetAdvection(GridT& grid, const FieldT& field, InterruptT* interrupt = nullptr): |
86 |
|
|
mTracker(grid, interrupt), mField(field), |
87 |
|
|
mSpatialScheme(math::HJWENO5_BIAS), |
88 |
|
✗ |
mTemporalScheme(math::TVD_RK2) {} |
89 |
|
|
|
90 |
|
✗ |
virtual ~LevelSetAdvection() {} |
91 |
|
|
|
92 |
|
|
/// @brief Return the spatial finite difference scheme |
93 |
|
✗ |
math::BiasedGradientScheme getSpatialScheme() const { return mSpatialScheme; } |
94 |
|
|
/// @brief Set the spatial finite difference scheme |
95 |
|
✗ |
void setSpatialScheme(math::BiasedGradientScheme scheme) { mSpatialScheme = scheme; } |
96 |
|
|
|
97 |
|
|
/// @brief Return the temporal integration scheme |
98 |
|
✗ |
math::TemporalIntegrationScheme getTemporalScheme() const { return mTemporalScheme; } |
99 |
|
|
/// @brief Set the spatial finite difference scheme |
100 |
|
✗ |
void setTemporalScheme(math::TemporalIntegrationScheme scheme) { mTemporalScheme = scheme; } |
101 |
|
|
|
102 |
|
|
/// @brief Return the spatial finite difference scheme |
103 |
|
✗ |
math::BiasedGradientScheme getTrackerSpatialScheme() const { |
104 |
|
✗ |
return mTracker.getSpatialScheme(); |
105 |
|
|
} |
106 |
|
|
/// @brief Set the spatial finite difference scheme |
107 |
|
✗ |
void setTrackerSpatialScheme(math::BiasedGradientScheme scheme) { |
108 |
|
|
mTracker.setSpatialScheme(scheme); |
109 |
|
|
} |
110 |
|
|
/// @brief Return the temporal integration scheme |
111 |
|
✗ |
math::TemporalIntegrationScheme getTrackerTemporalScheme() const { |
112 |
|
✗ |
return mTracker.getTemporalScheme(); |
113 |
|
|
} |
114 |
|
|
/// @brief Set the spatial finite difference scheme |
115 |
|
✗ |
void setTrackerTemporalScheme(math::TemporalIntegrationScheme scheme) { |
116 |
|
|
mTracker.setTemporalScheme(scheme); |
117 |
|
|
} |
118 |
|
|
|
119 |
|
|
/// @brief Return The number of normalizations performed per track or |
120 |
|
|
/// normalize call. |
121 |
|
✗ |
int getNormCount() const { return mTracker.getNormCount(); } |
122 |
|
|
/// @brief Set the number of normalizations performed per track or |
123 |
|
|
/// normalize call. |
124 |
|
✗ |
void setNormCount(int n) { mTracker.setNormCount(n); } |
125 |
|
|
|
126 |
|
|
/// @brief Return the grain-size used for multi-threading |
127 |
|
✗ |
int getGrainSize() const { return mTracker.getGrainSize(); } |
128 |
|
|
/// @brief Set the grain-size used for multi-threading. |
129 |
|
|
/// @note A grain size of 0 or less disables multi-threading! |
130 |
|
✗ |
void setGrainSize(int grainsize) { mTracker.setGrainSize(grainsize); } |
131 |
|
|
|
132 |
|
|
/// Advect the level set from its current time, time0, to its |
133 |
|
|
/// final time, time1. If time0>time1 backward advection is performed. |
134 |
|
|
/// |
135 |
|
|
/// @return number of CFL iterations used to advect from time0 to time1 |
136 |
|
|
size_t advect(ValueType time0, ValueType time1); |
137 |
|
|
|
138 |
|
|
private: |
139 |
|
|
// disallow copy construction and copy by assignment! |
140 |
|
|
LevelSetAdvection(const LevelSetAdvection&);// not implemented |
141 |
|
|
LevelSetAdvection& operator=(const LevelSetAdvection&);// not implemented |
142 |
|
|
|
143 |
|
|
// This templated private struct implements all the level set magic. |
144 |
|
|
template<typename MapT, math::BiasedGradientScheme SpatialScheme, |
145 |
|
|
math::TemporalIntegrationScheme TemporalScheme> |
146 |
|
|
struct Advect |
147 |
|
|
{ |
148 |
|
|
/// Main constructor |
149 |
|
|
Advect(LevelSetAdvection& parent); |
150 |
|
|
/// Shallow copy constructor called by tbb::parallel_for() threads |
151 |
|
|
Advect(const Advect& other); |
152 |
|
|
/// Destructor |
153 |
|
✗ |
virtual ~Advect() { if (mIsMaster) this->clearField(); } |
154 |
|
|
/// Advect the level set from its current time, time0, to its final time, time1. |
155 |
|
|
/// @return number of CFL iterations |
156 |
|
|
size_t advect(ValueType time0, ValueType time1); |
157 |
|
|
/// Used internally by tbb::parallel_for() |
158 |
|
✗ |
void operator()(const LeafRange& r) const |
159 |
|
|
{ |
160 |
|
✗ |
if (mTask) mTask(const_cast<Advect*>(this), r); |
161 |
|
✗ |
else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly"); |
162 |
|
|
} |
163 |
|
|
/// method calling tbb |
164 |
|
|
void cook(const char* msg, size_t swapBuffer = 0); |
165 |
|
|
/// Sample field and return the CFL time step |
166 |
|
|
typename GridT::ValueType sampleField(ValueType time0, ValueType time1); |
167 |
|
|
template <bool Aligned> void sample(const LeafRange& r, ValueType t0, ValueType t1); |
168 |
|
✗ |
inline void sampleXformed(const LeafRange& r, ValueType t0, ValueType t1) |
169 |
|
|
{ |
170 |
|
✗ |
this->sample<false>(r, t0, t1); |
171 |
|
|
} |
172 |
|
✗ |
inline void sampleAligned(const LeafRange& r, ValueType t0, ValueType t1) |
173 |
|
|
{ |
174 |
|
✗ |
this->sample<true>(r, t0, t1); |
175 |
|
|
} |
176 |
|
|
void clearField(); |
177 |
|
|
// Convex combination of Phi and a forward Euler advection steps: |
178 |
|
|
// Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * Speed(speed)*|Grad[Phi(0)]|); |
179 |
|
|
template <int Nominator, int Denominator> |
180 |
|
|
void euler(const LeafRange&, ValueType, Index, Index); |
181 |
|
✗ |
inline void euler01(const LeafRange& r, ValueType t) {this->euler<0,1>(r, t, 0, 1);} |
182 |
|
✗ |
inline void euler12(const LeafRange& r, ValueType t) {this->euler<1,2>(r, t, 1, 1);} |
183 |
|
✗ |
inline void euler34(const LeafRange& r, ValueType t) {this->euler<3,4>(r, t, 1, 2);} |
184 |
|
✗ |
inline void euler13(const LeafRange& r, ValueType t) {this->euler<1,3>(r, t, 1, 2);} |
185 |
|
|
|
186 |
|
|
LevelSetAdvection& mParent; |
187 |
|
|
VectorType* mVelocity; |
188 |
|
|
size_t* mOffsets; |
189 |
|
|
const MapT* mMap; |
190 |
|
|
typename std::function<void (Advect*, const LeafRange&)> mTask; |
191 |
|
|
const bool mIsMaster; |
192 |
|
|
}; // end of private Advect struct |
193 |
|
|
|
194 |
|
|
template<math::BiasedGradientScheme SpatialScheme> |
195 |
|
|
size_t advect1(ValueType time0, ValueType time1); |
196 |
|
|
|
197 |
|
|
template<math::BiasedGradientScheme SpatialScheme, |
198 |
|
|
math::TemporalIntegrationScheme TemporalScheme> |
199 |
|
|
size_t advect2(ValueType time0, ValueType time1); |
200 |
|
|
|
201 |
|
|
template<math::BiasedGradientScheme SpatialScheme, |
202 |
|
|
math::TemporalIntegrationScheme TemporalScheme, |
203 |
|
|
typename MapType> |
204 |
|
|
size_t advect3(ValueType time0, ValueType time1); |
205 |
|
|
|
206 |
|
|
TrackerT mTracker; |
207 |
|
|
//each thread needs a deep copy of the field since it might contain a ValueAccessor |
208 |
|
|
const FieldT mField; |
209 |
|
|
math::BiasedGradientScheme mSpatialScheme; |
210 |
|
|
math::TemporalIntegrationScheme mTemporalScheme; |
211 |
|
|
|
212 |
|
|
};//end of LevelSetAdvection |
213 |
|
|
|
214 |
|
|
|
215 |
|
|
template<typename GridT, typename FieldT, typename InterruptT> |
216 |
|
|
size_t |
217 |
|
✗ |
LevelSetAdvection<GridT, FieldT, InterruptT>::advect(ValueType time0, ValueType time1) |
218 |
|
|
{ |
219 |
|
✗ |
switch (mSpatialScheme) { |
220 |
|
✗ |
case math::FIRST_BIAS: |
221 |
|
✗ |
return this->advect1<math::FIRST_BIAS >(time0, time1); |
222 |
|
✗ |
case math::SECOND_BIAS: |
223 |
|
✗ |
return this->advect1<math::SECOND_BIAS >(time0, time1); |
224 |
|
✗ |
case math::THIRD_BIAS: |
225 |
|
✗ |
return this->advect1<math::THIRD_BIAS >(time0, time1); |
226 |
|
✗ |
case math::WENO5_BIAS: |
227 |
|
✗ |
return this->advect1<math::WENO5_BIAS >(time0, time1); |
228 |
|
✗ |
case math::HJWENO5_BIAS: |
229 |
|
✗ |
return this->advect1<math::HJWENO5_BIAS>(time0, time1); |
230 |
|
|
default: |
231 |
|
✗ |
OPENVDB_THROW(ValueError, "Spatial difference scheme not supported!"); |
232 |
|
|
} |
233 |
|
|
return 0; |
234 |
|
|
} |
235 |
|
|
|
236 |
|
|
|
237 |
|
|
template<typename GridT, typename FieldT, typename InterruptT> |
238 |
|
|
template<math::BiasedGradientScheme SpatialScheme> |
239 |
|
|
size_t |
240 |
|
✗ |
LevelSetAdvection<GridT, FieldT, InterruptT>::advect1(ValueType time0, ValueType time1) |
241 |
|
|
{ |
242 |
|
✗ |
switch (mTemporalScheme) { |
243 |
|
✗ |
case math::TVD_RK1: |
244 |
|
✗ |
return this->advect2<SpatialScheme, math::TVD_RK1>(time0, time1); |
245 |
|
✗ |
case math::TVD_RK2: |
246 |
|
✗ |
return this->advect2<SpatialScheme, math::TVD_RK2>(time0, time1); |
247 |
|
✗ |
case math::TVD_RK3: |
248 |
|
✗ |
return this->advect2<SpatialScheme, math::TVD_RK3>(time0, time1); |
249 |
|
|
default: |
250 |
|
✗ |
OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); |
251 |
|
|
} |
252 |
|
|
return 0; |
253 |
|
|
} |
254 |
|
|
|
255 |
|
|
|
256 |
|
|
template<typename GridT, typename FieldT, typename InterruptT> |
257 |
|
|
template<math::BiasedGradientScheme SpatialScheme, math::TemporalIntegrationScheme TemporalScheme> |
258 |
|
|
size_t |
259 |
|
✗ |
LevelSetAdvection<GridT, FieldT, InterruptT>::advect2(ValueType time0, ValueType time1) |
260 |
|
|
{ |
261 |
|
|
const math::Transform& trans = mTracker.grid().transform(); |
262 |
|
✗ |
if (trans.mapType() == math::UniformScaleMap::mapType()) { |
263 |
|
✗ |
return this->advect3<SpatialScheme, TemporalScheme, math::UniformScaleMap>(time0, time1); |
264 |
|
✗ |
} else if (trans.mapType() == math::UniformScaleTranslateMap::mapType()) { |
265 |
|
|
return this->advect3<SpatialScheme, TemporalScheme, math::UniformScaleTranslateMap>( |
266 |
|
✗ |
time0, time1); |
267 |
|
✗ |
} else if (trans.mapType() == math::UnitaryMap::mapType()) { |
268 |
|
✗ |
return this->advect3<SpatialScheme, TemporalScheme, math::UnitaryMap >(time0, time1); |
269 |
|
✗ |
} else if (trans.mapType() == math::TranslationMap::mapType()) { |
270 |
|
✗ |
return this->advect3<SpatialScheme, TemporalScheme, math::TranslationMap>(time0, time1); |
271 |
|
|
} else { |
272 |
|
✗ |
OPENVDB_THROW(ValueError, "MapType not supported!"); |
273 |
|
|
} |
274 |
|
|
return 0; |
275 |
|
|
} |
276 |
|
|
|
277 |
|
|
|
278 |
|
|
template<typename GridT, typename FieldT, typename InterruptT> |
279 |
|
|
template< |
280 |
|
|
math::BiasedGradientScheme SpatialScheme, |
281 |
|
|
math::TemporalIntegrationScheme TemporalScheme, |
282 |
|
|
typename MapT> |
283 |
|
|
size_t |
284 |
|
✗ |
LevelSetAdvection<GridT, FieldT, InterruptT>::advect3(ValueType time0, ValueType time1) |
285 |
|
|
{ |
286 |
|
✗ |
Advect<MapT, SpatialScheme, TemporalScheme> tmp(*this); |
287 |
|
✗ |
return tmp.advect(time0, time1); |
288 |
|
|
} |
289 |
|
|
|
290 |
|
|
|
291 |
|
|
/////////////////////////////////////////////////////////////////////// |
292 |
|
|
|
293 |
|
|
|
294 |
|
|
template<typename GridT, typename FieldT, typename InterruptT> |
295 |
|
|
template< |
296 |
|
|
typename MapT, |
297 |
|
|
math::BiasedGradientScheme SpatialScheme, |
298 |
|
|
math::TemporalIntegrationScheme TemporalScheme> |
299 |
|
|
inline |
300 |
|
✗ |
LevelSetAdvection<GridT, FieldT, InterruptT>:: |
301 |
|
|
Advect<MapT, SpatialScheme, TemporalScheme>:: |
302 |
|
|
Advect(LevelSetAdvection& parent) |
303 |
|
|
: mParent(parent) |
304 |
|
|
, mVelocity(nullptr) |
305 |
|
|
, mOffsets(nullptr) |
306 |
|
|
, mMap(parent.mTracker.grid().transform().template constMap<MapT>().get()) |
307 |
|
|
, mTask(0) |
308 |
|
✗ |
, mIsMaster(true) |
309 |
|
|
{ |
310 |
|
|
} |
311 |
|
|
|
312 |
|
|
|
313 |
|
|
template<typename GridT, typename FieldT, typename InterruptT> |
314 |
|
|
template< |
315 |
|
|
typename MapT, |
316 |
|
|
math::BiasedGradientScheme SpatialScheme, |
317 |
|
|
math::TemporalIntegrationScheme TemporalScheme> |
318 |
|
|
inline |
319 |
|
✗ |
LevelSetAdvection<GridT, FieldT, InterruptT>:: |
320 |
|
|
Advect<MapT, SpatialScheme, TemporalScheme>:: |
321 |
|
|
Advect(const Advect& other) |
322 |
|
✗ |
: mParent(other.mParent) |
323 |
|
✗ |
, mVelocity(other.mVelocity) |
324 |
|
✗ |
, mOffsets(other.mOffsets) |
325 |
|
✗ |
, mMap(other.mMap) |
326 |
|
✗ |
, mTask(other.mTask) |
327 |
|
✗ |
, mIsMaster(false) |
328 |
|
|
{ |
329 |
|
|
} |
330 |
|
|
|
331 |
|
|
|
332 |
|
|
template<typename GridT, typename FieldT, typename InterruptT> |
333 |
|
|
template< |
334 |
|
|
typename MapT, |
335 |
|
|
math::BiasedGradientScheme SpatialScheme, |
336 |
|
|
math::TemporalIntegrationScheme TemporalScheme> |
337 |
|
|
inline size_t |
338 |
|
✗ |
LevelSetAdvection<GridT, FieldT, InterruptT>:: |
339 |
|
|
Advect<MapT, SpatialScheme, TemporalScheme>:: |
340 |
|
|
advect(ValueType time0, ValueType time1) |
341 |
|
|
{ |
342 |
|
|
namespace ph = std::placeholders; |
343 |
|
|
|
344 |
|
|
//util::CpuTimer timer; |
345 |
|
|
size_t countCFL = 0; |
346 |
|
✗ |
if ( math::isZero(time0 - time1) ) return countCFL; |
347 |
|
|
const bool isForward = time0 < time1; |
348 |
|
✗ |
while ((isForward ? time0<time1 : time0>time1) && mParent.mTracker.checkInterrupter()) { |
349 |
|
|
/// Make sure we have enough temporal auxiliary buffers |
350 |
|
|
//timer.start( "\nallocate buffers" ); |
351 |
|
✗ |
mParent.mTracker.leafs().rebuildAuxBuffers(TemporalScheme == math::TVD_RK3 ? 2 : 1); |
352 |
|
|
//timer.stop(); |
353 |
|
|
|
354 |
|
✗ |
const ValueType dt = this->sampleField(time0, time1); |
355 |
|
✗ |
if ( math::isZero(dt) ) break;//V is essentially zero so terminate |
356 |
|
|
|
357 |
|
|
OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN //switch is resolved at compile-time |
358 |
|
|
switch(TemporalScheme) { |
359 |
|
✗ |
case math::TVD_RK1: |
360 |
|
|
// Perform one explicit Euler step: t1 = t0 + dt |
361 |
|
|
// Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(0) |
362 |
|
✗ |
mTask = std::bind(&Advect::euler01, ph::_1, ph::_2, dt); |
363 |
|
|
|
364 |
|
|
// Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) |
365 |
|
✗ |
this->cook("Advecting level set using TVD_RK1", 1); |
366 |
|
|
break; |
367 |
|
✗ |
case math::TVD_RK2: |
368 |
|
|
// Perform one explicit Euler step: t1 = t0 + dt |
369 |
|
|
// Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(0) |
370 |
|
✗ |
mTask = std::bind(&Advect::euler01, ph::_1, ph::_2, dt); |
371 |
|
|
|
372 |
|
|
// Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) |
373 |
|
✗ |
this->cook("Advecting level set using TVD_RK1 (step 1 of 2)", 1); |
374 |
|
|
|
375 |
|
|
// Convex combine explict Euler step: t2 = t0 + dt |
376 |
|
|
// Phi_t2(1) = 1/2 * Phi_t0(1) + 1/2 * (Phi_t1(0) - dt * V.Grad_t1(0)) |
377 |
|
✗ |
mTask = std::bind(&Advect::euler12, ph::_1, ph::_2, dt); |
378 |
|
|
|
379 |
|
|
// Cook and swap buffer 0 and 1 such that Phi_t2(0) and Phi_t1(1) |
380 |
|
✗ |
this->cook("Advecting level set using TVD_RK1 (step 2 of 2)", 1); |
381 |
|
|
break; |
382 |
|
✗ |
case math::TVD_RK3: |
383 |
|
|
// Perform one explicit Euler step: t1 = t0 + dt |
384 |
|
|
// Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(0) |
385 |
|
✗ |
mTask = std::bind(&Advect::euler01, ph::_1, ph::_2, dt); |
386 |
|
|
|
387 |
|
|
// Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) |
388 |
|
✗ |
this->cook("Advecting level set using TVD_RK3 (step 1 of 3)", 1); |
389 |
|
|
|
390 |
|
|
// Convex combine explict Euler step: t2 = t0 + dt/2 |
391 |
|
|
// Phi_t2(2) = 3/4 * Phi_t0(1) + 1/4 * (Phi_t1(0) - dt * V.Grad_t1(0)) |
392 |
|
✗ |
mTask = std::bind(&Advect::euler34, ph::_1, ph::_2, dt); |
393 |
|
|
|
394 |
|
|
// Cook and swap buffer 0 and 2 such that Phi_t2(0) and Phi_t1(2) |
395 |
|
✗ |
this->cook("Advecting level set using TVD_RK3 (step 2 of 3)", 2); |
396 |
|
|
|
397 |
|
|
// Convex combine explict Euler step: t3 = t0 + dt |
398 |
|
|
// Phi_t3(2) = 1/3 * Phi_t0(1) + 2/3 * (Phi_t2(0) - dt * V.Grad_t2(0) |
399 |
|
✗ |
mTask = std::bind(&Advect::euler13, ph::_1, ph::_2, dt); |
400 |
|
|
|
401 |
|
|
// Cook and swap buffer 0 and 2 such that Phi_t3(0) and Phi_t2(2) |
402 |
|
✗ |
this->cook("Advecting level set using TVD_RK3 (step 3 of 3)", 2); |
403 |
|
|
break; |
404 |
|
|
default: |
405 |
|
|
OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); |
406 |
|
|
}//end of compile-time resolved switch |
407 |
|
|
OPENVDB_NO_UNREACHABLE_CODE_WARNING_END |
408 |
|
|
|
409 |
|
✗ |
time0 += isForward ? dt : -dt; |
410 |
|
✗ |
++countCFL; |
411 |
|
✗ |
mParent.mTracker.leafs().removeAuxBuffers(); |
412 |
|
✗ |
this->clearField(); |
413 |
|
|
/// Track the narrow band |
414 |
|
✗ |
mParent.mTracker.track(); |
415 |
|
|
}//end wile-loop over time |
416 |
|
|
return countCFL;//number of CLF propagation steps |
417 |
|
|
} |
418 |
|
|
|
419 |
|
|
|
420 |
|
|
template<typename GridT, typename FieldT, typename InterruptT> |
421 |
|
|
template< |
422 |
|
|
typename MapT, |
423 |
|
|
math::BiasedGradientScheme SpatialScheme, |
424 |
|
|
math::TemporalIntegrationScheme TemporalScheme> |
425 |
|
|
inline typename GridT::ValueType |
426 |
|
✗ |
LevelSetAdvection<GridT, FieldT, InterruptT>:: |
427 |
|
|
Advect<MapT, SpatialScheme, TemporalScheme>:: |
428 |
|
|
sampleField(ValueType time0, ValueType time1) |
429 |
|
|
{ |
430 |
|
|
namespace ph = std::placeholders; |
431 |
|
|
|
432 |
|
✗ |
const int grainSize = mParent.mTracker.getGrainSize(); |
433 |
|
|
const size_t leafCount = mParent.mTracker.leafs().leafCount(); |
434 |
|
✗ |
if (leafCount==0) return ValueType(0.0); |
435 |
|
|
|
436 |
|
|
// Compute the prefix sum of offsets to active voxels |
437 |
|
✗ |
size_t size=0, voxelCount=mParent.mTracker.leafs().getPrefixSum(mOffsets, size, grainSize); |
438 |
|
|
|
439 |
|
|
// Sample the velocity field |
440 |
|
✗ |
if (mParent.mField.transform() == mParent.mTracker.grid().transform()) { |
441 |
|
✗ |
mTask = std::bind(&Advect::sampleAligned, ph::_1, ph::_2, time0, time1); |
442 |
|
|
} else { |
443 |
|
✗ |
mTask = std::bind(&Advect::sampleXformed, ph::_1, ph::_2, time0, time1); |
444 |
|
|
} |
445 |
|
✗ |
assert(voxelCount == mParent.mTracker.grid().activeVoxelCount()); |
446 |
|
✗ |
mVelocity = new VectorType[ voxelCount ]; |
447 |
|
✗ |
this->cook("Sampling advection field"); |
448 |
|
|
|
449 |
|
|
// Find the extrema of the magnitude of the velocities |
450 |
|
✗ |
ValueType maxAbsV = 0; |
451 |
|
✗ |
VectorType* v = mVelocity; |
452 |
|
✗ |
for (size_t i = 0; i < voxelCount; ++i, ++v) { |
453 |
|
✗ |
maxAbsV = math::Max(maxAbsV, ValueType(v->lengthSqr())); |
454 |
|
|
} |
455 |
|
|
|
456 |
|
|
// Compute the CFL number |
457 |
|
|
if (math::isApproxZero(maxAbsV, math::Delta<ValueType>::value())) return ValueType(0); |
458 |
|
✗ |
static const ValueType CFL = (TemporalScheme == math::TVD_RK1 ? ValueType(0.3) : |
459 |
|
|
TemporalScheme == math::TVD_RK2 ? ValueType(0.9) : |
460 |
|
|
ValueType(1.0))/math::Sqrt(ValueType(3.0)); |
461 |
|
✗ |
const ValueType dt = math::Abs(time1 - time0), dx = mParent.mTracker.voxelSize(); |
462 |
|
✗ |
return math::Min(dt, ValueType(CFL*dx/math::Sqrt(maxAbsV))); |
463 |
|
|
} |
464 |
|
|
|
465 |
|
|
|
466 |
|
|
template<typename GridT, typename FieldT, typename InterruptT> |
467 |
|
|
template< |
468 |
|
|
typename MapT, |
469 |
|
|
math::BiasedGradientScheme SpatialScheme, |
470 |
|
|
math::TemporalIntegrationScheme TemporalScheme> |
471 |
|
|
template<bool Aligned> |
472 |
|
|
inline void |
473 |
|
✗ |
LevelSetAdvection<GridT, FieldT, InterruptT>:: |
474 |
|
|
Advect<MapT, SpatialScheme, TemporalScheme>:: |
475 |
|
|
sample(const LeafRange& range, ValueType time0, ValueType time1) |
476 |
|
|
{ |
477 |
|
|
const bool isForward = time0 < time1; |
478 |
|
|
using VoxelIterT = typename LeafType::ValueOnCIter; |
479 |
|
✗ |
const MapT& map = *mMap; |
480 |
|
✗ |
const FieldT field( mParent.mField ); |
481 |
|
✗ |
mParent.mTracker.checkInterrupter(); |
482 |
|
✗ |
for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { |
483 |
|
✗ |
VectorType* vel = mVelocity + mOffsets[ leafIter.pos() ]; |
484 |
|
✗ |
for (VoxelIterT iter = leafIter->cbeginValueOn(); iter; ++iter, ++vel) { |
485 |
|
|
OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN |
486 |
|
✗ |
const VectorType v = Aligned ? field(iter.getCoord(), time0) ://resolved at compile time |
487 |
|
✗ |
field(map.applyMap(iter.getCoord().asVec3d()), time0); |
488 |
|
✗ |
*vel = isForward ? v : -v; |
489 |
|
|
OPENVDB_NO_TYPE_CONVERSION_WARNING_END |
490 |
|
|
} |
491 |
|
|
} |
492 |
|
|
} |
493 |
|
|
|
494 |
|
|
|
495 |
|
|
template<typename GridT, typename FieldT, typename InterruptT> |
496 |
|
|
template< |
497 |
|
|
typename MapT, |
498 |
|
|
math::BiasedGradientScheme SpatialScheme, |
499 |
|
|
math::TemporalIntegrationScheme TemporalScheme> |
500 |
|
|
inline void |
501 |
|
✗ |
LevelSetAdvection<GridT, FieldT, InterruptT>:: |
502 |
|
|
Advect<MapT, SpatialScheme, TemporalScheme>:: |
503 |
|
|
clearField() |
504 |
|
|
{ |
505 |
|
✗ |
delete [] mOffsets; |
506 |
|
✗ |
delete [] mVelocity; |
507 |
|
✗ |
mOffsets = nullptr; |
508 |
|
✗ |
mVelocity = nullptr; |
509 |
|
|
} |
510 |
|
|
|
511 |
|
|
|
512 |
|
|
template<typename GridT, typename FieldT, typename InterruptT> |
513 |
|
|
template< |
514 |
|
|
typename MapT, |
515 |
|
|
math::BiasedGradientScheme SpatialScheme, |
516 |
|
|
math::TemporalIntegrationScheme TemporalScheme> |
517 |
|
|
inline void |
518 |
|
✗ |
LevelSetAdvection<GridT, FieldT, InterruptT>:: |
519 |
|
|
Advect<MapT, SpatialScheme, TemporalScheme>:: |
520 |
|
|
cook(const char* msg, size_t swapBuffer) |
521 |
|
|
{ |
522 |
|
✗ |
mParent.mTracker.startInterrupter( msg ); |
523 |
|
|
|
524 |
|
✗ |
const int grainSize = mParent.mTracker.getGrainSize(); |
525 |
|
✗ |
const LeafRange range = mParent.mTracker.leafs().leafRange(grainSize); |
526 |
|
|
|
527 |
|
✗ |
grainSize == 0 ? (*this)(range) : tbb::parallel_for(range, *this); |
528 |
|
|
|
529 |
|
✗ |
mParent.mTracker.leafs().swapLeafBuffer(swapBuffer, grainSize == 0); |
530 |
|
|
|
531 |
|
✗ |
mParent.mTracker.endInterrupter(); |
532 |
|
|
} |
533 |
|
|
|
534 |
|
|
|
535 |
|
|
// Convex combination of Phi and a forward Euler advection steps: |
536 |
|
|
// Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * V.Grad(0)); |
537 |
|
|
template<typename GridT, typename FieldT, typename InterruptT> |
538 |
|
|
template< |
539 |
|
|
typename MapT, |
540 |
|
|
math::BiasedGradientScheme SpatialScheme, |
541 |
|
|
math::TemporalIntegrationScheme TemporalScheme> |
542 |
|
|
template <int Nominator, int Denominator> |
543 |
|
|
inline void |
544 |
|
✗ |
LevelSetAdvection<GridT, FieldT, InterruptT>:: |
545 |
|
|
Advect<MapT, SpatialScheme, TemporalScheme>:: |
546 |
|
|
euler(const LeafRange& range, ValueType dt, Index phiBuffer, Index resultBuffer) |
547 |
|
|
{ |
548 |
|
|
using SchemeT = math::BIAS_SCHEME<SpatialScheme>; |
549 |
|
|
using StencilT = typename SchemeT::template ISStencil<GridType>::StencilType; |
550 |
|
|
using VoxelIterT = typename LeafType::ValueOnCIter; |
551 |
|
|
using GradT = math::GradientBiased<MapT, SpatialScheme>; |
552 |
|
|
|
553 |
|
|
static const ValueType Alpha = ValueType(Nominator)/ValueType(Denominator); |
554 |
|
|
static const ValueType Beta = ValueType(1) - Alpha; |
555 |
|
|
|
556 |
|
✗ |
mParent.mTracker.checkInterrupter(); |
557 |
|
✗ |
const MapT& map = *mMap; |
558 |
|
✗ |
StencilT stencil(mParent.mTracker.grid()); |
559 |
|
✗ |
for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { |
560 |
|
✗ |
const VectorType* vel = mVelocity + mOffsets[ leafIter.pos() ]; |
561 |
|
✗ |
const ValueType* phi = leafIter.buffer(phiBuffer).data(); |
562 |
|
✗ |
ValueType* result = leafIter.buffer(resultBuffer).data(); |
563 |
|
✗ |
for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter, ++vel) { |
564 |
|
|
const Index i = voxelIter.pos(); |
565 |
|
✗ |
stencil.moveTo(voxelIter); |
566 |
|
✗ |
const ValueType a = |
567 |
|
✗ |
stencil.getValue() - dt * vel->dot(GradT::result(map, stencil, *vel)); |
568 |
|
✗ |
result[i] = Nominator ? Alpha * phi[i] + Beta * a : a; |
569 |
|
|
}//loop over active voxels in the leaf of the mask |
570 |
|
|
}//loop over leafs of the level set |
571 |
|
|
} |
572 |
|
|
|
573 |
|
|
|
574 |
|
|
//////////////////////////////////////// |
575 |
|
|
|
576 |
|
|
|
577 |
|
|
// Explicit Template Instantiation |
578 |
|
|
|
579 |
|
|
#ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION |
580 |
|
|
|
581 |
|
|
#ifdef OPENVDB_INSTANTIATE_LEVELSETADVECT |
582 |
|
|
#include <openvdb/util/ExplicitInstantiation.h> |
583 |
|
|
#endif |
584 |
|
|
|
585 |
|
|
OPENVDB_INSTANTIATE_CLASS LevelSetAdvection<FloatGrid, DiscreteField<Vec3SGrid, BoxSampler>, util::NullInterrupter>; |
586 |
|
|
OPENVDB_INSTANTIATE_CLASS LevelSetAdvection<DoubleGrid, DiscreteField<Vec3SGrid, BoxSampler>, util::NullInterrupter>; |
587 |
|
|
|
588 |
|
|
#endif // OPENVDB_USE_EXPLICIT_INSTANTIATION |
589 |
|
|
|
590 |
|
|
|
591 |
|
|
} // namespace tools |
592 |
|
|
} // namespace OPENVDB_VERSION_NAME |
593 |
|
|
} // namespace openvdb |
594 |
|
|
|
595 |
|
|
#endif // OPENVDB_TOOLS_LEVEL_SET_ADVECT_HAS_BEEN_INCLUDED |
596 |
|
|
|