Added: volumetric LR projections

This commit is contained in:
Kjetil Andre Johannessen 2016-11-11 10:25:25 +01:00 committed by Arne Morten Kvarving
parent 21d52dbbff
commit 66815bec75
3 changed files with 541 additions and 19 deletions

View File

@ -687,8 +687,26 @@ double ASMu3D::getParametricArea (int iel, int dir) const
<< dir << std::endl;
return DERR;
}
#undef DERR
double ASMu3D::getParametricVolume (int iel) const
{
#ifdef INDEX_CHECK
if (iel < 1 || (size_t)iel > MNPC.size())
{
std::cerr <<" *** ASMu3D::getParametricArea: Element index "<< iel
<<" out of range [1,"<< MNPC.size() <<"]."<< std::endl;
return DERR;
}
#endif
if (MNPC[iel-1].empty())
return 0.0;
LR::Element *el = lrspline->getElement(iel-1);
double du = el->getParmax(0) - el->getParmin(0);
double dv = el->getParmax(1) - el->getParmin(1);
double dw = el->getParmax(2) - el->getParmin(2);
return du*dv*dw;
}
bool ASMu3D::getElementCoordinates (Matrix& X, int iel) const
{
@ -1796,14 +1814,18 @@ bool ASMu3D::evalSolution (Matrix& sField, const Vector& locSol,
bool ASMu3D::evalSolution (Matrix& sField, const IntegrandBase& integrand,
const int* npe, char project) const
{
if (npe == nullptr || npe[0] != npe[1] || npe[0] != npe[2] || npe[1] != npe[2])
{
std::cerr <<" *** ASMu2D::evalSolution: LR B-splines require the"
<<" same number of evaluation points in u-, v- and w-direction."
<< std::endl;
return false;
}
// Project the secondary solution onto the spline basis
LR::LRSplineVolume* v = nullptr;
if (project == 'A')
v = this->projectSolutionLocalApprox(integrand);
else if (project == 'L')
v = this->projectSolutionLocal(integrand);
else if (project == 'W')
v = this->projectSolutionLeastSquare(integrand);
if (project == 'S')
v = this->scRecovery(integrand);
else if (project == 'D' || !npe)
v = this->projectSolution(integrand);
@ -1821,10 +1843,16 @@ bool ASMu3D::evalSolution (Matrix& sField, const IntegrandBase& integrand,
else if (v)
{
// Evaluate the projected field at the result sampling points
const Vector& svec = sField; // using utl::matrix cast operator
sField.resize(v->dimension(),
gpar[0].size()*gpar[1].size()*gpar[2].size());
v->gridEvaluator(gpar[0],gpar[1],gpar[2],const_cast<Vector&>(svec));
Go::Point p;
sField.resize(v->dimension(),gpar[0].size()*gpar[1].size()*gpar[2].size());
int iel = 0; // evaluation points are always structured in element order
for (size_t i = 0; i < gpar[0].size(); i++)
{
if ((i+1)%npe[0] == 0) iel++;
v->point(p,gpar[0][i],gpar[1][i],gpar[2][i],iel);
sField.fillColumn(i+1,p.begin());
}
delete v;
return true;
}
@ -1835,16 +1863,14 @@ bool ASMu3D::evalSolution (Matrix& sField, const IntegrandBase& integrand,
else if (v)
{
// Extract control point values from the spline object
sField.resize(v->dimension(),
v->numCoefs());
sField.fill(&(*v->coefs_begin()));
sField.resize(v->dimension(),v->nBasisFunctions());
for (int i = 0; i < v->nBasisFunctions(); i++)
sField.fillColumn(i+1,&(*v->getBasisfunction(i)->cp()));
delete v;
return true;
}
std::cerr <<" *** ASMu3D::evalSolution: Failure!";
if (project) std::cerr <<" project="<< project;
std::cerr << std::endl;
std::cerr <<" *** ASMu2D::evalSolution: Failure!"<< std::endl;
return false;
}

View File

@ -311,10 +311,29 @@ public:
//! \brief Returns the spline volume representing the basis of this patch.
virtual const LR::LRSplineVolume* getBasis(int = 1) const { return lrspline.get(); }
private:
//! \brief Projects the secondary solution field onto the primary basis.
//! \param[in] integrand Object with problem-specific data and methods
LR::LRSplineVolume* projectSolution(const IntegrandBase& integrand) const;
//! \brief Projects the secondary solution using a superconvergent approach.
//! \param[in] integrand Object with problem-specific data and methods
LR::LRSplineVolume* scRecovery(const IntegrandBase& integrand) const;
//! \brief Interpolates an LR spline at a given set of parametric coordinates.
//! \param[in] upar Parametric interpolation points in the u-direction
//! \param[in] vpar Parametric interpolation points in the v-direction
//! \param[in] wpar Parametric interpolation points in the v-direction
//! \param[in] points Interpolation values stored as one point per matrix row
//! \return A LRSplineVolume representation of the interpolated points
LR::LRSplineVolume* regularInterpolation(const RealArray& upar,
const RealArray& vpar,
const RealArray& wpar,
const Matrix& points) const;
public:
//! \brief Projects the secondary solution field onto the primary basis.
//! \param[in] integrand Object with problem-specific data and methods
virtual LR::LRSpline* evalSolution(const IntegrandBase& integrand) const { return nullptr; }
virtual LR::LRSpline* evalSolution(const IntegrandBase& integrand) const ;
//! \brief Evaluates the secondary solution field at the given points.
//! \param[out] sField Solution field
@ -338,7 +357,7 @@ public:
//! \param[in] continuous If \e true, a continuous L2-projection is used
virtual bool globalL2projection(Matrix& sField,
const IntegrandBase& integrand,
bool continuous = false) const { return false; }
bool continuous = false) const;
protected:

477
src/ASM/LR/ASMu3Drecovery.C Normal file
View File

@ -0,0 +1,477 @@
// $Id$
//==============================================================================
//!
//! \file ASMu3Drecovery.C
//!
//! \date November 2016
//!
//! \author Kjetil A. Johannessen and Knut Morten Okstad / SINTEF
//!
//! \brief Recovery techniques for unstructured LR B-splines.
//!
//==============================================================================
#include "LRSpline/LRSplineVolume.h"
#include "LRSpline/Element.h"
#include "ASMu3D.h"
#include "CoordinateMapping.h"
#include "GaussQuadrature.h"
#include "SparseMatrix.h"
#include "DenseMatrix.h"
#include "SplineUtils.h"
#include "Utilities.h"
#include "Profiler.h"
#include "IntegrandBase.h"
#include <array>
bool ASMu3D::getGrevilleParameters (RealArray& prm, int dir) const
{
if (!lrspline || dir < 0 || dir > 1) return false;
prm.clear();
prm.reserve(lrspline->nBasisFunctions());
for(LR::Basisfunction *b : lrspline->getAllBasisfunctions())
prm.push_back(b->getGrevilleParameter()[dir]);
return true;
}
/*!
\brief Expands a tensor parametrization point to an unstructured one.
\details Takes as input a tensor mesh, for instance
in[0] = {0,1}
in[1] = {2,3}
in[2] = {7,9}
and expands this to an unstructred representation, i.e.,
out[0] = {0,1,0,1,0,1,0,1}
out[1] = {2,2,3,3,2,2,3,3}
out[2] = {7,7,7,7,9,9,9,9}
*/
static void expandTensorGrid (const RealArray* in, RealArray* out)
{
out[0].resize(in[0].size()*in[1].size()*in[2].size());
out[1].resize(in[0].size()*in[1].size()*in[2].size());
out[2].resize(in[0].size()*in[1].size()*in[2].size());
size_t i, j, k, ip = 0;
for (k = 0; k < in[2].size(); k++)
for (j = 0; j < in[1].size(); j++)
for (i = 0; i < in[0].size(); i++, ip++) {
out[0][ip] = in[0][i];
out[1][ip] = in[1][j];
out[2][ip] = in[2][k];
}
}
LR::LRSplineVolume* ASMu3D::projectSolution (const IntegrandBase& integr) const
{
PROFILE2("ASMu3D::projectSolution");
// Compute parameter values of the result sampling points (Greville points)
std::array<RealArray,3> gpar;
for (int dir = 0; dir < 3; dir++)
if (!this->getGrevilleParameters(gpar[dir],dir))
return nullptr;
// Evaluate the secondary solution at all sampling points
Matrix sValues;
if (!this->evalSolution(sValues,integr,gpar.data()))
return nullptr;
// Project the results onto the spline basis to find control point
// values based on the result values evaluated at the Greville points.
// Note that we here implicitly assume that the number of Greville points
// equals the number of control points such that we don't have to resize
// the result array. Think that is always the case, but beware if trying
// other projection schemes later.
return this->regularInterpolation(gpar[0],gpar[1],gpar[2],sValues);
}
LR::LRSpline* ASMu3D::evalSolution (const IntegrandBase& integrand) const
{
return this->projectSolution(integrand);
}
bool ASMu3D::globalL2projection (Matrix& sField,
const IntegrandBase& integrand,
bool continuous) const
{
if (!lrspline) return true; // silently ignore empty patches
PROFILE2("ASMu3D::globalL2");
const int p1 = lrspline->order(0);
const int p2 = lrspline->order(1);
const int p3 = lrspline->order(2);
// Get Gaussian quadrature points
const int ng1 = continuous ? nGauss : p1 - 1;
const int ng2 = continuous ? nGauss : p2 - 1;
const int ng3 = continuous ? nGauss : p3 - 1;
const double* xg = GaussQuadrature::getCoord(ng1);
const double* yg = GaussQuadrature::getCoord(ng2);
const double* zg = GaussQuadrature::getCoord(ng3);
const double* wg = continuous ? GaussQuadrature::getWeight(nGauss) : nullptr;
if (!xg || !yg || !zg) return false;
if (continuous && !wg) return false;
// Set up the projection matrices
const size_t nnod = this->getNoNodes();
const size_t ncomp = integrand.getNoFields();
SparseMatrix A(SparseMatrix::SUPERLU);
StdVector B(nnod*ncomp);
A.redim(nnod,nnod);
double dA = 0.0;
Vector phi;
Matrix dNdu, Xnod, Jac;
Go::BasisDerivs spl1;
Go::BasisPts spl0;
// === Assembly loop over all elements in the patch ==========================
std::vector<LR::Element*>::iterator el = lrspline->elementBegin();
for (int iel = 1; el != lrspline->elementEnd(); el++, iel++)
{
if (continuous)
{
// Set up control point (nodal) coordinates for current element
if (!this->getElementCoordinates(Xnod,iel))
return false;
else if ((dA = 0.125*this->getParametricVolume(iel)) < 0.0)
return false; // topology error (probably logic error)
}
// Compute parameter values of the Gauss points over this element
std::array<RealArray,3> gpar, unstrGpar;
this->getGaussPointParameters(gpar[0],0,ng1,iel,xg);
this->getGaussPointParameters(gpar[1],1,ng2,iel,yg);
this->getGaussPointParameters(gpar[2],2,ng3,iel,zg);
// convert to unstructred mesh representation
expandTensorGrid(gpar.data(),unstrGpar.data());
// Evaluate the secondary solution at all integration points
if (!this->evalSolution(sField,integrand,unstrGpar.data()))
return false;
// set up basis function size (for extractBasis subroutine)
phi.resize((**el).nBasisFunctions());
// --- Integration loop over all Gauss points in each direction ----------
int ip = 0;
for (int k = 0; k < ng3; k++)
for (int j = 0; j < ng2; j++)
for (int i = 0; i < ng1; i++, ip++)
{
if (continuous)
{
lrspline->computeBasis(gpar[0][i],gpar[1][j],gpar[2][k],spl1,iel-1);
SplineUtils::extractBasis(spl1,phi,dNdu);
}
else
{
lrspline->computeBasis(gpar[0][i],gpar[1][j],gpar[2][k],spl0,iel-1);
phi = spl0.basisValues;
}
// Compute the Jacobian inverse and derivatives
double dJw = 1.0;
if (continuous)
{
dJw = dA*wg[i]*wg[j]*wg[k]*utl::Jacobian(Jac,dNdu,Xnod,dNdu,false);
if (dJw == 0.0) continue; // skip singular points
}
// Integrate the linear system A*x=B
for (size_t ii = 0; ii < phi.size(); ii++)
{
int inod = MNPC[iel-1][ii]+1;
for (size_t jj = 0; jj < phi.size(); jj++)
{
int jnod = MNPC[iel-1][jj]+1;
A(inod,jnod) += phi[ii]*phi[jj]*dJw;
}
for (size_t r = 1; r <= ncomp; r++)
B(inod+(r-1)*nnod) += phi[ii]*sField(r,ip+1)*dJw;
}
}
}
#if SP_DEBUG > 2
std::cout <<"---- Matrix A -----"<< A
<<"-------------------"<< std::endl;
std::cout <<"---- Vector B -----"<< B
<<"-------------------"<< std::endl;
#endif
// Solve the patch-global equation system
if (!A.solve(B)) return false;
// Store the control-point values of the projected field
sField.resize(ncomp,nnod);
for (size_t i = 1; i <= nnod; i++)
for (size_t j = 1; j <= ncomp; j++)
sField(j,i) = B(i+(j-1)*nnod);
return true;
}
/*!
\brief Evaluates monomials in 3D up to the specified order.
*/
static void evalMonomials (int p1, int p2, int p3, double x, double y, double z, Vector& P)
{
P.resize(p1*p2*p3);
P.fill(1.0);
int i, j, k, l, ip = 1;
for (k = 0; k < p3; k++)
for (j = 0; j < p2; j++)
for (i = 0; i < p1; i++, ip++)
{
for (l = 0; l < i; l++) P(ip) *= x;
for (l = 0; l < j; l++) P(ip) *= y;
for (l = 0; l < k; l++) P(ip) *= z;
}
}
LR::LRSplineVolume* ASMu3D::scRecovery (const IntegrandBase& integrand) const
{
PROFILE2("ASMu3D::scRecovery");
const int m = integrand.derivativeOrder();
const int p1 = lrspline->order(0);
const int p2 = lrspline->order(1);
const int p3 = lrspline->order(2);
// Get Gaussian quadrature point coordinates
const int ng1 = p1 - m;
const int ng2 = p2 - m;
const int ng3 = p3 - m;
const double* xg = GaussQuadrature::getCoord(ng1);
const double* yg = GaussQuadrature::getCoord(ng2);
const double* zg = GaussQuadrature::getCoord(ng3);
if (!xg || !yg || !zg) return nullptr;
// Compute parameter values of the Greville points
std::array<RealArray,3> gpar;
if (!this->getGrevilleParameters(gpar[0],0)) return nullptr;
if (!this->getGrevilleParameters(gpar[1],1)) return nullptr;
if (!this->getGrevilleParameters(gpar[2],2)) return nullptr;
const int n1 = p1 - m + 1; // Patch size in first parameter direction
const int n2 = p2 - m + 1; // Patch size in second parameter direction
const int n3 = p3 - m + 1; // Patch size in second parameter direction
const size_t nCmp = integrand.getNoFields(); // Number of result components
const size_t nPol = n1*n2*n3; // Number of terms in polynomial expansion
Matrix sValues(nCmp,gpar[0].size());
Vector P(nPol);
Go::Point X, G;
// Loop over all Greville points (one for each basis function)
size_t ip = 0;
std::vector<LR::Element*>::const_iterator elStart, elEnd, el;
std::vector<LR::Element*> supportElements;
for (LR::Basisfunction *b : lrspline->getAllBasisfunctions())
{
#if SP_DEBUG > 2
std::cout <<"Basis: "<< *b <<"\n ng1 ="<< ng1 <<"\n ng2 ="<< ng2 <<"\n ng3 ="<< ng3
<<"\n nPol="<< nPol << std::endl;
#endif
// Special case for basis functions with too many zero knot spans by using
// the extended support
// if(nel*ng1*ng2*ng3 < nPol)
if(true)
{
// KMO: Here I'm not sure how this will change when m > 1.
// In that case I think we would need smaller patches (as in the tensor
// splines case). But how to do that???
supportElements = b->getExtendedSupport();
elStart = supportElements.begin();
elEnd = supportElements.end();
#if SP_DEBUG > 2
std::cout <<"Extended basis:";
for (el = elStart; el != elEnd; el++)
std::cout <<"\n " << **el;
std::cout << std::endl;
#endif
}
else
{
elStart = b->supportedElementBegin();
elEnd = b->supportedElementEnd();
}
// Physical coordinates of current Greville point
lrspline->point(G,gpar[0][ip],gpar[1][ip],gpar[2][ip]);
// Set up the local projection matrices
DenseMatrix A(nPol,nPol);
Matrix B(nPol,nCmp);
// Loop over all non-zero knot-spans in the support of
// the basis function associated with current Greville point
for (el = elStart; el != elEnd; el++)
{
int iel = (**el).getId()+1;
// evaluate all gauss points for this element
std::array<RealArray,3> gaussPt, unstrGauss;
this->getGaussPointParameters(gaussPt[0],0,ng1,iel,xg);
this->getGaussPointParameters(gaussPt[1],1,ng2,iel,yg);
this->getGaussPointParameters(gaussPt[2],2,ng3,iel,zg);
#if SP_DEBUG > 2
std::cout << "Element " << **el << std::endl;
#endif
// convert to unstructred mesh representation
expandTensorGrid(gaussPt.data(),unstrGauss.data());
// Evaluate the secondary solution at all Gauss points
Matrix sField;
if (!this->evalSolution(sField,integrand,unstrGauss.data()))
return nullptr;
// Loop over the Gauss points in current knot-span
int i, j, k, ig = 1;
for (k = 0; k < ng3; k++)
for (j = 0; j < ng2; j++)
for (i = 0; i < ng1; i++, ig++)
{
// Evaluate the polynomial expansion at current Gauss point
lrspline->point(X,gaussPt[0][i],gaussPt[1][j],gaussPt[2][k]);
evalMonomials(n1,n2,n3,X[0]-G[0],X[1]-G[1],X[2]-G[2],P);
#if SP_DEBUG > 2
std::cout <<"Greville point: "<< G
<<"\nGauss point: "<< gaussPt[0][i] <<", "<< gaussPt[0][j] << ", " << gaussPt[0][k]
<<"\nMapped gauss point: "<< X
<<"\nP-matrix:"<< P <<"--------------------\n"<< std::endl;
#endif
for (size_t ii = 1; ii <= nPol; ii++)
{
// Accumulate the projection matrix, A += P^t * P
for (size_t jj = 1; jj <= nPol; jj++)
A(ii,jj) += P(ii)*P(jj);
// Accumulate the right-hand-side matrix, B += P^t * sigma
for (size_t jj = 1; jj <= nCmp; jj++)
B(ii,jj) += P(ii)*sField(jj,ig);
}
}
}
#if SP_DEBUG > 2
std::cout <<"---- Matrix A -----"<< A
<<"-------------------"<< std::endl;
std::cout <<"---- Vector B -----"<< B
<<"-------------------"<< std::endl;
#endif
// Solve the local equation system
if (!A.solve(B)) return nullptr;
// Evaluate the projected field at current Greville point (first row of B)
ip++;
for (size_t l = 1; l <= nCmp; l++)
sValues(l,ip) = B(1,l);
#if SP_DEBUG > 1
std::cout <<"Greville point "<< ip <<" (u,v,w) = "
<< gpar[0][ip-1] <<" "<< gpar[1][ip-1] <<" "<< gpar[2][ip-1] <<" :";
for (k = 1; k <= nCmp; k++)
std::cout <<" "<< sValues(k,ip);
std::cout << std::endl;
#endif
}
// Project the Greville point results onto the spline basis
// to find the control point values
return this->regularInterpolation(gpar[0],gpar[1],gpar[2],sValues);
}
LR::LRSplineVolume* ASMu3D::regularInterpolation (const RealArray& upar,
const RealArray& vpar,
const RealArray& wpar,
const Matrix& points) const
{
if (lrspline->rational())
{
std::cerr <<" *** ASMu3D::regularInterpolation:"
<<" Rational LR B-splines not supported yet."<< std::endl;
return nullptr;
}
// sanity check on input parameters
const size_t nBasis = lrspline->nBasisFunctions();
if (upar.size() != nBasis || vpar.size() != nBasis || wpar.size() != nBasis ||
points.cols() != nBasis)
{
std::cerr <<" *** ASMu3D::regularInterpolation:"
<<" Mismatching input array sizes.\n"
<<" size(upar)="<< upar.size() <<" size(vpar)="<< vpar.size()
<<" size(wpar)="<< wpar.size()
<<" size(points)="<< points.cols() <<" nBasis="<< nBasis
<< std::endl;
return nullptr;
}
SparseMatrix A(SparseMatrix::SUPERLU);
A.resize(nBasis, nBasis);
Matrix B2(points,true); // transpose to get one vector per field
StdVector B(B2);
Go::BasisPts splineValues;
// Evaluate all basis functions at all points, stored in the A-matrix
// (same row = same evaluation point)
for (size_t i = 0; i < nBasis; i++)
{
int iel = lrspline->getElementContaining(upar[i], vpar[i], wpar[i]);
lrspline->computeBasis(upar[i],vpar[i],wpar[i],splineValues, iel);
size_t k = 0;
for (auto& function : lrspline->getElement(iel)->support()) {
int j = function->getId();
A(i+1,j+1) = splineValues.basisValues[k++];
}
}
// Solve for all solution components - one right-hand-side for each
if (!A.solve(B))
return nullptr;
// Copy all basis functions and mesh
LR::LRSplineVolume* ans = lrspline->copy();
ans->rebuildDimension(points.rows());
// Back to interleaved data
std::vector<double> interleave;
interleave.reserve(B.dim());
for (size_t i = 0; i < nBasis; ++i)
for (size_t j = 0; j < points.rows(); j++) {
interleave.push_back(B(1+j*points.cols()+i));
}
ans->setControlPoints(interleave);
return ans;
}