mirror of
https://github.com/CloudCompare/PoissonRecon.git
synced 2026-08-30 17:10:28 +08:00
2275 lines
124 KiB
C++
2275 lines
124 KiB
C++
/*
|
|
Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
Redistributions of source code must retain the above copyright notice, this list of
|
|
conditions and the following disclaimer. Redistributions in binary form must reproduce
|
|
the above copyright notice, this list of conditions and the following disclaimer
|
|
in the documentation and/or other materials provided with the distribution.
|
|
|
|
Neither the name of the Johns Hopkins University nor the names of its contributors
|
|
may be used to endorse or promote products derived from this software without specific
|
|
prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
DAMAGE.
|
|
*/
|
|
|
|
template< class Real , int Degree , bool HasGradients >
|
|
struct _ConstraintCalculator_
|
|
{
|
|
static inline Real _CalculateConstraint_( const PointData< Real , HasGradients >& p , const Polynomial< Degree >& px , const Polynomial< Degree >& py , const Polynomial< Degree >& pz , const Polynomial< Degree >& dpx , const Polynomial< Degree >& dpy , const Polynomial< Degree >& dpz , Real valueWeight , Real gradientWeight );
|
|
static inline Real _CalculateConstraint_( const PointData< Real , HasGradients >& p , const Polynomial< Degree >& px , const Polynomial< Degree >& py , const Polynomial< Degree >& pz , const Polynomial< Degree >& dpx , const Polynomial< Degree >& dpy , const Polynomial< Degree >& dpz );
|
|
#if POINT_DATA_RES
|
|
static inline void _CalculateCoarser_( int c , PointData< Real , HasGradients >& p , Real value , Point3D< Real > gradient , Real valueWeight , Real gradientWeight );
|
|
#else // !POINT_DATA_RES
|
|
static inline void _CalculateCoarser_( PointData< Real , HasGradients >& p , Real value , Point3D< Real > gradient , Real valueWeight , Real gradientWeight );
|
|
#endif // POINT_DATA_RES
|
|
|
|
};
|
|
template< class Real , int Degree >
|
|
struct _ConstraintCalculator_< Real , Degree , false >
|
|
{
|
|
static inline Real _CalculateConstraint_( const PointData< Real , false >& p , const Polynomial< Degree >& px , const Polynomial< Degree >& py , const Polynomial< Degree >& pz , const Polynomial< Degree >& dpx , const Polynomial< Degree >& dpy , const Polynomial< Degree >& dpz , Real valueWeight , Real gradientWeight )
|
|
{
|
|
#if POINT_DATA_RES
|
|
Real constraint = 0;
|
|
for( int c=0 ; c<PointData< Real , false >::SAMPLES ; c++ ) if( p[c].weight )
|
|
{
|
|
const Point3D< Real > q = p[c].position;
|
|
constraint += (Real)( px( q[0] ) * py( q[1] ) * pz( q[2] ) * p[c].weight * p[c].value );
|
|
}
|
|
return constraint * valueWeight;
|
|
#else // !POINT_DATA_RES
|
|
const Point3D< Real > q = p.position;
|
|
return (Real)( px( q[0] ) * py( q[1] ) * pz( q[2] ) * p.weight * p.value ) * valueWeight;
|
|
#endif // POINT_DATA_RES
|
|
}
|
|
static inline Real _CalculateConstraint_( const PointData< Real , false >& p , const Polynomial< Degree >& px , const Polynomial< Degree >& py , const Polynomial< Degree >& pz , const Polynomial< Degree >& dpx , const Polynomial< Degree >& dpy , const Polynomial< Degree >& dpz )
|
|
{
|
|
#if POINT_DATA_RES
|
|
Real constraint = 0;
|
|
for( int c=0 ; c<PointData< Real , false >::SAMPLES ; c++ ) if( p[c].weight )
|
|
{
|
|
const Point3D< Real > q = p[c].position;
|
|
constraint += (Real)( px( q[0] ) * py( q[1] ) * pz( q[2] ) * p[c]._value );
|
|
}
|
|
return constraint;
|
|
#else // !POINT_DATA_RES
|
|
const Point3D< Real > q = p.position;
|
|
return (Real)( px( q[0] ) * py( q[1] ) * pz( q[2] ) * p._value );
|
|
#endif // POINT_DATA_RES
|
|
}
|
|
#if POINT_DATA_RES
|
|
static inline void _CalculateCoarser_( int c , PointData< Real , false >& p , Real value , Point3D< Real > gradient , Real valueWeight , Real gradientWeight ){ p[c]._value = value * valueWeight * p[c].weight; }
|
|
#else // !POINT_DATA_RES
|
|
static inline void _CalculateCoarser_( PointData< Real , false >& p , Real value , Point3D< Real > gradient , Real valueWeight , Real gradientWeight ){ p._value = value * valueWeight * p.weight; }
|
|
#endif // POINT_DATA_RES
|
|
};
|
|
template< class Real , int Degree >
|
|
struct _ConstraintCalculator_< Real , Degree , true >
|
|
{
|
|
static inline Real _CalculateConstraint_( const PointData< Real , true >& p , const Polynomial< Degree >& px , const Polynomial< Degree >& py , const Polynomial< Degree >& pz , const Polynomial< Degree >& dpx , const Polynomial< Degree >& dpy , const Polynomial< Degree >& dpz , Real valueWeight , Real gradientWeight )
|
|
{
|
|
#if POINT_DATA_RES
|
|
Real constraint = 0;
|
|
for( int c=0 ; c<PointData< Real , true >::SAMPLES ; c++ ) if( p[c].weight )
|
|
{
|
|
const Point3D< Real > q = p[c].position;
|
|
double _px = px( q[0] ) , _py = py( q[1] ) , _pz = pz( q[2] );
|
|
constraint +=
|
|
(
|
|
(Real)( _px * _py * _pz * p[c].value ) * valueWeight +
|
|
Point3D< Real >::Dot( Point3D< Real >( dpx( q[0] ) * _py * _pz , _px * dpy( q[1] ) * _pz , _px * _py * dpz( q[2] ) ) , p[c].gradient ) * gradientWeight
|
|
) * p[c].weight;
|
|
}
|
|
return constraint;
|
|
#else // !POINT_DATA_RES
|
|
const Point3D< Real > q = p.position;
|
|
double _px = px( q[0] ) , _py = py( q[1] ) , _pz = pz( q[2] );
|
|
return
|
|
(
|
|
(Real)( _px * _py * _pz * p.value ) * valueWeight +
|
|
Point3D< Real >::Dot( Point3D< Real >( dpx( q[0] ) * _py * _pz , _px * dpy( q[1] ) * _pz , _px * _py * dpz( q[2] ) ) , p.gradient ) * gradientWeight
|
|
) * p.weight;
|
|
#endif // POINT_DATA_RES
|
|
}
|
|
static inline Real _CalculateConstraint_( const PointData< Real , true >& p , const Polynomial< Degree >& px , const Polynomial< Degree >& py , const Polynomial< Degree >& pz , const Polynomial< Degree >& dpx , const Polynomial< Degree >& dpy , const Polynomial< Degree >& dpz )
|
|
{
|
|
#if POINT_DATA_RES
|
|
Real constraint = 0;
|
|
for( int c=0 ; c<PointData< Real , true >::SAMPLES ; c++ ) if( p[c].weight )
|
|
{
|
|
const Point3D< Real > q = p[c].position;
|
|
double _px = px( q[0] ) , _py = py( q[1] ) , _pz = pz( q[2] );
|
|
constraint +=
|
|
(Real)( _px * _py * _pz * p[c]._value ) +
|
|
Point3D< Real >::Dot( Point3D< Real >( dpx( q[0] ) * _py * _pz , _px * dpy( q[1] ) * _pz , _px * _py * dpz( q[2] ) ) , p[c]._gradient );
|
|
}
|
|
return constraint;
|
|
#else // !POINT_DATA_RES
|
|
const Point3D< Real > q = p.position;
|
|
double _px = px( q[0] ) , _py = py( q[1] ) , _pz = pz( q[2] );
|
|
return
|
|
(Real)( _px * _py * _pz * p._value ) +
|
|
Point3D< Real >::Dot( Point3D< Real >( dpx( q[0] ) * _py * _pz , _px * dpy( q[1] ) * _pz , _px * _py * dpz( q[2] ) ) , p._gradient );
|
|
#endif // POINT_DATA_RES
|
|
}
|
|
#if POINT_DATA_RES
|
|
static inline void _CalculateCoarser_( int c , PointData< Real , true >& p , Real value , Point3D< Real > gradient , Real valueWeight , Real gradientWeight ){ p[c]._value = value * valueWeight * p[c].weight ; p[c]._gradient = gradient * gradientWeight * p[c].weight; }
|
|
#else // !POINT_DATA_RES
|
|
static inline void _CalculateCoarser_( PointData< Real , true >& p , Real value , Point3D< Real > gradient , Real valueWeight , Real gradientWeight ){ p._value = value * valueWeight * p.weight ; p._gradient = gradient * gradientWeight * p.weight; }
|
|
#endif // POINT_DATA_RES
|
|
};
|
|
|
|
template< >
|
|
template< class I >
|
|
double FEMSystemFunctor< 0 , BOUNDARY_FREE >::_integrate( const I& integrator , const int off1[] , const int off2[] ) const
|
|
{
|
|
#define D_DOT( D1 , D2 ) { integrator.dot( off1[0] , off2[0] , D1 , D2 ) , integrator.dot( off1[1] , off2[1] , D1 , D2 ) , integrator.dot( off1[2] , off2[2] , D1 , D2 ) }
|
|
double d00[] = D_DOT( 0 , 0 );
|
|
return
|
|
(
|
|
d00[0] * d00[1] * d00[2]
|
|
) * massWeight;
|
|
#undef D_DOT
|
|
}
|
|
template< >
|
|
template< class I >
|
|
double FEMSystemFunctor< 0 , BOUNDARY_NEUMANN >::_integrate( const I& integrator , const int off1[] , const int off2[] ) const
|
|
{
|
|
#define D_DOT( D1 , D2 ) { integrator.dot( off1[0] , off2[0] , D1 , D2 ) , integrator.dot( off1[1] , off2[1] , D1 , D2 ) , integrator.dot( off1[2] , off2[2] , D1 , D2 ) }
|
|
double d00[] = D_DOT( 0 , 0 );
|
|
return
|
|
(
|
|
d00[0] * d00[1] * d00[2]
|
|
) * massWeight;
|
|
#undef D_DOT
|
|
}
|
|
template< >
|
|
template< class I >
|
|
double FEMSystemFunctor< 0 , BOUNDARY_DIRICHLET >::_integrate( const I& integrator , const int off1[] , const int off2[] ) const
|
|
{
|
|
#define D_DOT( D1 , D2 ) { integrator.dot( off1[0] , off2[0] , D1 , D2 ) , integrator.dot( off1[1] , off2[1] , D1 , D2 ) , integrator.dot( off1[2] , off2[2] , D1 , D2 ) }
|
|
double d00[] = D_DOT( 0 , 0 );
|
|
return
|
|
(
|
|
d00[0] * d00[1] * d00[2]
|
|
) * massWeight;
|
|
#undef D_DOT
|
|
}
|
|
template< >
|
|
template< class I >
|
|
double FEMSystemFunctor< 1 , BOUNDARY_FREE >::_integrate( const I& integrator , const int off1[] , const int off2[] ) const
|
|
{
|
|
#define D_DOT( D1 , D2 ) { integrator.dot( off1[0] , off2[0] , D1 , D2 ) , integrator.dot( off1[1] , off2[1] , D1 , D2 ) , integrator.dot( off1[2] , off2[2] , D1 , D2 ) }
|
|
double d00[] = D_DOT( 0 , 0 ) , d11[] = D_DOT( 1 , 1 );
|
|
return
|
|
(
|
|
d00[0] * d00[1] * d00[2]
|
|
) * massWeight
|
|
+
|
|
(
|
|
d11[0] * d00[1] * d00[2] +
|
|
d11[1] * d00[2] * d00[0] +
|
|
d11[2] * d00[0] * d00[1]
|
|
) * lapWeight;
|
|
#undef D_DOT
|
|
}
|
|
template< >
|
|
template< class I >
|
|
double FEMSystemFunctor< 1 , BOUNDARY_NEUMANN >::_integrate( const I& integrator , const int off1[] , const int off2[] ) const
|
|
{
|
|
#define D_DOT( D1 , D2 ) { integrator.dot( off1[0] , off2[0] , D1 , D2 ) , integrator.dot( off1[1] , off2[1] , D1 , D2 ) , integrator.dot( off1[2] , off2[2] , D1 , D2 ) }
|
|
double d00[] = D_DOT( 0 , 0 ) , d11[] = D_DOT( 1 , 1 );
|
|
return
|
|
(
|
|
d00[0] * d00[1] * d00[2]
|
|
) * massWeight
|
|
+
|
|
(
|
|
d11[0] * d00[1] * d00[2] +
|
|
d11[1] * d00[2] * d00[0] +
|
|
d11[2] * d00[0] * d00[1]
|
|
) * lapWeight;
|
|
#undef D_DOT
|
|
}
|
|
template< >
|
|
template< class I >
|
|
double FEMSystemFunctor< 1 , BOUNDARY_DIRICHLET >::_integrate( const I& integrator , const int off1[] , const int off2[] ) const
|
|
{
|
|
#define D_DOT( D1 , D2 ) { integrator.dot( off1[0] , off2[0] , D1 , D2 ) , integrator.dot( off1[1] , off2[1] , D1 , D2 ) , integrator.dot( off1[2] , off2[2] , D1 , D2 ) }
|
|
double d00[] = D_DOT( 0 , 0 ) , d11[] = D_DOT( 1 , 1 );
|
|
return
|
|
(
|
|
d00[0] * d00[1] * d00[2]
|
|
) * massWeight
|
|
+
|
|
(
|
|
d11[0] * d00[1] * d00[2] +
|
|
d11[1] * d00[2] * d00[0] +
|
|
d11[2] * d00[0] * d00[1]
|
|
) * lapWeight;
|
|
#undef D_DOT
|
|
}
|
|
|
|
template< int FEMDegree , BoundaryType BType >
|
|
template< class I >
|
|
double FEMSystemFunctor< FEMDegree , BType >::_integrate( const I& integrator , const int off1[] , const int off2[] ) const
|
|
{
|
|
#define D_DOT( D1 , D2 ) { integrator.dot( off1[0] , off2[0] , D1 , D2 ) , integrator.dot( off1[1] , off2[1] , D1 , D2 ) , integrator.dot( off1[2] , off2[2] , D1 , D2 ) }
|
|
double d00[] = D_DOT( 0 , 0 ) , d02[] = D_DOT( 0 , 2 ) , d20[] = D_DOT( 2 , 0 ) , d22[] = D_DOT( 2 , 2 ) , d11[] = D_DOT( 1 , 1 );
|
|
return
|
|
(
|
|
d00[0] * d00[1] * d00[2]
|
|
) * massWeight
|
|
+
|
|
(
|
|
d11[0] * d00[1] * d00[2] +
|
|
d11[1] * d00[2] * d00[0] +
|
|
d11[2] * d00[0] * d00[1]
|
|
) * lapWeight
|
|
+
|
|
(
|
|
d22[0] * d00[1] * d00[2] + // Unmixed
|
|
d22[1] * d00[2] * d00[0] + // Unmixed
|
|
d22[2] * d00[0] * d00[1] + // Unmixed
|
|
d00[0] * ( d02[1] * d20[2] + d20[1] * d02[2] ) + // Mixed
|
|
d00[1] * ( d02[2] * d20[0] + d20[2] * d02[0] ) + // Mixed
|
|
d00[2] * ( d02[0] * d20[1] + d20[0] * d02[1] ) // Mixed
|
|
) * biLapWeight;
|
|
#undef D_DOT
|
|
}
|
|
template< int SFDegree , BoundaryType SFBType , int FEMDegree , BoundaryType FEMBType >
|
|
template< bool Reverse , class I >
|
|
double FEMSFConstraintFunctor< SFDegree , SFBType , FEMDegree , FEMBType >::_integrate( const I& integrator , const int off1[] , const int off2[] ) const
|
|
{
|
|
#define D_DOT( D1 , D2 ) { integrator.dot( off1[0] , off2[0] , Reverse ? D2 : D1 , Reverse ? D1 : D2 ) , integrator.dot( off1[1] , off2[1] , Reverse ? D2 : D1 , Reverse ? D1 : D2 ) , integrator.dot( off1[2] , off2[2] , Reverse ? D2 : D1 , Reverse ? D1 : D2 ) }
|
|
double d00[] = D_DOT( 0 , 0 ) , d02[] = D_DOT( 0 , 2 ) , d20[] = D_DOT( 2 , 0 ) , d22[] = D_DOT( 2 , 2 ) , d11[] = D_DOT( 1 , 1 );
|
|
if( SFDegree==0 || FEMDegree==0 )
|
|
return d00[0] * d00[1] * d00[2] * massWeight;
|
|
else if( SFDegree<=1 || FEMDegree<=1 )
|
|
return
|
|
(
|
|
d00[0] * d00[1] * d00[2]
|
|
) * massWeight
|
|
+
|
|
(
|
|
d11[0] * d00[1] * d00[2] +
|
|
d11[1] * d00[2] * d00[0] +
|
|
d11[2] * d00[0] * d00[1]
|
|
) * lapWeight;
|
|
else
|
|
return
|
|
(
|
|
d00[0] * d00[1] * d00[2]
|
|
) * massWeight
|
|
+
|
|
(
|
|
d11[0] * d00[1] * d00[2] +
|
|
d11[1] * d00[2] * d00[0] +
|
|
d11[2] * d00[0] * d00[1]
|
|
) * lapWeight
|
|
+
|
|
(
|
|
d22[0] * d00[1] * d00[2] + // Unmixed
|
|
d22[1] * d00[2] * d00[0] + // Unmixed
|
|
d22[2] * d00[0] * d00[1] + // Unmixed
|
|
d00[0] * ( d02[1] * d20[2] + d20[1] * d02[2] ) + // Mixed
|
|
d00[1] * ( d02[2] * d20[0] + d20[2] * d02[0] ) + // Mixed
|
|
d00[2] * ( d02[0] * d20[1] + d20[0] * d02[1] ) // Mixed
|
|
) * biLapWeight;
|
|
#undef D_DOT
|
|
}
|
|
template< int VFDegree , BoundaryType VFBType , int FEMDegree , BoundaryType FEMBType >
|
|
template< bool Reverse , class I >
|
|
Point3D< double > FEMVFConstraintFunctor< VFDegree , VFBType , FEMDegree , FEMBType >::_integrate( const I& integrator , const int off1[] , const int off2[] ) const
|
|
{
|
|
#define D_DOT( D1 , D2 ) { integrator.dot( off1[0] , off2[0] , Reverse ? D2 : D1 , Reverse ? D1 : D2 ) , integrator.dot( off1[1] , off2[1] , Reverse ? D2 : D1 , Reverse ? D1 : D2 ) , integrator.dot( off1[2] , off2[2] , Reverse ? D2 : D1 , Reverse ? D1 : D2 ) }
|
|
if( FEMDegree==0 ) fprintf( stderr , "[ERROR] FEMDegree does not support differentiation: %d\n" , FEMDegree ) , exit( 0 );
|
|
if( VFDegree==0 || FEMDegree==1 )
|
|
{
|
|
double d00[] = D_DOT( 0 , 0 ) , d01[] = D_DOT( 0 , 1 );
|
|
return
|
|
Point3D< double >
|
|
(
|
|
d01[0] * d00[1] * d00[2] ,
|
|
d01[1] * d00[2] * d00[0] ,
|
|
d01[2] * d00[0] * d00[1]
|
|
) * lapWeight;
|
|
}
|
|
else
|
|
{
|
|
double d00[] = D_DOT( 0 , 0 ) , d10[] = D_DOT( 1 , 0 ) , d01[] = D_DOT( 0 , 1 ) , d02[] = D_DOT( 0 , 2 ) , d12[] = D_DOT( 1 , 2 );
|
|
return
|
|
Point3D< double >
|
|
(
|
|
d01[0] * d00[1] * d00[2] ,
|
|
d01[1] * d00[2] * d00[0] ,
|
|
d01[2] * d00[0] * d00[1]
|
|
) * lapWeight
|
|
+
|
|
Point3D< double >
|
|
(
|
|
d12[0] * d00[1] * d00[2] + d10[0] * ( d00[1] * d02[2] + d02[1] * d00[2] ) ,
|
|
d12[1] * d00[2] * d00[0] + d10[1] * ( d00[2] * d02[0] + d02[2] * d00[0] ) ,
|
|
d12[2] * d00[0] * d00[1] + d10[2] * ( d00[0] * d02[1] + d02[0] * d00[1] )
|
|
) * biLapWeight;
|
|
}
|
|
#undef D_DOT
|
|
}
|
|
|
|
template< int Degree1 , BoundaryType BType1 , int Degree2 , BoundaryType BType2 >
|
|
template< bool Reverse , class _FEMSystemFunctor >
|
|
void SystemCoefficients< Degree1 , BType1 , Degree2 , BType2 >::SetCentralConstraintStencil( const _FEMSystemFunctor& F , const Integrator& integrator , Stencil< double , OverlapSize >& stencil )
|
|
{
|
|
int center = ( 1<<integrator.depth() )>>1;
|
|
int offset[] = { center , center , center };
|
|
for( int x=0 ; x<OverlapSize ; x++ ) for( int y=0 ; y<OverlapSize ; y++ ) for( int z=0 ; z<OverlapSize ; z++ )
|
|
{
|
|
int _offset[] = { x+center-OverlapEnd , y+center-OverlapEnd , z+center-OverlapEnd };
|
|
stencil( x , y , z ) = F.template integrate< Reverse >( integrator , _offset , offset );
|
|
}
|
|
}
|
|
template< int Degree1 , BoundaryType BType1 , int Degree2 , BoundaryType BType2 >
|
|
template< bool Reverse , class _FEMSystemFunctor >
|
|
void SystemCoefficients< Degree1 , BType1 , Degree2 , BType2 >::SetCentralConstraintStencils( const _FEMSystemFunctor& F , const ChildIntegrator& integrator , Stencil< double , OverlapSize > stencils[2][2][2] )
|
|
{
|
|
int center = ( 1<<integrator.childDepth() )>>1;
|
|
// [NOTE] We want the center to be at the first node of the brood
|
|
// Which is not the case when childDepth is 1.
|
|
center = ( center>>1 )<<1;
|
|
for( int i=0 ; i<2 ; i++ ) for( int j=0 ; j<2 ; j++ ) for( int k=0 ; k<2 ; k++ )
|
|
{
|
|
int offset[] = { center+i , center+j , center+k };
|
|
for( int x=0 ; x<OverlapSize ; x++ ) for( int y=0 ; y<OverlapSize ; y++ ) for( int z=0 ; z<OverlapSize ; z++ )
|
|
{
|
|
int _offset[] = { x+center/2-OverlapEnd , y+center/2-OverlapEnd , z+center/2-OverlapEnd };
|
|
stencils[i][j][k]( x , y , z ) = F.template integrate< Reverse >( integrator , _offset , offset );
|
|
}
|
|
}
|
|
}
|
|
template< int Degree1 , BoundaryType BType1 , int Degree2 , BoundaryType BType2 >
|
|
template< bool Reverse , class _FEMSystemFunctor >
|
|
void SystemCoefficients< Degree1 , BType1 , Degree2 , BType2 >::SetCentralConstraintStencil( const _FEMSystemFunctor& F , const Integrator& integrator , Stencil< Point3D< double > , OverlapSize >& stencil )
|
|
{
|
|
int center = ( 1<<integrator.depth() )>>1;
|
|
int offset[] = { center , center , center };
|
|
for( int x=0 ; x<OverlapSize ; x++ ) for( int y=0 ; y<OverlapSize ; y++ ) for( int z=0 ; z<OverlapSize ; z++ )
|
|
{
|
|
int _offset[] = { x+center-OverlapEnd , y+center-OverlapEnd , z+center-OverlapEnd };
|
|
stencil( x , y , z ) = F.template integrate< Reverse >( integrator , _offset , offset );
|
|
}
|
|
}
|
|
template< int Degree1 , BoundaryType BType1 , int Degree2 , BoundaryType BType2 >
|
|
template< bool Reverse , class _FEMSystemFunctor >
|
|
void SystemCoefficients< Degree1 , BType1 , Degree2 , BType2 >::SetCentralConstraintStencils( const _FEMSystemFunctor& F , const ChildIntegrator& integrator , Stencil< Point3D< double > , OverlapSize > stencils[2][2][2] )
|
|
{
|
|
int center = ( 1<<integrator.childDepth() )>>1;
|
|
// [NOTE] We want the center to be at the first node of the brood
|
|
// Which is not the case when childDepth is 1.
|
|
center = ( center>>1 )<<1;
|
|
for( int i=0 ; i<2 ; i++ ) for( int j=0 ; j<2 ; j++ ) for( int k=0 ; k<2 ; k++ )
|
|
{
|
|
int offset[] = { center+i , center+j , center+k };
|
|
for( int x=0 ; x<OverlapSize ; x++ ) for( int y=0 ; y<OverlapSize ; y++ ) for( int z=0 ; z<OverlapSize ; z++ )
|
|
{
|
|
int _offset[] = { x+center/2-OverlapEnd , y+center/2-OverlapEnd , z+center/2-OverlapEnd };
|
|
stencils[i][j][k]( x , y , z ) = F.template integrate< Reverse >( integrator , _offset , offset );
|
|
}
|
|
}
|
|
}
|
|
template< int Degree1 , BoundaryType BType1 , int Degree2 , BoundaryType BType2 >
|
|
template< class _FEMSystemFunctor >
|
|
void SystemCoefficients< Degree1 , BType1 , Degree2 , BType2 >::SetCentralSystemStencil( const _FEMSystemFunctor& F , const Integrator& integrator , Stencil< double , OverlapSize >& stencil )
|
|
{
|
|
int center = ( 1<<integrator.depth() )>>1;
|
|
int offset[] = { center , center , center };
|
|
for( int x=0 ; x<OverlapSize ; x++ ) for( int y=0 ; y<OverlapSize ; y++ ) for( int z=0 ; z<OverlapSize ; z++ )
|
|
{
|
|
int _offset[] = { x+center-OverlapEnd , y+center-OverlapEnd , z+center-OverlapEnd };
|
|
stencil( x , y , z ) = F.integrate( integrator , _offset , offset );
|
|
}
|
|
}
|
|
template< int Degree1 , BoundaryType BType1 , int Degree2 , BoundaryType BType2 >
|
|
template< class _FEMSystemFunctor >
|
|
void SystemCoefficients< Degree1 , BType1 , Degree2 , BType2 >::SetCentralSystemStencils( const _FEMSystemFunctor& F , const ChildIntegrator& integrator , Stencil< double , OverlapSize > stencils[2][2][2] )
|
|
{
|
|
int center = ( 1<<integrator.childDepth() )>>1;
|
|
// [NOTE] We want the center to be at the first node of the brood
|
|
// Which is not the case when childDepth is 1.
|
|
center = ( center>>1 )<<1;
|
|
for( int i=0 ; i<2 ; i++ ) for( int j=0 ; j<2 ; j++ ) for( int k=0 ; k<2 ; k++ )
|
|
{
|
|
int offset[] = { center+i , center+j , center+k };
|
|
for( int x=0 ; x<OverlapSize ; x++ ) for( int y=0 ; y<OverlapSize ; y++ ) for( int z=0 ; z<OverlapSize ; z++ )
|
|
{
|
|
int _offset[] = { x+center/2-OverlapEnd , y+center/2-OverlapEnd , z+center/2-OverlapEnd };
|
|
stencils[i][j][k]( x , y , z ) = F.integrate( integrator , _offset , offset );
|
|
}
|
|
}
|
|
}
|
|
|
|
template< class Real >
|
|
template< int FEMDegree >
|
|
void Octree< Real >::_setMultiColorIndices( int start , int end , std::vector< std::vector< int > >& indices ) const
|
|
{
|
|
static const int OverlapRadius = - BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapStart;
|
|
|
|
const int modulus = OverlapRadius+1;
|
|
indices.resize( modulus*modulus*modulus );
|
|
int count[modulus*modulus*modulus];
|
|
memset( count , 0 , sizeof(int)*modulus*modulus*modulus );
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=start ; i<end ; i++ ) if( _isValidFEMNode( _sNodes.treeNodes[i] ) )
|
|
{
|
|
// [NOTE] We have to use the global offset so that it's positive
|
|
int d , off[3];
|
|
_sNodes.treeNodes[i]->depthAndOffset( d , off );
|
|
int idx = (modulus*modulus) * ( off[2]%modulus ) + modulus * ( off[1]%modulus ) + ( off[0]%modulus );
|
|
#pragma omp atomic
|
|
count[idx]++;
|
|
}
|
|
|
|
for( int i=0 ; i<modulus*modulus*modulus ; i++ ) indices[i].reserve( count[i] ) , count[i]=0;
|
|
for( int i=start ; i<end ; i++ ) if( _isValidFEMNode( _sNodes.treeNodes[i] ) )
|
|
{
|
|
int d , off[3];
|
|
_sNodes.treeNodes[i]->depthAndOffset( d , off );
|
|
int idx = (modulus*modulus) * ( off[2]%modulus ) + modulus * ( off[1]%modulus ) + ( off[0]%modulus );
|
|
indices[idx].push_back( i - start );
|
|
}
|
|
}
|
|
|
|
template< class Real >
|
|
template< class C , int FEMDegree , BoundaryType BType >
|
|
void Octree< Real >::_downSample( LocalDepth highDepth , DenseNodeData< C , FEMDegree >& constraints ) const
|
|
{
|
|
typedef typename TreeOctNode::NeighborKey< -BSplineSupportSizes< FEMDegree >::UpSampleStart , BSplineSupportSizes< FEMDegree >::UpSampleEnd > UpSampleKey;
|
|
|
|
LocalDepth lowDepth = highDepth-1;
|
|
if( lowDepth<0 ) return;
|
|
|
|
typename BSplineEvaluationData< FEMDegree , BType >::UpSampleEvaluator upSampleEvaluator;
|
|
BSplineEvaluationData< FEMDegree , BType >::SetUpSampleEvaluator( upSampleEvaluator , lowDepth );
|
|
std::vector< UpSampleKey > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( lowDepth ) );
|
|
|
|
Stencil< double , BSplineSupportSizes< FEMDegree >::UpSampleSize > upSampleStencil;
|
|
int lowCenter = ( 1<<lowDepth )>>1;
|
|
for( int i=0 ; i<BSplineSupportSizes< FEMDegree >::UpSampleSize ; i++ ) for( int j=0 ; j<BSplineSupportSizes< FEMDegree >::UpSampleSize ; j++ ) for( int k=0 ; k<BSplineSupportSizes< FEMDegree >::UpSampleSize ; k++ )
|
|
upSampleStencil( i , j , k ) =
|
|
upSampleEvaluator.value( lowCenter , 2*lowCenter + i + BSplineSupportSizes< FEMDegree >::UpSampleStart ) *
|
|
upSampleEvaluator.value( lowCenter , 2*lowCenter + j + BSplineSupportSizes< FEMDegree >::UpSampleStart ) *
|
|
upSampleEvaluator.value( lowCenter , 2*lowCenter + k + BSplineSupportSizes< FEMDegree >::UpSampleStart );
|
|
|
|
// Iterate over all (valid) parent nodes
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(lowDepth) ; i<_sNodesEnd(lowDepth) ; i++ ) if( _isValidFEMNode( _sNodes.treeNodes[i] ) )
|
|
{
|
|
TreeOctNode* pNode = _sNodes.treeNodes[i];
|
|
|
|
UpSampleKey& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
LocalDepth d ; LocalOffset off;
|
|
_localDepthAndOffset( pNode , d , off );
|
|
|
|
neighborKey.template getNeighbors< false >( pNode );
|
|
|
|
// Get the child neighbors
|
|
typename TreeOctNode::Neighbors< BSplineSupportSizes< FEMDegree >::UpSampleSize > neighbors;
|
|
neighborKey.template getChildNeighbors< false >( 0 , _localToGlobal( d ) , neighbors );
|
|
|
|
C& coarseConstraint = constraints[i];
|
|
|
|
// Want to make sure test if contained children are interior.
|
|
// This is more conservative because we are test that overlapping children are interior
|
|
bool isInterior = _isInteriorlyOverlapped< FEMDegree , FEMDegree >( pNode );
|
|
if( isInterior )
|
|
{
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::UpSampleSize ; ii++ ) for( int jj=0 ; jj<BSplineSupportSizes< FEMDegree >::UpSampleSize ; jj++ ) for( int kk=0 ; kk<BSplineSupportSizes< FEMDegree >::UpSampleSize ; kk++ )
|
|
{
|
|
const TreeOctNode* cNode = neighbors.neighbors[ii][jj][kk];
|
|
if( IsActiveNode( cNode ) ) coarseConstraint += (C)( constraints[ cNode->nodeData.nodeIndex ] * upSampleStencil( ii , jj , kk ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
double upSampleValues[3][ BSplineSupportSizes< FEMDegree >::UpSampleSize ];
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::UpSampleSize ; ii++ )
|
|
{
|
|
upSampleValues[0][ii] = upSampleEvaluator.value( off[0] , 2*off[0] + ii + BSplineSupportSizes< FEMDegree >::UpSampleStart );
|
|
upSampleValues[1][ii] = upSampleEvaluator.value( off[1] , 2*off[1] + ii + BSplineSupportSizes< FEMDegree >::UpSampleStart );
|
|
upSampleValues[2][ii] = upSampleEvaluator.value( off[2] , 2*off[2] + ii + BSplineSupportSizes< FEMDegree >::UpSampleStart );
|
|
}
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::UpSampleSize ; ii++ ) for( int jj=0 ; jj<BSplineSupportSizes< FEMDegree >::UpSampleSize ; jj++ )
|
|
{
|
|
double dxy = upSampleValues[0][ii] * upSampleValues[1][jj];
|
|
for( int kk=0 ; kk<BSplineSupportSizes< FEMDegree >::UpSampleSize ; kk++ )
|
|
{
|
|
const TreeOctNode* cNode = neighbors.neighbors[ii][jj][kk];
|
|
if( _isValidFEMNode( cNode ) ) coarseConstraint += (C)( constraints[ cNode->nodeData.nodeIndex ] * dxy * upSampleValues[2][kk] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
template< class Real >
|
|
template< class C , int FEMDegree , BoundaryType BType >
|
|
void Octree< Real >::_upSample( LocalDepth highDepth , DenseNodeData< C , FEMDegree >& coefficients ) const
|
|
{
|
|
static const int LeftDownSampleRadius = -( ( BSplineSupportSizes< FEMDegree >::DownSample0Start < BSplineSupportSizes< FEMDegree >::DownSample1Start ) ? BSplineSupportSizes< FEMDegree >::DownSample0Start : BSplineSupportSizes< FEMDegree >::DownSample1Start );
|
|
static const int RightDownSampleRadius = ( ( BSplineSupportSizes< FEMDegree >::DownSample0End > BSplineSupportSizes< FEMDegree >::DownSample1End ) ? BSplineSupportSizes< FEMDegree >::DownSample0End : BSplineSupportSizes< FEMDegree >::DownSample1End );
|
|
typedef TreeOctNode::NeighborKey< LeftDownSampleRadius , RightDownSampleRadius > DownSampleKey;
|
|
|
|
LocalDepth lowDepth = highDepth-1;
|
|
if( lowDepth<0 ) return;
|
|
|
|
typename BSplineEvaluationData< FEMDegree , BType >::UpSampleEvaluator upSampleEvaluator;
|
|
BSplineEvaluationData< FEMDegree , BType >::SetUpSampleEvaluator( upSampleEvaluator , lowDepth );
|
|
std::vector< DownSampleKey > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( lowDepth ) );
|
|
|
|
static const int DownSampleSize = BSplineSupportSizes< FEMDegree >::DownSample0Size > BSplineSupportSizes< FEMDegree >::DownSample1Size ? BSplineSupportSizes< FEMDegree >::DownSample0Size : BSplineSupportSizes< FEMDegree >::DownSample1Size;
|
|
Stencil< double , DownSampleSize > downSampleStencils[ Cube::CORNERS ];
|
|
int lowCenter = ( 1<<lowDepth )>>1;
|
|
for( int c=0 ; c<Cube::CORNERS ; c++ )
|
|
{
|
|
int cx , cy , cz;
|
|
Cube::FactorCornerIndex( c , cx , cy , cz );
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::DownSampleSize[cx] ; ii++ )
|
|
for( int jj=0 ; jj<BSplineSupportSizes< FEMDegree >::DownSampleSize[cy] ; jj++ )
|
|
for( int kk=0 ; kk<BSplineSupportSizes< FEMDegree >::DownSampleSize[cz] ; kk++ )
|
|
downSampleStencils[c]( ii , jj , kk ) =
|
|
upSampleEvaluator.value( lowCenter + ii + BSplineSupportSizes< FEMDegree >::DownSampleStart[cx] , 2*lowCenter + cx ) *
|
|
upSampleEvaluator.value( lowCenter + jj + BSplineSupportSizes< FEMDegree >::DownSampleStart[cy] , 2*lowCenter + cy ) *
|
|
upSampleEvaluator.value( lowCenter + kk + BSplineSupportSizes< FEMDegree >::DownSampleStart[cz] , 2*lowCenter + cz ) ;
|
|
}
|
|
|
|
// For Dirichlet constraints, can't get to all children from parents because boundary nodes are invalid
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(highDepth) ; i<_sNodesEnd(highDepth) ; i++ ) if( _isValidFEMNode( _sNodes.treeNodes[i] ) )
|
|
{
|
|
TreeOctNode *cNode = _sNodes.treeNodes[i] , *pNode = cNode->parent;
|
|
int c = (int)( cNode-pNode->children );
|
|
|
|
DownSampleKey& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
LocalDepth d ; LocalOffset off;
|
|
_localDepthAndOffset( pNode , d , off );
|
|
typename TreeOctNode::Neighbors< LeftDownSampleRadius + RightDownSampleRadius + 1 >& neighbors = neighborKey.template getNeighbors< false >( pNode );
|
|
|
|
// Want to make sure test if contained children are interior.
|
|
// This is more conservative because we are test that overlapping children are interior
|
|
bool isInterior = _isInteriorlyOverlapped< FEMDegree , FEMDegree >( pNode );
|
|
|
|
C& fineCoefficient = coefficients[ cNode->nodeData.nodeIndex ];
|
|
|
|
int cx , cy , cz;
|
|
Cube::FactorCornerIndex( c , cx , cy , cz );
|
|
|
|
if( isInterior )
|
|
{
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::DownSampleSize[cx] ; ii++ ) for( int jj=0 ; jj<BSplineSupportSizes< FEMDegree >::DownSampleSize[cy] ; jj++ )
|
|
{
|
|
int _ii = ii + BSplineSupportSizes< FEMDegree >::DownSampleStart[cx] + LeftDownSampleRadius;
|
|
int _jj = jj + BSplineSupportSizes< FEMDegree >::DownSampleStart[cy] + LeftDownSampleRadius;
|
|
for( int kk=0 ; kk<BSplineSupportSizes< FEMDegree >::DownSampleSize[cz] ; kk++ )
|
|
{
|
|
int _kk = kk + BSplineSupportSizes< FEMDegree >::DownSampleStart[cz] + LeftDownSampleRadius;
|
|
const TreeOctNode* _pNode = neighbors.neighbors[_ii][_jj][_kk];
|
|
if( _pNode ) fineCoefficient += (C)( coefficients[ _pNode->nodeData.nodeIndex ] * downSampleStencils[c]( ii , jj , kk ) );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
double downSampleValues[3][ BSplineSupportSizes< FEMDegree >::DownSample0Size > BSplineSupportSizes< FEMDegree >::DownSample1Size ? BSplineSupportSizes< FEMDegree >::DownSample0Size : BSplineSupportSizes< FEMDegree >::DownSample1Size ];
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::DownSampleSize[cx] ; ii++ ) downSampleValues[0][ii] = upSampleEvaluator.value( off[0] + ii + BSplineSupportSizes< FEMDegree >::DownSampleStart[cx] , 2*off[0] + cx );
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::DownSampleSize[cy] ; ii++ ) downSampleValues[1][ii] = upSampleEvaluator.value( off[1] + ii + BSplineSupportSizes< FEMDegree >::DownSampleStart[cy] , 2*off[1] + cy );
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::DownSampleSize[cz] ; ii++ ) downSampleValues[2][ii] = upSampleEvaluator.value( off[2] + ii + BSplineSupportSizes< FEMDegree >::DownSampleStart[cz] , 2*off[2] + cz );
|
|
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::DownSampleSize[cx] ; ii++ ) for( int jj=0 ; jj<BSplineSupportSizes< FEMDegree >::DownSampleSize[cy] ; jj++ )
|
|
{
|
|
double dxy = downSampleValues[0][ii] * downSampleValues[1][jj];
|
|
int _ii = ii + BSplineSupportSizes< FEMDegree >::DownSampleStart[cx] + LeftDownSampleRadius;
|
|
int _jj = jj + BSplineSupportSizes< FEMDegree >::DownSampleStart[cy] + LeftDownSampleRadius;
|
|
for( int kk=0 ; kk<BSplineSupportSizes< FEMDegree >::DownSampleSize[cz] ; kk++ )
|
|
{
|
|
int _kk = kk + BSplineSupportSizes< FEMDegree >::DownSampleStart[cz] + LeftDownSampleRadius;
|
|
const TreeOctNode* _pNode = neighbors.neighbors[_ii][_jj][_kk];
|
|
if( _isValidFEMNode( _pNode ) ) fineCoefficient += (C)( coefficients[ _pNode->nodeData.nodeIndex ] * dxy * downSampleValues[2][kk] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template< class Real >
|
|
template< class C , int FEMDegree , BoundaryType BType >
|
|
void Octree< Real >::_UpSample( LocalDepth highDepth , ConstPointer( C ) lowCoefficients , Pointer( C ) highCoefficients , int threads )
|
|
{
|
|
static const int LeftDownSampleRadius = -( ( BSplineSupportSizes< FEMDegree >::DownSample0Start < BSplineSupportSizes< FEMDegree >::DownSample1Start ) ? BSplineSupportSizes< FEMDegree >::DownSample0Start : BSplineSupportSizes< FEMDegree >::DownSample1Start );
|
|
static const int RightDownSampleRadius = ( ( BSplineSupportSizes< FEMDegree >::DownSample0End > BSplineSupportSizes< FEMDegree >::DownSample1End ) ? BSplineSupportSizes< FEMDegree >::DownSample0End : BSplineSupportSizes< FEMDegree >::DownSample1End );
|
|
typedef TreeOctNode::NeighborKey< LeftDownSampleRadius , RightDownSampleRadius > DownSampleKey;
|
|
|
|
LocalDepth lowDepth = highDepth - 1;
|
|
if( lowDepth<0 ) return;
|
|
|
|
typename BSplineEvaluationData< FEMDegree , BType >::UpSampleEvaluator upSampleEvaluator;
|
|
BSplineEvaluationData< FEMDegree , BType >::SetUpSampleEvaluator( upSampleEvaluator , lowDepth );
|
|
std::vector< DownSampleKey > neighborKeys( std::max< int >( 1 , threads ) );
|
|
|
|
static const int DownSampleSize = BSplineSupportSizes< FEMDegree >::DownSample0Size > BSplineSupportSizes< FEMDegree >::DownSample1Size ? BSplineSupportSizes< FEMDegree >::DownSample0Size : BSplineSupportSizes< FEMDegree >::DownSample1Size;
|
|
Stencil< double , DownSampleSize > downSampleStencils[ Cube::CORNERS ];
|
|
int lowCenter = ( 1<<lowDepth )>>1;
|
|
for( int c=0 ; c<Cube::CORNERS ; c++ )
|
|
{
|
|
int cx , cy , cz;
|
|
Cube::FactorCornerIndex( c , cx , cy , cz );
|
|
static const int DownSampleSize = BSplineSupportSizes< FEMDegree >::DownSample0Size > BSplineSupportSizes< FEMDegree >::DownSample1Size ? BSplineSupportSizes< FEMDegree >::DownSample0Size : BSplineSupportSizes< FEMDegree >::DownSample1Size;
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::DownSampleSize[cx] ; ii++ )
|
|
for( int jj=0 ; jj<BSplineSupportSizes< FEMDegree >::DownSampleSize[cy] ; jj++ )
|
|
for( int kk=0 ; kk<BSplineSupportSizes< FEMDegree >::DownSampleSize[cz] ; kk++ )
|
|
downSampleStencils[c]( ii , jj , kk ) =
|
|
upSampleEvaluator.value( lowCenter + ii + BSplineSupportSizes< FEMDegree >::DownSampleStart[cx] , 2*lowCenter + cx ) *
|
|
upSampleEvaluator.value( lowCenter + jj + BSplineSupportSizes< FEMDegree >::DownSampleStart[cy] , 2*lowCenter + cy ) *
|
|
upSampleEvaluator.value( lowCenter + kk + BSplineSupportSizes< FEMDegree >::DownSampleStart[cz] , 2*lowCenter + cz ) ;
|
|
}
|
|
int lowBegin = _BSplineBegin< FEMDegree , BType >( lowDepth ) , lowEnd = _BSplineEnd< FEMDegree , BType >( lowDepth );
|
|
int highBegin = _BSplineBegin< FEMDegree , BType >( highDepth ) , highEnd = _BSplineEnd< FEMDegree , BType >( highDepth );
|
|
int lowDim = lowEnd - lowBegin , highDim = highEnd - highBegin;
|
|
// Iterate over all child nodes. (This is required since there can be child nodes whose parent is inactive.)
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int k=0 ; k<highDim ; k++ ) for( int j=0 ; j<highDim ; j++ ) for( int i=0 ; i<highDim ; i++ )
|
|
{
|
|
DownSampleKey& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
LocalOffset off , _off;
|
|
off[0] = i + highBegin , off[1] = j + highBegin , off[2] = k + highBegin;
|
|
int highIdx = i + j * highDim + k * highDim * highDim;
|
|
_off[0] = off[0]>>1 , _off[1] = off[1]>>1 , _off[2] = off[2]>>1;
|
|
|
|
// Want to make sure test if contained children are interior.
|
|
// This is more conservative because we are test that overlapping children are interior
|
|
bool isInterior = _IsInteriorlyOverlapped< FEMDegree , FEMDegree >( lowDepth , _off );
|
|
int cx = off[0]&1 , cy = off[1]&1 , cz = off[2]&1;
|
|
int c = Cube::CornerIndex( cx , cy , cz );
|
|
|
|
C& highCoefficient = highCoefficients[ highIdx ];
|
|
|
|
if( isInterior )
|
|
{
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::DownSampleSize[cx] ; ii++ ) for( int jj=0 ; jj<BSplineSupportSizes< FEMDegree >::DownSampleSize[cy] ; jj++ )
|
|
{
|
|
int _i = _off[0] + ii + BSplineSupportSizes< FEMDegree >::DownSampleStart[cx] - lowBegin;
|
|
int _j = _off[1] + jj + BSplineSupportSizes< FEMDegree >::DownSampleStart[cy] - lowBegin;
|
|
for( int kk=0 ; kk<BSplineSupportSizes< FEMDegree >::DownSampleSize[cz] ; kk++ )
|
|
{
|
|
int _k = _off[2] + kk + BSplineSupportSizes< FEMDegree >::DownSampleStart[cz] - lowBegin;
|
|
highCoefficient += (C)( lowCoefficients[ _i + _j*lowDim + _k*lowDim*lowDim ] * downSampleStencils[c]( ii , jj , kk ) );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
double downSampleValues[3][ BSplineSupportSizes< FEMDegree >::DownSample0Size > BSplineSupportSizes< FEMDegree >::DownSample1Size ? BSplineSupportSizes< FEMDegree >::DownSample0Size : BSplineSupportSizes< FEMDegree >::DownSample1Size ];
|
|
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::DownSampleSize[cx] ; ii++ ) downSampleValues[0][ii] = upSampleEvaluator.value( _off[0] + ii + BSplineSupportSizes< FEMDegree >::DownSampleStart[cx] , off[0] );
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::DownSampleSize[cy] ; ii++ ) downSampleValues[1][ii] = upSampleEvaluator.value( _off[1] + ii + BSplineSupportSizes< FEMDegree >::DownSampleStart[cy] , off[1] );
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::DownSampleSize[cz] ; ii++ ) downSampleValues[2][ii] = upSampleEvaluator.value( _off[2] + ii + BSplineSupportSizes< FEMDegree >::DownSampleStart[cz] , off[2] );
|
|
|
|
for( int ii=0 ; ii<BSplineSupportSizes< FEMDegree >::DownSampleSize[cx] ; ii++ ) for( int jj=0 ; jj<BSplineSupportSizes< FEMDegree >::DownSampleSize[cy] ; jj++ )
|
|
{
|
|
double dxy = downSampleValues[0][ii] * downSampleValues[1][jj];
|
|
int _i = _off[0] + ii + BSplineSupportSizes< FEMDegree >::DownSampleStart[cx] - lowBegin;
|
|
int _j = _off[1] + jj + BSplineSupportSizes< FEMDegree >::DownSampleStart[cy] - lowBegin;
|
|
if( _i>=0 && _i<lowDim && _j>=0 && _j<lowDim )
|
|
for( int kk=0 ; kk<BSplineSupportSizes< FEMDegree >::DownSampleSize[cz] ; kk++ )
|
|
{
|
|
int _k = _off[2] + kk + BSplineSupportSizes< FEMDegree >::DownSampleStart[cz] - lowBegin;
|
|
if( _k>=0 && _k<lowDim ) highCoefficient += (C)( lowCoefficients[ _i + _j*lowDim + _k*lowDim*lowDim ] * dxy * downSampleValues[2][kk] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template< class Real >
|
|
template< class C , int FEMDegree , BoundaryType BType >
|
|
DenseNodeData< C , FEMDegree > Octree< Real >::coarseCoefficients( const DenseNodeData< C , FEMDegree >& coefficients ) const
|
|
{
|
|
DenseNodeData< Real , FEMDegree > coarseCoefficients( _sNodesEnd(_maxDepth-1) );
|
|
memset( &coarseCoefficients[0] , 0 , sizeof(Real)*_sNodesEnd(_maxDepth-1) );
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(0) ; i<_sNodesEnd(_maxDepth-1) ; i++ ) coarseCoefficients[i] = coefficients[i];
|
|
for( LocalDepth d=1 ; d<_maxDepth ; d++ ) _upSample< C , FEMDegree , BType >( d , coarseCoefficients );
|
|
return coarseCoefficients;
|
|
}
|
|
template< class Real >
|
|
template< class C , int FEMDegree , BoundaryType BType >
|
|
DenseNodeData< C , FEMDegree > Octree< Real >::coarseCoefficients( const SparseNodeData< C , FEMDegree >& coefficients ) const
|
|
{
|
|
DenseNodeData< Real , FEMDegree > coarseCoefficients( _sNodesEnd(_maxDepth-1) );
|
|
memset( &coarseCoefficients[0] , 0 , sizeof(Real)*_sNodesEnd(_maxDepth-1) );
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(0) ; i<_sNodesEnd(_maxDepth-1) ; i++ )
|
|
{
|
|
const C* c = coefficients( _sNodes.treeNodes[i] );
|
|
if( c ) coarseCoefficients[i] = *c;
|
|
}
|
|
for( LocalDepth d=1 ; d<_maxDepth ; d++ ) _upSample< C , FEMDegree , BType >( d , coarseCoefficients );
|
|
return coarseCoefficients;
|
|
}
|
|
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType >
|
|
Real Octree< Real >::_coarserFunctionValue( Point3D< Real > p , const PointSupportKey< FEMDegree >& neighborKey , const TreeOctNode* pointNode , const BSplineData< FEMDegree , BType >& bsData , const DenseNodeData< Real , FEMDegree >& upSampledCoefficients ) const
|
|
{
|
|
static const int SupportSize = BSplineSupportSizes< FEMDegree >::SupportSize;
|
|
static const int LeftSupportRadius = - BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
static const int RightSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
static const int LeftPointSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
static const int RightPointSupportRadius = - BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
|
|
double pointValue = 0;
|
|
LocalDepth depth = _localDepth( pointNode );
|
|
if( depth<0 ) return (Real)0.;
|
|
|
|
// Iterate over all basis functions that overlap the point at the coarser resolution
|
|
{
|
|
const typename TreeOctNode::Neighbors< SupportSize >& neighbors = neighborKey.neighbors[ _localToGlobal( depth-1 ) ];
|
|
LocalDepth _d ; LocalOffset _off;
|
|
_localDepthAndOffset( pointNode->parent , _d , _off );
|
|
int fStart , fEnd;
|
|
BSplineData< FEMDegree , BType >::FunctionSpan( _d , fStart , fEnd );
|
|
|
|
double pointValues[ DIMENSION ][SupportSize];
|
|
memset( pointValues , 0 , sizeof(double) * DIMENSION * SupportSize );
|
|
|
|
for( int dd=0 ; dd<DIMENSION ; dd++ ) for( int i=-LeftPointSupportRadius ; i<=RightPointSupportRadius ; i++ )
|
|
{
|
|
int fIdx = BSplineData< FEMDegree , BType >::FunctionIndex( _d , _off[dd]+i );
|
|
if( fIdx>=fStart && fIdx<fEnd ) pointValues[dd][i+LeftPointSupportRadius] = bsData.baseBSplines[ fIdx ][LeftSupportRadius-i]( p[dd] );
|
|
}
|
|
|
|
for( int j=0 ; j<SupportSize ; j++ ) for( int k=0 ; k<SupportSize ; k++ )
|
|
{
|
|
double xyValue = pointValues[0][j] * pointValues[1][k];
|
|
double _pointValue = 0;
|
|
for( int l=0 ; l<SupportSize ; l++ )
|
|
{
|
|
const TreeOctNode* _node = neighbors.neighbors[j][k][l];
|
|
if( _isValidFEMNode( _node ) ) _pointValue += pointValues[2][l] * double( upSampledCoefficients[_node->nodeData.nodeIndex] );
|
|
}
|
|
pointValue += _pointValue * xyValue;
|
|
}
|
|
}
|
|
return Real( pointValue );
|
|
}
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType >
|
|
Point3D< Real > Octree< Real >::_coarserFunctionGradient( Point3D< Real > p , const PointSupportKey< FEMDegree >& neighborKey , const TreeOctNode* pointNode , const BSplineData< FEMDegree , BType >& bsData , const DenseNodeData< Real , FEMDegree >& upSampledCoefficients ) const
|
|
{
|
|
static const int SupportSize = BSplineSupportSizes< FEMDegree >::SupportSize;
|
|
static const int LeftSupportRadius = - BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
static const int RightSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
static const int LeftPointSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
static const int RightPointSupportRadius = - BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
|
|
Point3D< double > pointGradient;
|
|
LocalDepth depth = _localDepth( pointNode );
|
|
if( depth<=0 ) return Real(0.);
|
|
|
|
// Iterate over all basis functions that overlap the point at the coarser resolution
|
|
{
|
|
const typename TreeOctNode::Neighbors< SupportSize >& neighbors = neighborKey.neighbors[ _localToGlobal( depth-1 ) ];
|
|
LocalDepth _d ; LocalOffset _off;
|
|
_localDepthAndOffset( pointNode->parent , _d , _off );
|
|
int fStart , fEnd;
|
|
BSplineData< FEMDegree , BType >::FunctionSpan( _d , fStart , fEnd );
|
|
|
|
double _pointValues[ DIMENSION ][SupportSize] , dPointValues[ DIMENSION ][SupportSize];
|
|
memset( _pointValues , 0 , sizeof(double) * DIMENSION * SupportSize );
|
|
memset( dPointValues , 0 , sizeof(double) * DIMENSION * SupportSize );
|
|
|
|
for( int dd=0 ; dd<DIMENSION ; dd++ ) for( int i=-LeftPointSupportRadius ; i<=RightPointSupportRadius ; i++ )
|
|
{
|
|
int fIdx = BSplineData< FEMDegree , BType >::FunctionIndex( _d , _off[dd]+i );
|
|
if( fIdx>=fStart && fIdx<fEnd )
|
|
{
|
|
_pointValues[dd][i+LeftPointSupportRadius] = bsData.baseBSplines[ fIdx ][LeftSupportRadius-i]( p[dd] );
|
|
dPointValues[dd][i+LeftPointSupportRadius] = bsData.dBaseBSplines[ fIdx ][LeftSupportRadius-i]( p[dd] );
|
|
}
|
|
}
|
|
|
|
for( int j=0 ; j<SupportSize ; j++ ) for( int k=0 ; k<SupportSize ; k++ )
|
|
{
|
|
double _x_yValue = _pointValues[0][j] * _pointValues[1][k];
|
|
double dx_yValue = dPointValues[0][j] * _pointValues[1][k];
|
|
double _xdyValue = _pointValues[0][j] * dPointValues[1][k];
|
|
double __pointValue = 0 , _dPointValue = 0;
|
|
for( int l=0 ; l<SupportSize ; l++ )
|
|
{
|
|
const TreeOctNode* _node = neighbors.neighbors[j][k][l];
|
|
if( _isValidFEMNode( _node ) )
|
|
{
|
|
__pointValue += _pointValues[2][l] * double( upSampledCoefficients[_node->nodeData.nodeIndex] );
|
|
_dPointValue += dPointValues[2][l] * double( upSampledCoefficients[_node->nodeData.nodeIndex] );
|
|
}
|
|
}
|
|
|
|
pointGradient += Point3D< double >( __pointValue * dx_yValue , __pointValue * _xdyValue , _dPointValue * _x_yValue );
|
|
}
|
|
}
|
|
return Point3D< Real >( pointGradient );
|
|
}
|
|
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType >
|
|
Real Octree< Real >::_finerFunctionValue( Point3D< Real > p , const PointSupportKey< FEMDegree >& neighborKey , const TreeOctNode* pointNode , const BSplineData< FEMDegree , BType >& bsData , const DenseNodeData< Real , FEMDegree >& finerCoefficients ) const
|
|
{
|
|
typename TreeOctNode::Neighbors< BSplineSupportSizes< FEMDegree >::SupportSize > childNeighbors;
|
|
static const int LeftPointSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
static const int RightPointSupportRadius = -BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
static const int LeftSupportRadius = -BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
static const int RightSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
|
|
double pointValue = 0;
|
|
LocalDepth depth = _localDepth( pointNode );
|
|
neighborKey.template getChildNeighbors< false >( _childIndex( pointNode , p ) , _localToGlobal( depth ) , childNeighbors );
|
|
for( int j=-LeftPointSupportRadius ; j<=RightPointSupportRadius ; j++ )
|
|
for( int k=-LeftPointSupportRadius ; k<=RightPointSupportRadius ; k++ )
|
|
for( int l=-LeftPointSupportRadius ; l<=RightPointSupportRadius ; l++ )
|
|
{
|
|
const TreeOctNode* _node = childNeighbors.neighbors[j+LeftPointSupportRadius][k+LeftPointSupportRadius][l+LeftPointSupportRadius];
|
|
if( _isValidFEMNode( _node ) )
|
|
{
|
|
int fIdx[3];
|
|
functionIndex< FEMDegree , BType >( _node , fIdx );
|
|
pointValue +=
|
|
bsData.baseBSplines[ fIdx[0] ][LeftSupportRadius-j]( p[0] ) *
|
|
bsData.baseBSplines[ fIdx[1] ][LeftSupportRadius-k]( p[1] ) *
|
|
bsData.baseBSplines[ fIdx[2] ][LeftSupportRadius-l]( p[2] ) *
|
|
double( finerCoefficients[ _node->nodeData.nodeIndex ] );
|
|
}
|
|
}
|
|
return Real( pointValue );
|
|
}
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType >
|
|
Point3D< Real > Octree< Real >::_finerFunctionGradient( Point3D< Real > p , const PointSupportKey< FEMDegree >& neighborKey , const TreeOctNode* pointNode , const BSplineData< FEMDegree , BType >& bsData , const DenseNodeData< Real , FEMDegree >& finerCoefficients ) const
|
|
{
|
|
typename TreeOctNode::Neighbors< BSplineSupportSizes< FEMDegree >::SupportSize > childNeighbors;
|
|
static const int LeftPointSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
static const int RightPointSupportRadius = -BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
static const int LeftSupportRadius = -BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
static const int RightSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
|
|
Point3D< double > pointGradient = 0;
|
|
LocalDepth depth = _localDepth( pointNode );
|
|
neighborKey.template getChildNeighbors< false >( _childIndex( pointNode , p ) , _localToGlobal( depth ) , childNeighbors );
|
|
for( int j=-LeftPointSupportRadius ; j<=RightPointSupportRadius ; j++ )
|
|
for( int k=-LeftPointSupportRadius ; k<=RightPointSupportRadius ; k++ )
|
|
for( int l=-LeftPointSupportRadius ; l<=RightPointSupportRadius ; l++ )
|
|
{
|
|
const TreeOctNode* _node = childNeighbors.neighbors[j+LeftPointSupportRadius][k+LeftPointSupportRadius][l+LeftPointSupportRadius];
|
|
if( _isValidFEMNode( _node ) )
|
|
{
|
|
int fIdx[3];
|
|
functionIndex< FEMDegree , BType >( _node , fIdx );
|
|
double x = bsData. baseBSplines[ fIdx[0] ][LeftSupportRadius-j]( p[0] ) , y = bsData. baseBSplines[ fIdx[1] ][LeftSupportRadius-k]( p[1] ) , z = bsData. baseBSplines[ fIdx[2] ][LeftSupportRadius-l]( p[2] );
|
|
double dx = bsData.dBaseBSplines[ fIdx[0] ][LeftSupportRadius-j]( p[0] ) , dy = bsData.dBaseBSplines[ fIdx[1] ][LeftSupportRadius-k]( p[1] ) , dz = bsData.dBaseBSplines[ fIdx[2] ][LeftSupportRadius-l]( p[2] );
|
|
pointGradient += Point3D< double >( dx * y * z , x * dy * z , x * y * dz ) * (double)( finerCoefficients[ _node->nodeData.nodeIndex ] );
|
|
}
|
|
}
|
|
return Point3D< Real >( pointGradient );
|
|
}
|
|
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType , bool HasGradients >
|
|
void Octree< Real >::_setPointValuesFromCoarser( InterpolationInfo< HasGradients >& interpolationInfo , LocalDepth highDepth , const BSplineData< FEMDegree , BType >& bsData , const DenseNodeData< Real , FEMDegree >& upSampledCoefficients )
|
|
{
|
|
LocalDepth lowDepth = highDepth-1;
|
|
if( lowDepth<0 ) return;
|
|
std::vector< PointSupportKey< FEMDegree > > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( lowDepth ) );
|
|
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(highDepth) ; i<_sNodesEnd(highDepth) ; i++ ) if( _isValidFEMNode( _sNodes.treeNodes[i] ) )
|
|
{
|
|
PointSupportKey< FEMDegree >& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
PointData< Real , HasGradients >* pData = interpolationInfo( _sNodes.treeNodes[i] );
|
|
if( pData )
|
|
{
|
|
neighborKey.template getNeighbors< false >( _sNodes.treeNodes[i]->parent );
|
|
#if POINT_DATA_RES
|
|
for( int c=0 ; c<PointData< Real , HasGradients >::SAMPLES ; c++ ) if( (*pData)[c].weight )
|
|
_ConstraintCalculator_< Real , FEMDegree , HasGradients >::_CalculateCoarser_
|
|
(
|
|
c , *pData ,
|
|
_coarserFunctionValue( (*pData)[c].position , neighborKey , _sNodes.treeNodes[i] , bsData , upSampledCoefficients ) ,
|
|
HasGradients ? _coarserFunctionGradient( (*pData)[c].position , neighborKey , _sNodes.treeNodes[i] , bsData , upSampledCoefficients ) : Point3D< Real >() ,
|
|
interpolationInfo.valueWeight , interpolationInfo.gradientWeight
|
|
);
|
|
#else // !POINT_DATA_RES
|
|
_ConstraintCalculator_< Real , FEMDegree , HasGradients >::_CalculateCoarser_
|
|
(
|
|
*pData ,
|
|
_coarserFunctionValue( pData->position , neighborKey , _sNodes.treeNodes[i] , bsData , upSampledCoefficients ) ,
|
|
HasGradients ? _coarserFunctionGradient( pData->position , neighborKey , _sNodes.treeNodes[i] , bsData , upSampledCoefficients ) : Point3D< Real >() ,
|
|
interpolationInfo.valueWeight , interpolationInfo.gradientWeight
|
|
);
|
|
#endif // POINT_DATA_RES
|
|
}
|
|
}
|
|
}
|
|
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType , bool HasGradients >
|
|
void Octree< Real >::_updateCumulativeInterpolationConstraintsFromFiner( const InterpolationInfo< HasGradients >& interpolationInfo , const BSplineData< FEMDegree , BType >& bsData , LocalDepth highDepth , const DenseNodeData< Real , FEMDegree >& finerCoefficients , DenseNodeData< Real , FEMDegree >& coarserConstraints ) const
|
|
{
|
|
static const int SupportSize = BSplineSupportSizes< FEMDegree >::SupportSize;
|
|
static const int LeftPointSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
static const int RightPointSupportRadius = -BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
static const int LeftSupportRadius = -BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
static const int RightSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
|
|
// Note: We can't iterate over the finer point nodes as the point weights might be
|
|
// scaled incorrectly, due to the adaptive exponent. So instead, we will iterate
|
|
// over the coarser nodes and evaluate the finer solution at the associated points.
|
|
LocalDepth lowDepth = highDepth-1;
|
|
if( lowDepth<0 ) return;
|
|
size_t start = _sNodesBegin(lowDepth) , end = _sNodesEnd(lowDepth);
|
|
std::vector< PointSupportKey< FEMDegree > > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( lowDepth ) );
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(lowDepth) ; i<_sNodesEnd(lowDepth) ; i++ ) if( _isValidSpaceNode( _sNodes.treeNodes[i] ) )
|
|
{
|
|
PointSupportKey< FEMDegree >& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
const PointData< Real , HasGradients >* pData = interpolationInfo( _sNodes.treeNodes[i] );
|
|
if( pData )
|
|
{
|
|
typename TreeOctNode::Neighbors< SupportSize >& neighbors = neighborKey.template getNeighbors< false >( _sNodes.treeNodes[i] );
|
|
// evaluate the solution @( depth ) at the current point @( depth-1 )
|
|
#if POINT_DATA_RES
|
|
for( int c=0 ; c<PointData< Real , HasGradients >::SAMPLES ; c++ ) if( (*pData)[c].weight )
|
|
#endif // POINT_DATA_RES
|
|
{
|
|
#if POINT_DATA_RES
|
|
Real finerPointDValue = _finerFunctionValue( (*pData)[c].position , neighborKey , _sNodes.treeNodes[i] , bsData , finerCoefficients ) * interpolationInfo.valueWeight * (*pData)[c].weight;
|
|
Point3D< Real > finerPointDGradient = HasGradients ? _finerFunctionGradient( (*pData)[c].position , neighborKey , _sNodes.treeNodes[i] , bsData , finerCoefficients ) * interpolationInfo.gradientWeight * (*pData)[c].weight : Point3D< Real >();
|
|
Point3D< Real > p = (*pData)[c].position;
|
|
#else // !POINT_DATA_RES
|
|
Real finerPointDValue = _finerFunctionValue( pData->position , neighborKey , _sNodes.treeNodes[i] , bsData , finerCoefficients ) * interpolationInfo.valueWeight * pData->weight;
|
|
Point3D< Real > finerPointDGradient = HasGradients ? _finerFunctionGradient( pData->position , neighborKey , _sNodes.treeNodes[i] , bsData , finerCoefficients ) * interpolationInfo.gradientWeight * pData->weight : Point3D< Real >();
|
|
Point3D< Real > p = pData->position;
|
|
#endif // POINT_DATA_RES
|
|
// Update constraints for all nodes @( depth-1 ) that overlap the point
|
|
int idx[3];
|
|
functionIndex< FEMDegree , BType >( _sNodes.treeNodes[i] , idx );
|
|
for( int x=-LeftPointSupportRadius ; x<=RightPointSupportRadius ; x++ ) for( int y=-LeftPointSupportRadius ; y<=RightPointSupportRadius ; y++ ) for( int z=-LeftPointSupportRadius ; z<=RightPointSupportRadius ; z++ )
|
|
{
|
|
const TreeOctNode* _node = neighbors.neighbors[x+LeftPointSupportRadius][y+LeftPointSupportRadius][z+LeftPointSupportRadius];
|
|
if( _isValidFEMNode( _node ) )
|
|
{
|
|
double px = bsData.baseBSplines[idx[0]+x][LeftSupportRadius-x]( p[0] ) , py = bsData.baseBSplines[idx[1]+y][LeftSupportRadius-y]( p[1] ) , pz = bsData.baseBSplines[idx[2]+z][LeftSupportRadius-z]( p[2] );
|
|
#pragma omp atomic
|
|
coarserConstraints[ _node->nodeData.nodeIndex ] += (Real)( px * py * pz * finerPointDValue );
|
|
if( HasGradients )
|
|
{
|
|
double dpx = bsData.dBaseBSplines[idx[0]+x][LeftSupportRadius-x]( p[0] ) , dpy = bsData.dBaseBSplines[idx[1]+y][LeftSupportRadius-y]( p[1] ) , dpz = bsData.dBaseBSplines[idx[2]+z][LeftSupportRadius-z]( p[2] );
|
|
#pragma omp atomic
|
|
coarserConstraints[ _node->nodeData.nodeIndex ] += Point3D< Real >::Dot( finerPointDGradient , Point3D< Real >( dpx * py * pz , px * dpy * pz , px * py * dpz ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType , class FEMSystemFunctor , bool HasGradients >
|
|
int Octree< Real >::_setMatrixRow( const FEMSystemFunctor& F , const InterpolationInfo< HasGradients >* interpolationInfo , const typename TreeOctNode::Neighbors< BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapSize >& neighbors , Pointer( MatrixEntry< Real > ) row , int offset , const typename BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::FunctionIntegrator::template Integrator< DERIVATIVES( FEMDegree ) , DERIVATIVES( FEMDegree ) >& integrator , const Stencil< double , BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapSize >& stencil , const BSplineData< FEMDegree , BType >& bsData ) const
|
|
{
|
|
static const int SupportSize = BSplineSupportSizes< FEMDegree >::SupportSize;
|
|
static const int OverlapRadius = - BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapStart;
|
|
static const int OverlapSize = BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapSize;
|
|
static const int LeftSupportRadius = -BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
static const int RightSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
static const int LeftPointSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
static const int RightPointSupportRadius = -BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
|
|
bool hasYZPoints[SupportSize] , hasZPoints[SupportSize][SupportSize];
|
|
Real diagonal = 0;
|
|
// Given a node:
|
|
// -- for each node in its support:
|
|
// ---- if the supporting node contains a point:
|
|
// ------ evaluate the x, y, and z B-splines of the nodes supporting the point
|
|
// splineValues \in [-LeftSupportRadius,RightSupportRadius] x [-LeftSupportRadius,RightSupportRadius] x [-LeftSupportRadius,RightSupportRadius] x [0,Dimension) x [-LeftPointSupportRadius,RightPointSupportRadius]
|
|
#if POINT_DATA_RES
|
|
Real _splineValues[PointData< Real , HasGradients >::SAMPLES][SupportSize][SupportSize][SupportSize][DIMENSION][SupportSize];
|
|
Real wSplineValues[PointData< Real , HasGradients >::SAMPLES][SupportSize][SupportSize][SupportSize][DIMENSION][SupportSize];
|
|
Real dSplineValues[PointData< Real , HasGradients >::SAMPLES][SupportSize][SupportSize][SupportSize][DIMENSION][SupportSize];
|
|
memset( _splineValues , 0 , sizeof( Real ) * PointData< Real , HasGradients >::SAMPLES * SupportSize * SupportSize * SupportSize * DIMENSION *SupportSize );
|
|
memset( wSplineValues , 0 , sizeof( Real ) * PointData< Real , HasGradients >::SAMPLES * SupportSize * SupportSize * SupportSize * DIMENSION *SupportSize );
|
|
memset( dSplineValues , 0 , sizeof( Real ) * PointData< Real , HasGradients >::SAMPLES * SupportSize * SupportSize * SupportSize * DIMENSION *SupportSize );
|
|
#else // !POINT_DATA_RES
|
|
Real _splineValues[SupportSize][SupportSize][SupportSize][DIMENSION][SupportSize];
|
|
Real wSplineValues[SupportSize][SupportSize][SupportSize][DIMENSION][SupportSize];
|
|
Real dSplineValues[SupportSize][SupportSize][SupportSize][DIMENSION][SupportSize];
|
|
memset( _splineValues , 0 , sizeof( Real ) * SupportSize * SupportSize * SupportSize * DIMENSION *SupportSize );
|
|
memset( wSplineValues , 0 , sizeof( Real ) * SupportSize * SupportSize * SupportSize * DIMENSION *SupportSize );
|
|
memset( dSplineValues , 0 , sizeof( Real ) * SupportSize * SupportSize * SupportSize * DIMENSION *SupportSize );
|
|
#endif // NEW_POINT_DATA
|
|
|
|
int count = 0;
|
|
const TreeOctNode* node = neighbors.neighbors[OverlapRadius][OverlapRadius][OverlapRadius];
|
|
LocalDepth d ; LocalOffset off;
|
|
_localDepthAndOffset( node , d , off );
|
|
int fStart , fEnd;
|
|
BSplineData< FEMDegree , BType >::FunctionSpan( d , fStart , fEnd );
|
|
bool isInterior = _isInteriorlyOverlapped< FEMDegree , FEMDegree >( node );
|
|
|
|
if( interpolationInfo )
|
|
{
|
|
// Iterate over all neighboring nodes that may have a constraining point
|
|
// -- For each one, compute the values of the spline functions supported on the point
|
|
for( int j=0 ; j<SupportSize ; j++ )
|
|
{
|
|
hasYZPoints[j] = false;
|
|
for( int k=0 ; k<SupportSize ; k++ ) hasZPoints[j][k] = false;
|
|
}
|
|
for( int j=-LeftSupportRadius , jj=0 ; j<=RightSupportRadius ; j++ , jj++ )
|
|
for( int k=-LeftSupportRadius , kk=0 ; k<=RightSupportRadius ; k++ , kk++ )
|
|
for( int l=-LeftSupportRadius , ll=0 ; l<=RightSupportRadius ; l++ , ll++ )
|
|
{
|
|
const TreeOctNode* _node = neighbors.neighbors[OverlapRadius+j][OverlapRadius+k][OverlapRadius+l];
|
|
if( _isValidSpaceNode( _node ) && (*interpolationInfo)( _node ) )
|
|
{
|
|
int pOff[] = { off[0]+j , off[1]+k , off[2]+l };
|
|
hasYZPoints[jj] = hasZPoints[jj][kk] = true;
|
|
const PointData< Real , HasGradients >& pData = *( (*interpolationInfo)( _node ) );
|
|
|
|
#if POINT_DATA_RES
|
|
for( int c=0 ; c<PointData< Real , HasGradients >::SAMPLES ; c++ ) if( pData[c].weight )
|
|
#endif // POINT_DATA_RES
|
|
{
|
|
#if POINT_DATA_RES
|
|
Real (*__splineValues)[SupportSize] = _splineValues[c][jj][kk][ll];
|
|
Real (*_wSplineValues)[SupportSize] = wSplineValues[c][jj][kk][ll];
|
|
Real (*_dSplineValues)[SupportSize] = dSplineValues[c][jj][kk][ll];
|
|
Real weight = pData[c].weight;
|
|
Point3D< Real > p = pData[c].position;
|
|
#else // !POINT_DATA_RES
|
|
Real (*__splineValues)[SupportSize] = _splineValues[jj][kk][ll];
|
|
Real (*_wSplineValues)[SupportSize] = wSplineValues[jj][kk][ll];
|
|
Real (*_dSplineValues)[SupportSize] = dSplineValues[jj][kk][ll];
|
|
Real weight = pData.weight;
|
|
Point3D< Real > p = pData.position;
|
|
#endif // POINT_DATA_RES
|
|
|
|
// evaluate the point p at all the nodes whose functions have it in their support
|
|
for( int s=-LeftPointSupportRadius ; s<=RightPointSupportRadius ; s++ ) for( int dd=0 ; dd<DIMENSION ; dd++ )
|
|
{
|
|
int fIdx = BSplineData< FEMDegree , BType >::FunctionIndex( d , pOff[dd]+s );
|
|
if( fIdx>=fStart && fIdx<fEnd )
|
|
{
|
|
_wSplineValues[dd][ s+LeftPointSupportRadius ] = __splineValues[dd][ s+LeftPointSupportRadius ] = Real( bsData.baseBSplines[ fIdx ][ -s+LeftSupportRadius ]( p[dd] ) );
|
|
if( HasGradients ) _dSplineValues[dd][ s+LeftPointSupportRadius ] = Real( bsData.dBaseBSplines[ fIdx ][ -s+LeftSupportRadius ]( p[dd] ) );
|
|
}
|
|
}
|
|
// The value of the function of the node that we started with
|
|
Real value = __splineValues[0][-j+LeftPointSupportRadius] * __splineValues[1][-k+LeftPointSupportRadius] * __splineValues[2][-l+LeftPointSupportRadius];
|
|
Real weightedValue = value * interpolationInfo->valueWeight * weight;
|
|
Point3D< Real > weightedGradient;
|
|
if( HasGradients )
|
|
{
|
|
Point3D< Real > gradient
|
|
(
|
|
_dSplineValues[0][-j+LeftPointSupportRadius] * __splineValues[1][-k+LeftPointSupportRadius] * __splineValues[2][-l+LeftPointSupportRadius] ,
|
|
__splineValues[0][-j+LeftPointSupportRadius] * _dSplineValues[1][-k+LeftPointSupportRadius] * __splineValues[2][-l+LeftPointSupportRadius] ,
|
|
__splineValues[0][-j+LeftPointSupportRadius] * __splineValues[1][-k+LeftPointSupportRadius] * _dSplineValues[2][-l+LeftPointSupportRadius]
|
|
);
|
|
weightedGradient = gradient * interpolationInfo->gradientWeight * weight;
|
|
diagonal += value * weightedValue + Point3D< Real >::Dot( gradient , weightedGradient );
|
|
}
|
|
else diagonal += value * weightedValue;
|
|
|
|
// Pre-multiply the x-coordinate values so that when we evaluate at one of the neighboring basis functions
|
|
// we get the product of the values of the center base function and the base function of the neighboring node
|
|
if( HasGradients ) for( int s=0 ; s<SupportSize ; s++ ) _wSplineValues[0][s] *= weightedValue , _dSplineValues[0][s] *= weightedGradient[0] , _dSplineValues[1][s] *= weightedGradient[1] , _dSplineValues[2][s] *= weightedGradient[2];
|
|
else for( int s=0 ; s<SupportSize ; s++ ) _wSplineValues[0][s] *= weightedValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Real pointValues[OverlapSize][OverlapSize][OverlapSize];
|
|
if( interpolationInfo )
|
|
{
|
|
memset( pointValues , 0 , sizeof(Real) * OverlapSize * OverlapSize * OverlapSize );
|
|
// Iterate over all supported neighbors that could have a point constraint
|
|
for( int i=-LeftSupportRadius ; i<=RightSupportRadius ; i++ ) if( hasYZPoints[i+LeftSupportRadius] )
|
|
for( int j=-LeftSupportRadius ; j<=RightSupportRadius ; j++ ) if( hasZPoints[i+LeftSupportRadius][j+LeftSupportRadius] )
|
|
for( int k=-LeftSupportRadius ; k<=RightSupportRadius ; k++ )
|
|
{
|
|
const TreeOctNode* _node = neighbors.neighbors[i+OverlapRadius][j+OverlapRadius][k+OverlapRadius];
|
|
if( _isValidSpaceNode( _node ) && (*interpolationInfo)( _node ) )
|
|
{
|
|
const PointData< Real , HasGradients >& pData = *( (*interpolationInfo)( _node ) );
|
|
#if POINT_DATA_RES
|
|
for( int c=0 ; c<PointData< Real , HasGradients >::SAMPLES ; c++ ) if( pData[c].weight )
|
|
#endif // POINT_DATA_RES
|
|
{
|
|
#if POINT_DATA_RES
|
|
Real (*__splineValues)[SupportSize] = _splineValues[c][i+LeftSupportRadius][j+LeftSupportRadius][k+LeftSupportRadius];
|
|
Real (*_wSplineValues)[SupportSize] = wSplineValues[c][i+LeftSupportRadius][j+LeftSupportRadius][k+LeftSupportRadius];
|
|
Real (*_dSplineValues)[SupportSize] = dSplineValues[c][i+LeftSupportRadius][j+LeftSupportRadius][k+LeftSupportRadius];
|
|
#else // !POINT_DATA_RES
|
|
Real (*__splineValues)[SupportSize] = _splineValues[i+LeftSupportRadius][j+LeftSupportRadius][k+LeftSupportRadius];
|
|
Real (*_wSplineValues)[SupportSize] = wSplineValues[i+LeftSupportRadius][j+LeftSupportRadius][k+LeftSupportRadius];
|
|
Real (*_dSplineValues)[SupportSize] = dSplineValues[i+LeftSupportRadius][j+LeftSupportRadius][k+LeftSupportRadius];
|
|
#endif // POINT_DATA_RES
|
|
// Iterate over all neighbors whose support contains the point and accumulate the mutual integral
|
|
for( int ii=-LeftPointSupportRadius ; ii<=RightPointSupportRadius ; ii++ )
|
|
for( int jj=-LeftPointSupportRadius ; jj<=RightPointSupportRadius ; jj++ )
|
|
if( HasGradients )
|
|
{
|
|
Real partialW_SplineValue = _wSplineValues[0][ii+LeftPointSupportRadius ] * __splineValues[1][jj+LeftPointSupportRadius ];
|
|
Real partial__SplineValue = __splineValues[0][ii+LeftPointSupportRadius ] * __splineValues[1][jj+LeftPointSupportRadius ];
|
|
Real partialD0SplineValue = _dSplineValues[0][ii+LeftPointSupportRadius ] * __splineValues[1][jj+LeftPointSupportRadius ];
|
|
Real partialD1SplineValue = __splineValues[0][ii+LeftPointSupportRadius ] * _dSplineValues[1][jj+LeftPointSupportRadius ];
|
|
Real* _pointValues = pointValues[i+ii+OverlapRadius][j+jj+OverlapRadius] + k + OverlapRadius;
|
|
Real* ___splineValues = __splineValues[2] + LeftPointSupportRadius;
|
|
Real* __dSplineValues = _dSplineValues[2] + LeftPointSupportRadius;
|
|
TreeOctNode* const * _neighbors = neighbors.neighbors[i+ii+OverlapRadius][j+jj+OverlapRadius] + k + OverlapRadius;
|
|
for( int kk=-LeftPointSupportRadius ; kk<=RightPointSupportRadius ; kk++ ) if( _isValidFEMNode( _neighbors[kk] ) )
|
|
_pointValues[kk] +=
|
|
partialW_SplineValue * ___splineValues[kk] + partialD0SplineValue * ___splineValues[kk] + partialD1SplineValue * ___splineValues[kk] + partial__SplineValue * __dSplineValues[kk];
|
|
}
|
|
else
|
|
{
|
|
Real partialWSplineValue = _wSplineValues[0][ii+LeftPointSupportRadius ] * __splineValues[1][jj+LeftPointSupportRadius ];
|
|
Real* _pointValues = pointValues[i+ii+OverlapRadius][j+jj+OverlapRadius] + k + OverlapRadius;
|
|
Real* ___splineValues = __splineValues[2] + LeftPointSupportRadius;
|
|
TreeOctNode* const * _neighbors = neighbors.neighbors[i+ii+OverlapRadius][j+jj+OverlapRadius] + k + OverlapRadius;
|
|
for( int kk=-LeftPointSupportRadius ; kk<=RightPointSupportRadius ; kk++ ) if( _isValidFEMNode( _neighbors[kk] ) )
|
|
_pointValues[kk] += partialWSplineValue * ___splineValues[kk];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pointValues[OverlapRadius][OverlapRadius][OverlapRadius] = diagonal;
|
|
int nodeIndex = neighbors.neighbors[OverlapRadius][OverlapRadius][OverlapRadius]->nodeData.nodeIndex;
|
|
if( isInterior ) // General case, so try to make fast
|
|
{
|
|
const TreeOctNode* const * _nodes = &neighbors.neighbors[0][0][0];
|
|
const double* _stencil = &stencil( 0 , 0 , 0 );
|
|
Real* _values = &pointValues[0][0][0];
|
|
const static int CenterIndex = OverlapSize*OverlapSize*OverlapRadius + OverlapSize*OverlapRadius + OverlapRadius;
|
|
if( interpolationInfo ) for( int i=0 ; i<OverlapSize*OverlapSize*OverlapSize ; i++ ) _values[i] = Real( _stencil[i] + _values[i] );
|
|
else for( int i=0 ; i<OverlapSize*OverlapSize*OverlapSize ; i++ ) _values[i] = Real( _stencil[i] );
|
|
|
|
row[count++] = MatrixEntry< Real >( nodeIndex-offset , _values[CenterIndex] );
|
|
for( int i=0 ; i<OverlapSize*OverlapSize*OverlapSize ; i++ ) if( i!=CenterIndex && _isValidFEMNode( _nodes[i] ) )
|
|
row[count++] = MatrixEntry< Real >( _nodes[i]->nodeData.nodeIndex-offset , _values[i] );
|
|
}
|
|
else
|
|
{
|
|
LocalDepth d ; LocalOffset off;
|
|
_localDepthAndOffset( node , d , off );
|
|
Real temp = (Real)F.integrate( integrator , off , off );
|
|
if( interpolationInfo ) temp += pointValues[OverlapRadius][OverlapRadius][OverlapRadius];
|
|
row[count++] = MatrixEntry< Real >( nodeIndex-offset , temp );
|
|
for( int x=0 ; x<OverlapSize ; x++ ) for( int y=0 ; y<OverlapSize ; y++ ) for( int z=0 ; z<OverlapSize ; z++ )
|
|
if( (x!=OverlapRadius || y!=OverlapRadius || z!=OverlapRadius) && _isValidFEMNode( neighbors.neighbors[x][y][z] ) )
|
|
{
|
|
const TreeOctNode* _node = neighbors.neighbors[x][y][z];
|
|
LocalDepth _d ; LocalOffset _off;
|
|
_localDepthAndOffset( _node , _d , _off );
|
|
Real temp = (Real)F.integrate( integrator , _off , off );
|
|
if( interpolationInfo ) temp += pointValues[x][y][z];
|
|
row[count++] = MatrixEntry< Real >( _node->nodeData.nodeIndex-offset , temp );
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType , class FEMSystemFunctor , bool HasGradients >
|
|
int Octree< Real >::_getMatrixAndUpdateConstraints( const FEMSystemFunctor& F , const InterpolationInfo< HasGradients >* interpolationInfo , SparseMatrix< Real >& matrix , DenseNodeData< Real , FEMDegree >& constraints , typename BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::FunctionIntegrator::template Integrator< DERIVATIVES( FEMDegree ) , DERIVATIVES( FEMDegree ) >& integrator , typename BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::FunctionIntegrator::template ChildIntegrator< DERIVATIVES( FEMDegree ) , DERIVATIVES( FEMDegree ) >& childIntegrator , const BSplineData< FEMDegree , BType >& bsData , LocalDepth depth , const DenseNodeData< Real , FEMDegree >& metSolution , bool coarseToFine )
|
|
{
|
|
static const int OverlapRadius = - BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapStart;
|
|
static const int OverlapSize = BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapSize;
|
|
|
|
size_t start = _sNodesBegin(depth) , end = _sNodesEnd(depth) , range = end-start;
|
|
Stencil< double , OverlapSize > stencil , stencils[2][2][2];
|
|
SystemCoefficients< FEMDegree , BType , FEMDegree , BType >::SetCentralSystemStencil ( F , integrator , stencil );
|
|
SystemCoefficients< FEMDegree , BType , FEMDegree , BType >::SetCentralSystemStencils( F , childIntegrator , stencils );
|
|
matrix.Resize( (int)range );
|
|
std::vector< AdjacenctNodeKey > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( depth ) );
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=0 ; i<(int)range ; i++ ) if( _isValidFEMNode( _sNodes.treeNodes[i+start] ) )
|
|
{
|
|
AdjacenctNodeKey& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
TreeOctNode* node = _sNodes.treeNodes[i+start];
|
|
// Get the matrix row size
|
|
typename TreeOctNode::Neighbors< OverlapSize > neighbors;
|
|
neighborKey.template getNeighbors< false , OverlapRadius , OverlapRadius >( node , neighbors );
|
|
int count = _getMatrixRowSize< FEMDegree , BType >( neighbors );
|
|
// Allocate memory for the row
|
|
matrix.SetRowSize( i , count );
|
|
|
|
// Set the row entries
|
|
matrix.rowSizes[i] = _setMatrixRow( F , interpolationInfo , neighbors , matrix[i] , (int)start , integrator , stencil , bsData );
|
|
if( coarseToFine && depth>0 )
|
|
{
|
|
// Offset the constraints using the solution from lower resolutions.
|
|
int x , y , z;
|
|
Cube::FactorCornerIndex( (int)( node - node->parent->children ) , x , y , z );
|
|
typename TreeOctNode::Neighbors< OverlapSize > pNeighbors;
|
|
neighborKey.template getNeighbors< false , OverlapRadius , OverlapRadius >( node->parent , pNeighbors );
|
|
_updateConstraintsFromCoarser( F , interpolationInfo , neighbors , pNeighbors , node , constraints , metSolution , childIntegrator , stencils[x][y][z] , bsData );
|
|
}
|
|
}
|
|
memoryUsage();
|
|
return 1;
|
|
}
|
|
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType , class FEMSystemFunctor , bool HasGradients >
|
|
int Octree< Real >::_getSliceMatrixAndUpdateConstraints( const FEMSystemFunctor& F , const InterpolationInfo< HasGradients >* interpolationInfo , SparseMatrix< Real >& matrix , DenseNodeData< Real , FEMDegree >& constraints , typename BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::FunctionIntegrator::template Integrator< DERIVATIVES( FEMDegree ) , DERIVATIVES( FEMDegree ) >& integrator , typename BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::FunctionIntegrator::template ChildIntegrator< DERIVATIVES( FEMDegree ) , DERIVATIVES( FEMDegree ) >& childIntegrator , const BSplineData< FEMDegree , BType >& bsData , LocalDepth depth , int slice , const DenseNodeData< Real , FEMDegree >& metSolution , bool coarseToFine )
|
|
{
|
|
static const int OverlapSize = BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapSize;
|
|
static const int OverlapRadius = -BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapStart;
|
|
|
|
int nStart = _sNodesBegin( depth , slice ) , nEnd = _sNodesEnd( depth , slice );
|
|
size_t range = nEnd - nStart;
|
|
Stencil< double , OverlapSize > stencil , stencils[2][2][2];
|
|
SystemCoefficients< FEMDegree , BType , FEMDegree , BType >::SetCentralSystemStencil ( F , integrator , stencil );
|
|
SystemCoefficients< FEMDegree , BType , FEMDegree , BType >::SetCentralSystemStencils( F , childIntegrator , stencils );
|
|
|
|
matrix.Resize( (int)range );
|
|
std::vector< AdjacenctNodeKey > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( depth ) );
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=0 ; i<(int)range ; i++ ) if( _isValidFEMNode( _sNodes.treeNodes[i+nStart] ) )
|
|
{
|
|
AdjacenctNodeKey& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
TreeOctNode* node = _sNodes.treeNodes[i+nStart];
|
|
// Get the matrix row size
|
|
typename TreeOctNode::Neighbors< OverlapSize > neighbors;
|
|
neighborKey.template getNeighbors< false , OverlapRadius , OverlapRadius >( node , neighbors );
|
|
int count = _getMatrixRowSize< FEMDegree , BType >( neighbors );
|
|
|
|
// Allocate memory for the row
|
|
matrix.SetRowSize( i , count );
|
|
|
|
// Set the row entries
|
|
matrix.rowSizes[i] = _setMatrixRow( F , interpolationInfo , neighbors , matrix[i] , _sNodesBegin( depth , slice ) , integrator , stencil , bsData );
|
|
|
|
if( coarseToFine && depth>0 )
|
|
{
|
|
// Offset the constraints using the solution from lower resolutions.
|
|
int x , y , z;
|
|
Cube::FactorCornerIndex( (int)( node - node->parent->children ) , x , y , z );
|
|
typename TreeOctNode::Neighbors< OverlapSize > pNeighbors;
|
|
neighborKey.template getNeighbors< false, OverlapRadius , OverlapRadius >( node->parent , pNeighbors );
|
|
_updateConstraintsFromCoarser( F , interpolationInfo , neighbors , pNeighbors , node , constraints , metSolution , childIntegrator , stencils[x][y][z] , bsData );
|
|
}
|
|
}
|
|
#if !defined( _WIN32 ) && !defined( _WIN64 )
|
|
#pragma message( "[WARNING] I'm not sure how expensive this system call is on non-Windows system. (You may want to comment this out.)" )
|
|
#endif // !_WIN32 && !_WIN64
|
|
memoryUsage();
|
|
return 1;
|
|
}
|
|
|
|
#ifndef MOD
|
|
#define MOD( a , b ) ( (a)>0 ? (a) % (b) : ( (b) - ( -(a) % (b) ) ) % (b) )
|
|
#endif // MOD
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType , class FEMSystemFunctor , bool HasGradients >
|
|
int Octree< Real >::_solveSystemGS( const FEMSystemFunctor& F , const BSplineData< FEMDegree , BType >& bsData , InterpolationInfo< HasGradients >* interpolationInfo , LocalDepth depth , DenseNodeData< Real , FEMDegree >& solution , DenseNodeData< Real , FEMDegree >& constraints , DenseNodeData< Real , FEMDegree >& metSolutionConstraints , int iters , bool coarseToFine , _SolverStats& stats , bool computeNorms )
|
|
{
|
|
const int OverlapRadius = -BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapStart;
|
|
typename BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::FunctionIntegrator::template Integrator< DERIVATIVES( FEMDegree ) , DERIVATIVES( FEMDegree ) > integrator;
|
|
typename BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::FunctionIntegrator::template ChildIntegrator< DERIVATIVES( FEMDegree ) , DERIVATIVES( FEMDegree ) > childIntegrator;
|
|
BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::SetIntegrator( integrator , depth );
|
|
if( depth>0 ) BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::SetChildIntegrator( childIntegrator , depth-1 );
|
|
|
|
DenseNodeData< Real , FEMDegree >& metSolution = metSolutionConstraints; // This stores the up-sampled solution up to depth-2
|
|
DenseNodeData< Real , FEMDegree >& metConstraints = metSolutionConstraints; // This stores the down-sampled constraints up to depth
|
|
|
|
int sliceBegin = _BSplineBegin< FEMDegree , BType >( depth ) , sliceEnd = _BSplineEnd< FEMDegree , BType >( depth );
|
|
double& systemTime = stats. systemTime;
|
|
double& solveTime = stats. solveTime;
|
|
double& evaluateTime = stats.evaluateTime;
|
|
systemTime = solveTime = evaluateTime = 0.;
|
|
|
|
if( coarseToFine )
|
|
{
|
|
if( depth>0 )
|
|
{
|
|
// Up-sample the cumulative change in solution @(depth-2) into the cumulative change in solution @(depth-1)
|
|
if( depth-2>=0 ) _upSample< Real , FEMDegree , BType >( depth-1 , metSolution );
|
|
// Add in the change in solution @(depth-1)
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(depth-1) ; i<_sNodesEnd(depth-1) ; i++ ) metSolution[i] += solution[i];
|
|
// evaluate the points @(depth) using the cumulative change in solution @(depth-1)
|
|
if( interpolationInfo )
|
|
{
|
|
evaluateTime = Time();
|
|
_setPointValuesFromCoarser( *interpolationInfo , depth , bsData , metSolution );
|
|
evaluateTime = Time() - evaluateTime;
|
|
}
|
|
}
|
|
}
|
|
else if( depth<_maxDepth ) for( int i=_sNodesBegin(depth) ; i<_sNodesEnd(depth) ; i++ ) constraints[i] -= metConstraints[i];
|
|
double bNorm = 0 , inRNorm = 0 , outRNorm = 0;
|
|
if( depth>=0 )
|
|
{
|
|
// Add padding space if we are computing residuals
|
|
int frontOffset = computeNorms ? OverlapRadius : 0 , backOffset = computeNorms ? OverlapRadius : 0;
|
|
// Set the number of in-memory slices required for a temporally blocked solver
|
|
int solveSlices = std::max< int >( 0 , std::min< int >( OverlapRadius*iters - (OverlapRadius-1) , sliceEnd-sliceBegin ) ) , matrixSlices = std::max< int >( 1 , std::min< int >( solveSlices+frontOffset+backOffset , sliceEnd-sliceBegin ) );
|
|
// The list of matrices for each in-memory slices
|
|
std::vector< SparseMatrix< Real > > _M( matrixSlices );
|
|
// The list of multi-colored indices for each in-memory slice
|
|
std::vector< std::vector< std::vector< int > > > __mcIndices( solveSlices );
|
|
|
|
int dir = coarseToFine ? -1 : 1 , start = coarseToFine ? sliceEnd-1 : sliceBegin , end = coarseToFine ? sliceBegin-1 : sliceEnd;
|
|
for( int frontSlice=start-frontOffset*dir , backSlice = frontSlice-OverlapRadius*(iters-1)*dir ; backSlice!=end+backOffset*dir ; frontSlice+=dir , backSlice+=dir )
|
|
{
|
|
double t;
|
|
if( frontSlice+frontOffset*dir>=sliceBegin && frontSlice+frontOffset*dir<sliceEnd )
|
|
{
|
|
int s = frontSlice+frontOffset*dir , _s = MOD( s , matrixSlices );
|
|
t = Time();
|
|
// Compute the system matrix
|
|
_getSliceMatrixAndUpdateConstraints( F , interpolationInfo , _M[_s] , constraints , integrator , childIntegrator , bsData , depth , s , metSolution , coarseToFine );
|
|
systemTime += Time()-t;
|
|
// Compute residuals
|
|
if( computeNorms )
|
|
{
|
|
ConstPointer( Real ) B = GetPointer( &constraints[0] + _sNodesBegin( depth ) , _sNodesSize( depth ) ) + ( _sNodesBegin( depth , s ) - _sNodesBegin( depth ) );
|
|
Pointer( Real ) X = GetPointer( &solution[0] + _sNodesBegin( depth ) , _sNodesSize( depth ) ) + ( _sNodesBegin( depth , s ) - _sNodesBegin( depth ) );
|
|
#pragma omp parallel for num_threads( threads ) reduction( + : bNorm , inRNorm )
|
|
for( int j=0 ; j<_M[_s].rows ; j++ )
|
|
{
|
|
Real temp = Real(0);
|
|
ConstPointer( MatrixEntry< Real > ) start = _M[_s][j];
|
|
ConstPointer( MatrixEntry< Real > ) end = start + _M[_s].rowSizes[j];
|
|
ConstPointer( MatrixEntry< Real > ) e;
|
|
for( e=start ; e!=end ; e++ ) temp += X[ e->N ] * e->Value;
|
|
bNorm += B[j]*B[j];
|
|
inRNorm += (temp-B[j]) * (temp-B[j]);
|
|
}
|
|
}
|
|
}
|
|
t = Time();
|
|
// Compute the multicolor indices
|
|
if( iters && frontSlice>=sliceBegin && frontSlice<sliceEnd )
|
|
{
|
|
int s = frontSlice , _s = MOD( s , matrixSlices ) , __s = MOD( s , solveSlices );
|
|
for( int i=0 ; i<int( __mcIndices[__s].size() ) ; i++ ) __mcIndices[__s][i].clear();
|
|
_setMultiColorIndices< FEMDegree >( _sNodesBegin( depth , s ) , _sNodesEnd( depth , s ) , __mcIndices[__s] );
|
|
}
|
|
// Advance through the in-memory slices, taking an appropriately sized stride
|
|
for( int slice=frontSlice ; slice*dir>=backSlice*dir ; slice-=OverlapRadius*dir )
|
|
if( slice>=sliceBegin && slice<sliceEnd )
|
|
{
|
|
int s = slice , _s = MOD( s , matrixSlices ) , __s = MOD( s , solveSlices );
|
|
// Do the GS solver
|
|
ConstPointer( Real ) B = GetPointer( &constraints[0] + _sNodesBegin( depth) , _sNodesSize( depth ) ) + ( _sNodesBegin( depth , s ) - _sNodesBegin( depth ) );
|
|
Pointer( Real ) X = GetPointer( &solution[0] + _sNodesBegin( depth ) , _sNodesSize( depth ) ) + ( _sNodesBegin( depth , s ) - _sNodesBegin( depth ) );
|
|
SparseMatrix< Real >::SolveGS( __mcIndices[__s] , _M[_s] , B , X , !coarseToFine , threads );
|
|
}
|
|
solveTime += Time() - t;
|
|
// Compute residuals
|
|
if( computeNorms && backSlice-backOffset*dir>=sliceBegin && backSlice-backOffset*dir<sliceEnd )
|
|
{
|
|
int s = backSlice-backOffset*dir , _s = MOD( s , matrixSlices );
|
|
ConstPointer( Real ) B = GetPointer( &constraints[0] + _sNodesBegin( depth ) , _sNodesSize( depth ) ) + ( _sNodesBegin( depth , s ) - _sNodesBegin( depth ) );
|
|
Pointer( Real ) X = GetPointer( &solution[0] + _sNodesBegin( depth ) , _sNodesSize( depth ) ) + ( _sNodesBegin( depth , s ) - _sNodesBegin( depth ) );
|
|
#pragma omp parallel for num_threads( threads ) reduction( + : outRNorm )
|
|
for( int j=0 ; j<_M[_s].rows ; j++ )
|
|
{
|
|
Real temp = Real(0);
|
|
ConstPointer( MatrixEntry< Real > ) start = _M[_s][j];
|
|
ConstPointer( MatrixEntry< Real > ) end = start + _M[_s].rowSizes[j];
|
|
ConstPointer( MatrixEntry< Real > ) e;
|
|
for( e=start ; e!=end ; e++ ) temp += X[ e->N ] * e->Value;
|
|
outRNorm += (temp-B[j]) * (temp-B[j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if( computeNorms ) stats.bNorm2 = bNorm , stats.inRNorm2 = inRNorm , stats.outRNorm2 = outRNorm;
|
|
|
|
if( !coarseToFine && depth>0 )
|
|
{
|
|
// Explicitly compute the restriction of the met solution onto the coarser nodes
|
|
// and down-sample the previous accumulation
|
|
{
|
|
_updateCumulativeIntegralConstraintsFromFiner( F , bsData , depth , solution , metConstraints );
|
|
if( interpolationInfo ) _updateCumulativeInterpolationConstraintsFromFiner( *interpolationInfo , bsData , depth , solution , metConstraints );
|
|
if( depth<_maxDepth ) _downSample< Real , FEMDegree , BType >( depth , metConstraints );
|
|
}
|
|
}
|
|
memoryUsage();
|
|
|
|
return iters;
|
|
}
|
|
#undef MOD
|
|
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType , class FEMSystemFunctor , bool HasGradients >
|
|
int Octree< Real >::_solveSystemCG( const FEMSystemFunctor& F , const BSplineData< FEMDegree , BType >& bsData , InterpolationInfo< HasGradients >* interpolationInfo , LocalDepth depth , DenseNodeData< Real , FEMDegree >& solution , DenseNodeData< Real , FEMDegree >& constraints , DenseNodeData< Real , FEMDegree >& metSolutionConstraints , int iters , bool coarseToFine , _SolverStats& stats , bool computeNorms , double accuracy )
|
|
{
|
|
typename BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::FunctionIntegrator::template Integrator< DERIVATIVES( FEMDegree ) , DERIVATIVES( FEMDegree ) > integrator;
|
|
typename BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::FunctionIntegrator::template ChildIntegrator< DERIVATIVES( FEMDegree ) , DERIVATIVES( FEMDegree ) > childIntegrator;
|
|
BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::SetIntegrator( integrator , depth );
|
|
if( depth>0 ) BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::SetChildIntegrator( childIntegrator , depth-1 );
|
|
|
|
DenseNodeData< Real , FEMDegree >& metSolution = metSolutionConstraints; // This stores the up-sampled solution up to depth-2
|
|
DenseNodeData< Real , FEMDegree >& metConstraints = metSolutionConstraints; // This stores the down-sampled constraints up to depth
|
|
|
|
int iter = 0;
|
|
Pointer( Real ) X = GetPointer( & solution[0] + _sNodesBegin(depth) , _sNodesSize(depth) );
|
|
Pointer( Real ) B = GetPointer( &constraints[0] + _sNodesBegin(depth) , _sNodesSize(depth) );
|
|
SparseMatrix< Real > M;
|
|
double& systemTime = stats. systemTime;
|
|
double& solveTime = stats. solveTime;
|
|
double& evaluateTime = stats.evaluateTime;
|
|
systemTime = solveTime = evaluateTime = 0.;
|
|
|
|
if( coarseToFine )
|
|
{
|
|
if( depth>0 )
|
|
{
|
|
// Up-sample the cumulative change in solution @(depth-2) into the cumulative change in solution @(depth-1)
|
|
if( depth-2>=0 ) _upSample< Real , FEMDegree , BType >( depth-1 , metSolution );
|
|
// Add in the change in solution @(depth-1)
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(depth-1) ; i<_sNodesEnd(depth-1) ; i++ ) metSolution[i] += solution[i];
|
|
// evaluate the points @(depth) using the cumulative change in solution @(depth-1)
|
|
if( interpolationInfo )
|
|
{
|
|
evaluateTime = Time();
|
|
_setPointValuesFromCoarser( *interpolationInfo , depth , bsData , metSolution );
|
|
evaluateTime = Time() - evaluateTime;
|
|
}
|
|
}
|
|
}
|
|
else if( depth<_maxDepth ) for( int i=_sNodesBegin(depth) ; i<_sNodesEnd(depth) ; i++ ) constraints[i] -= metConstraints[i];
|
|
|
|
// Get the system matrix (and adjust the right-hand-side based on the coarser solution if prolonging)
|
|
systemTime = Time();
|
|
_getMatrixAndUpdateConstraints( F , interpolationInfo , M , constraints , integrator , childIntegrator , bsData , depth , metSolution , coarseToFine );
|
|
systemTime = Time()-systemTime;
|
|
|
|
solveTime = Time();
|
|
// Solve the linear system
|
|
accuracy = Real( accuracy / 100000 ) * M.rows;
|
|
int dim = _BSplineEnd< FEMDegree , BType >( depth ) - _BSplineBegin< FEMDegree , BType >( depth );
|
|
int nonZeroRows = 0;
|
|
for( int i=0 ; i<M.rows ; i++ ) if( M.rowSizes[i] ) nonZeroRows++;
|
|
bool addDCTerm = ( nonZeroRows==dim*dim*dim && ( !interpolationInfo || !interpolationInfo->valueWeight ) && HasPartitionOfUnity< BType >() && F.vanishesOnConstants() );
|
|
double bNorm = 0 , inRNorm = 0 , outRNorm = 0;
|
|
if( computeNorms )
|
|
{
|
|
#pragma omp parallel for num_threads( threads ) reduction( + : bNorm , inRNorm )
|
|
for( int j=0 ; j<M.rows ; j++ )
|
|
{
|
|
Real temp = Real(0);
|
|
ConstPointer( MatrixEntry< Real > ) start = M[j];
|
|
ConstPointer( MatrixEntry< Real > ) end = start + M.rowSizes[j];
|
|
ConstPointer( MatrixEntry< Real > ) e;
|
|
for( e=start ; e!=end ; e++ ) temp += X[ e->N ] * e->Value;
|
|
bNorm += B[j] * B[j];
|
|
inRNorm += ( temp-B[j] ) * ( temp-B[j] );
|
|
}
|
|
}
|
|
|
|
iters = std::min< int >( nonZeroRows , iters );
|
|
if( iters ) iter += SparseMatrix< Real >::SolveCG( M , ( ConstPointer( Real ) )B , iters , X , Real( accuracy ) , 0 , addDCTerm , false , threads );
|
|
|
|
solveTime = Time()-solveTime;
|
|
if( computeNorms )
|
|
{
|
|
#pragma omp parallel for num_threads( threads ) reduction( + : outRNorm )
|
|
for( int j=0 ; j<M.rows ; j++ )
|
|
{
|
|
Real temp = Real(0);
|
|
ConstPointer( MatrixEntry< Real > ) start = M[j];
|
|
ConstPointer( MatrixEntry< Real > ) end = start + M.rowSizes[j];
|
|
ConstPointer( MatrixEntry< Real > ) e;
|
|
for( e=start ; e!=end ; e++ ) temp += X[ e->N ] * e->Value;
|
|
outRNorm += ( temp-B[j] ) * ( temp-B[j] );
|
|
}
|
|
stats.bNorm2 = bNorm , stats.inRNorm2 = inRNorm , stats.outRNorm2 = outRNorm;
|
|
}
|
|
|
|
// Copy the old solution into the buffer, write in the new solution, compute the change, and update the met solution
|
|
if( !coarseToFine && depth>0 )
|
|
{
|
|
// Explicitly compute the restriction of the met solution onto the coarser nodes
|
|
// and down-sample the previous accumulation
|
|
{
|
|
_updateCumulativeIntegralConstraintsFromFiner( F , bsData , depth , solution , metConstraints );
|
|
if( interpolationInfo ) _updateCumulativeInterpolationConstraintsFromFiner( *interpolationInfo , bsData , depth , solution , metConstraints );
|
|
if( depth>_maxDepth ) _downSample< Real , FEMDegree , BType >( depth , metConstraints );
|
|
}
|
|
}
|
|
memoryUsage();
|
|
return iter;
|
|
}
|
|
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType >
|
|
int Octree< Real >::_getMatrixRowSize( const typename TreeOctNode::Neighbors< BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapSize >& neighbors ) const
|
|
{
|
|
static const int OverlapSize = BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapSize;
|
|
static const int OverlapRadius = - BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapStart;
|
|
|
|
int count = 0;
|
|
int nodeIndex = neighbors.neighbors[OverlapRadius][OverlapRadius][OverlapRadius]->nodeData.nodeIndex;
|
|
const TreeOctNode* const * _nodes = &neighbors.neighbors[0][0][0];
|
|
for( int i=0 ; i<OverlapSize*OverlapSize*OverlapSize ; i++ ) if( _isValidFEMNode( _nodes[i] ) ) count++;
|
|
return count;
|
|
}
|
|
|
|
|
|
template< class Real >
|
|
template< int FEMDegree1 , int FEMDegree2 >
|
|
void Octree< Real >::_SetParentOverlapBounds( const TreeOctNode* node , int& startX , int& endX , int& startY , int& endY , int& startZ , int& endZ )
|
|
{
|
|
const int OverlapStart = BSplineOverlapSizes< FEMDegree1 , FEMDegree2 >::OverlapStart;
|
|
|
|
if( node->parent )
|
|
{
|
|
int x , y , z , c = (int)( node - node->parent->children );
|
|
Cube::FactorCornerIndex( c , x , y , z );
|
|
startX = BSplineOverlapSizes< FEMDegree1 , FEMDegree2 >::ParentOverlapStart[x]-OverlapStart , endX = BSplineOverlapSizes< FEMDegree1 , FEMDegree2 >::ParentOverlapEnd[x]-OverlapStart+1;
|
|
startY = BSplineOverlapSizes< FEMDegree1 , FEMDegree2 >::ParentOverlapStart[y]-OverlapStart , endY = BSplineOverlapSizes< FEMDegree1 , FEMDegree2 >::ParentOverlapEnd[y]-OverlapStart+1;
|
|
startZ = BSplineOverlapSizes< FEMDegree1 , FEMDegree2 >::ParentOverlapStart[z]-OverlapStart , endZ = BSplineOverlapSizes< FEMDegree1 , FEMDegree2 >::ParentOverlapEnd[z]-OverlapStart+1;
|
|
}
|
|
}
|
|
|
|
// It is assumed that at this point, the evaluationg of the current depth's points, using the coarser resolution solution
|
|
// has already happened
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType , class FEMSystemFunctor , bool HasGradients >
|
|
void Octree< Real >::_updateConstraintsFromCoarser( const FEMSystemFunctor& F , const InterpolationInfo< HasGradients >* interpolationInfo , const typename TreeOctNode::Neighbors< BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapSize >& neighbors , const typename TreeOctNode::Neighbors< BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapSize >& pNeighbors , TreeOctNode* node , DenseNodeData< Real , FEMDegree >& constraints , const DenseNodeData< Real , FEMDegree >& metSolution , const typename BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::FunctionIntegrator::template ChildIntegrator< DERIVATIVES( FEMDegree ) , DERIVATIVES( FEMDegree ) >& childIntegrator , const Stencil< double , BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapSize >& lapStencil , const BSplineData< FEMDegree , BType >& bsData ) const
|
|
{
|
|
static const int LeftSupportRadius = -BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
static const int RightSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
static const int OverlapRadius = - BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapStart;
|
|
|
|
if( _localDepth( node )<=0 ) return;
|
|
// This is a conservative estimate as we only need to make sure that the parent nodes don't overlap the child (not the parent itself)
|
|
bool isInterior = _isInteriorlyOverlapped< FEMDegree , FEMDegree >( node->parent );
|
|
LocalDepth d ; LocalOffset off;
|
|
_localDepthAndOffset( node , d , off );
|
|
|
|
// Offset the constraints using the solution from lower resolutions.
|
|
int startX , endX , startY , endY , startZ , endZ;
|
|
_SetParentOverlapBounds< FEMDegree , FEMDegree >( node , startX , endX , startY , endY , startZ , endZ );
|
|
|
|
for( int x=startX ; x<endX ; x++ ) for( int y=startY ; y<endY ; y++ ) for( int z=startZ ; z<endZ ; z++ )
|
|
if( _isValidFEMNode( pNeighbors.neighbors[x][y][z] ) )
|
|
{
|
|
const TreeOctNode* _node = pNeighbors.neighbors[x][y][z];
|
|
Real _solution = metSolution[ _node->nodeData.nodeIndex ];
|
|
{
|
|
if( isInterior ) constraints[ node->nodeData.nodeIndex ] -= Real( lapStencil( x , y , z ) * _solution );
|
|
else
|
|
{
|
|
LocalDepth _d ; LocalOffset _off;
|
|
_localDepthAndOffset( _node , _d , _off );
|
|
constraints[ node->nodeData.nodeIndex ] -= (Real)F.integrate( childIntegrator , _off , off ) * _solution;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( interpolationInfo )
|
|
{
|
|
double constraint = 0;
|
|
int fIdx[3];
|
|
functionIndex< FEMDegree , BType >( node , fIdx );
|
|
// evaluate the current node's basis function at adjacent points
|
|
for( int x=-LeftSupportRadius ; x<=RightSupportRadius ; x++ ) for( int y=-LeftSupportRadius ; y<=RightSupportRadius ; y++ ) for( int z=-LeftSupportRadius ; z<=RightSupportRadius ; z++ )
|
|
{
|
|
const TreeOctNode* _node = neighbors.neighbors[x+OverlapRadius][y+OverlapRadius][z+OverlapRadius];
|
|
if( _isValidSpaceNode( _node ) && (*interpolationInfo)( _node ) )
|
|
{
|
|
const PointData< Real , HasGradients >& pData = *( (*interpolationInfo)( _node ) );
|
|
constraint += _ConstraintCalculator_< Real , FEMDegree , HasGradients >::_CalculateConstraint_
|
|
(
|
|
pData ,
|
|
bsData. baseBSplines[ fIdx[0] ][x+LeftSupportRadius] ,
|
|
bsData. baseBSplines[ fIdx[1] ][y+LeftSupportRadius] ,
|
|
bsData. baseBSplines[ fIdx[2] ][z+LeftSupportRadius] ,
|
|
bsData.dBaseBSplines[ fIdx[0] ][x+LeftSupportRadius] ,
|
|
bsData.dBaseBSplines[ fIdx[1] ][y+LeftSupportRadius] ,
|
|
bsData.dBaseBSplines[ fIdx[2] ][z+LeftSupportRadius]
|
|
);
|
|
}
|
|
}
|
|
constraints[ node->nodeData.nodeIndex ] -= Real( constraint );
|
|
}
|
|
}
|
|
|
|
// Given the solution @( depth ) add to the met constraints @( depth-1 )
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType , class FEMSystemFunctor >
|
|
void Octree< Real >::_updateCumulativeIntegralConstraintsFromFiner( const FEMSystemFunctor& F , const BSplineData< FEMDegree , BType >& bsData , LocalDepth highDepth , const DenseNodeData< Real , FEMDegree >& fineSolution , DenseNodeData< Real , FEMDegree >& coarseConstraints ) const
|
|
{
|
|
typename BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::FunctionIntegrator::template ChildIntegrator< DERIVATIVES( FEMDegree ) , DERIVATIVES( FEMDegree ) > childIntegrator;
|
|
BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::SetChildIntegrator( childIntegrator , highDepth-1 );
|
|
|
|
static const int OverlapSize = BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapSize;
|
|
static const int OverlapRadius = - BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapStart;
|
|
typedef typename TreeOctNode::NeighborKey< -BSplineSupportSizes< FEMDegree >::SupportStart , BSplineSupportSizes< FEMDegree >::SupportEnd >SupportKey;
|
|
|
|
if( highDepth<=0 ) return;
|
|
// Get the stencil describing the Laplacian relating coefficients @(depth) with coefficients @(depth-1)
|
|
Stencil< double , OverlapSize > stencils[2][2][2];
|
|
SystemCoefficients< FEMDegree , BType , FEMDegree , BType >::SetCentralSystemStencils( F , childIntegrator , stencils );
|
|
size_t start = _sNodesBegin( highDepth) , end = _sNodesEnd(highDepth) , range = end-start;
|
|
int lStart = _sNodesBegin(highDepth-1);
|
|
|
|
// Iterate over the nodes @( depth )
|
|
std::vector< SupportKey > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( highDepth )-1 );
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(highDepth) ; i<_sNodesEnd(highDepth) ; i++ ) if( _isValidFEMNode( _sNodes.treeNodes[i] ) )
|
|
{
|
|
SupportKey& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
TreeOctNode* node = _sNodes.treeNodes[i];
|
|
|
|
// Offset the coarser constraints using the solution from the current resolutions.
|
|
int x , y , z , c;
|
|
c = int( node - node->parent->children );
|
|
Cube::FactorCornerIndex( c , x , y , z );
|
|
{
|
|
typename TreeOctNode::Neighbors< OverlapSize > pNeighbors;
|
|
neighborKey.template getNeighbors< false , OverlapRadius , OverlapRadius >( node->parent , pNeighbors );
|
|
const Stencil< double , OverlapSize >& stencil = stencils[x][y][z];
|
|
|
|
bool isInterior = _isInteriorlyOverlapped< FEMDegree , FEMDegree >( node->parent );
|
|
LocalDepth d ; LocalOffset off;
|
|
_localDepthAndOffset( node , d , off );
|
|
|
|
// Offset the constraints using the solution from finer resolutions.
|
|
int startX , endX , startY , endY , startZ , endZ;
|
|
_SetParentOverlapBounds< FEMDegree , FEMDegree >( node , startX , endX , startY , endY , startZ , endZ );
|
|
|
|
Real solution = fineSolution[ node->nodeData.nodeIndex ];
|
|
for( int x=startX ; x<endX ; x++ ) for( int y=startY ; y<endY ; y++ ) for( int z=startZ ; z<endZ ; z++ )
|
|
if( _isValidFEMNode( pNeighbors.neighbors[x][y][z] ) )
|
|
{
|
|
const TreeOctNode* _node = pNeighbors.neighbors[x][y][z];
|
|
if( isInterior )
|
|
#pragma omp atomic
|
|
coarseConstraints[ _node->nodeData.nodeIndex ] += Real( stencil( x , y , z ) * solution );
|
|
else
|
|
{
|
|
LocalDepth _d ; LocalOffset _off;
|
|
_localDepthAndOffset( _node , _d , _off );
|
|
#pragma omp atomic
|
|
coarseConstraints[ _node->nodeData.nodeIndex ] += Real( F.integrate( childIntegrator , _off , off ) * solution );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType , class FEMSystemFunctor , bool HasGradients >
|
|
void Octree< Real >::setSystemMatrix( const FEMSystemFunctor& F , const InterpolationInfo< HasGradients >* interpolationInfo , LocalDepth depth , SparseMatrix< Real >& matrix ) const
|
|
{
|
|
if( depth<0 || depth>_maxDepth ) fprintf( stderr , "[ERROR] System depth out of bounds: %d <= %d <= %d\n" , 0 , depth , _maxDepth ) , exit( 0 );
|
|
typename BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::FunctionIntegrator::template Integrator< DERIVATIVES( FEMDegree ) , DERIVATIVES( FEMDegree ) > integrator;
|
|
BSplineIntegrationData< FEMDegree , BType , FEMDegree , BType >::SetIntegrator( integrator , depth );
|
|
BSplineData< FEMDegree , BType > bsData( depth );
|
|
|
|
static const int OverlapRadius = - BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapStart;
|
|
static const int OverlapSize = BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapSize;
|
|
|
|
Stencil< double , OverlapSize > stencil;
|
|
SystemCoefficients< FEMDegree , BType , FEMDegree , BType >::SetCentralSystemStencil ( F , integrator , stencil );
|
|
|
|
matrix.Resize( _sNodesSize(depth) );
|
|
std::vector< AdjacenctNodeKey > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( depth ) );
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(depth) ; i<_sNodesEnd( depth ) ; i++ ) if( _isValidFEMNode( _sNodes.treeNodes[i] ) )
|
|
{
|
|
int ii = i - _sNodesBegin(depth);
|
|
AdjacenctNodeKey& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
|
|
typename TreeOctNode::Neighbors< OverlapSize > neighbors;
|
|
neighborKey.template getNeighbors< false , OverlapRadius , OverlapRadius >( _sNodes.treeNodes[i] , neighbors );
|
|
|
|
matrix.SetRowSize( ii , _getMatrixRowSize< FEMDegree , BType >( neighbors ) );
|
|
matrix.rowSizes[ii] = _setMatrixRow( F , interpolationInfo , neighbors , matrix[ii] , _sNodesBegin(depth) , integrator , stencil , bsData );
|
|
}
|
|
}
|
|
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType , class FEMSystemFunctor , bool HasGradients >
|
|
DenseNodeData< Real , FEMDegree > Octree< Real >::solveSystem( const FEMSystemFunctor& F , InterpolationInfo< HasGradients >* interpolationInfo , DenseNodeData< Real , FEMDegree >& constraints , LocalDepth maxSolveDepth , const typename Octree< Real >::SolverInfo& solverInfo )
|
|
{
|
|
BSplineData< FEMDegree , BType > bsData( maxSolveDepth );
|
|
|
|
maxSolveDepth = std::min< LocalDepth >( maxSolveDepth , _maxDepth );
|
|
int iter = 0;
|
|
const int _iters = std::max< int >( 0 , solverInfo.iters );
|
|
|
|
DenseNodeData< Real , FEMDegree > solution( _sNodesEnd( _maxDepth ) );
|
|
memset( &solution[0] , 0 , sizeof(Real) * _sNodesEnd( _maxDepth ) );
|
|
|
|
DenseNodeData< Real , FEMDegree > metSolution( _sNodesEnd( _maxDepth-1 ) );
|
|
memset( &metSolution[0] , 0 , sizeof(Real)*_sNodesEnd( _maxDepth-1 ) );
|
|
for( LocalDepth d=0 ; d<=maxSolveDepth ; d++ )
|
|
{
|
|
int iters = (int)ceil( _iters * pow( solverInfo.lowResIterMultiplier , maxSolveDepth-d ) );
|
|
_SolverStats sStats;
|
|
if( !d ) iter = _solveSystemCG( F , bsData , interpolationInfo , d , solution , constraints , metSolution , _sNodesSize(d) , true , sStats , solverInfo.showResidual , 0 );
|
|
else
|
|
{
|
|
if( d>solverInfo.cgDepth ) iter = _solveSystemGS( F , bsData , interpolationInfo , d , solution , constraints , metSolution , iters , true , sStats , solverInfo.showResidual );
|
|
else iter = _solveSystemCG( F , bsData , interpolationInfo , d , solution , constraints , metSolution , iters , true , sStats , solverInfo.showResidual , solverInfo.cgAccuracy );
|
|
}
|
|
int femNodes = 0;
|
|
#pragma omp parallel for reduction( + : femNodes )
|
|
for( int i=_sNodesBegin(d) ; i<_sNodesEnd(d) ; i++ ) if( _isValidFEMNode( _sNodes.treeNodes[i] ) ) femNodes++;
|
|
if( solverInfo.verbose )
|
|
{
|
|
if( maxSolveDepth<10 ) printf( "Depth[%d/%d]:\t" , d , maxSolveDepth );
|
|
else printf( "Depth[%2d/%d]:\t" , d , maxSolveDepth );
|
|
printf( "Evaluated / Got / Solved in: %6.3f / %6.3f / %6.3f\t(%.3f MB)\tNodes: %d\n" , sStats.evaluateTime , sStats.systemTime , sStats.solveTime , _localMemoryUsage , femNodes );
|
|
}
|
|
if( solverInfo.showResidual && iters )
|
|
{
|
|
for( LocalDepth dd=0 ; dd<d ; dd++ ) printf( " " );
|
|
printf( "%s: %.4e -> %.4e -> %.4e (%.2e) [%d]\n" , d<=solverInfo.cgDepth ? "CG" : "GS" , sqrt( sStats.bNorm2 ) , sqrt( sStats.inRNorm2 ) , sqrt( sStats.outRNorm2 ) , sqrt( sStats.outRNorm2 / sStats.bNorm2 ) , iters );
|
|
}
|
|
}
|
|
memoryUsage();
|
|
return solution;
|
|
}
|
|
|
|
template< class Real >
|
|
template< int FEMDegree >
|
|
DenseNodeData< Real , FEMDegree > Octree< Real >::initDenseNodeData( void )
|
|
{
|
|
DenseNodeData< Real , FEMDegree > constraints( _sNodes.size() );
|
|
memset( &constraints[0] , 0 , sizeof(Real)*_sNodes.size() );
|
|
return constraints;
|
|
}
|
|
template< > template< > float Octree< float >::_Dot( const float & r1 , const float & r2 ){ return r1*r2; }
|
|
template< > template< > double Octree< double >::_Dot( const double& r1 , const double& r2 ){ return r1*r2; }
|
|
template< > template< > float Octree< float >::_Dot( const Point3D< float >& p1 , const Point3D< float >& p2 ){ return Point3D< float >::Dot( p1 , p2 ); }
|
|
template< > template< > double Octree< double >::_Dot( const Point3D< double >& p1 , const Point3D< double >& p2 ){ return Point3D< double >::Dot( p1 , p2 ); }
|
|
template< > template< > bool Octree< float >::_IsZero( const float & r ){ return r==0; }
|
|
template< > template< > bool Octree< double >::_IsZero( const double& r ){ return r==0; }
|
|
template< > template< > bool Octree< float >::_IsZero( const Point3D< float >& p ){ return p[0]==0 && p[1]==0 && p[2]==0; }
|
|
template< > template< > bool Octree< double >::_IsZero( const Point3D< double >& p ){ return p[0]==0 && p[1]==0 && p[2]==0; }
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType FEMBType , int CDegree , BoundaryType CBType , class FEMConstraintFunctor , class Coefficients , class D , class _D >
|
|
void Octree< Real >::_addFEMConstraints( const FEMConstraintFunctor& F , const Coefficients& coefficients , DenseNodeData< Real , FEMDegree >& constraints , LocalDepth maxDepth )
|
|
{
|
|
typedef typename TreeOctNode::NeighborKey< -BSplineSupportSizes< FEMDegree >::SupportStart , BSplineSupportSizes< FEMDegree >::SupportEnd > SupportKey;
|
|
const int CFEMOverlapSize = BSplineOverlapSizes< CDegree , FEMDegree >::OverlapSize;
|
|
const int LeftCFEMOverlapRadius = -BSplineOverlapSizes< CDegree , FEMDegree >::OverlapStart;
|
|
const int RightCFEMOverlapRadius = BSplineOverlapSizes< CDegree , FEMDegree >::OverlapEnd;
|
|
const int LeftFEMCOverlapRadius = -BSplineOverlapSizes< FEMDegree , CDegree >::OverlapStart;
|
|
const int RightFEMCOverlapRadius = BSplineOverlapSizes< FEMDegree , CDegree >::OverlapEnd;
|
|
|
|
// To set the constraints, we iterate over the
|
|
// splatted normals and compute the dot-product of the
|
|
// divergence of the normal field with all the basis functions.
|
|
// Within the same depth: set directly as a gather
|
|
// Coarser depths
|
|
maxDepth = std::min< LocalDepth >( maxDepth , _maxDepth );
|
|
DenseNodeData< Real , FEMDegree >* __constraints = new DenseNodeData< Real , FEMDegree >( _sNodesEnd(maxDepth-1) );
|
|
DenseNodeData< Real , FEMDegree >& _constraints = *__constraints;
|
|
memset( &_constraints[0] , 0 , sizeof(Real)*( _sNodesEnd(maxDepth-1) ) );
|
|
memoryUsage();
|
|
|
|
for( LocalDepth d=maxDepth ; d>=0 ; d-- )
|
|
{
|
|
Stencil< _D , CFEMOverlapSize > stencil , stencils[2][2][2];
|
|
typename SystemCoefficients< CDegree , CBType , FEMDegree , FEMBType >:: Integrator integrator;
|
|
typename SystemCoefficients< FEMDegree , FEMBType , CDegree , CBType >::ChildIntegrator childIntegrator;
|
|
BSplineIntegrationData< CDegree , CBType , FEMDegree , FEMBType >::SetIntegrator( integrator , d );
|
|
if( d>0 ) BSplineIntegrationData< FEMDegree , FEMBType , CDegree , CBType >::SetChildIntegrator( childIntegrator , d-1 );
|
|
SystemCoefficients< CDegree , CBType , FEMDegree , FEMBType >::template SetCentralConstraintStencil < false >( F, integrator , stencil );
|
|
SystemCoefficients< FEMDegree , FEMBType , CDegree , CBType >::template SetCentralConstraintStencils< true >( F, childIntegrator , stencils );
|
|
|
|
std::vector< SupportKey > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( d ) );
|
|
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(d) ; i<_sNodesEnd(d) ; i++ )
|
|
{
|
|
SupportKey& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
TreeOctNode* node = _sNodes.treeNodes[i];
|
|
int startX=0 , endX=CFEMOverlapSize , startY=0 , endY=CFEMOverlapSize , startZ=0 , endZ=CFEMOverlapSize;
|
|
typename TreeOctNode::Neighbors< CFEMOverlapSize > neighbors;
|
|
neighborKey.template getNeighbors< false , LeftFEMCOverlapRadius , RightFEMCOverlapRadius >( node , neighbors );
|
|
bool isInterior = _isInteriorlyOverlapped< FEMDegree , CDegree >( node ) , isInterior2 = _isInteriorlyOverlapped< CDegree , FEMDegree >( node->parent );
|
|
|
|
LocalDepth d ; LocalOffset off;
|
|
_localDepthAndOffset( node , d , off );
|
|
// Set constraints from current depth
|
|
// Gather the constraints from the vector-field at _node into the constraint stored with node
|
|
if( _isValidFEMNode( node ) )
|
|
{
|
|
for( int x=startX ; x<endX ; x++ ) for( int y=startY ; y<endY ; y++ ) for( int z=startZ ; z<endZ ; z++ )
|
|
{
|
|
const TreeOctNode* _node = neighbors.neighbors[x][y][z];
|
|
if( isValidFEMNode< CDegree , CBType >( _node ) )
|
|
{
|
|
const D* d = coefficients( _node );
|
|
if( d )
|
|
if( isInterior ) constraints[i] += _Dot( (D)stencil( x , y , z ) , *d );
|
|
else
|
|
{
|
|
LocalDepth _d ; LocalOffset _off;
|
|
_localDepthAndOffset( _node , _d , _off );
|
|
constraints[i] += _Dot( *d , (D)F.template integrate< false >( integrator , _off , off ) );
|
|
}
|
|
}
|
|
}
|
|
_SetParentOverlapBounds< CDegree , FEMDegree >( node , startX , endX , startY , endY , startZ , endZ );
|
|
}
|
|
if( !isValidFEMNode< CDegree , CBType >( node ) ) continue;
|
|
const D* _data = coefficients( node );
|
|
if( !_data ) continue;
|
|
const D& data = *_data;
|
|
if( _IsZero( data ) ) continue;
|
|
|
|
// Set the _constraints for the parents
|
|
if( d>0 )
|
|
{
|
|
int cx , cy , cz;
|
|
Cube::FactorCornerIndex( (int)( node - node->parent->children ) , cx , cy ,cz );
|
|
const Stencil< _D , CFEMOverlapSize >& _stencil = stencils[cx][cy][cz];
|
|
|
|
neighborKey.template getNeighbors< false , LeftCFEMOverlapRadius , RightCFEMOverlapRadius >( node->parent , neighbors );
|
|
|
|
for( int x=startX ; x<endX ; x++ ) for( int y=startY ; y<endY ; y++ ) for( int z=startZ ; z<endZ ; z++ )
|
|
{
|
|
TreeOctNode* _node = neighbors.neighbors[x][y][z];
|
|
if( _node && ( isInterior2 || _isValidFEMNode( _node ) ) )
|
|
{
|
|
TreeOctNode* _node = neighbors.neighbors[x][y][z];
|
|
Real c;
|
|
if( isInterior2 ) c = _Dot( (D)_stencil( x , y , z ) , data );
|
|
else
|
|
{
|
|
LocalDepth _d ; LocalOffset _off;
|
|
_localDepthAndOffset( _node , _d , _off );
|
|
c = _Dot( data , (D)F.template integrate< true >( childIntegrator , _off , off ) );
|
|
}
|
|
#pragma omp atomic
|
|
_constraints[ _node->nodeData.nodeIndex ] += c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
memoryUsage();
|
|
}
|
|
|
|
// Fine-to-coarse down-sampling of constraints
|
|
for( LocalDepth d=maxDepth-1 ; d>0 ; d-- ) _downSample< Real , FEMDegree , FEMBType >( d , _constraints );
|
|
|
|
// Add the accumulated constraints from all finer depths
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=0 ; i<_sNodesEnd(maxDepth-1) ; i++ ) constraints[i] += _constraints[i];
|
|
|
|
delete __constraints;
|
|
|
|
DenseNodeData< D , CDegree > _coefficients( _sNodesEnd(maxDepth-1) );
|
|
memset( &_coefficients[0] , 0 , sizeof(D) * _sNodesEnd(maxDepth-1) );
|
|
for( LocalDepth d=maxDepth-1 ; d>=0 ; d-- )
|
|
{
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(d) ; i<_sNodesEnd(d) ; i++ ) if( isValidFEMNode< CDegree , CBType >( _sNodes.treeNodes[i] ) )
|
|
{
|
|
const D* d = coefficients( _sNodes.treeNodes[i] );
|
|
if( d ) _coefficients[i] += *d;
|
|
}
|
|
}
|
|
|
|
// Coarse-to-fine up-sampling of coefficients
|
|
for( LocalDepth d=1 ; d<maxDepth ; d++ ) _upSample< D , CDegree , CBType >( d , _coefficients );
|
|
|
|
// Compute the contribution from all coarser depths
|
|
for( LocalDepth d=1 ; d<=maxDepth ; d++ )
|
|
{
|
|
size_t start = _sNodesBegin( d ) , end = _sNodesEnd( d ) , range = end - start;
|
|
Stencil< _D , CFEMOverlapSize > stencils[2][2][2];
|
|
typename SystemCoefficients< CDegree , CBType , FEMDegree , FEMBType >::ChildIntegrator childIntegrator;
|
|
BSplineIntegrationData< CDegree , CBType , FEMDegree , FEMBType >::SetChildIntegrator( childIntegrator , d-1 );
|
|
SystemCoefficients< CDegree , CBType , FEMDegree , FEMBType >::template SetCentralConstraintStencils< false >( F , childIntegrator , stencils );
|
|
std::vector< SupportKey > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( d-1 ) );
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(d) ; i<_sNodesEnd(d) ; i++ ) if( _isValidFEMNode( _sNodes.treeNodes[i] ) )
|
|
{
|
|
SupportKey& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
TreeOctNode* node = _sNodes.treeNodes[i];
|
|
int startX , endX , startY , endY , startZ , endZ;
|
|
_SetParentOverlapBounds< FEMDegree , CDegree >( node , startX , endX , startY , endY , startZ , endZ );
|
|
typename TreeOctNode::Neighbors< CFEMOverlapSize > pNeighbors;
|
|
neighborKey.template getNeighbors< false , LeftFEMCOverlapRadius , RightFEMCOverlapRadius >( node->parent , pNeighbors );
|
|
|
|
bool isInterior = _isInteriorlyOverlapped< FEMDegree , CDegree >( node->parent );
|
|
int cx , cy , cz;
|
|
if( d>0 )
|
|
{
|
|
int c = int( node - node->parent->children );
|
|
Cube::FactorCornerIndex( c , cx , cy , cz );
|
|
}
|
|
else cx = cy = cz = 0;
|
|
Stencil< _D , CFEMOverlapSize >& _stencil = stencils[cx][cy][cz];
|
|
|
|
Real constraint = Real(0);
|
|
LocalDepth d ; LocalOffset off;
|
|
_localDepthAndOffset( node , d , off );
|
|
for( int x=startX ; x<endX ; x++ ) for( int y=startY ; y<endY ; y++ ) for( int z=startZ ; z<endZ ; z++ )
|
|
{
|
|
TreeOctNode* _node = pNeighbors.neighbors[x][y][z];
|
|
if( isValidFEMNode< CDegree , CBType >( _node ) )
|
|
{
|
|
if( isInterior ) constraint += _Dot( _coefficients[ _node->nodeData.nodeIndex ] , (D)_stencil( x , y , z ) );
|
|
else
|
|
{
|
|
LocalDepth _d ; LocalOffset _off;
|
|
_localDepthAndOffset ( _node , _d , _off );
|
|
constraint += _Dot( _coefficients[ _node->nodeData.nodeIndex ] , (D)F.template integrate< false >( childIntegrator , _off , off ) );
|
|
}
|
|
}
|
|
}
|
|
constraints[i] += constraint;
|
|
}
|
|
}
|
|
memoryUsage();
|
|
}
|
|
|
|
template< class Real >
|
|
template< int FEMDegree , BoundaryType BType , bool HasGradients >
|
|
void Octree< Real >::addInterpolationConstraints( const InterpolationInfo< HasGradients >& interpolationInfo , DenseNodeData< Real , FEMDegree >& constraints , LocalDepth maxDepth )
|
|
{
|
|
typedef typename TreeOctNode::NeighborKey< -BSplineSupportSizes< FEMDegree >::SupportStart , BSplineSupportSizes< FEMDegree >::SupportEnd > SupportKey;
|
|
maxDepth = std::min< LocalDepth >( maxDepth , _maxDepth );
|
|
{
|
|
static const int OverlapSize = BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapSize;
|
|
static const int LeftSupportRadius = -BSplineSupportSizes< FEMDegree >::SupportStart;
|
|
static const int RightSupportRadius = BSplineSupportSizes< FEMDegree >::SupportEnd;
|
|
static const int OverlapRadius = - BSplineOverlapSizes< FEMDegree , FEMDegree >::OverlapStart;
|
|
BSplineData< FEMDegree , BType > bsData( _maxDepth );
|
|
for( int d=0 ; d<=maxDepth ; d++ )
|
|
{
|
|
std::vector< SupportKey > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( maxDepth ) );
|
|
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(d) ; i<_sNodesEnd(d) ; i++ ) if( _isValidFEMNode( _sNodes.treeNodes[i] ) )
|
|
{
|
|
TreeOctNode* node = _sNodes.treeNodes[i];
|
|
SupportKey& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
typename TreeOctNode::Neighbors< OverlapSize > neighbors;
|
|
neighborKey.template getNeighbors< false , OverlapRadius , OverlapRadius >( node , neighbors );
|
|
|
|
double constraint = 0;
|
|
int fIdx[3];
|
|
functionIndex< FEMDegree , BType >( node , fIdx );
|
|
// evaluate the current node's basis function at adjacent points
|
|
for( int x=-LeftSupportRadius ; x<=RightSupportRadius ; x++ ) for( int y=-LeftSupportRadius ; y<=RightSupportRadius ; y++ ) for( int z=-LeftSupportRadius ; z<=RightSupportRadius ; z++ )
|
|
{
|
|
const TreeOctNode* _node = neighbors.neighbors[x+OverlapRadius][y+OverlapRadius][z+OverlapRadius];
|
|
if( _isValidSpaceNode( _node ) && interpolationInfo( _node ) )
|
|
{
|
|
const PointData< Real , HasGradients >& pData = *( interpolationInfo( _node ) );
|
|
constraint += _ConstraintCalculator_< Real , FEMDegree , HasGradients >::_CalculateConstraint_
|
|
(
|
|
pData ,
|
|
bsData. baseBSplines[ fIdx[0] ][x+LeftSupportRadius] ,
|
|
bsData. baseBSplines[ fIdx[1] ][y+LeftSupportRadius] ,
|
|
bsData. baseBSplines[ fIdx[2] ][z+LeftSupportRadius] ,
|
|
bsData.dBaseBSplines[ fIdx[0] ][x+LeftSupportRadius] ,
|
|
bsData.dBaseBSplines[ fIdx[1] ][y+LeftSupportRadius] ,
|
|
bsData.dBaseBSplines[ fIdx[2] ][z+LeftSupportRadius] ,
|
|
interpolationInfo.valueWeight , interpolationInfo.gradientWeight
|
|
);
|
|
}
|
|
}
|
|
constraints[ node->nodeData.nodeIndex ] += (Real)constraint;
|
|
}
|
|
}
|
|
memoryUsage();
|
|
}
|
|
}
|
|
template< class Real >
|
|
template< int FEMDegree1 , BoundaryType FEMBType1 , int FEMDegree2 , BoundaryType FEMBType2 , class DotFunctor , bool HasGradients , class Coefficients1 , class Coefficients2 >
|
|
double Octree< Real >::_dot( const DotFunctor& F , const InterpolationInfo< HasGradients >* iInfo , const Coefficients1& coefficients1 , const Coefficients2& coefficients2 ) const
|
|
{
|
|
double dot = 0;
|
|
|
|
// Calculate the contribution from @(depth,depth)
|
|
{
|
|
typedef typename TreeOctNode::ConstNeighborKey< -BSplineSupportSizes< FEMDegree1 >::SupportStart , BSplineSupportSizes< FEMDegree1 >::SupportEnd > SupportKey;
|
|
const int OverlapSize = BSplineOverlapSizes< FEMDegree1 , FEMDegree2 >::OverlapSize;
|
|
const int LeftOverlapRadius = -BSplineOverlapSizes< FEMDegree1 , FEMDegree2 >::OverlapStart;
|
|
const int RightOverlapRadius = BSplineOverlapSizes< FEMDegree1 , FEMDegree2 >::OverlapEnd;
|
|
|
|
for( LocalDepth d=0 ; d<=_maxDepth ; d++ )
|
|
{
|
|
Stencil< double , OverlapSize > stencil;
|
|
typename SystemCoefficients< FEMDegree1 , FEMBType1 , FEMDegree2 , FEMBType2 >::Integrator integrator;
|
|
BSplineIntegrationData< FEMDegree1 , FEMBType1 , FEMDegree2 , FEMBType2 >::SetIntegrator( integrator , d );
|
|
SystemCoefficients< FEMDegree1 , FEMBType1 , FEMDegree2 , FEMBType2 >::template SetCentralConstraintStencil< false , DotFunctor >( F , integrator , stencil );
|
|
|
|
std::vector< SupportKey > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( d ) );
|
|
|
|
#pragma omp parallel for num_threads( threads ) reduction( + : dot )
|
|
for( int i=_sNodesBegin(d) ; i<_sNodesEnd(d) ; i++ )
|
|
{
|
|
const TreeOctNode* node = _sNodes.treeNodes[i];
|
|
const Real* _data1;
|
|
if( isValidFEMNode< FEMDegree1 , FEMBType1 >( node ) && ( _data1=coefficients1(node) ) )
|
|
{
|
|
SupportKey& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
typename TreeOctNode::ConstNeighbors< OverlapSize > neighbors;
|
|
neighborKey.template getNeighbors< LeftOverlapRadius , RightOverlapRadius >( node , neighbors );
|
|
bool isInterior = _isInteriorlyOverlapped< FEMDegree1 , FEMDegree2 >( node );
|
|
|
|
LocalDepth d ; LocalOffset off;
|
|
_localDepthAndOffset( node , d , off );
|
|
|
|
for( int x=0 ; x<OverlapSize ; x++ ) for( int y=0 ; y<OverlapSize ; y++ ) for( int z=0 ; z<OverlapSize ; z++ )
|
|
{
|
|
const TreeOctNode* _node = neighbors.neighbors[x][y][z];
|
|
const Real* _data2;
|
|
if( isValidFEMNode< FEMDegree2 , FEMBType2 >( _node ) && ( _data2=coefficients2( _node ) ) )
|
|
if( isInterior ) dot += (*_data1) * (*_data2 ) * stencil( x , y , z );
|
|
else
|
|
{
|
|
LocalDepth _d ; LocalOffset _off;
|
|
_localDepthAndOffset( _node , _d , _off );
|
|
dot += (*_data1) * (*_data2) * F.template integrate< false >( integrator , off , _off );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Calculate the contribution from @(<depth,depth)
|
|
{
|
|
typedef typename TreeOctNode::ConstNeighborKey< -BSplineSupportSizes< FEMDegree1 >::SupportStart , BSplineSupportSizes< FEMDegree1 >::SupportEnd > SupportKey;
|
|
const int OverlapSize = BSplineOverlapSizes< FEMDegree2 , FEMDegree1 >::OverlapSize;
|
|
const int LeftOverlapRadius = -BSplineOverlapSizes< FEMDegree2 , FEMDegree1 >::OverlapStart;
|
|
const int RightOverlapRadius = BSplineOverlapSizes< FEMDegree2 , FEMDegree1 >::OverlapEnd;
|
|
|
|
DenseNodeData< Real , FEMDegree1 > cumulative1( _sNodesEnd( _maxDepth-1 ) );
|
|
if( _maxDepth>0 ) memset( &cumulative1[0] , 0 , sizeof(Real) * _sNodesEnd( _maxDepth-1 ) );
|
|
|
|
for( LocalDepth d=1 ; d<=_maxDepth ; d++ )
|
|
{
|
|
// Update the cumulative coefficients with the coefficients @(depth-1)
|
|
#pragma omp parallel for
|
|
for( int i=_sNodesBegin(d-1) ; i<_sNodesEnd(d-1) ; i++ )
|
|
{
|
|
const Real* _data1 = coefficients1( _sNodes.treeNodes[i] );
|
|
if( _data1 ) cumulative1[i] += *_data1;
|
|
}
|
|
|
|
Stencil< double , OverlapSize > stencils[2][2][2];
|
|
typename SystemCoefficients< FEMDegree1 , FEMBType1 , FEMDegree2 , FEMBType2 >::ChildIntegrator childIntegrator;
|
|
BSplineIntegrationData< FEMDegree1 , FEMBType1 , FEMDegree2 , FEMBType2 >::SetChildIntegrator( childIntegrator , d-1 );
|
|
SystemCoefficients< FEMDegree1 , FEMBType1 , FEMDegree2 , FEMBType2 >::template SetCentralConstraintStencils< false >( F, childIntegrator , stencils );
|
|
|
|
std::vector< SupportKey > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( d-1 ) );
|
|
|
|
#pragma omp parallel for num_threads( threads ) reduction( + : dot )
|
|
for( int i=_sNodesBegin(d) ; i<_sNodesEnd(d) ; i++ )
|
|
{
|
|
const TreeOctNode* node = _sNodes.treeNodes[i];
|
|
const Real* _data2;
|
|
if( isValidFEMNode< FEMDegree2 , FEMBType2 >( node ) && ( _data2=coefficients2( node ) ) )
|
|
{
|
|
SupportKey& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
bool isInterior = _isInteriorlyOverlapped< FEMDegree1 , FEMDegree2 >( node->parent );
|
|
|
|
LocalDepth d ; LocalOffset off;
|
|
_localDepthAndOffset( node , d , off );
|
|
|
|
int cx , cy , cz;
|
|
Cube::FactorCornerIndex( (int)( node - node->parent->children ) , cx , cy ,cz );
|
|
const Stencil< double , OverlapSize >& _stencil = stencils[cx][cy][cz];
|
|
typename TreeOctNode::ConstNeighbors< OverlapSize > neighbors;
|
|
neighborKey.template getNeighbors< LeftOverlapRadius , RightOverlapRadius >( node->parent , neighbors );
|
|
|
|
int startX , endX , startY , endY , startZ , endZ;
|
|
_SetParentOverlapBounds< FEMDegree2 , FEMDegree1 >( node , startX , endX , startY , endY , startZ , endZ );
|
|
for( int x=startX ; x<endX ; x++ ) for( int y=startY ; y<endY ; y++ ) for( int z=startZ ; z<endZ ; z++ )
|
|
{
|
|
const TreeOctNode* _node = neighbors.neighbors[x][y][z];
|
|
const Real* _data1;
|
|
if( isValidFEMNode< FEMDegree1 , FEMBType1 >( _node ) && ( _data1=cumulative1(_node) ) )
|
|
{
|
|
if( isInterior ) dot += (*_data1) * (*_data2) * _stencil( x , y , z );
|
|
else
|
|
{
|
|
LocalDepth _d ; LocalOffset _off;
|
|
_localDepthAndOffset( _node , _d , _off );
|
|
dot += (*_data1) * (*_data2) * F.template integrate< false >( childIntegrator , _off , off );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Up sample the cumulative coefficients for the next level
|
|
if( d<_maxDepth ) _upSample< Real , FEMDegree1 , FEMBType1 >( d , cumulative1 );
|
|
}
|
|
}
|
|
|
|
// Calculate the contribution from @(>depth,depth)
|
|
{
|
|
typedef typename TreeOctNode::ConstNeighborKey< -BSplineSupportSizes< FEMDegree2 >::SupportStart , BSplineSupportSizes< FEMDegree2 >::SupportEnd > SupportKey;
|
|
const int OverlapSize = BSplineOverlapSizes< FEMDegree1 , FEMDegree2 >::OverlapSize;
|
|
const int LeftOverlapRadius = -BSplineOverlapSizes< FEMDegree1 , FEMDegree2 >::OverlapStart;
|
|
const int RightOverlapRadius = BSplineOverlapSizes< FEMDegree1 , FEMDegree2 >::OverlapEnd;
|
|
|
|
DenseNodeData< Real , FEMDegree2 > cumulative2( _sNodesEnd( _maxDepth-1 ) );
|
|
if( _maxDepth>0 ) memset( &cumulative2[0] , 0 , sizeof(Real) * _sNodesEnd( _maxDepth-1 ) );
|
|
|
|
for( LocalDepth d=_maxDepth ; d>0 ; d-- )
|
|
{
|
|
Stencil< double , OverlapSize > stencils[2][2][2];
|
|
typename SystemCoefficients< FEMDegree2 , FEMBType2 , FEMDegree1 , FEMBType1 >::ChildIntegrator childIntegrator;
|
|
BSplineIntegrationData< FEMDegree2 , FEMBType2 , FEMDegree1 , FEMBType1 >::SetChildIntegrator( childIntegrator , d-1 );
|
|
SystemCoefficients< FEMDegree2 , FEMBType2 , FEMDegree1 , FEMBType1 >::template SetCentralConstraintStencils< true >( F , childIntegrator , stencils );
|
|
|
|
std::vector< SupportKey > neighborKeys( std::max< int >( 1 , threads ) );
|
|
for( size_t i=0 ; i<neighborKeys.size() ; i++ ) neighborKeys[i].set( _localToGlobal( d-1 ) );
|
|
|
|
// Update the cumulative constraints @(depth-1) from @(depth)
|
|
#pragma omp parallel for num_threads( threads )
|
|
for( int i=_sNodesBegin(d) ; i<_sNodesEnd(d) ; i++ )
|
|
{
|
|
const TreeOctNode* node = _sNodes.treeNodes[i];
|
|
const Real* _data1;
|
|
if( isValidFEMNode< FEMDegree1 , FEMBType1 >( node ) && ( _data1=coefficients1( node ) ) )
|
|
{
|
|
SupportKey& neighborKey = neighborKeys[ omp_get_thread_num() ];
|
|
bool isInterior = _isInteriorlyOverlapped< FEMDegree2 , FEMDegree1 >( node->parent );
|
|
|
|
LocalDepth d ; LocalOffset off;
|
|
_localDepthAndOffset( node , d , off );
|
|
|
|
int cx , cy , cz;
|
|
Cube::FactorCornerIndex( (int)( node - node->parent->children ) , cx , cy ,cz );
|
|
const Stencil< double , OverlapSize >& _stencil = stencils[cx][cy][cz];
|
|
typename TreeOctNode::ConstNeighbors< OverlapSize > neighbors;
|
|
neighborKey.template getNeighbors< LeftOverlapRadius , RightOverlapRadius >( node->parent , neighbors );
|
|
|
|
int startX , endX , startY , endY , startZ , endZ;
|
|
_SetParentOverlapBounds< FEMDegree1 , FEMDegree2 >( node , startX , endX , startY , endY , startZ , endZ );
|
|
|
|
for( int x=startX ; x<endX ; x++ ) for( int y=startY ; y<endY ; y++ ) for( int z=startZ ; z<endZ ; z++ )
|
|
{
|
|
const TreeOctNode* _node = neighbors.neighbors[x][y][z];
|
|
if( isValidFEMNode< FEMDegree2 , FEMBType2 >( _node ) )
|
|
{
|
|
Real _dot;
|
|
if( isInterior ) _dot = (*_data1) * _stencil( x , y , z );
|
|
else
|
|
{
|
|
LocalDepth _d ; LocalOffset _off;
|
|
_localDepthAndOffset( _node , _d , _off );
|
|
_dot = (*_data1) * F.template integrate< true >( childIntegrator , _off , off );
|
|
}
|
|
#pragma omp atomic
|
|
cumulative2[ _node->nodeData.nodeIndex ] += _dot;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Update the dot-product using the cumulative constraints @(depth-1)
|
|
#pragma omp parallel for num_threads( threads ) reduction( + : dot )
|
|
for( int i=_sNodesBegin(d-1) ; i<_sNodesEnd(d-1) ; i++ )
|
|
{
|
|
const TreeOctNode* node = _sNodes.treeNodes[i];
|
|
const Real* _data2;
|
|
if( isValidFEMNode< FEMDegree2 , FEMBType2 >( node ) && ( _data2=coefficients2( node ) ) ) dot += cumulative2[ node->nodeData.nodeIndex ] * (*_data2);
|
|
}
|
|
|
|
// Down-sample the cumulative constraints from @(depth-1) to @(depth-2) for the next pass
|
|
if( d-1>0 ) _downSample< Real , FEMDegree2 , FEMBType2 >( d-1 , cumulative2 );
|
|
}
|
|
}
|
|
|
|
if( iInfo )
|
|
{
|
|
MultiThreadedEvaluator< FEMDegree1 , FEMBType1 > mt1( this , coefficients1 , threads );
|
|
MultiThreadedEvaluator< FEMDegree2 , FEMBType2 > mt2( this , coefficients2 , threads );
|
|
|
|
#pragma omp parallel for num_threads( threads ) reduction( + : dot )
|
|
for( int i=_sNodesBegin(0) ; i<_sNodesEnd(_maxDepth) ; i++ )
|
|
{
|
|
if( _isValidSpaceNode( _sNodes.treeNodes[i] ) && !_isValidSpaceNode( _sNodes.treeNodes[i]->children ) && (*iInfo)( _sNodes.treeNodes[i] ) )
|
|
{
|
|
|
|
const PointData< Real , HasGradients >& pData = *( (*iInfo)( _sNodes.treeNodes[i] ) );
|
|
#if POINT_DATA_RES
|
|
for( int c=0 ; c<PointData< Real , false >::SAMPLES ; c++ ) if( pData[c].weight )
|
|
{
|
|
Point3D< Real > p = pData[c].position;
|
|
Real w = pData[c].weight;
|
|
if( HasGradients )
|
|
{
|
|
std::pair< Real , Point3D< Real > > v1 = mt1.valueAndGradient( p , omp_get_thread_num() );
|
|
std::pair< Real , Point3D< Real > > v2 = mt2.valueAndGradient( p , omp_get_thread_num() );
|
|
dot += v1.first * v2.first * w * iInfo->valueWeight + Point3D< Real >::Dot( v1.second , v2.second ) * w * iInfo->gradientWeight;
|
|
}
|
|
else dot += mt1.value( p , omp_get_thread_num() ) * mt2.value( p , omp_get_thread_num() ) * w * iInfo->valueWeight;
|
|
}
|
|
#else // !POINT_DATA_RES
|
|
Point3D< Real > p = pData.position;
|
|
Real w = pData.weight;
|
|
if( HasGradients )
|
|
{
|
|
std::pair< Real , Point3D< Real > > v1 = mt1.valueAndGradient( p , omp_get_thread_num() );
|
|
std::pair< Real , Point3D< Real > > v2 = mt2.valueAndGradient( p , omp_get_thread_num() );
|
|
dot += v1.first * v2.first * w * iInfo->valueWeight + Point3D< Real >::Dot( v1.second , v2.second ) * w * iInfo->gradientWeight;
|
|
}
|
|
else dot += mt1.value( p , omp_get_thread_num() ) * mt2.value( p , omp_get_thread_num() ) * w * iInfo->valueWeight;
|
|
#endif // POINT_DATA_RES
|
|
}
|
|
}
|
|
}
|
|
|
|
return dot;
|
|
}
|