Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | // | ||
4 | /////////////////////////////////////////////////////////////////////////// | ||
5 | // | ||
6 | /// @author Ken Museth | ||
7 | /// | ||
8 | /// @file VelocityFields.h | ||
9 | /// | ||
10 | /// @brief Defines two simple wrapper classes for advection velocity | ||
11 | /// fields as well as VelocitySampler and VelocityIntegrator | ||
12 | /// | ||
13 | /// | ||
14 | /// @details DiscreteField wraps a velocity grid and EnrightField is mostly | ||
15 | /// intended for debugging (it's an analytical divergence free and | ||
16 | /// periodic field). They both share the same API required by the | ||
17 | /// LevelSetAdvection class defined in LevelSetAdvect.h. Thus, any | ||
18 | /// class with this API should work with LevelSetAdvection. | ||
19 | /// | ||
20 | /// @warning Note the Field wrapper classes below always assume the velocity | ||
21 | /// is represented in the world-frame of reference. For DiscreteField | ||
22 | /// this implies the input grid must contain velocities in world | ||
23 | /// coordinates. | ||
24 | |||
25 | #ifndef OPENVDB_TOOLS_VELOCITY_FIELDS_HAS_BEEN_INCLUDED | ||
26 | #define OPENVDB_TOOLS_VELOCITY_FIELDS_HAS_BEEN_INCLUDED | ||
27 | |||
28 | #include <tbb/parallel_reduce.h> | ||
29 | #include <openvdb/Platform.h> | ||
30 | #include <openvdb/openvdb.h> | ||
31 | #include "Interpolation.h" // for Sampler, etc. | ||
32 | #include <openvdb/math/FiniteDifference.h> | ||
33 | |||
34 | namespace openvdb { | ||
35 | OPENVDB_USE_VERSION_NAMESPACE | ||
36 | namespace OPENVDB_VERSION_NAME { | ||
37 | namespace tools { | ||
38 | |||
39 | /// @brief Thin wrapper class for a velocity grid | ||
40 | /// @note Consider replacing BoxSampler with StaggeredBoxSampler | ||
41 | template <typename VelGridT, typename Interpolator = BoxSampler> | ||
42 | ✗ | class DiscreteField | |
43 | { | ||
44 | public: | ||
45 | typedef typename VelGridT::ValueType VectorType; | ||
46 | typedef typename VectorType::ValueType ValueType; | ||
47 | static_assert(std::is_floating_point<ValueType>::value, | ||
48 | "DiscreteField requires a floating point grid."); | ||
49 | |||
50 | DiscreteField(const VelGridT &vel) | ||
51 | : mAccessor(vel.tree()) | ||
52 | , mTransform(&vel.transform()) | ||
53 | { | ||
54 | } | ||
55 | |||
56 | /// @brief Copy constructor | ||
57 | ✗ | DiscreteField(const DiscreteField& other) | |
58 | : mAccessor(other.mAccessor.tree()) | ||
59 | ✗ | , mTransform(other.mTransform) | |
60 | { | ||
61 | } | ||
62 | |||
63 | /// @return const reference to the transform between world and index space | ||
64 | /// @note Use this method to determine if a client grid is | ||
65 | /// aligned with the coordinate space of the velocity grid. | ||
66 | ✗ | const math::Transform& transform() const { return *mTransform; } | |
67 | |||
68 | /// @return the interpolated velocity at the world space position xyz | ||
69 | /// | ||
70 | /// @warning Not threadsafe since it uses a ValueAccessor! So use | ||
71 | /// one instance per thread (which is fine since its lightweight). | ||
72 | ✗ | inline VectorType operator() (const Vec3d& xyz, ValueType/*dummy time*/) const | |
73 | { | ||
74 | ✗ | return Interpolator::sample(mAccessor, mTransform->worldToIndex(xyz)); | |
75 | } | ||
76 | |||
77 | /// @return the velocity at the coordinate space position ijk | ||
78 | /// | ||
79 | /// @warning Not threadsafe since it uses a ValueAccessor! So use | ||
80 | /// one instance per thread (which is fine since its lightweight). | ||
81 | inline VectorType operator() (const Coord& ijk, ValueType/*dummy time*/) const | ||
82 | { | ||
83 | ✗ | return mAccessor.getValue(ijk); | |
84 | } | ||
85 | |||
86 | private: | ||
87 | const typename VelGridT::ConstAccessor mAccessor;//Not thread-safe | ||
88 | const math::Transform* mTransform; | ||
89 | |||
90 | }; // end of DiscreteField | ||
91 | |||
92 | /////////////////////////////////////////////////////////////////////// | ||
93 | |||
94 | /// @brief Analytical, divergence-free and periodic velocity field | ||
95 | /// @note Primarily intended for debugging! | ||
96 | /// @warning This analytical velocity only produce meaningful values | ||
97 | /// in the unit box in world space. In other words make sure any level | ||
98 | /// set surface is fully enclosed in the axis aligned bounding box | ||
99 | /// spanning 0->1 in world units. | ||
100 | template <typename ScalarT = float> | ||
101 | class EnrightField | ||
102 | { | ||
103 | public: | ||
104 | typedef ScalarT ValueType; | ||
105 | typedef math::Vec3<ScalarT> VectorType; | ||
106 | static_assert(std::is_floating_point<ScalarT>::value, | ||
107 | "EnrightField requires a floating point grid."); | ||
108 | |||
109 | EnrightField() {} | ||
110 | |||
111 | /// @return const reference to the identity transform between world and index space | ||
112 | /// @note Use this method to determine if a client grid is | ||
113 | /// aligned with the coordinate space of this velocity field | ||
114 | math::Transform transform() const { return math::Transform(); } | ||
115 | |||
116 | /// @return the velocity in world units, evaluated at the world | ||
117 | /// position xyz and at the specified time | ||
118 | inline VectorType operator() (const Vec3d& xyz, ValueType time) const; | ||
119 | |||
120 | /// @return the velocity at the coordinate space position ijk | ||
121 | inline VectorType operator() (const Coord& ijk, ValueType time) const | ||
122 | { | ||
123 | return (*this)(ijk.asVec3d(), time); | ||
124 | } | ||
125 | }; // end of EnrightField | ||
126 | |||
127 | template <typename ScalarT> | ||
128 | inline math::Vec3<ScalarT> | ||
129 | EnrightField<ScalarT>::operator() (const Vec3d& xyz, ValueType time) const | ||
130 | { | ||
131 | const ScalarT pi = math::pi<ScalarT>(); | ||
132 | const ScalarT phase = pi / ScalarT(3); | ||
133 | const ScalarT Px = pi * ScalarT(xyz[0]), Py = pi * ScalarT(xyz[1]), Pz = pi * ScalarT(xyz[2]); | ||
134 | const ScalarT tr = math::Cos(ScalarT(time) * phase); | ||
135 | const ScalarT a = math::Sin(ScalarT(2)*Py); | ||
136 | const ScalarT b = -math::Sin(ScalarT(2)*Px); | ||
137 | const ScalarT c = math::Sin(ScalarT(2)*Pz); | ||
138 | return math::Vec3<ScalarT>( | ||
139 | tr * ( ScalarT(2) * math::Pow2(math::Sin(Px)) * a * c ), | ||
140 | tr * ( b * math::Pow2(math::Sin(Py)) * c ), | ||
141 | tr * ( b * a * math::Pow2(math::Sin(Pz)) )); | ||
142 | } | ||
143 | |||
144 | |||
145 | /////////////////////////////////////////////////////////////////////// | ||
146 | |||
147 | /// Class to hold a Vec3 field interpreted as a velocity field. | ||
148 | /// Primarily exists to provide a method(s) that integrate a passive | ||
149 | /// point forward in the velocity field for a single time-step (dt) | ||
150 | template<typename GridT = Vec3fGrid, | ||
151 | bool Staggered = false, | ||
152 | size_t Order = 1> | ||
153 | class VelocitySampler | ||
154 | { | ||
155 | public: | ||
156 | typedef typename GridT::ConstAccessor AccessorType; | ||
157 | typedef typename GridT::ValueType ValueType; | ||
158 | |||
159 | /// @brief Constructor from a grid | ||
160 | 1257 | VelocitySampler(const GridT& grid): | |
161 | mGrid(&grid), | ||
162 |
2/48✓ Branch 1 taken 240 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 10 taken 480 times.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
|
1257 | mAcc(grid.getAccessor()) |
163 | { | ||
164 | } | ||
165 | /// @brief Copy-constructor | ||
166 | 2327 | VelocitySampler(const VelocitySampler& other): | |
167 | 2327 | mGrid(other.mGrid), | |
168 | 2327 | mAcc(mGrid->getAccessor()) | |
169 | { | ||
170 | } | ||
171 | /// @brief Samples the velocity at world position onto result. Supports both | ||
172 | /// staggered (i.e. MAC) and collocated velocity grids. | ||
173 | /// | ||
174 | /// @return @c true if any one of the sampled values is active. | ||
175 | /// | ||
176 | /// @warning Not threadsafe since it uses a ValueAccessor! So use | ||
177 | /// one instance per thread (which is fine since its lightweight). | ||
178 | template <typename LocationType> | ||
179 | 202321038 | inline bool sample(const LocationType& world, ValueType& result) const | |
180 | { | ||
181 | 202321038 | const Vec3R xyz = mGrid->worldToIndex(Vec3R(world[0], world[1], world[2])); | |
182 | 202321038 | bool active = Sampler<Order, Staggered>::sample(mAcc, xyz, result); | |
183 | 202321038 | return active; | |
184 | } | ||
185 | |||
186 | /// @brief Samples the velocity at world position onto result. Supports both | ||
187 | /// staggered (i.e. MAC) and co-located velocity grids. | ||
188 | /// | ||
189 | /// @warning Not threadsafe since it uses a ValueAccessor! So use | ||
190 | /// one instance per thread (which is fine since its lightweight). | ||
191 | template <typename LocationType> | ||
192 | inline ValueType sample(const LocationType& world) const | ||
193 | { | ||
194 | const Vec3R xyz = mGrid->worldToIndex(Vec3R(world[0], world[1], world[2])); | ||
195 | return Sampler<Order, Staggered>::sample(mAcc, xyz); | ||
196 | } | ||
197 | |||
198 | private: | ||
199 | // holding the Grids for the transforms | ||
200 | const GridT* mGrid; // Velocity vector field | ||
201 | AccessorType mAcc; | ||
202 | };// end of VelocitySampler class | ||
203 | |||
204 | /////////////////////////////////////////////////////////////////////// | ||
205 | |||
206 | /// @brief Performs Runge-Kutta time integration of variable order in | ||
207 | /// a static velocity field. | ||
208 | /// | ||
209 | /// @note Note that the order of the velocity sampling is controlled | ||
210 | /// with the SampleOrder template parameter, which defaults | ||
211 | /// to one, i.e. a tri-linear interpolation kernel. | ||
212 | template<typename GridT = Vec3fGrid, | ||
213 | bool Staggered = false, | ||
214 | size_t SampleOrder = 1> | ||
215 | ✗ | class VelocityIntegrator | |
216 | { | ||
217 | public: | ||
218 | typedef typename GridT::ValueType VecType; | ||
219 | typedef typename VecType::ValueType ElementType; | ||
220 | |||
221 | VelocityIntegrator(const GridT& velGrid): | ||
222 | mVelSampler(velGrid) | ||
223 | { | ||
224 | } | ||
225 | /// @brief Variable order Runge-Kutta time integration for a single time step | ||
226 | /// | ||
227 | /// @param dt Time sub-step for the Runge-Kutte integrator of order OrderRK | ||
228 | /// @param world Location in world space coordinates (both input and output) | ||
229 | template<size_t OrderRK, typename LocationType> | ||
230 | 30006456 | inline void rungeKutta(const ElementType dt, LocationType& world) const | |
231 | { | ||
232 | BOOST_STATIC_ASSERT(OrderRK <= 4); | ||
233 | 30006456 | VecType P(static_cast<ElementType>(world[0]), | |
234 | static_cast<ElementType>(world[1]), | ||
235 | static_cast<ElementType>(world[2])); | ||
236 | // Note the if-branching below is optimized away at compile time | ||
237 | if (OrderRK == 0) { | ||
238 | return;// do nothing | ||
239 | } else if (OrderRK == 1) { | ||
240 | VecType V0; | ||
241 | 4288427 | mVelSampler.sample(P, V0); | |
242 | 4288427 | P = dt * V0; | |
243 | } else if (OrderRK == 2) { | ||
244 | VecType V0, V1; | ||
245 | 2000008 | mVelSampler.sample(P, V0); | |
246 | 2000008 | mVelSampler.sample(P + ElementType(0.5) * dt * V0, V1); | |
247 | 2000008 | P = dt * V1; | |
248 | } else if (OrderRK == 3) { | ||
249 | VecType V0, V1, V2; | ||
250 | 2000008 | mVelSampler.sample(P, V0); | |
251 | 2000008 | mVelSampler.sample(P + ElementType(0.5) * dt * V0, V1); | |
252 | 2000008 | mVelSampler.sample(P + dt * (ElementType(2.0) * V1 - V0), V2); | |
253 | 2000008 | P = dt * (V0 + ElementType(4.0) * V1 + V2) * ElementType(1.0 / 6.0); | |
254 | } else if (OrderRK == 4) { | ||
255 | VecType V0, V1, V2, V3; | ||
256 | 21718013 | mVelSampler.sample(P, V0); | |
257 | 21718013 | mVelSampler.sample(P + ElementType(0.5) * dt * V0, V1); | |
258 | 21718013 | mVelSampler.sample(P + ElementType(0.5) * dt * V1, V2); | |
259 | 21718013 | mVelSampler.sample(P + dt * V2, V3); | |
260 | 21718013 | P = dt * (V0 + ElementType(2.0) * (V1 + V2) + V3) * ElementType(1.0 / 6.0); | |
261 | } | ||
262 | typedef typename LocationType::ValueType OutType; | ||
263 | 22006440 | world += LocationType(static_cast<OutType>(P[0]), | |
264 | static_cast<OutType>(P[1]), | ||
265 | static_cast<OutType>(P[2])); | ||
266 | } | ||
267 | private: | ||
268 | VelocitySampler<GridT, Staggered, SampleOrder> mVelSampler; | ||
269 | };// end of VelocityIntegrator class | ||
270 | |||
271 | |||
272 | } // namespace tools | ||
273 | } // namespace OPENVDB_VERSION_NAME | ||
274 | } // namespace openvdb | ||
275 | |||
276 | #endif // OPENVDB_TOOLS_VELOCITY_FIELDS_HAS_BEEN_INCLUDED | ||
277 |