1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 Copyright (c) 2011-2021 The plumed team
3 (see the PEOPLE file at the root of the distribution for a list of names)
4
5 See http://www.plumed.org for more information.
6
7 This file is part of plumed, version 2.
8
9 plumed is free software: you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 plumed is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with plumed. If not, see <http://www.gnu.org/licenses/>.
21 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 #ifndef __PLUMED_tools_Tensor_h
23 #define __PLUMED_tools_Tensor_h
24
25 #include "MatrixSquareBracketsAccess.h"
26 #include "Vector.h"
27 #include "LoopUnroller.h"
28 #include "Exception.h"
29
30 #include <array>
31
32 namespace PLMD {
33
34 /// Small class to contain local utilities.
35 /// Should not be used outside of the TensorGeneric class.
36 class TensorGenericAux {
37 public:
38 // local redefinition, just to avoid including lapack.h here
39 static void local_dsyevr(const char *jobz, const char *range, const char *uplo, int *n,
40 double *a, int *lda, double *vl, double *vu, int *
41 il, int *iu, double *abstol, int *m, double *w,
42 double *z__, int *ldz, int *isuppz, double *work,
43 int *lwork, int *iwork, int *liwork, int *info);
44 };
45
46 /**
47 \ingroup TOOLBOX
48 Class implementing fixed size matrices of doubles
49
50 \tparam n The number rows
51 \tparam m The number columns
52
53 This class implements a matrix of doubles with size fixed at
54 compile time. It is useful for small fixed size objects (e.g.
55 3x3 tensors) as it does not waste space to store the vector size.
56 Moreover, as the compiler knows the size, it can be completely
57 opimized inline.
58 Most of the loops are explicitly unrolled using PLMD::LoopUnroller class
59 Matrix elements are initialized to zero by default. Notice that
60 this means that constructor is a bit slow. This point might change
61 in future if we find performance issues.
62 It takes advantage of MatrixSquareBracketsAccess to provide both
63 () and [] syntax for access.
64 Several functions are declared as friends even if not necessary so as to
65 properly appear in Doxygen documentation.
66
67 Aliases are defined to simplify common declarations (Tensor, Tensor2d, Tensor3d, Tensor4d).
68 Also notice that some operations are only available for 3x3 tensors.
69
70 Example of usage
71 \verbatim
72
73 #include "Tensor.h"
74
75 using namespace PLMD;
76
77 int main(){
78 Tensor a;
79 TensorGeneric<3,2> b;
80 TensorGeneric<3,2> c=matmul(a,b);
81 return 0;
82 }
83
84 \endverbatim
85 */
86 template <unsigned n,unsigned m>
87 class TensorGeneric:
88 public MatrixSquareBracketsAccess<TensorGeneric<n,m>,double>
89 {
90 std::array<double,n*m> d;
91 /// Auxiliary private function for constructor
92 void auxiliaryConstructor();
93 /// Auxiliary private function for constructor
94 template<typename... Args>
95 void auxiliaryConstructor(double first,Args... arg);
96 public:
97 /// Constructor accepting n*m double parameters.
98 /// Can be used as Tensor<2,2>(1.0,2.0,3.0,4.0)
99 /// In case a wrong number of parameters is given, a static assertion will fail.
100 template<typename... Args>
101 TensorGeneric(double first,Args... arg);
102 /// initialize the tensor to zero
103 TensorGeneric();
104 /// initialize a tensor as an external product of two Vector
105 TensorGeneric(const VectorGeneric<n>&v1,const VectorGeneric<m>&v2);
106 /// set it to zero
107 void zero();
108 /// access element
109 double & operator() (unsigned i,unsigned j);
110 /// access element
111 const double & operator() (unsigned i,unsigned j)const;
112 /// increment
113 TensorGeneric& operator +=(const TensorGeneric<n,m>& b);
114 /// decrement
115 TensorGeneric& operator -=(const TensorGeneric<n,m>& b);
116 /// multiply
117 TensorGeneric& operator *=(double);
118 /// divide
119 TensorGeneric& operator /=(double);
120 /// return +t
121 TensorGeneric operator +()const;
122 /// return -t
123 TensorGeneric operator -()const;
124 /// set j-th column
125 TensorGeneric& setCol(unsigned j,const VectorGeneric<n> & c);
126 /// set i-th row
127 TensorGeneric& setRow(unsigned i,const VectorGeneric<m> & r);
128 /// get j-th column
129 VectorGeneric<n> getCol(unsigned j)const;
130 /// get i-th row
131 VectorGeneric<m> getRow(unsigned i)const;
132 /// return t1+t2
133 template<unsigned n_,unsigned m_>
134 friend TensorGeneric<n_,m_> operator+(const TensorGeneric<n_,m_>&,const TensorGeneric<n_,m_>&);
135 /// return t1+t2
136 template<unsigned n_,unsigned m_>
137 friend TensorGeneric<n_,m_> operator-(const TensorGeneric<n_,m_>&,const TensorGeneric<n_,m_>&);
138 /// scale the tensor by a factor s
139 template<unsigned n_,unsigned m_>
140 friend TensorGeneric<n_,m_> operator*(double,const TensorGeneric<n_,m_>&);
141 /// scale the tensor by a factor s
142 template<unsigned n_,unsigned m_>
143 friend TensorGeneric<n_,m_> operator*(const TensorGeneric<n_,m_>&,double s);
144 /// scale the tensor by a factor 1/s
145 template<unsigned n_,unsigned m_>
146 friend TensorGeneric<n_,m_> operator/(const TensorGeneric<n_,m_>&,double s);
147 /// returns the determinant
148 double determinant()const;
149 /// return an identity tensor
150 static TensorGeneric<n,n> identity();
151 /// return the matrix inverse
152 TensorGeneric inverse()const;
153 /// return the transpose matrix
154 TensorGeneric<m,n> transpose()const;
155 /// matrix-matrix multiplication
156 template<unsigned n_,unsigned m_,unsigned l_>
157 friend TensorGeneric<n_,l_> matmul(const TensorGeneric<n_,m_>&,const TensorGeneric<m_,l_>&);
158 /// matrix-vector multiplication
159 template<unsigned n_,unsigned m_>
160 friend VectorGeneric<n_> matmul(const TensorGeneric<n_,m_>&,const VectorGeneric<m_>&);
161 /// vector-matrix multiplication
162 template<unsigned n_,unsigned m_>
163 friend VectorGeneric<n_> matmul(const VectorGeneric<m_>&,const TensorGeneric<m_,n_>&);
164 /// vector-vector multiplication (maps to dotProduct)
165 template<unsigned n_>
166 friend double matmul(const VectorGeneric<n_>&,const VectorGeneric<n_>&);
167 /// matrix-matrix-matrix multiplication
168 template<unsigned n_,unsigned m_,unsigned l_,unsigned i_>
169 friend TensorGeneric<n_,i_> matmul(const TensorGeneric<n_,m_>&,const TensorGeneric<m_,l_>&,const TensorGeneric<l_,i_>&);
170 /// matrix-matrix-vector multiplication
171 template<unsigned n_,unsigned m_,unsigned l_>
172 friend VectorGeneric<n_> matmul(const TensorGeneric<n_,m_>&,const TensorGeneric<m_,l_>&,const VectorGeneric<l_>&);
173 /// vector-matrix-matrix multiplication
174 template<unsigned n_,unsigned m_,unsigned l_>
175 friend VectorGeneric<l_> matmul(const VectorGeneric<n_>&,const TensorGeneric<n_,m_>&,const TensorGeneric<m_,l_>&);
176 /// vector-matrix-vector multiplication
177 template<unsigned n_,unsigned m_>
178 friend double matmul(const VectorGeneric<n_>&,const TensorGeneric<n_,m_>&,const VectorGeneric<m_>&);
179 /// returns the determinant of a tensor
180 friend double determinant(const TensorGeneric<3,3>&);
181 /// returns the inverse of a tensor (same as inverse())
182 friend TensorGeneric<3,3> inverse(const TensorGeneric<3,3>&);
183 /// returns the transpose of a tensor (same as transpose())
184 template<unsigned n_,unsigned m_>
185 friend TensorGeneric<n_,m_> transpose(const TensorGeneric<m_,n_>&);
186 /// returns the transpose of a tensor (same as TensorGeneric(const VectorGeneric&,const VectorGeneric&))
187 template<unsigned n_,unsigned m_>
188 friend TensorGeneric<n_,m_> extProduct(const VectorGeneric<n>&,const VectorGeneric<m>&);
189 friend TensorGeneric<3,3> dcrossDv1(const VectorGeneric<3>&,const VectorGeneric<3>&);
190 friend TensorGeneric<3,3> dcrossDv2(const VectorGeneric<3>&,const VectorGeneric<3>&);
191 friend TensorGeneric<3,3> VcrossTensor(const VectorGeneric<3>&,const TensorGeneric<3,3>&);
192 friend TensorGeneric<3,3> VcrossTensor(const TensorGeneric<3,3>&,const VectorGeneric<3>&);
193 /// Derivative of a normalized vector
194 friend TensorGeneric<3,3> deriNorm(const VectorGeneric<3>&,const TensorGeneric<3,3>&);
195 /// << operator.
196 /// Allows printing tensor `t` with `std::cout<<t;`
197 template<unsigned n_,unsigned m_>
198 friend std::ostream & operator<<(std::ostream &os, const TensorGeneric<n_,m_>&);
199 /// Diagonalize tensor.
200 /// Syntax is the same as Matrix::diagMat.
201 /// In addition, it is possible to call if with m_ smaller than n_. In this case,
202 /// only the first (smaller) m_ eigenvalues and eigenvectors are retrieved.
203 /// If case lapack fails (info!=0) it throws an exception.
204 /// Notice that tensor is assumed to be symmetric!!!
205 template<unsigned n_,unsigned m_>
206 friend void diagMatSym(const TensorGeneric<n_,n_>&,VectorGeneric<m_>&evals,TensorGeneric<m_,n_>&evec);
207 };
208
209 template <unsigned n,unsigned m>
auxiliaryConstructor()210 void TensorGeneric<n,m>::auxiliaryConstructor()
211 {}
212
213 template <unsigned n,unsigned m>
214 template<typename... Args>
auxiliaryConstructor(double first,Args...arg)215 void TensorGeneric<n,m>::auxiliaryConstructor(double first,Args... arg)
216 {
217 d[n*m-(sizeof...(Args))-1]=first;
218 auxiliaryConstructor(arg...);
219 }
220
221 template <unsigned n,unsigned m>
222 template<typename... Args>
TensorGeneric(double first,Args...arg)223 TensorGeneric<n,m>::TensorGeneric(double first,Args... arg)
224 {
225 static_assert((sizeof...(Args))+1==n*m,"you are trying to initialize a Tensor with the wrong number of arguments");
226 auxiliaryConstructor(first,arg...);
227 }
228
229 template<unsigned n,unsigned m>
TensorGeneric()230 TensorGeneric<n,m>::TensorGeneric() {
231 LoopUnroller<n*m>::_zero(d.data());
232 }
233
234 template<unsigned n,unsigned m>
TensorGeneric(const VectorGeneric<n> & v1,const VectorGeneric<m> & v2)235 TensorGeneric<n,m>::TensorGeneric(const VectorGeneric<n>&v1,const VectorGeneric<m>&v2) {
236 for(unsigned i=0; i<n; i++)for(unsigned j=0; j<m; j++)d[i*m+j]=v1[i]*v2[j];
237 }
238
239 template<unsigned n,unsigned m>
zero()240 void TensorGeneric<n,m>::zero() {
241 LoopUnroller<n*m>::_zero(d.data());
242 }
243
244 template<unsigned n,unsigned m>
operator()245 double & TensorGeneric<n,m>::operator() (unsigned i,unsigned j) {
246 #ifdef _GLIBCXX_DEBUG
247 // index i is implicitly checked by the std::array class
248 plumed_assert(j<m);
249 #endif
250 return d[m*i+j];
251 }
252
253 template<unsigned n,unsigned m>
operator()254 const double & TensorGeneric<n,m>::operator() (unsigned i,unsigned j)const {
255 #ifdef _GLIBCXX_DEBUG
256 // index i is implicitly checked by the std::array class
257 plumed_assert(j<m);
258 #endif
259 return d[m*i+j];
260 }
261
262 template<unsigned n,unsigned m>
263 TensorGeneric<n,m>& TensorGeneric<n,m>::operator +=(const TensorGeneric<n,m>& b) {
264 LoopUnroller<n*m>::_add(d.data(),b.d.data());
265 return *this;
266 }
267
268 template<unsigned n,unsigned m>
269 TensorGeneric<n,m>& TensorGeneric<n,m>::operator -=(const TensorGeneric<n,m>& b) {
270 LoopUnroller<n*m>::_sub(d.data(),b.d.data());
271 return *this;
272 }
273
274 template<unsigned n,unsigned m>
275 TensorGeneric<n,m>& TensorGeneric<n,m>::operator *=(double s) {
276 LoopUnroller<n*m>::_mul(d.data(),s);
277 return *this;
278 }
279
280 template<unsigned n,unsigned m>
281 TensorGeneric<n,m>& TensorGeneric<n,m>::operator /=(double s) {
282 LoopUnroller<n*m>::_mul(d.data(),1.0/s);
283 return *this;
284 }
285
286 template<unsigned n,unsigned m>
287 TensorGeneric<n,m> TensorGeneric<n,m>::operator+()const {
288 return *this;
289 }
290
291 template<unsigned n,unsigned m>
292 TensorGeneric<n,m> TensorGeneric<n,m>::operator-()const {
293 TensorGeneric<n,m> r;
294 LoopUnroller<n*m>::_neg(r.d.data(),d.data());
295 return r;
296 }
297
298 template<unsigned n,unsigned m>
setCol(unsigned j,const VectorGeneric<n> & c)299 TensorGeneric<n,m>& TensorGeneric<n,m>::setCol(unsigned j,const VectorGeneric<n> & c) {
300 for(unsigned i=0; i<n; ++i) (*this)(i,j)=c(i);
301 return *this;
302 }
303
304 template<unsigned n,unsigned m>
setRow(unsigned i,const VectorGeneric<m> & r)305 TensorGeneric<n,m>& TensorGeneric<n,m>::setRow(unsigned i,const VectorGeneric<m> & r) {
306 for(unsigned j=0; j<m; ++j) (*this)(i,j)=r(j);
307 return *this;
308 }
309
310 template<unsigned n,unsigned m>
getCol(unsigned j)311 VectorGeneric<n> TensorGeneric<n,m>::getCol(unsigned j)const {
312 VectorGeneric<n> v;
313 for(unsigned i=0; i<n; ++i) v(i)=(*this)(i,j);
314 return v;
315 }
316
317 template<unsigned n,unsigned m>
getRow(unsigned i)318 VectorGeneric<m> TensorGeneric<n,m>::getRow(unsigned i)const {
319 VectorGeneric<m> v;
320 for(unsigned j=0; j<m; ++j) v(j)=(*this)(i,j);
321 return v;
322 }
323
324 template<unsigned n,unsigned m>
325 TensorGeneric<n,m> operator+(const TensorGeneric<n,m>&t1,const TensorGeneric<n,m>&t2) {
326 TensorGeneric<n,m> t(t1);
327 t+=t2;
328 return t;
329 }
330
331 template<unsigned n,unsigned m>
332 TensorGeneric<n,m> operator-(const TensorGeneric<n,m>&t1,const TensorGeneric<n,m>&t2) {
333 TensorGeneric<n,m> t(t1);
334 t-=t2;
335 return t;
336 }
337
338 template<unsigned n,unsigned m>
339 TensorGeneric<n,m> operator*(const TensorGeneric<n,m>&t1,double s) {
340 TensorGeneric<n,m> t(t1);
341 t*=s;
342 return t;
343 }
344
345 template<unsigned n,unsigned m>
346 TensorGeneric<n,m> operator*(double s,const TensorGeneric<n,m>&t1) {
347 return t1*s;
348 }
349
350 template<unsigned n,unsigned m>
351 TensorGeneric<n,m> operator/(const TensorGeneric<n,m>&t1,double s) {
352 return t1*(1.0/s);
353 }
354
355 template<>
356 inline
determinant()357 double TensorGeneric<3,3>::determinant()const {
358 return
359 d[0]*d[4]*d[8]
360 + d[1]*d[5]*d[6]
361 + d[2]*d[3]*d[7]
362 - d[0]*d[5]*d[7]
363 - d[1]*d[3]*d[8]
364 - d[2]*d[4]*d[6];
365 }
366
367 template<unsigned n,unsigned m>
368 inline
identity()369 TensorGeneric<n,n> TensorGeneric<n,m>::identity() {
370 TensorGeneric<n,n> t;
371 for(unsigned i=0; i<n; i++) t(i,i)=1.0;
372 return t;
373 }
374
375 template<unsigned n,unsigned m>
transpose()376 TensorGeneric<m,n> TensorGeneric<n,m>::transpose()const {
377 TensorGeneric<m,n> t;
378 for(unsigned i=0; i<m; i++)for(unsigned j=0; j<n; j++) t(i,j)=(*this)(j,i);
379 return t;
380 }
381
382 template<>
383 inline
inverse()384 TensorGeneric<3,3> TensorGeneric<3,3>::inverse()const {
385 TensorGeneric t;
386 double invdet=1.0/determinant();
387 for(unsigned i=0; i<3; i++) for(unsigned j=0; j<3; j++)
388 t(j,i)=invdet*( (*this)((i+1)%3,(j+1)%3)*(*this)((i+2)%3,(j+2)%3)
389 -(*this)((i+1)%3,(j+2)%3)*(*this)((i+2)%3,(j+1)%3));
390 return t;
391 }
392
393 template<unsigned n,unsigned m,unsigned l>
matmul(const TensorGeneric<n,m> & a,const TensorGeneric<m,l> & b)394 TensorGeneric<n,l> matmul(const TensorGeneric<n,m>&a,const TensorGeneric<m,l>&b) {
395 TensorGeneric<n,l> t;
396 for(unsigned i=0; i<n; i++) for(unsigned j=0; j<l; j++) for(unsigned k=0; k<m; k++) {
397 t(i,j)+=a(i,k)*b(k,j);
398 }
399 return t;
400 }
401
402 template<unsigned n,unsigned m>
matmul(const TensorGeneric<n,m> & a,const VectorGeneric<m> & b)403 VectorGeneric<n> matmul(const TensorGeneric<n,m>&a,const VectorGeneric<m>&b) {
404 VectorGeneric<n> t;
405 for(unsigned i=0; i<n; i++) for(unsigned j=0; j<m; j++) t(i)+=a(i,j)*b(j);
406 return t;
407 }
408
409 template<unsigned n,unsigned m>
matmul(const VectorGeneric<m> & a,const TensorGeneric<m,n> & b)410 VectorGeneric<n> matmul(const VectorGeneric<m>&a,const TensorGeneric<m,n>&b) {
411 VectorGeneric<n> t;
412 for(unsigned i=0; i<n; i++) for(unsigned j=0; j<m; j++) t(i)+=a(j)*b(j,i);
413 return t;
414 }
415
416 template<unsigned n_>
matmul(const VectorGeneric<n_> & a,const VectorGeneric<n_> & b)417 double matmul(const VectorGeneric<n_>&a,const VectorGeneric<n_>&b) {
418 return dotProduct(a,b);
419 }
420
421 template<unsigned n,unsigned m,unsigned l,unsigned i>
matmul(const TensorGeneric<n,m> & a,const TensorGeneric<m,l> & b,const TensorGeneric<l,i> & c)422 TensorGeneric<n,i> matmul(const TensorGeneric<n,m>&a,const TensorGeneric<m,l>&b,const TensorGeneric<l,i>&c) {
423 return matmul(matmul(a,b),c);
424 }
425
426 template<unsigned n,unsigned m,unsigned l>
matmul(const TensorGeneric<n,m> & a,const TensorGeneric<m,l> & b,const VectorGeneric<l> & c)427 VectorGeneric<n> matmul(const TensorGeneric<n,m>&a,const TensorGeneric<m,l>&b,const VectorGeneric<l>&c) {
428 return matmul(matmul(a,b),c);
429 }
430
431 template<unsigned n,unsigned m,unsigned l>
matmul(const VectorGeneric<n> & a,const TensorGeneric<n,m> & b,const TensorGeneric<m,l> & c)432 VectorGeneric<l> matmul(const VectorGeneric<n>&a,const TensorGeneric<n,m>&b,const TensorGeneric<m,l>&c) {
433 return matmul(matmul(a,b),c);
434 }
435
436 template<unsigned n,unsigned m>
matmul(const VectorGeneric<n> & a,const TensorGeneric<n,m> & b,const VectorGeneric<m> & c)437 double matmul(const VectorGeneric<n>&a,const TensorGeneric<n,m>&b,const VectorGeneric<m>&c) {
438 return matmul(matmul(a,b),c);
439 }
440
441 inline
determinant(const TensorGeneric<3,3> & t)442 double determinant(const TensorGeneric<3,3>&t) {
443 return t.determinant();
444 }
445
446 inline
inverse(const TensorGeneric<3,3> & t)447 TensorGeneric<3,3> inverse(const TensorGeneric<3,3>&t) {
448 return t.inverse();
449 }
450
451 template<unsigned n,unsigned m>
transpose(const TensorGeneric<m,n> & t)452 TensorGeneric<n,m> transpose(const TensorGeneric<m,n>&t) {
453 return t.transpose();
454 }
455
456 template<unsigned n,unsigned m>
extProduct(const VectorGeneric<n> & v1,const VectorGeneric<m> & v2)457 TensorGeneric<n,m> extProduct(const VectorGeneric<n>&v1,const VectorGeneric<m>&v2) {
458 return TensorGeneric<n,m>(v1,v2);
459 }
460
461 inline
dcrossDv1(const VectorGeneric<3> & v1,const VectorGeneric<3> & v2)462 TensorGeneric<3,3> dcrossDv1(const VectorGeneric<3>&v1,const VectorGeneric<3>&v2) {
463 (void) v1; // this is to avoid warnings. still the syntax of this function is a bit dummy...
464 return TensorGeneric<3,3>(
465 0.0, v2[2],-v2[1],
466 -v2[2], 0.0, v2[0],
467 v2[1],-v2[0], 0.0);
468 }
469
470 inline
dcrossDv2(const VectorGeneric<3> & v1,const VectorGeneric<3> & v2)471 TensorGeneric<3,3> dcrossDv2(const VectorGeneric<3>&v1,const VectorGeneric<3>&v2) {
472 (void) v2; // this is to avoid warnings. still the syntax of this function is a bit dummy...
473 return TensorGeneric<3,3>(
474 0.0,-v1[2],v1[1],
475 v1[2],0.0,-v1[0],
476 -v1[1],v1[0],0.0);
477 }
478
479 template<unsigned n,unsigned m>
480 std::ostream & operator<<(std::ostream &os, const TensorGeneric<n,m>& t) {
481 for(unsigned i=0; i<n; i++)for(unsigned j=0; j<m; j++) {
482 if(i!=(n-1) || j!=(m-1)) os<<t(i,j)<<" ";
483 else os<<t(i,j);
484 }
485 return os;
486 }
487
488 /// \ingroup TOOLBOX
489 typedef TensorGeneric<1,1> Tensor1d;
490 /// \ingroup TOOLBOX
491 typedef TensorGeneric<2,2> Tensor2d;
492 /// \ingroup TOOLBOX
493 typedef TensorGeneric<3,3> Tensor3d;
494 /// \ingroup TOOLBOX
495 typedef TensorGeneric<4,4> Tensor4d;
496 /// \ingroup TOOLBOX
497 typedef TensorGeneric<5,5> Tensor5d;
498 /// \ingroup TOOLBOX
499 typedef Tensor3d Tensor;
500
501 inline
VcrossTensor(const VectorGeneric<3> & v1,const TensorGeneric<3,3> & v2)502 TensorGeneric<3,3> VcrossTensor(const VectorGeneric<3>&v1,const TensorGeneric<3,3>&v2) {
503
504 TensorGeneric<3,3> t;
505 for(unsigned i=0; i<3; i++) {
506 t.setRow(i,matmul(dcrossDv2(v1,v1),v2.getRow(i)));
507 }
508 return t;
509 }
510
511 inline
VcrossTensor(const TensorGeneric<3,3> & v2,const VectorGeneric<3> & v1)512 TensorGeneric<3,3> VcrossTensor(const TensorGeneric<3,3>&v2,const VectorGeneric<3>&v1) {
513 TensorGeneric<3,3> t;
514 for(unsigned i=0; i<3; i++) {
515 t.setRow(i,-matmul(dcrossDv2(v1,v1),v2.getRow(i)));
516 }
517 return t;
518 }
519
520
521 inline
deriNorm(const VectorGeneric<3> & v1,const TensorGeneric<3,3> & v2)522 TensorGeneric<3,3> deriNorm(const VectorGeneric<3>&v1,const TensorGeneric<3,3>&v2) {
523 // delta(v) = delta(v1/v1.norm) = 1/v1.norm*(delta(v1) - (v.delta(v1))cross v;
524 double over_norm = 1./v1.modulo();
525 return over_norm*(v2 - over_norm*over_norm*(extProduct(matmul(v2,v1),v1)));
526 }
527
528 template<unsigned n,unsigned m>
diagMatSym(const TensorGeneric<n,n> & mat,VectorGeneric<m> & evals,TensorGeneric<m,n> & evec)529 void diagMatSym(const TensorGeneric<n,n>&mat,VectorGeneric<m>&evals,TensorGeneric<m,n>&evec) {
530 // some guess number to make sure work is large enough.
531 // for correctness it should be >=20. However, it is recommended to be the block size.
532 // I put some likely exaggerated number
533 constexpr int bs=100;
534 // temporary data, on stack so as to avoid allocations
535 std::array<int,10*n> iwork;
536 std::array<double,(6+bs)*n> work;
537 std::array<int,2*m> isup;
538 int nn=n; // dimension of matrix
539 double vl=0.0, vu=1.0; // ranges - not used
540 int one=1,mm=m; // minimum and maximum index
541 double abstol=0.0; // tolerance
542 int mout=0; // number of eigenvalues found (same as mm)
543 int info=0; // result
544 int liwork=iwork.size();
545 int lwork=work.size();
546 TensorGenericAux::local_dsyevr("V", (n==m?"A":"I"), "U", &nn, const_cast<double*>(&mat[0][0]), &nn, &vl, &vu, &one, &mm,
547 &abstol, &mout, &evals[0], &evec[0][0], &nn,
548 isup.data(), work.data(), &lwork, iwork.data(), &liwork, &info);
549 if(info!=0) plumed_error()<<"Error diagonalizing matrix\n"
550 <<"Matrix:\n"<<mat<<"\n"
551 <<"Info: "<<info<<"\n";
552 plumed_assert(mout==m);
553 // This changes eigenvectors so that the first non-null element
554 // of each of them is positive
555 // We can do it because the phase is arbitrary, and helps making
556 // the result reproducible
557 for(int i=0; i<m; ++i) {
558 int j=0;
559 for(j=0; j<n; j++) if(evec(i,j)*evec(i,j)>1e-14) break;
560 if(j<n) if(evec(i,j)<0.0) for(j=0; j<n; j++) evec(i,j)*=-1;
561 }
562 }
563
564
565 }
566
567 #endif
568
569