1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
4 \\ / O peration | Version: 3.2
5 \\ / A nd | Web: http://www.foam-extend.org
6 \\/ M anipulation | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation, either version 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
26 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
31 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
33 template <class Cmpt, int length>
34 const char* const TensorN<Cmpt, length>::typeName =
35 ("tensor" + name(length)).c_str();
37 template <class Cmpt, int length>
38 const TensorN<Cmpt, length> TensorN<Cmpt, length>::zero(0);
40 template <class Cmpt, int length>
41 const TensorN<Cmpt, length> TensorN<Cmpt, length>::one(1);
44 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
47 template <class Cmpt, int length>
48 inline TensorN<Cmpt, length>::TensorN()
52 //- Construct given VectorSpace
53 template <class Cmpt, int length>
54 inline TensorN<Cmpt, length>::TensorN
56 const VectorSpace<TensorN<Cmpt, length>, Cmpt, length*length>& vs
59 VectorSpace<TensorN<Cmpt, length>, Cmpt, length*length>(vs)
63 //- Construct from component
64 template <class Cmpt, int length>
65 inline TensorN<Cmpt, length>::TensorN(const Cmpt& tx)
67 VectorSpaceOps<TensorN<Cmpt, length>::nComponents,0>::eqOpS
76 //- Construct from Istream
77 template <class Cmpt, int length>
78 inline TensorN<Cmpt, length>::TensorN(Istream& is)
80 VectorSpace<TensorN<Cmpt, length>, Cmpt, length*length>(is)
84 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
86 //- Return tensor transpose
87 template <class Cmpt, int length>
88 inline TensorN<Cmpt, length> TensorN<Cmpt, length>::T() const
90 TensorN<Cmpt, length> transpose;
93 for (label row = 0; row < TensorN<Cmpt, length>::rowLength; row++)
96 for (label col = 0; col < TensorN<Cmpt, length>::rowLength; col++)
98 transpose.v_[i] = this->v_[j];
100 j += TensorN<Cmpt, length>::rowLength;
107 //- Return tensor diagonal
108 template <class Cmpt, int length>
109 inline DiagTensorN<Cmpt, length> TensorN<Cmpt, length>::diag() const
111 DiagTensorN<Cmpt, length> dt;
114 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
116 dt[i] = this->v_[diagI];
117 diagI += TensorN<Cmpt, length>::rowLength + 1;
123 //- Negative sum the vertical off-diagonal components
124 template <class Cmpt, int length>
125 inline TensorN<Cmpt, length> TensorN<Cmpt, length>::negSumDiag() const
127 TensorN<Cmpt, length> negsumdiag;
129 // Zero main diagonal
131 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
133 negsumdiag.v_[diagI] = 0.0;
134 diagI += TensorN<Cmpt, length>::rowLength + 1;
138 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
141 for (label j = 0; j < TensorN<Cmpt, length>::rowLength; j++)
145 negsumdiag.v_[k] = this->v_[k];
146 negsumdiag.v_[diagI] -= this->v_[k];
149 diagI += TensorN<Cmpt, length>::rowLength + 1;
156 //- Assign to a SphericalTensorN
157 template <class Cmpt, int length>
159 TensorN<Cmpt, length>::operator=(const SphericalTensorN<Cmpt, length>& st)
162 for (label i = 0; i < TensorN<Cmpt, length>::nComponents; i++)
167 diag += TensorN<Cmpt, length>::rowLength + 1;
171 this->v_[i] = pTraits<Cmpt>::zero;
177 //- Assign to a DiagTensorN
178 template <class Cmpt, int length>
180 TensorN<Cmpt, length>::operator=(const DiagTensorN<Cmpt, length>& dt)
184 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
186 for (label j = 0; j < TensorN<Cmpt, length>::rowLength; j++)
194 this->v_[k] = pTraits<Cmpt>::zero;
203 //- Transform the tensor
204 //- The components are assumed to be individual scalars
205 //- i.e. transform has no effect
206 template<class Cmpt, int length>
207 inline TensorN<Cmpt, length> transform
210 const TensorN<Cmpt, length>& v
217 // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
219 template <class Cmpt, int length>
220 inline direction TensorN<Cmpt, length>::cmpt
228 i > TensorN<Cmpt, length>::rowLength - 1
229 || j > TensorN<Cmpt, length>::rowLength - 1
234 "direction TensorN<Cmpt, length>::cmpt()\n"
236 " const direction i,\n"
237 " const direction j\n"
239 ) << "Direction out of range (0 "
240 << TensorN<Cmpt, length>::rowLength - 1 << ")"
241 << " and (i j) = (" << i << " " << j << ")"
242 << abort(FatalError);
245 return i*TensorN<Cmpt, length>::rowLength + j;
249 template <class Cmpt, int length>
250 inline const Cmpt& TensorN<Cmpt, length>::operator()
256 return this->operator[](i*TensorN<Cmpt, length>::rowLength + j);
260 template <class Cmpt, int length>
261 inline Cmpt& TensorN<Cmpt, length>::operator()
267 return this->operator[](i*TensorN<Cmpt, length>::rowLength + j);
271 // * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * //
273 //- Inner-product between two tensors
274 template <class Cmpt, int length>
276 innerProduct<TensorN<Cmpt, length>, TensorN<Cmpt, length> >::type
277 operator&(const TensorN<Cmpt, length>& t1, const TensorN<Cmpt, length>& t2)
279 TensorN<Cmpt, length> result(TensorN<Cmpt, length>::zero);
286 for (label row = 0; row < TensorN<Cmpt, length>::rowLength; row++)
288 for (label col = 0; col < TensorN<Cmpt, length>::rowLength; col++)
290 Cmpt& r = result.v_[i];
297 row2 < TensorN<Cmpt, length>::rowLength;
301 r += t1.v_[m]*t2.v_[n];
303 n += TensorN<Cmpt, length>::rowLength;
307 j += TensorN<Cmpt, length>::rowLength;
313 //- Inner-product between diagonal tensor and tensor
314 template <class Cmpt, int length>
316 innerProduct<DiagTensorN<Cmpt, length>, TensorN<Cmpt, length> >::type
319 const DiagTensorN<Cmpt, length>& dt1,
320 const TensorN<Cmpt, length>& t2
323 TensorN<Cmpt, length> result;
326 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
328 const Cmpt& xx = dt1.v_[i];
330 for (label j = 0; j < TensorN<Cmpt, length>::rowLength; j++)
332 result.v_[k] = xx*t2.v_[k];
340 //- Inner-product between tensor and diagonal tensor
341 template <class Cmpt, int length>
343 innerProduct<TensorN<Cmpt, length>, DiagTensorN<Cmpt, length> >::type
346 const TensorN<Cmpt, length>& t1,
347 const DiagTensorN<Cmpt, length>& dt2
350 TensorN<Cmpt, length> result;
353 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
355 for (label j = 0; j < TensorN<Cmpt, length>::rowLength; j++)
357 result.v_[k] = t1.v_[k]*dt2.v_[j];
366 //- Inner-product between spherical tensor and tensor
367 template <class Cmpt, int length>
369 innerProduct<SphericalTensorN<Cmpt, length>, TensorN<Cmpt, length> >::type
372 const SphericalTensorN<Cmpt, length>& st1,
373 const TensorN<Cmpt, length>& t2
376 const Cmpt& s = st1.v_[0];
377 TensorN<Cmpt, length> res;
378 VectorSpaceOps<TensorN<Cmpt, length>::nComponents,0>::opSV
390 //- Inner-product between tensor and spherical tensor
391 template <class Cmpt, int length>
393 innerProduct<TensorN<Cmpt, length>, SphericalTensorN<Cmpt, length> >::type
396 const TensorN<Cmpt, length>& t1,
397 const SphericalTensorN<Cmpt, length>& st2
400 const Cmpt& s = st2.v_[0];
401 TensorN<Cmpt, length> res;
402 VectorSpaceOps<TensorN<Cmpt, length>::nComponents,0>::opVS
414 //- Inner-product between a tensor and a vector
415 template <class Cmpt, int length>
417 innerProduct<TensorN<Cmpt, length>, VectorN<Cmpt, length> >::type
418 operator&(const TensorN<Cmpt, length>& t, const VectorN<Cmpt, length>& v)
420 VectorN<Cmpt, length> result(VectorN<Cmpt, length>::zero);
423 for (label row = 0; row < TensorN<Cmpt, length>::rowLength; row++)
425 Cmpt& r = result.v_[row];
427 for (label col = 0; col < TensorN<Cmpt, length>::rowLength; col++)
429 r += t.v_[i]*v.v_[col];
438 //- Inner-product between a vector and a tensor
439 template <class Cmpt, int length>
441 innerProduct<VectorN<Cmpt, length>, TensorN<Cmpt, length> >::type
442 operator&(const VectorN<Cmpt, length>& v, const TensorN<Cmpt, length>& t)
444 VectorN<Cmpt, length> result(VectorN<Cmpt, length>::zero);
446 for (label col = 0; col < TensorN<Cmpt, length>::rowLength; col++)
449 Cmpt& r = result.v_[col];
451 for (label row = 0; row < TensorN<Cmpt, length>::rowLength; row++)
453 r += v.v_[row]*t.v_[j];
454 j += TensorN<Cmpt, length>::rowLength;
462 //- Outer-product between two vectors
463 template <class Cmpt, int length>
465 outerProduct<VectorN<Cmpt, length>, VectorN<Cmpt, length> >::type
466 operator*(const VectorN<Cmpt, length>& v1, const VectorN<Cmpt, length>& v2)
468 TensorN<Cmpt, length> result(TensorN<Cmpt, length>::zero);
471 for (label row = 0; row < TensorN<Cmpt, length>::rowLength; row++)
473 for (label col = 0; col < TensorN<Cmpt, length>::rowLength; col++)
475 result.v_[i] = v1.v_[row]*v2.v_[col];
484 //- Addition of TensorN and TensorN
485 template <class Cmpt, int length>
486 inline TensorN<Cmpt, length>
487 operator+(const TensorN<Cmpt, length>& t1, const TensorN<Cmpt, length>& t2)
489 TensorN<Cmpt, length> res;
490 VectorSpaceOps<TensorN<Cmpt, length>::nComponents,0>::op
502 //- Addition of TensorN and DiagTensorN
503 template <class Cmpt, int length>
504 inline TensorN<Cmpt, length>
505 operator+(const TensorN<Cmpt, length>& t1, const DiagTensorN<Cmpt, length>& dt2)
507 TensorN<Cmpt, length> result(t1);
510 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
512 result.v_[diag] += dt2.v_[i];
513 diag += TensorN<Cmpt, length>::rowLength + 1;
520 //- Addition of DiagTensorN and TensorN
521 template <class Cmpt, int length>
522 inline TensorN<Cmpt, length>
523 operator+(const DiagTensorN<Cmpt, length>& dt1, const TensorN<Cmpt, length>& t2)
525 TensorN<Cmpt, length> result(t2);
528 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
530 result.v_[diag] += dt1.v_[i];
531 diag += TensorN<Cmpt, length>::rowLength + 1;
538 //- Addition of TensorN and SphericalTensorN
539 template <class Cmpt, int length>
540 inline TensorN<Cmpt, length>
543 const TensorN<Cmpt, length>& t1,
544 const SphericalTensorN<Cmpt, length>& st2
547 TensorN<Cmpt, length> result(t1);
549 const Cmpt& s = st2.v_[0];
551 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
553 result.v_[diag] += s;
554 diag += TensorN<Cmpt, length>::rowLength + 1;
561 //- Addition of SphericalTensorN and TensorN
562 template <class Cmpt, int length>
563 inline TensorN<Cmpt, length>
566 const SphericalTensorN<Cmpt, length>& st1,
567 const TensorN<Cmpt, length>& t2
570 TensorN<Cmpt, length> result(t2);
572 const Cmpt& s = st1.v_[0];
574 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
576 result.v_[diag] += s;
577 diag += TensorN<Cmpt, length>::rowLength + 1;
584 //- Subtraction of TensorN and TensorN
585 template <class Cmpt, int length>
586 inline TensorN<Cmpt, length>
587 operator-(const TensorN<Cmpt, length>& t1, const TensorN<Cmpt, length>& t2)
589 TensorN<Cmpt, length> res;
590 VectorSpaceOps<TensorN<Cmpt, length>::nComponents,0>::op
602 //- Subtraction of TensorN and DiagTensorN
603 template <class Cmpt, int length>
604 inline TensorN<Cmpt, length>
605 operator-(const TensorN<Cmpt, length>& t1, const DiagTensorN<Cmpt, length>& dt2)
607 TensorN<Cmpt, length> result(t1);
610 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
612 result.v_[diag] -= dt2.v_[i];
613 diag += TensorN<Cmpt, length>::rowLength + 1;
620 //- Subtraction of DiagTensorN and TensorN
621 template <class Cmpt, int length>
622 inline TensorN<Cmpt, length>
623 operator-(const DiagTensorN<Cmpt, length>& dt1, const TensorN<Cmpt, length>& t2)
625 TensorN<Cmpt, length> result(-t2);
628 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
630 result.v_[diag] += dt1.v_[i];
631 diag += TensorN<Cmpt, length>::rowLength + 1;
638 //- Subtraction of TensorN and SphericalTensorN
639 template <class Cmpt, int length>
640 inline TensorN<Cmpt, length>
643 const TensorN<Cmpt, length>& t1,
644 const SphericalTensorN<Cmpt, length>& st2
647 TensorN<Cmpt, length> result(t1);
649 const Cmpt& s = st2.v_[0];
651 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
653 result.v_[diag] -= s;
654 diag += TensorN<Cmpt, length>::rowLength + 1;
661 //- Subtraction of SphericalTensorN and TensorN
662 template <class Cmpt, int length>
663 inline TensorN<Cmpt, length>
666 const SphericalTensorN<Cmpt, length>& st1,
667 const TensorN<Cmpt, length>& t2
670 TensorN<Cmpt, length> result(-t2);
672 const Cmpt& s = st1.v_[0];
674 for (label i = 0; i < TensorN<Cmpt, length>::rowLength; i++)
676 result.v_[diag] += s;
677 diag += TensorN<Cmpt, length>::rowLength + 1;
684 //- Division of a scalar by a TensorN
685 template <class Cmpt, int length>
686 inline TensorN<Cmpt, length>
687 operator/(const scalar s, const TensorN<Cmpt, length>& t)
692 //- Inner Product of a VectorN by an inverse TensorN
693 template <class Cmpt, int length>
694 inline VectorN<Cmpt, length>
695 operator/(const VectorN<Cmpt, length>& v, const TensorN<Cmpt, length>& t)
700 //- Inner Product of a TensorN by an inverse TensorN
701 template <class Cmpt, int length>
702 inline TensorN<Cmpt, length>
703 operator/(const TensorN<Cmpt, length>& t1, const TensorN<Cmpt, length>& t2)
709 //- Inner Product of a DiagTensorN and an inverse TensorN
710 template <class Cmpt, int length>
711 inline TensorN<Cmpt, length>
712 operator/(const DiagTensorN<Cmpt, length>& dt1, const TensorN<Cmpt, length>& t2)
714 return dt1 & inv(t2);
718 //- Inner Product of a TensorN and an inverse DiagTensorN
719 template <class Cmpt, int length>
720 inline TensorN<Cmpt, length>
721 operator/(const TensorN<Cmpt, length>& t1, const DiagTensorN<Cmpt, length>& dt2)
723 return t1 & inv(dt2);
727 //- Inner Product of a SphericalTensorN and an inverse TensorN
728 template <class Cmpt, int length>
729 inline TensorN<Cmpt, length>
732 const SphericalTensorN<Cmpt, length>& st1,
733 const TensorN<Cmpt, length>& t2
736 return st1.v_[0] * inv(t2);
740 //- Inner Product of a TensorN and an inverse SphericalTensorN
741 template <class Cmpt, int length>
742 inline TensorN<Cmpt, length>
745 const TensorN<Cmpt, length>& t1,
746 const SphericalTensorN<Cmpt, length>& st2
749 TensorN<Cmpt, length> result;
751 const Cmpt& s = st2[0];
752 for (label i = 0; i < TensorN<Cmpt, length>::nComponents; i++)
754 result.v_[i] = t1.v_[i]/s;
761 template <class Cmpt, int length>
762 inline Cmpt det(const TensorN<Cmpt, length>& t)
764 // Calculate determinant via sub-determinants
765 Cmpt result = pTraits<Cmpt>::zero;
767 TensorN<Cmpt, length - 1> subMatrix;
769 for (label i = 0; i < length; i++)
773 // Build sub-matrix, skipping the
774 for (label j = 0; j < length; j++)
777 if (j == i) continue;
779 for (label k = 1; k < length; k++)
781 subMatrix(nj, k) = t(j, k);
787 // Handle +/- sign switch
788 result += pow(-1, i)*t(i, 0)*det(subMatrix);
795 // Determinant: partial specialisation for rank 1
797 inline Cmpt det(const TensorN<Cmpt, 1>& t)
803 // Determinant: partial specialisation for rank 2
805 inline Cmpt det(const TensorN<Cmpt, 2>& t)
807 return t(0, 0)*t(1, 1) - t(0, 1)*t(1, 0);
811 //- Return the inverse of a tensor give the determinant
812 // Uses Gauss-Jordan Elimination with full pivoting
813 template <class Cmpt, int length>
814 inline TensorN<Cmpt, length> inv(const TensorN<Cmpt, length>& t)
816 TensorN<Cmpt, length> result(t);
818 label iRow = 0, iCol = 0;
819 Cmpt largestCoeff, temp;
820 Cmpt* __restrict__ srcIter;
821 Cmpt* __restrict__ destIter;
823 // Lists used for bookkeeping on the pivoting
824 List<label> indexCol(length), indexRow(length), iPivot(length);
830 // Main loop over columns to be reduced
831 for (label i = 0; i < length; i++)
833 largestCoeff = pTraits<Cmpt>::zero;
835 // Search for pivot element
838 for (label j = 0; j < length; j++)
842 for (int k = 0; k < length; k++)
848 (temp = Foam::mag(result[curRowOffset+k]))
859 curRowOffset += length;
863 // We now have the pivot element
864 // Interchange rows if needed
867 srcIter = &result(iRow, 0);
868 destIter = &result(iCol, 0);
870 for (label j = 0; j < length; j++)
872 Swap((*srcIter), (*destIter));
880 //Check for singularity
881 srcIter = &result(iCol, iCol); // Dummy pointer to reduce indexing
882 if ((*srcIter) == Cmpt(0))
886 "inline TensorN<Cmpt, length> inv("
887 "const TensorN<Cmpt, length>& t)"
888 ) << "Singular tensor" << length << Foam::abort(FatalError);
891 // Divide the pivot row by pivot element
892 temp = pTraits<Cmpt>::one/(*srcIter);
893 (*srcIter) = pTraits<Cmpt>::one;
895 srcIter = &result(iCol, 0);
896 for (label j = 0; j < length; j++)
902 // Reduce the rows, excluding the pivot row
903 for (label j = 0; j < length; j++)
907 destIter = &result(j, 0);
908 srcIter = &result(iCol, 0);
911 destIter[iCol] = pTraits<Cmpt>::zero;
913 for (label k = 0; k < length; k++)
915 (*destIter) -= (*srcIter)*temp;
923 // Unscamble the solution
924 for (label i = length - 1; i >= 0; i--)
926 if (indexRow[i] != indexCol[i])
928 srcIter = &result[indexRow[i]];
929 destIter = &result[indexCol[i]];
930 for (label j = 0; j < length; j++)
932 Swap((*srcIter), (*destIter));
943 //- Inverse: partial template specialisation for rank 1
944 template <class Cmpt>
945 inline TensorN<Cmpt, 1> inv(const TensorN<Cmpt, 1>& t)
947 return TensorN<Cmpt, 1>(1/t(0, 0));
951 //- Inverse: partial template specialisation for rank 2
952 template <class Cmpt>
953 inline TensorN<Cmpt, 2> inv(const TensorN<Cmpt, 2>& t)
955 TensorN<Cmpt, 2> result(t);
957 Cmpt oneOverDet = 1/(t(0, 0)*t(1, 1) - t(0, 1)*t(1, 0));
959 result(0, 0) = oneOverDet*t(1, 1);
960 result(0, 1) = -oneOverDet*t(0, 1);
961 result(1, 0) = -oneOverDet*t(1, 0);
962 result(1, 1) = oneOverDet*t(0, 0);
968 //- Return tensor diagonal
969 template <class Cmpt, int length>
970 inline DiagTensorN<Cmpt, length> diag(const TensorN<Cmpt, length>& t)
975 //- Return tensor diagonal
976 template <class Cmpt, int length>
977 inline TensorN<Cmpt, length> negSumDiag(const TensorN<Cmpt, length>& t)
979 return t.negSumDiag();
982 //- Return the component sum
983 // template <class Cmpt, int length>
984 // inline Cmpt sum(const TensorN<Cmpt, length>& t)
986 // Cmpt result=Cmpt::zero;
987 // for(int i=0; i<TensorN<Cmpt, length>::nComponents; i++)
994 // * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * //
996 template<class Cmpt, int length>
997 class outerProduct<TensorN<Cmpt, length>, Cmpt>
1001 typedef TensorN<Cmpt, length> type;
1004 template<class Cmpt, int length>
1005 class outerProduct<Cmpt, TensorN<Cmpt, length> >
1009 typedef TensorN<Cmpt, length> type;
1012 template<class Cmpt, int length>
1013 class innerProduct<DiagTensorN<Cmpt, length>, TensorN<Cmpt, length> >
1017 typedef TensorN<Cmpt, length> type;
1021 template<class Cmpt, int length>
1022 class innerProduct<TensorN<Cmpt, length>, DiagTensorN<Cmpt, length> >
1026 typedef TensorN<Cmpt, length> type;
1030 template<class Cmpt, int length>
1031 class innerProduct<SphericalTensorN<Cmpt, length>, TensorN<Cmpt, length> >
1035 typedef TensorN<Cmpt, length> type;
1039 template<class Cmpt, int length>
1040 class innerProduct<TensorN<Cmpt, length>, SphericalTensorN<Cmpt, length> >
1044 typedef TensorN<Cmpt, length> type;
1048 template<class Cmpt, int length>
1049 class innerProduct<VectorN<Cmpt, length>, TensorN<Cmpt, length> >
1053 typedef VectorN<Cmpt, length> type;
1057 template<class Cmpt, int length>
1058 class innerProduct<TensorN<Cmpt, length>, VectorN<Cmpt, length> >
1062 typedef VectorN<Cmpt, length> type;
1066 template<class Cmpt, int length>
1067 class innerProduct<TensorN<Cmpt, length>, TensorN<Cmpt, length> >
1071 typedef TensorN<Cmpt, length> type;
1075 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
1077 } // End namespace Foam
1079 // ************************************************************************* //