Commit 054249dc authored by Praetorius, Simon's avatar Praetorius, Simon

preconditioners added

parent 7dbedfa6
/******************************************************************************
*
* Extension of AMDiS - Adaptive multidimensional simulations
*
* Copyright (C) 2013 Dresden University of Technology. All Rights Reserved.
* Web: https://fusionforge.zih.tu-dresden.de/projects/amdis
*
* Authors: Simon Praetorius et al.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*
* See also license.opensource.txt in the distribution.
*
******************************************************************************/
#include "CahnHilliard_.h"
// #include "Views.h"
#include "SignedDistFunctors.h"
#include "PhaseFieldConvert.h"
#include "HL_SignedDistTraverse.h"
#include "Recovery.h"
using namespace AMDiS;
CahnHilliard_::CahnHilliard_(const std::string &name_) :
super(name_),
useMobility(false),
useReinit(false),
doubleWell(0),
gamma(1.0),
eps(0.1),
minusEps(-0.1),
epsInv(10.0),
minusEpsInv(-10.0),
epsSqr(0.01),
minusEpsSqr(-0.01)
{
// parameters for CH
Parameters::get(name_ + "->use mobility", useMobility); // mobility
Parameters::get(name_ + "->gamma", gamma); // mobility
Parameters::get(name_ + "->epsilon", eps); // interface width
// type of double well: 0= [0,1], 1= [-1,1]
Parameters::get(name_ + "->double-well type", doubleWell);
Parameters::get(name + "->use reinit", useReinit);
// transformation of the parameters
minusEps = -eps;
epsInv = 1.0/eps;
minusEpsInv = -epsInv;
epsSqr = sqr(eps);
minusEpsSqr = -epsSqr;
}
void CahnHilliard_::solveInitialProblem(AdaptInfo *adaptInfo)
{
Flag initFlag = initDataFromFile(adaptInfo);
if (!initFlag.isSet(DATA_ADOPTED)) {
int initialInterface = 0;
Initfile::get(name + "->initial interface", initialInterface);
double initialEps = eps;
Initfile::get(name + "->initial epsilon", initialEps);
if (initialInterface == 0) {
/// horizontale Linie
double a= 0.0, dir= -1.0;
Initfile::get(name + "->line->pos", a);
Initfile::get(name + "->line->direction", dir);
prob->getSolution()->getDOFVector(1)->interpol(new Plane(a, dir));
}
else if (initialInterface == 1) {
/// schraege Linie
double theta = m_pi/4.0;
prob->getSolution()->getDOFVector(1)->interpol(new PlaneRotation(0.0, theta, 1.0));
transformDOFInterpolation(prob->getSolution()->getDOFVector(1),new PlaneRotation(0.0, -theta, -1.0), new AMDiS::Min<double>);
}
else if (initialInterface == 2) {
/// Ellipse
double a= 1.0, b= 1.0;
Initfile::get(name + "->ellipse->a", a);
Initfile::get(name + "->ellipse->b", b);
prob->getSolution()->getDOFVector(1)->interpol(new Ellipse(a,b));
}
else if (initialInterface == 3) {
/// zwei horizontale Linien
double a= 0.75, b= 0.375;
Initfile::get(name + "->lines->pos1", a);
Initfile::get(name + "->lines->pos2", b);
prob->getSolution()->getDOFVector(1)->interpol(new Plane(a, -1.0));
transformDOFInterpolation(prob->getSolution()->getDOFVector(1),new Plane(b, 1.0), new AMDiS::Max<double>);
}
else if (initialInterface == 4) {
/// Kreis
double radius= 1.0;
Initfile::get(name + "->kreis->radius", radius);
prob->getSolution()->getDOFVector(1)->interpol(new Circle(radius));
} else if (initialInterface == 5) {
/// Rechteck
double width = 0.5;
double height = 0.3;
WorldVector<double> center; center.set(0.5);
Initfile::get(name + "->rectangle->width", width);
Initfile::get(name + "->rectangle->height", height);
Initfile::get(name + "->rectangle->center", center);
prob->getSolution()->getDOFVector(1)->interpol(new Rectangle(width, height, center));
}
// TODO: Redistancing einfügen!
if (useReinit) {
FiniteElemSpace* feSpace = FiniteElemSpace::provideFeSpace(
const_cast<DOFAdmin*>(getMesh()->getVertexAdmin()),
Lagrange::getLagrange(getMesh()->getDim(), 1),
getMesh(),
"P1");
DOFVector<double> tmp(feSpace, "tmp");
tmp.interpol(prob->getSolution()->getDOFVector(1));
HL_SignedDistTraverse reinit("reinit", getMesh()->getDim());
reinit.calcSignedDistFct(adaptInfo, &tmp);
#ifndef HAVE_PARALLEL_DOMAIN_AMDIS
Recovery recovery(L2_NORM, 1);
recovery.recoveryUh(&tmp, *prob->getSolution()->getDOFVector(1));
#else
prob->getSolution()->getDOFVector(1)->interpol(&tmp);
#endif
}
/// create phase-field from signed-dist-function
if (doubleWell == 0) {
forEachDOF(prob->getSolution()->getDOFVector(1),
new SignedDistToPhaseField(initialEps));
} else {
forEachDOF(prob->getSolution()->getDOFVector(1),
new SignedDistToCh(initialEps));
}
}
}
void CahnHilliard_::fillOperators()
{
const FiniteElemSpace* feSpace = prob->getFeSpace();
int degree = feSpace->getBasisFcts()->getDegree();
// c
Operator *opChMnew = new Operator(feSpace, feSpace);
opChMnew->addTerm(new Simple_ZOT);
Operator *opChMold = new Operator(feSpace, feSpace);
opChMold->addTerm(new VecAtQP_ZOT(prob->getSolution()->getDOFVector(1)));
// -nabla*(grad(c))
Operator *opChL = new Operator(feSpace, feSpace);
opChL->addTerm(new Simple_SOT);
// div(M(c)grad(mu)), with M(c)=gamma/4*(c^2-1)^2
Operator *opChLM = new Operator(feSpace, feSpace);
if (useMobility) {
if (doubleWell == 0)
opChLM->addTerm(new VecAtQP_SOT(
prob->getSolution()->getDOFVector(1),
new MobilityCH0(gamma, degree)));
else
opChLM->addTerm(new VecAtQP_SOT(
prob->getSolution()->getDOFVector(1),
new MobilityCH1(gamma, degree)));
} else
opChLM->addTerm(new Simple_SOT(gamma));
// -2*c_old^3 + 3/2*c_old^2
Operator *opChMPowExpl = new Operator(feSpace, feSpace);
opChMPowExpl->addTerm(new VecAtQP_ZOT(
prob->getSolution()->getDOFVector(1),
new AMDiS::Pow<3>(-2.0, 3*degree)));
if (doubleWell == 0) {
opChMPowExpl->addTerm(new VecAtQP_ZOT(
prob->getSolution()->getDOFVector(1),
new AMDiS::Pow<2>(3.0/2.0, 2*degree)));
}
// -3*c_old^2 * c
Operator *opChMPowImpl = new Operator(feSpace, feSpace);
opChMPowImpl->addTerm(new VecAtQP_ZOT(
prob->getSolution()->getDOFVector(1),
new AMDiS::Pow<2>(-3.0, 2*degree)));
if (doubleWell == 0) {
opChMPowImpl->addTerm(new VecAtQP_ZOT(
prob->getSolution()->getDOFVector(1),
NULL, 3.0));
opChMPowImpl->addTerm(new Simple_ZOT(-0.5));
} else {
opChMPowImpl->addZeroOrderTerm(new Simple_ZOT(1.0));
}
// mu + eps^2*laplace(c) + c - 3*(c_old^2)*c = -2*c_old^3 [+ BC]
// ----------------------------------------------------------------------
prob->addMatrixOperator(*opChMPowImpl,0,1); /// < -3*c*c_old^2 , psi >
prob->addMatrixOperator(*opChL,0,1, &minusEpsSqr); /// < -eps^2*grad(c) , grad(psi) >
prob->addMatrixOperator(*opChMnew,0,0); /// < mu , psi >
// . . . vectorOperators . . . . . . . . . . . . . . .
prob->addVectorOperator(*opChMPowExpl,0); /// < -2*c_old^3 , psi >
// dt(c) = laplace(mu) - u*grad(c)
// -----------------------------------
prob->addMatrixOperator(*opChMnew,1,1); /// < c , psi >
prob->addMatrixOperator(*opChLM,1,0, getTau()); /// < tau*grad(mu) , grad(psi) >
// . . . vectorOperators . . . . . . . . . . . . . . .
prob->addVectorOperator(*opChMold,1); /// < c^old , psi >
}
/******************************************************************************
*
* Extension of AMDiS - Adaptive multidimensional simulations
*
* Copyright (C) 2013 Dresden University of Technology. All Rights Reserved.
* Web: https://fusionforge.zih.tu-dresden.de/projects/amdis
*
* Authors: Simon Praetorius et al.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*
* See also license.opensource.txt in the distribution.
*
******************************************************************************/
#ifndef CAHN_HILLIARD_PRECON_H
#define CAHN_HILLIARD_PRECON_H
#include "AMDiS.h"
#include "BaseProblem.h"
#include "chns.h"
using namespace AMDiS;
class CahnHilliard_ : public BaseProblem<ProblemStat>
{
public: // definition of types
typedef BaseProblem<ProblemStat> super;
public: // public methods
CahnHilliard_(const std::string &name_);
~CahnHilliard_() {};
void solveInitialProblem(AdaptInfo *adaptInfo);
double getEpsilon() { return eps; }
int getDoubleWellType() { return doubleWell; }
void fillOperators() override;
void fillBoundaryConditions() override {}
protected: // protected variables
bool useMobility;
bool useReinit;
unsigned dim;
int doubleWell;
double gamma;
double eps;
double minusEps;
double epsInv;
double minusEpsInv;
double epsSqr;
double minusEpsSqr;
};
#endif // CAHN_HILLIARD_PRECON_H
This diff is collapsed.
/******************************************************************************
*
* AMDiS - Adaptive multidimensional simulations
*
* Copyright (C) 2013 Dresden University of Technology. All Rights Reserved.
* Web: https://fusionforge.zih.tu-dresden.de/projects/amdis
*
* Authors:
* Simon Vey, Thomas Witkowski, Andreas Naumann, Simon Praetorius, et al.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*
* This file is part of AMDiS
*
* See also license.opensource.txt in the distribution.
*
******************************************************************************/
/** \file PetscSolverNavierStokes2.h */
#ifndef AMDIS_PETSC_SOLVER_NAVIER_STOKES2_H
#define AMDIS_PETSC_SOLVER_NAVIER_STOKES2_H
#include "parallel/PetscSolverGlobalMatrix.h"
namespace AMDiS { namespace Parallel {
using namespace std;
struct NavierStokesSchurData2 {
KSP kspMass;
KSP kspLaplace;
Mat matConDif;
};
class PetscSolverNavierStokes2 : public PetscSolverGlobalMatrix
{
private:
class LinearInterpolation : public AbstractFunction<double, double>
{
public:
LinearInterpolation(double c1, double c2, double factor=1.0)
: AbstractFunction<double, double>(0)
{ a = (c1-c2)/2.0*factor; b = (c1+c2)/2.0*factor; cmin=std::min(c1,c2)*factor; cmax=std::max(c1,c2)*factor;}
double operator()(const double& x) const
{ double result = b+a*x;
if (result<cmin) result = cmin;
if (result>cmax) result = cmax;
return result;
}
private:
double a,b,cmin,cmax;
};
class LinearInterpolation2 : public BinaryAbstractFunction<double, double, double>
{
public:
LinearInterpolation2(double c1, double c2, double factor=1.0)
: BinaryAbstractFunction<double, double, double>(0)
{ a = (c1-c2)/2.0*factor; b = (c1+c2)/2.0*factor; cmin=std::min(c1,c2)*factor; cmax=std::max(c1,c2)*factor;}
double operator()(const double& u, const double& x) const
{
double result = b+a*x;
if (result<cmin) result = cmin;
if (result>cmax) result = cmax;
return result * u;
}
private:
double a,b,cmin,cmax;
};
class EinsMinus : public AbstractFunction<double, double>
{
public:
EinsMinus(double d)
: AbstractFunction<double, double>(0),
c(d)
{}
double operator()(const double& x) const
{
return c * std::max(1.0-x,0.000001);
}
private:
double c;
};
public:
/// Creator class
class Creator : public LinearSolverCreator
{
public:
virtual ~Creator() {}
/// Returns a new PetscSolver object.
LinearSolver* create()
{
return new PetscSolverNavierStokes2(this->name);
}
};
PetscSolverNavierStokes2(string name);
void setStokesData(double *invTauPtr, SystemVector *vec, double *nu1_=NULL, double *nu2_=NULL, double *rho1_=NULL, double *rho2_=NULL)
{
nu = new double;
(*nu) = 0.0;
invTau = invTauPtr;
solution = vec;
nu1=nu1_;
nu2=nu2_;
rho1=rho1_;
rho2=rho2_;
}
void setStokesData(double *nuPtr, double *invTauPtr, SystemVector *vec)
{
nu = nuPtr;
invTau = invTauPtr;
solution = vec;
}
void setPhase(DOFVector<double> *d)
{
phase = d;
}
protected:
/// Implementation of \ref LinearSolver::solveLinearSystem()
int solveLinearSystem(const SolverMatrix<Matrix<DOFMatrix*> >& A,
SystemVector& x,
SystemVector& b,
bool createMatrixData,
bool storeMatrixData);
void initSolver(KSP &ksp);
void initPreconditioner(PC pc);
void exitPreconditioner(PC pc);
void addDirichletBC(DOFMatrix &m, BoundaryType b);
private:
int pressureComponent;
bool pressureNullSpace;
/// If true, old solution is used for initial guess in solver phase.
bool useOldInitialGuess;
/// 0: approximate solve 1: direct solver
int velocitySolutionMode;
/// 0: approximate solve 1: direct solver
int massSolutionMode;
/// 0: approximate solve 1: direct solver
int laplaceSolutionMode;
PetscSolver *massMatrixSolver, *laplaceMatrixSolver, *conDifMatrixSolver;
NavierStokesSchurData2 matShellContext;
double *nu, *invTau, *nu1,*nu2,*rho1,*rho2;
SystemVector* solution;
DOFVector<double>* phase;
std::vector<int> neumannBC;
const Matrix<DOFMatrix*>* systemMat;
};
} }
#endif
/******************************************************************************
*
* Extension of AMDiS - Adaptive multidimensional simulations
*
* Copyright (C) 2013 Dresden University of Technology. All Rights Reserved.
* Web: https://fusionforge.zih.tu-dresden.de/projects/amdis
*
* Authors: Simon Praetorius et al.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*
* See also license.opensource.txt in the distribution.
*
******************************************************************************/
#include "PetscSolverPfc.h"
#include "parallel/PetscHelper.h"
#include "parallel/PetscSolverGlobalMatrix.h"
namespace AMDiS { namespace Parallel {
using namespace std;
PetscErrorCode pcPfcMatMultSchur(Mat mat, Vec b, Vec x)
{
void *ctx;
MatShellGetContext(mat, &ctx);
PfcData* data = static_cast<PfcData*>(ctx);
Vec y1, y2, y3;
VecDuplicate(b, &y1);
VecDuplicate(b, &y2);
VecDuplicate(b, &y3);
MatMult(data->matK, b, y1); // y1 := K*b
KSPSolve(data->kspM, y1, y2); // M*y2 = y1
MatMult(data->matK, y2, y3); // y3 := K*y2
// KSPSolve(data->kspM, y3, y2); // M*y2 = y3
// MatMult(data->matK, y2, y3); // y3 := K*y2
VecScale(y3, data->delta); // y3 = delta*y3
MatMultAdd(data->matM, b, y3, x); // x = M*b + y3
VecAXPY(x, -2.0*(data->delta), y1); // x := M*b - 2*delta*K*b + delta*B*b
VecDestroy(&y1);
VecDestroy(&y2);
VecDestroy(&y3);
PetscFunctionReturn(0);
}
/// solve Pfc Preconditioner
PetscErrorCode pcPfcShell(PC pc, Vec b, Vec x) // solve Px=b
{ FUNCNAME("pcPfcShell()");
void *ctx;
PCShellGetContext(pc, &ctx);
PfcData* data = static_cast<PfcData*>(ctx);
Vec b1, b2, b3, x1, x2, x3;
VecNestGetSubVec(b, 0, &b1);
VecNestGetSubVec(b, 1, &b2);
VecNestGetSubVec(b, 2, &b3);
VecNestGetSubVec(x, 0, &x1);
VecNestGetSubVec(x, 1, &x2);
VecNestGetSubVec(x, 2, &x3);
// Hilfsvariablen
Vec y1, y2, tmp;
VecDuplicate(b1, &y1);
VecDuplicate(b1, &y2);
VecDuplicate(b1, &tmp);
KSPSolve(data->kspM, b1, y1); // M*y1 = b1
MatMult(data->matK, y1, tmp); // tmp := K*y1
VecAYPX(tmp, -(data->tau), b2); // tmp := b2 - tau*tmp
KSPSolve(data->kspMpK, tmp, y2); // (M+sqrt(tau)K)*y2 = tmp
MatMult(data->matM, y2, tmp); // tmp := M*y2
KSPSolve(data->kspSchur, tmp, x2); // S*x2 = tmp
VecCopy(x2, x1); // x1 := x2
VecAXPBYPCZ(x1, 1.0, 1.0/(data->delta), -1.0/(data->delta), y1, y2); // x1 = 1*y1 + factor*y2 - factor*x1
MatMult(data->matK, x2, tmp); // tmp := K*x2
VecAYPX(tmp, -1.0, b3); // tmp := b3 - tmp
KSPSolve(data->kspM2, tmp, x3); // M*x3 = tmp
VecDestroy(&y1);
VecDestroy(&y2);
VecDestroy(&tmp);
PetscFunctionReturn(0);
}
void PetscSolverPfc::initSolver(KSP &ksp)
{
// Create FGMRES based outer solver
KSPCreate(meshDistributor->getMpiComm(0), &ksp);
KSPSetOperators(ksp, getMatInterior(), getMatInterior(), SAME_NONZERO_PATTERN);
if (getInfo() >= 10)
KSPMonitorSet(ksp, KSPMonitorDefault, PETSC_NULL, PETSC_NULL);
else if (getInfo() >= 20)
KSPMonitorSet(ksp, KSPMonitorTrueResidualNorm, PETSC_NULL, PETSC_NULL);
petsc_helper::setSolver(ksp, "pfc_", KSPFGMRES, PCNONE, getRelative(), getTolerance(), getMaxIterations());
KSPSetFromOptions(ksp);
if (useOldInitialGuess)
KSPSetInitialGuessNonzero(ksp, PETSC_TRUE);
}
void PetscSolverPfc::initPreconditioner(PC pc)
{
FUNCNAME("PetscSolverPfc::initPreconditioner()");
TEST_EXIT(tau)("tau pointer not set!\n");
PCSetType(getPc(), PCSHELL);
PCShellSetApply(getPc(), pcPfcShell);
PCShellSetContext(getPc(), &data);
const FiniteElemSpace *feSpace = componentSpaces[0];
// create mass-matrix
DOFMatrix matrixM(feSpace, feSpace);
Operator massOp(feSpace, feSpace);
Simple_ZOT zot;
massOp.addTerm(&zot);
matrixM.assembleOperator(massOp);
solverM = createSubSolver(0, "M_");
solverM->fillPetscMatrix(&matrixM);
data.matM = solverM->getMatInterior();
data.kspM = solverM->getSolver();
solverM2 = createSubSolver(0, "M2_");
solverM2->fillPetscMatrix(&matrixM);
data.kspM2 = solverM2->getSolver();