1 //============================================================================
2 //  Copyright (c) Kitware, Inc.
3 //  All rights reserved.
4 //  See LICENSE.txt for details.
5 //  This software is distributed WITHOUT ANY WARRANTY; without even
6 //  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
7 //  PURPOSE.  See the above copyright notice for more information.
8 //
9 //  Copyright 2015 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
10 //  Copyright 2015 UT-Battelle, LLC.
11 //  Copyright 2015 Los Alamos National Security.
12 //
13 //  Under the terms of Contract DE-NA0003525 with NTESS,
14 //  the U.S. Government retains certain rights in this software.
15 //
16 //  Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
17 //  Laboratory (LANL), the U.S. Government retains certain rights in
18 //  this software.
19 //============================================================================
20 #ifndef vtk_m_exec_Jacobian_h
21 #define vtk_m_exec_Jacobian_h
22 
23 #include <vtkm/Assert.h>
24 #include <vtkm/CellShape.h>
25 #include <vtkm/Math.h>
26 #include <vtkm/Matrix.h>
27 #include <vtkm/VectorAnalysis.h>
28 
29 namespace vtkm
30 {
31 namespace exec
32 {
33 namespace internal
34 {
35 
36 template <typename T>
37 struct Space2D
38 {
39   using Vec3 = vtkm::Vec<T, 3>;
40   using Vec2 = vtkm::Vec<T, 2>;
41 
42   Vec3 Origin;
43   Vec3 Basis0;
44   Vec3 Basis1;
45 
46   VTKM_EXEC
Space2DSpace2D47   Space2D(const Vec3& origin, const Vec3& pointFirst, const Vec3& pointLast)
48   {
49     this->Origin = origin;
50 
51     this->Basis0 = vtkm::Normal(pointFirst - this->Origin);
52 
53     Vec3 n = vtkm::Cross(this->Basis0, pointLast - this->Origin);
54     this->Basis1 = vtkm::Normal(vtkm::Cross(this->Basis0, n));
55   }
56 
57   VTKM_EXEC
ConvertCoordToSpaceSpace2D58   Vec2 ConvertCoordToSpace(const Vec3 coord) const
59   {
60     Vec3 vec = coord - this->Origin;
61     return Vec2(vtkm::Dot(vec, this->Basis0), vtkm::Dot(vec, this->Basis1));
62   }
63 
64   template <typename U>
ConvertVecFromSpaceSpace2D65   VTKM_EXEC vtkm::Vec<U, 3> ConvertVecFromSpace(const vtkm::Vec<U, 2> vec) const
66   {
67     return vec[0] * this->Basis0 + vec[1] * this->Basis1;
68   }
69 };
70 
71 } //namespace internal
72 
73 #define VTKM_DERIVATIVE_WEIGHTS_HEXAHEDRON(pc, rc, call)                                           \
74   call(0, (-rc[1] * rc[2]), (-rc[0] * rc[2]), (-rc[0] * rc[1]));                                   \
75   call(1, (rc[1] * rc[2]), (-pc[0] * rc[2]), (-pc[0] * rc[1]));                                    \
76   call(2, (pc[1] * rc[2]), (pc[0] * rc[2]), (-pc[0] * pc[1]));                                     \
77   call(3, (-pc[1] * rc[2]), (rc[0] * rc[2]), (-rc[0] * pc[1]));                                    \
78   call(4, (-rc[1] * pc[2]), (-rc[0] * pc[2]), (rc[0] * rc[1]));                                    \
79   call(5, (rc[1] * pc[2]), (-pc[0] * pc[2]), (pc[0] * rc[1]));                                     \
80   call(6, (pc[1] * pc[2]), (pc[0] * pc[2]), (pc[0] * pc[1]));                                      \
81   call(7, (-pc[1] * pc[2]), (rc[0] * pc[2]), (rc[0] * pc[1]))
82 
83 #define VTKM_DERIVATIVE_WEIGHTS_WEDGE(pc, rc, call)                                                \
84   call(0, (-rc[2]), (-rc[2]), (pc[0] - rc[1]));                                                    \
85   call(1, (0.0f), (rc[2]), (-pc[1]));                                                              \
86   call(2, (rc[2]), (0.0f), (-pc[0]));                                                              \
87   call(3, (-pc[2]), (-pc[2]), (rc[0] - pc[1]));                                                    \
88   call(4, (0.0f), (pc[2]), (pc[1]));                                                               \
89   call(5, (pc[2]), (0.0f), (pc[0]))
90 
91 #define VTKM_DERIVATIVE_WEIGHTS_PYRAMID(pc, rc, call)                                              \
92   call(0, (-rc[1] * rc[2]), (-rc[0] * rc[2]), (-rc[0] * rc[1]));                                   \
93   call(1, (rc[1] * rc[2]), (-pc[0] * rc[2]), (-pc[0] * rc[1]));                                    \
94   call(2, (pc[1] * rc[2]), (pc[0] * rc[2]), (-pc[0] * pc[1]));                                     \
95   call(3, (-pc[1] * rc[2]), (rc[0] * rc[2]), (-rc[0] * pc[1]));                                    \
96   call(4, (0.0f), (0.0f), (1.0f))
97 
98 #define VTKM_DERIVATIVE_WEIGHTS_QUAD(pc, rc, call)                                                 \
99   call(0, (-rc[1]), (-rc[0]));                                                                     \
100   call(1, (rc[1]), (-pc[0]));                                                                      \
101   call(2, (pc[1]), (pc[0]));                                                                       \
102   call(3, (-pc[1]), (rc[0]))
103 
104 //-----------------------------------------------------------------------------
105 // This returns the Jacobian of a hexahedron's (or other 3D cell's) coordinates
106 // with respect to parametric coordinates. Explicitly, this is (d is partial
107 // derivative):
108 //
109 //   |                     |
110 //   | dx/du  dx/dv  dx/dw |
111 //   |                     |
112 //   | dy/du  dy/dv  dy/dw |
113 //   |                     |
114 //   | dz/du  dz/dv  dz/dw |
115 //   |                     |
116 //
117 
118 #define VTKM_ACCUM_JACOBIAN_3D(pointIndex, weight0, weight1, weight2)                              \
119   jacobian(0, 0) += static_cast<JacobianType>(wCoords[pointIndex][0] * (weight0));                 \
120   jacobian(1, 0) += static_cast<JacobianType>(wCoords[pointIndex][1] * (weight0));                 \
121   jacobian(2, 0) += static_cast<JacobianType>(wCoords[pointIndex][2] * (weight0));                 \
122   jacobian(0, 1) += static_cast<JacobianType>(wCoords[pointIndex][0] * (weight1));                 \
123   jacobian(1, 1) += static_cast<JacobianType>(wCoords[pointIndex][1] * (weight1));                 \
124   jacobian(2, 1) += static_cast<JacobianType>(wCoords[pointIndex][2] * (weight1));                 \
125   jacobian(0, 2) += static_cast<JacobianType>(wCoords[pointIndex][0] * (weight2));                 \
126   jacobian(1, 2) += static_cast<JacobianType>(wCoords[pointIndex][1] * (weight2));                 \
127   jacobian(2, 2) += static_cast<JacobianType>(wCoords[pointIndex][2] * (weight2))
128 
129 template <typename WorldCoordType, typename ParametricCoordType, typename JacobianType>
JacobianFor3DCell(const WorldCoordType & wCoords,const vtkm::Vec<ParametricCoordType,3> & pcoords,vtkm::Matrix<JacobianType,3,3> & jacobian,vtkm::CellShapeTagHexahedron)130 VTKM_EXEC void JacobianFor3DCell(const WorldCoordType& wCoords,
131                                  const vtkm::Vec<ParametricCoordType, 3>& pcoords,
132                                  vtkm::Matrix<JacobianType, 3, 3>& jacobian,
133                                  vtkm::CellShapeTagHexahedron)
134 {
135   vtkm::Vec<JacobianType, 3> pc(pcoords);
136   vtkm::Vec<JacobianType, 3> rc = vtkm::Vec<JacobianType, 3>(JacobianType(1)) - pc;
137 
138   jacobian = vtkm::Matrix<JacobianType, 3, 3>(JacobianType(0));
139   VTKM_DERIVATIVE_WEIGHTS_HEXAHEDRON(pc, rc, VTKM_ACCUM_JACOBIAN_3D);
140 }
141 
142 template <typename WorldCoordType, typename ParametricCoordType, typename JacobianType>
JacobianFor3DCell(const WorldCoordType & wCoords,const vtkm::Vec<ParametricCoordType,3> & pcoords,vtkm::Matrix<JacobianType,3,3> & jacobian,vtkm::CellShapeTagWedge)143 VTKM_EXEC void JacobianFor3DCell(const WorldCoordType& wCoords,
144                                  const vtkm::Vec<ParametricCoordType, 3>& pcoords,
145                                  vtkm::Matrix<JacobianType, 3, 3>& jacobian,
146                                  vtkm::CellShapeTagWedge)
147 {
148   vtkm::Vec<JacobianType, 3> pc(pcoords);
149   vtkm::Vec<JacobianType, 3> rc = vtkm::Vec<JacobianType, 3>(JacobianType(1)) - pc;
150 
151   jacobian = vtkm::Matrix<JacobianType, 3, 3>(JacobianType(0));
152   VTKM_DERIVATIVE_WEIGHTS_WEDGE(pc, rc, VTKM_ACCUM_JACOBIAN_3D);
153 }
154 
155 template <typename WorldCoordType, typename ParametricCoordType, typename JacobianType>
JacobianFor3DCell(const WorldCoordType & wCoords,const vtkm::Vec<ParametricCoordType,3> & pcoords,vtkm::Matrix<JacobianType,3,3> & jacobian,vtkm::CellShapeTagPyramid)156 VTKM_EXEC void JacobianFor3DCell(const WorldCoordType& wCoords,
157                                  const vtkm::Vec<ParametricCoordType, 3>& pcoords,
158                                  vtkm::Matrix<JacobianType, 3, 3>& jacobian,
159                                  vtkm::CellShapeTagPyramid)
160 {
161   vtkm::Vec<JacobianType, 3> pc(pcoords);
162   vtkm::Vec<JacobianType, 3> rc = vtkm::Vec<JacobianType, 3>(JacobianType(1)) - pc;
163 
164   jacobian = vtkm::Matrix<JacobianType, 3, 3>(JacobianType(0));
165   VTKM_DERIVATIVE_WEIGHTS_PYRAMID(pc, rc, VTKM_ACCUM_JACOBIAN_3D);
166 }
167 
168 // Derivatives in quadrilaterals are computed in much the same way as
169 // hexahedra.  Review the documentation for hexahedra derivatives for details
170 // on the math.  The major difference is that the equations are performed in
171 // a 2D space built with make_SpaceForQuadrilateral.
172 
173 #define VTKM_ACCUM_JACOBIAN_2D(pointIndex, weight0, weight1)                                       \
174   wcoords2d = space.ConvertCoordToSpace(wCoords[pointIndex]);                                      \
175   jacobian(0, 0) += wcoords2d[0] * (weight0);                                                      \
176   jacobian(1, 0) += wcoords2d[1] * (weight0);                                                      \
177   jacobian(0, 1) += wcoords2d[0] * (weight1);                                                      \
178   jacobian(1, 1) += wcoords2d[1] * (weight1)
179 
180 template <typename WorldCoordType,
181           typename ParametricCoordType,
182           typename SpaceType,
183           typename JacobianType>
JacobianFor2DCell(const WorldCoordType & wCoords,const vtkm::Vec<ParametricCoordType,3> & pcoords,const vtkm::exec::internal::Space2D<SpaceType> & space,vtkm::Matrix<JacobianType,2,2> & jacobian,vtkm::CellShapeTagQuad)184 VTKM_EXEC void JacobianFor2DCell(const WorldCoordType& wCoords,
185                                  const vtkm::Vec<ParametricCoordType, 3>& pcoords,
186                                  const vtkm::exec::internal::Space2D<SpaceType>& space,
187                                  vtkm::Matrix<JacobianType, 2, 2>& jacobian,
188                                  vtkm::CellShapeTagQuad)
189 {
190   vtkm::Vec<JacobianType, 2> pc(static_cast<JacobianType>(pcoords[0]),
191                                 static_cast<JacobianType>(pcoords[1]));
192   vtkm::Vec<JacobianType, 2> rc = vtkm::Vec<JacobianType, 2>(JacobianType(1)) - pc;
193 
194   vtkm::Vec<SpaceType, 2> wcoords2d;
195   jacobian = vtkm::Matrix<JacobianType, 2, 2>(JacobianType(0));
196   VTKM_DERIVATIVE_WEIGHTS_QUAD(pc, rc, VTKM_ACCUM_JACOBIAN_2D);
197 }
198 
199 #if 0
200 // This code doesn't work, so I'm bailing on it. Instead, I'm just grabbing a
201 // triangle and finding the derivative of that. If you can do better, please
202 // implement it.
203 template<typename WorldCoordType,
204          typename ParametricCoordType,
205          typename JacobianType>
206 VTKM_EXEC
207 void JacobianFor2DCell(const WorldCoordType &wCoords,
208                        const vtkm::Vec<ParametricCoordType,3> &pcoords,
209                        const vtkm::exec::internal::Space2D<JacobianType> &space,
210                        vtkm::Matrix<JacobianType,2,2> &jacobian,
211                        vtkm::CellShapeTagPolygon)
212 {
213   const vtkm::IdComponent numPoints = wCoords.GetNumberOfComponents();
214   vtkm::Vec<JacobianType,2> pc(pcoords[0], pcoords[1]);
215   JacobianType deltaAngle = 2*vtkm::Pi<JacobianType>()/numPoints;
216 
217   jacobian = vtkm::Matrix<JacobianType,2,2>(0);
218   for (vtkm::IdComponent pointIndex = 0; pointIndex < numPoints; pointIndex++)
219   {
220     JacobianType angle = pointIndex*deltaAngle;
221     vtkm::Vec<JacobianType,2> nodePCoords(0.5f*(vtkm::Cos(angle)+1),
222                                           0.5f*(vtkm::Sin(angle)+1));
223 
224     // This is the vector pointing from the user provided parametric coordinate
225     // to the node at pointIndex in parametric space.
226     vtkm::Vec<JacobianType,2> pvec = nodePCoords - pc;
227 
228     // The weight (the derivative of the interpolation factor) happens to be
229     // pvec scaled by the cube root of pvec's magnitude.
230     JacobianType magSqr = vtkm::MagnitudeSquared(pvec);
231     JacobianType invMag = vtkm::RSqrt(magSqr);
232     JacobianType scale = invMag*invMag*invMag;
233     vtkm::Vec<JacobianType,2> weight = scale*pvec;
234 
235     vtkm::Vec<JacobianType,2> wcoords2d =
236         space.ConvertCoordToSpace(wCoords[pointIndex]);
237     jacobian(0,0) += wcoords2d[0] * weight[0];
238     jacobian(1,0) += wcoords2d[1] * weight[0];
239     jacobian(0,1) += wcoords2d[0] * weight[1];
240     jacobian(1,1) += wcoords2d[1] * weight[1];
241   }
242 }
243 #endif
244 
245 #undef VTKM_ACCUM_JACOBIAN_3D
246 #undef VTKM_ACCUM_JACOBIAN_2D
247 }
248 } // namespace vtkm::exec
249 #endif //vtk_m_exec_Jacobian_h
250