1732 lines
39 KiB
C++
1732 lines
39 KiB
C++
// Problem classes, which contain
|
|
// needed functionality for an
|
|
// interior-point filter-line search solver
|
|
//
|
|
//
|
|
//
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <array>
|
|
|
|
#include "mfem.hpp"
|
|
#include "problems.hpp"
|
|
#include "nodepair.hpp"
|
|
|
|
using namespace std;
|
|
using namespace mfem;
|
|
|
|
|
|
OptProblem::OptProblem() {}
|
|
|
|
void OptProblem::CalcObjectiveGrad(const BlockVector &x, BlockVector &y) const
|
|
{
|
|
Duf(x, y.GetBlock(0));
|
|
Dmf(x, y.GetBlock(1));
|
|
}
|
|
|
|
OptProblem::~OptProblem()
|
|
{
|
|
block_offsetsx.DeleteAll();
|
|
}
|
|
|
|
|
|
// min E(d) s.t. g(d) >= 0
|
|
// min_(d,s) E(d) s.t. c(d,s) := g(d) - s = 0, s >= 0
|
|
|
|
/*ContactProblem::ContactProblem(int dimd, int dimg) : OptProblem(), dimD(dimd), dimS(dimg), block_offsetsx(3)
|
|
{
|
|
dimU = dimD;
|
|
dimM = dimS;
|
|
dimC = dimS;
|
|
block_offsetsx[0] = 0;
|
|
block_offsetsx[1] = dimU;
|
|
block_offsetsx[2] = dimM;
|
|
block_offsetsx.PartialSum();
|
|
ml.SetSize(dimM); ml = 0.0;
|
|
}*/
|
|
|
|
ContactProblem::ContactProblem() : OptProblem(), block_offsetsx(3)
|
|
{
|
|
/*dimU = dimD;
|
|
dimM = dimS;
|
|
dimC = dimS;
|
|
block_offsetsx[0] = 0;
|
|
block_offsetsx[1] = dimU;
|
|
block_offsetsx[2] = dimM;
|
|
block_offsetsx.PartialSum();
|
|
ml.SetSize(dimM); ml = 0.0;*/
|
|
}
|
|
|
|
void ContactProblem::InitializeParentData(int dimd, int dims)
|
|
{
|
|
dimU = dimd;
|
|
dimM = dims;
|
|
dimC = dims;
|
|
block_offsetsx[0] = 0;
|
|
block_offsetsx[1] = dimU;
|
|
block_offsetsx[2] = dimM;
|
|
block_offsetsx.PartialSum();
|
|
ml.SetSize(dimM); ml = 0.0;
|
|
}
|
|
|
|
double ContactProblem::CalcObjective(const BlockVector &x) const { return E(x.GetBlock(0)); }
|
|
|
|
void ContactProblem::Duf(const BlockVector &x, Vector &y) const { DdE(x.GetBlock(0), y); }
|
|
|
|
void ContactProblem::Dmf(const BlockVector &x, Vector &y) const { y = 0.0; }
|
|
|
|
SparseMatrix* ContactProblem::Duuf(const BlockVector &x) { return DddE(x.GetBlock(0)); }
|
|
|
|
SparseMatrix* ContactProblem::Dumf(const BlockVector &x) { return nullptr; }
|
|
|
|
SparseMatrix* ContactProblem::Dmuf(const BlockVector &x) { return nullptr; }
|
|
|
|
SparseMatrix* ContactProblem::Dmmf(const BlockVector &x) { return nullptr; }
|
|
|
|
void ContactProblem::c(const BlockVector &x, Vector &y) const // c(u,m) = g(u) - m
|
|
{
|
|
g(x.GetBlock(0), y);
|
|
y.Add(-1.0, x.GetBlock(1));
|
|
}
|
|
|
|
SparseMatrix* ContactProblem::Duc(const BlockVector &x) { return Ddg(x.GetBlock(0)); }
|
|
|
|
SparseMatrix* ContactProblem::Dmc(const BlockVector &x)
|
|
{
|
|
Vector negIdentDiag(dimM);
|
|
negIdentDiag = -1.0;
|
|
return new SparseMatrix(negIdentDiag);
|
|
}
|
|
|
|
ContactProblem::~ContactProblem() {}
|
|
|
|
|
|
|
|
|
|
//ObstacleProblem::ObstacleProblem(FiniteElementSpace *fes, double (*fSource)(const Vector &)) : ContactProblem(fes->GetTrueVSize(), fes->GetTrueVSize()), Vh(fes), f(dimD)
|
|
ObstacleProblem::ObstacleProblem(FiniteElementSpace *fes, double (*fSource)(const Vector &)) : ContactProblem()
|
|
{
|
|
Vh = fes;
|
|
dimD = fes->GetTrueVSize();
|
|
dimS = fes->GetTrueVSize();
|
|
InitializeParentData(dimD, dimS);
|
|
Kform = new BilinearForm(Vh);
|
|
Kform->AddDomainIntegrator(new MassIntegrator);
|
|
Kform->AddDomainIntegrator(new DiffusionIntegrator);
|
|
Kform->Assemble();
|
|
Kform->Finalize();
|
|
Kform->FormSystemMatrix(empty_tdof_list, K);
|
|
|
|
FunctionCoefficient fcoeff(fSource);
|
|
fform = new LinearForm(Vh);
|
|
fform->AddDomainIntegrator(new DomainLFIntegrator(fcoeff));
|
|
fform->Assemble();
|
|
f.SetSize(dimD);
|
|
f.Set(1.0, *fform);
|
|
|
|
Vector iDiag(dimD); iDiag = 1.0;
|
|
J = new SparseMatrix(iDiag);
|
|
|
|
}
|
|
|
|
double ObstacleProblem::E(const Vector &d) const
|
|
{
|
|
Vector Kd(dimD); Kd = 0.0;
|
|
K.Mult(d, Kd);
|
|
return 0.5 * InnerProduct(d, Kd) - InnerProduct(f, d);
|
|
}
|
|
|
|
void ObstacleProblem::DdE(const Vector &d, Vector &gradE) const
|
|
{
|
|
K.Mult(d, gradE);
|
|
gradE.Add(-1.0, f);
|
|
}
|
|
|
|
SparseMatrix* ObstacleProblem::DddE(const Vector &d)
|
|
{
|
|
return new SparseMatrix(K);
|
|
}
|
|
|
|
// g(d) = d >= 0
|
|
void ObstacleProblem::g(const Vector &d, Vector &gd) const
|
|
{
|
|
gd.Set(1.0, d);
|
|
}
|
|
|
|
SparseMatrix* ObstacleProblem::Ddg(const Vector &d)
|
|
{
|
|
return new SparseMatrix(*J);
|
|
}
|
|
|
|
ObstacleProblem::~ObstacleProblem()
|
|
{
|
|
delete Kform;
|
|
delete fform;
|
|
delete J;
|
|
}
|
|
|
|
//-------------------
|
|
DirichletObstacleProblem::DirichletObstacleProblem(FiniteElementSpace *fes, Vector &x0DC, double (*fSource)(const Vector &),
|
|
double (*obstacleSource)(const Vector &),
|
|
Array<int> tdof_list, bool reduced = true) : ContactProblem()
|
|
{
|
|
Vh = fes;
|
|
ess_tdof_list = tdof_list;
|
|
dimD = fes->GetTrueVSize();
|
|
if(reduced)
|
|
{
|
|
dimS = dimD - ess_tdof_list.Size();
|
|
}
|
|
else
|
|
{
|
|
dimS = dimD;
|
|
}
|
|
InitializeParentData(dimD, dimS);
|
|
|
|
xDC.SetSize(dimD);
|
|
xDC.Set(1.0, x0DC);
|
|
|
|
// define Hessian of energy objective
|
|
// K = [[ \hat{K} 0]
|
|
// [ 0 I]]
|
|
// where \hat{K} acts on dofs not constrained by the Dirichlet condition
|
|
// I acts on dofs constrained by the Dirichlet condition
|
|
Kform = new BilinearForm(Vh);
|
|
Kform->AddDomainIntegrator(new DiffusionIntegrator);
|
|
Kform->Assemble();
|
|
Kform->EliminateVDofs(ess_tdof_list);
|
|
Kform->Finalize();
|
|
K = new SparseMatrix(Kform->SpMat());
|
|
|
|
// define right hand side dual-vector
|
|
// f_i = int fSource(x) \phi_i(x) dx, where {\phi_i}_i is the FE basis
|
|
// f = f - K1 xDC, where K1 contains the eliminated part of K
|
|
FunctionCoefficient fcoeff(fSource);
|
|
fform = new LinearForm(Vh);
|
|
fform->AddDomainIntegrator(new DomainLFIntegrator(fcoeff));
|
|
fform->Assemble();
|
|
f.SetSize(dimD);
|
|
f.Set(1.0, *fform);
|
|
Kform->EliminateVDofsInRHS(ess_tdof_list, xDC, f);
|
|
|
|
// define obstacle function
|
|
FunctionCoefficient psicoeff(obstacleSource);
|
|
GridFunction psi_gf(Vh);
|
|
psi_gf.ProjectCoefficient(psicoeff);
|
|
psi.SetSize(dimS); psi = 0.0;
|
|
//psi.Set(1.0, psi_gf);
|
|
|
|
// ------ construct dimS x dimD Jacobian with zero columns correspodning to Dirichlet dofs
|
|
J = new SparseMatrix(dimS, dimD);
|
|
bool freeDof;
|
|
int rowCount = 0;
|
|
for(int j = 0; j < dimD; j++)
|
|
{
|
|
freeDof = true;
|
|
for(int i = 0; i < ess_tdof_list.Size(); i++)
|
|
{
|
|
if( j == ess_tdof_list[i])
|
|
{
|
|
freeDof = false;
|
|
}
|
|
}
|
|
|
|
Array<int> col_tmp; mfem::Vector v_tmp;
|
|
col_tmp.SetSize(1); v_tmp.SetSize(1);
|
|
col_tmp[0] = j;
|
|
if (freeDof)
|
|
{
|
|
v_tmp(0) = 1.0;
|
|
J->SetRow(rowCount, col_tmp, v_tmp);
|
|
psi(rowCount) = psi_gf(j);
|
|
rowCount += 1;
|
|
}
|
|
else if(dimD == dimS)
|
|
{
|
|
v_tmp(0) = 0.0;
|
|
J->SetRow(rowCount, col_tmp, v_tmp);
|
|
psi(rowCount) = psi_gf(j)-0.01;
|
|
rowCount += 1;
|
|
}
|
|
}
|
|
J->Finalize();
|
|
}
|
|
|
|
double DirichletObstacleProblem::E(const Vector &d) const
|
|
{
|
|
Vector Kd(dimD); Kd = 0.0;
|
|
K->Mult(d, Kd);
|
|
return 0.5 * InnerProduct(d, Kd) - InnerProduct(f, d);
|
|
}
|
|
|
|
void DirichletObstacleProblem::DdE(const Vector &d, Vector &gradE) const
|
|
{
|
|
K->Mult(d, gradE);
|
|
gradE.Add(-1.0, f);
|
|
}
|
|
|
|
SparseMatrix* DirichletObstacleProblem::DddE(const Vector &d)
|
|
{
|
|
return new SparseMatrix(*K);
|
|
}
|
|
|
|
// g(d) = d - \psi >= 0
|
|
// d - \psi - s = 0
|
|
// s >= 0
|
|
void DirichletObstacleProblem::g(const Vector &d, Vector &gd) const
|
|
{
|
|
J->Mult(d, gd);
|
|
gd.Add(-1., psi);
|
|
}
|
|
|
|
SparseMatrix* DirichletObstacleProblem::Ddg(const Vector &d)
|
|
{
|
|
return new SparseMatrix(*J);
|
|
}
|
|
|
|
DirichletObstacleProblem::~DirichletObstacleProblem()
|
|
{
|
|
delete J;
|
|
delete K;
|
|
delete Kform;
|
|
delete fform;
|
|
}
|
|
|
|
|
|
ReducedContactProblem::ReducedContactProblem(ContactProblem * contactin, Array<int> activeConstraintsin, Array<int> fixedDofsin) : ContactProblem()
|
|
{
|
|
contact = contactin;
|
|
activeConstraints = activeConstraintsin;
|
|
fixedDofs = fixedDofsin;
|
|
dimD = contact->GetDimD();
|
|
dimS = activeConstraints.Size();
|
|
InitializeParentData(dimD, dimS);
|
|
dimSin = contact->GetDimS();
|
|
}
|
|
|
|
ReducedContactProblem::~ReducedContactProblem() {}
|
|
|
|
double ReducedContactProblem::E(const Vector &d) const
|
|
{
|
|
return contact->E(d);
|
|
}
|
|
|
|
void ReducedContactProblem::DdE(const Vector &d, Vector &gradE) const
|
|
{
|
|
contact->DdE(d, gradE);
|
|
}
|
|
|
|
SparseMatrix * ReducedContactProblem::DddE(const Vector &d)
|
|
{
|
|
return contact->DddE(d);
|
|
}
|
|
|
|
void ReducedContactProblem::g(const Vector &d, Vector &gd) const
|
|
{
|
|
Vector gdin(dimSin); gdin = 0.0;
|
|
contact->g(d, gdin);
|
|
for(int i = 0; i < dimS; i++)
|
|
{
|
|
gd(i) = gdin(activeConstraints[i]);
|
|
}
|
|
}
|
|
|
|
SparseMatrix * ReducedContactProblem::Ddg(const Vector &d)
|
|
{
|
|
SparseMatrix * Jin;
|
|
Jin = contact->Ddg(d);
|
|
SparseMatrix * J;
|
|
J = new SparseMatrix(dimS, dimD);
|
|
for(int i = 0; i < dimS; i++)
|
|
{
|
|
Array<int> col_tmp;
|
|
mfem::Vector v_tmp;
|
|
col_tmp = 0;
|
|
v_tmp = 0.0;
|
|
Jin->GetRow(activeConstraints[i], col_tmp, v_tmp);
|
|
bool freeDof;
|
|
for(int j = 0; j < v_tmp.Size(); j++)
|
|
{
|
|
freeDof = true;
|
|
for(int k = 0; k < fixedDofs.Size(); k++)
|
|
{
|
|
if(col_tmp[j] == fixedDofs[k])
|
|
{
|
|
freeDof = false;
|
|
}
|
|
}
|
|
if(!freeDof)
|
|
{
|
|
v_tmp(j) = 0.0;
|
|
}
|
|
}
|
|
J->SetRow(i, col_tmp, v_tmp);
|
|
}
|
|
J->Finalize();
|
|
J->Print();
|
|
delete Jin;
|
|
return J;
|
|
}
|
|
|
|
|
|
// quadratic approximation of the objective
|
|
// linear approximation of the gap function constraint
|
|
// E(d) = 1 / 2 d^T K d + f^T d
|
|
// g(d) = J d + g0
|
|
QPContactProblem::QPContactProblem(const SparseMatrix Kin, const SparseMatrix Jin, const Vector fin, const Vector g0in) : ContactProblem()
|
|
{
|
|
K = new SparseMatrix(Kin);
|
|
J = new SparseMatrix(Jin);
|
|
K->Finalize();
|
|
J->Finalize();
|
|
f.SetSize(fin.Size()); f = fin;
|
|
g0.SetSize(g0in.Size()); g0 = g0in;
|
|
MFEM_VERIFY(K->Width() == J->Width(), "K and J do not have the same number of columns.");
|
|
MFEM_VERIFY(J->Height() == g0.Size(), "Number of rows of J is not equal to length of g0 vector.");
|
|
MFEM_VERIFY(K->Height() == f.Size(), "Number of rows of K is not equal to length of f vector.");
|
|
|
|
dimD = K->Height();
|
|
dimS = J->Height();
|
|
InitializeParentData(dimD, dimS);
|
|
}
|
|
|
|
// E(d) = 1 / 2 d^T K d + f^T d
|
|
double QPContactProblem::E(const Vector &d) const
|
|
{
|
|
Vector Kd(dimD); Kd = 0.0;
|
|
K->Mult(d, Kd);
|
|
return 0.5 * InnerProduct(d, Kd) + InnerProduct(f, d);
|
|
}
|
|
|
|
|
|
// gradient(E) = K d + f
|
|
void QPContactProblem::DdE(const Vector &d, Vector &gradE) const
|
|
{
|
|
K->Mult(d, gradE);
|
|
gradE.Add(1.0, f);
|
|
}
|
|
|
|
// Hessian(E) = K
|
|
SparseMatrix* QPContactProblem::DddE(const Vector &d)
|
|
{
|
|
return new SparseMatrix(*K);
|
|
}
|
|
|
|
// g(d) = J * d + g0 >= 0
|
|
void QPContactProblem::g(const Vector &d, Vector &gd) const
|
|
{
|
|
J->Mult(d, gd);
|
|
gd.Add(1.0, g0);
|
|
}
|
|
|
|
// Jacobian(g) = J
|
|
SparseMatrix* QPContactProblem::Ddg(const Vector &d)
|
|
{
|
|
return new SparseMatrix(*J);
|
|
}
|
|
|
|
QPContactProblem::~QPContactProblem()
|
|
{
|
|
delete K;
|
|
delete J;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// -------------- utility functions --------------------
|
|
|
|
bool ifequalarray(const Array<int> a1, const Array<int> a2)
|
|
{
|
|
if (a1.Size()!=a2.Size())
|
|
{
|
|
return false;
|
|
}
|
|
for (int i=0; i<a1.Size(); i++)
|
|
{
|
|
if (a1[i] != a2[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FindSurfaceToProject(Mesh& mesh, const int elem, int& cbdrface)
|
|
{
|
|
Array<int> attr;
|
|
attr.Append(3);
|
|
Array<int> faces;
|
|
Array<int> ori;
|
|
std::vector<Array<int> > facesVertices;
|
|
std::vector<int > faceid;
|
|
mesh.GetElementFaces(elem, faces, ori);
|
|
int face = -1;
|
|
for (int i=0; i<faces.Size(); i++)
|
|
{
|
|
face = faces[i];
|
|
Array<int> faceVert;
|
|
if (!mesh.FaceIsInterior(face)) // if on the boundary
|
|
{
|
|
mesh.GetFaceVertices(face, faceVert);
|
|
faceVert.Sort();
|
|
facesVertices.push_back(faceVert);
|
|
faceid.push_back(face);
|
|
}
|
|
}
|
|
int bdrface = facesVertices.size();
|
|
|
|
Array<int> bdryFaces;
|
|
// This shoulnd't need to be rebuilt
|
|
std::vector<Array<int> > bdryVerts;
|
|
for (int b=0; b<mesh.GetNBE(); ++b)
|
|
{
|
|
if (attr.FindSorted(mesh.GetBdrAttribute(b)) >= 0) // found the contact surface
|
|
{
|
|
bdryFaces.Append(b);
|
|
Array<int> vert;
|
|
mesh.GetBdrElementVertices(b, vert);
|
|
vert.Sort();
|
|
bdryVerts.push_back(vert);
|
|
}
|
|
}
|
|
|
|
int bdrvert = bdryVerts.size();
|
|
cbdrface = -1; // the face number of the contact surface element
|
|
int count_cbdrface = 0; // the number of matching surfaces, used for checks
|
|
|
|
for (int i=0; i<bdrface; i++)
|
|
{
|
|
for (int j=0; j<bdrvert; j++)
|
|
{
|
|
if (ifequalarray(facesVertices[i], bdryVerts[j]))
|
|
{
|
|
cbdrface = faceid[i];
|
|
count_cbdrface += 1;
|
|
}
|
|
}
|
|
}
|
|
MFEM_VERIFY(count_cbdrface == 1,"projection surface not found");
|
|
|
|
};
|
|
|
|
mfem::Vector GetNormalVector(Mesh & mesh, const int elem, const double *ref,
|
|
int & refFace, int & refNormal, bool & interior)
|
|
{
|
|
ElementTransformation *trans = mesh.GetElementTransformation(elem);
|
|
const int dim = mesh.Dimension();
|
|
const int spaceDim = trans->GetSpaceDim();
|
|
|
|
MFEM_VERIFY(spaceDim == 3, "");
|
|
|
|
mfem::Vector n(spaceDim);
|
|
|
|
IntegrationPoint ip;
|
|
ip.Set(ref, dim);
|
|
|
|
trans->SetIntPoint(&ip);
|
|
//CalcOrtho(trans->Jacobian(), n); // Works only for face transformations
|
|
const DenseMatrix jac = trans->Jacobian();
|
|
|
|
int dimNormal = -1;
|
|
int normalSide = -1;
|
|
|
|
const double tol = 1.0e-8;
|
|
//cout << "-----------------\n";
|
|
/*for(int i = 0; i < dim; i++)
|
|
{
|
|
cout << "ref_" << i << " = " << ref[i] << endl;
|
|
}*/
|
|
for (int i=0; i<dim; ++i)
|
|
{
|
|
const double d0 = std::abs(ref[i]);
|
|
const double d1 = std::abs(ref[i] - 1.0);
|
|
|
|
const double d = std::min(d0, d1);
|
|
// TODO: this works only for hexahedral meshes!
|
|
|
|
if (d < tol)
|
|
{
|
|
MFEM_VERIFY(dimNormal == -1, "");
|
|
dimNormal = i;
|
|
|
|
if (d0 < tol)
|
|
{
|
|
normalSide = 0;
|
|
}
|
|
else
|
|
{
|
|
normalSide = 1;
|
|
}
|
|
}
|
|
}
|
|
// closest point on the boundary
|
|
if (dimNormal < 0 || normalSide < 0) // node is inside the element
|
|
{
|
|
interior = 1;
|
|
n = 0.0;
|
|
return n;
|
|
}
|
|
|
|
MFEM_VERIFY(dimNormal >= 0 && normalSide >= 0, "");
|
|
refNormal = dimNormal;
|
|
|
|
MFEM_VERIFY(dim == 3, "");
|
|
|
|
{
|
|
// Find the reference face
|
|
if (dimNormal == 0)
|
|
{
|
|
refFace = (normalSide == 1) ? 2 : 4;
|
|
}
|
|
else if (dimNormal == 1)
|
|
{
|
|
refFace = (normalSide == 1) ? 3 : 1;
|
|
}
|
|
else
|
|
{
|
|
refFace = (normalSide == 1) ? 5 : 0;
|
|
}
|
|
}
|
|
|
|
std::vector<mfem::
|
|
Vector> tang(2);
|
|
|
|
int tangDir[2] = {-1, -1};
|
|
{
|
|
int t = 0;
|
|
for (int i=0; i<dim; ++i)
|
|
{
|
|
if (i != dimNormal)
|
|
{
|
|
tangDir[t] = i;
|
|
t++;
|
|
}
|
|
}
|
|
|
|
MFEM_VERIFY(t == 2, "");
|
|
}
|
|
|
|
for (int i=0; i<2; ++i)
|
|
{
|
|
tang[i].SetSize(3);
|
|
|
|
mfem::Vector tangRef(3);
|
|
tangRef = 0.0;
|
|
tangRef[tangDir[i]] = 1.0;
|
|
|
|
jac.Mult(tangRef, tang[i]);
|
|
}
|
|
|
|
mfem::Vector c(3); // Cross product
|
|
|
|
c[0] = (tang[0][1] * tang[1][2]) - (tang[0][2] * tang[1][1]);
|
|
c[1] = (tang[0][2] * tang[1][0]) - (tang[0][0] * tang[1][2]);
|
|
c[2] = (tang[0][0] * tang[1][1]) - (tang[0][1] * tang[1][0]);
|
|
|
|
c /= c.Norml2();
|
|
|
|
mfem::Vector nref(3);
|
|
nref = 0.0;
|
|
nref[dimNormal] = 1.0;
|
|
|
|
mfem::Vector ndir(3);
|
|
jac.Mult(nref, ndir);
|
|
|
|
ndir /= ndir.Norml2();
|
|
|
|
const double dp = ndir * c;
|
|
|
|
// TODO: eliminate c?
|
|
n = c;
|
|
if (dp < 0.0)
|
|
{
|
|
n *= -1.0;
|
|
}
|
|
interior = 0;
|
|
return n;
|
|
}
|
|
|
|
// WARNING: global variable, just for this little example.
|
|
std::array<std::array<int, 3>, 8> HEX_VERT =
|
|
{
|
|
{ {0,0,0},
|
|
{1,0,0},
|
|
{1,1,0},
|
|
{0,1,0},
|
|
{0,0,1},
|
|
{1,0,1},
|
|
{1,1,1},
|
|
{0,1,1}
|
|
}
|
|
};
|
|
|
|
int GetHexVertex(int cdim, int c, int fa, int fb, mfem::Vector & refCrd)
|
|
{
|
|
int ref[3];
|
|
ref[cdim] = c;
|
|
ref[cdim == 0 ? 1 : 0] = fa;
|
|
ref[cdim == 2 ? 1 : 2] = fb;
|
|
|
|
for (int i=0; i<3; ++i) { refCrd[i] = ref[i]; }
|
|
|
|
int refv = -1;
|
|
|
|
for (int i=0; i<8; ++i)
|
|
{
|
|
bool match = true;
|
|
for (int j=0; j<3; ++j)
|
|
{
|
|
if (ref[j] != HEX_VERT[i][j]) { match = false; }
|
|
}
|
|
|
|
if (match) { refv = i; }
|
|
}
|
|
|
|
MFEM_VERIFY(refv >= 0, "");
|
|
|
|
return refv;
|
|
}
|
|
|
|
// Coordinates in xyz are assumed to be ordered as [X, Y, Z]
|
|
// where X is the list of x-coordinates for all points and so on.
|
|
// conn: connectivity of the target surface elements
|
|
// xi: surface reference cooridnates for the cloest point, involves a linear transformation from [0,1] to [-1,1]
|
|
void FindPointsInMesh(Mesh & mesh, mfem::Vector const& xyz, Array<int>& conn,
|
|
mfem::Vector& xi)
|
|
{
|
|
const int dim = mesh.Dimension();
|
|
const int np = xyz.Size() / dim;
|
|
|
|
MFEM_VERIFY(np * dim == xyz.Size(), "");
|
|
|
|
mesh.EnsureNodes();
|
|
|
|
FindPointsGSLIB finder;
|
|
|
|
finder.SetDistanceToleranceForPointsFoundOnBoundary(0.5);
|
|
|
|
const double bb_t = 0.5;
|
|
finder.Setup(mesh, bb_t);
|
|
|
|
finder.FindPoints(xyz,mfem::Ordering::byVDIM);
|
|
|
|
/// Return code for each point searched by FindPoints: inside element (0), on
|
|
/// element boundary (1), or not found (2).
|
|
Array<unsigned int> codes = finder.GetCode();
|
|
|
|
/// Return element number for each point found by FindPoints.
|
|
Array<unsigned int> elems = finder.GetElem();
|
|
|
|
/// Return reference coordinates for each point found by FindPoints.
|
|
mfem::Vector refcrd = finder.GetReferencePosition();
|
|
|
|
/// Return distance between the sought and the found point in physical space,
|
|
/// for each point found by FindPoints.
|
|
mfem::Vector dist = finder.GetDist();
|
|
|
|
MFEM_VERIFY(dist.Size() == np, "");
|
|
MFEM_VERIFY(refcrd.Size() == np * dim, "");
|
|
MFEM_VERIFY(elems.Size() == np, "");
|
|
MFEM_VERIFY(codes.Size() == np, "");
|
|
|
|
bool allfound = true;
|
|
for (auto code : codes)
|
|
if (code == 2) { allfound = false; }
|
|
if(!allfound)
|
|
{
|
|
for(int i = 0; i < dist.Size(); i++)
|
|
{
|
|
cout << "distance between saught and found points = " << dist(i) << endl;
|
|
}
|
|
}
|
|
MFEM_VERIFY(allfound, "A point was not found");
|
|
|
|
cout << "Maximum distance of projected points: " << dist.Max() << endl;
|
|
|
|
// extract information
|
|
for (int i=0; i<np; ++i)
|
|
{
|
|
int refFace, refNormal;
|
|
// int refNormalSide;
|
|
bool is_interior = -1;
|
|
mfem::Vector normal = GetNormalVector(mesh, elems[i],
|
|
refcrd.GetData() + (i*dim),
|
|
refFace, refNormal, is_interior);
|
|
int phyFace;
|
|
if (is_interior)
|
|
{
|
|
phyFace = -1; // the id of the face that has the closest point
|
|
FindSurfaceToProject(mesh, elems[i], phyFace);
|
|
|
|
Array<int> cbdrVert;
|
|
mesh.GetFaceVertices(phyFace, cbdrVert);
|
|
mfem::Vector xs(dim);
|
|
xs[0] = xyz[i*dim];
|
|
xs[1] = xyz[i*dim + 1];
|
|
xs[2] = xyz[i*dim + 2];
|
|
|
|
mfem::Vector xi_tmp(dim-1);
|
|
// get nodes!
|
|
|
|
GridFunction *nodes = mesh.GetNodes();
|
|
DenseMatrix coords(4,3);
|
|
for (int k=0; k<4; k++)
|
|
{
|
|
for (int j=0; j<3; j++)
|
|
{
|
|
coords(k,j) = (*nodes)[cbdrVert[k]*3+j];
|
|
}
|
|
}
|
|
SlaveToMaster(coords, xs, xi_tmp);
|
|
|
|
for (int j=0; j<dim-1; ++j)
|
|
{
|
|
xi[i*(dim-1)+j] = xi_tmp[j];
|
|
}
|
|
// now get get the projection to the surface
|
|
}
|
|
else
|
|
{
|
|
mfem::Vector faceRefCrd(dim-1);
|
|
{
|
|
int fd = 0;
|
|
for (int j=0; j<dim; ++j)
|
|
{
|
|
if (j == refNormal)
|
|
{
|
|
// refNormalSide = (refcrd[(i*dim) + j] > 0.5);
|
|
}
|
|
else
|
|
{
|
|
faceRefCrd[fd] = refcrd[(i*dim) + j];
|
|
fd++;
|
|
}
|
|
}
|
|
|
|
MFEM_VERIFY(fd == dim-1, "");
|
|
}
|
|
for (int j=0; j<dim-1; ++j)
|
|
{
|
|
xi[i*(dim-1)+j] = faceRefCrd[j]*2.0 - 1.0;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// Get the element face
|
|
Array<int> faces;
|
|
Array<int> ori;
|
|
int face;
|
|
|
|
if (is_interior)
|
|
{
|
|
face = phyFace;
|
|
}
|
|
else
|
|
{
|
|
mesh.GetElementFaces(elems[i], faces, ori);
|
|
face = faces[refFace];
|
|
}
|
|
|
|
Array<int> faceVert;
|
|
mesh.GetFaceVertices(face, faceVert);
|
|
|
|
|
|
for (int p=0; p<4; p++)
|
|
{
|
|
conn[4*i+p] = faceVert[p];
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* Constructor. */
|
|
ExContactBlockTL::ExContactBlockTL(int ref_levels)
|
|
:
|
|
ContactProblem(),
|
|
mesh1{nullptr},
|
|
mesh2{nullptr},
|
|
fec1{nullptr},
|
|
fec2{nullptr},
|
|
fespace1{nullptr},
|
|
fespace2{nullptr},
|
|
nodes1{nullptr},
|
|
nodes2{nullptr},
|
|
x1{nullptr},
|
|
x2{nullptr},
|
|
b1{nullptr},
|
|
b2{nullptr},
|
|
lambda1_func{nullptr},
|
|
lambda2_func{nullptr},
|
|
mu1_func{nullptr},
|
|
mu2_func{nullptr},
|
|
a1{nullptr},
|
|
a2{nullptr},
|
|
K{nullptr},
|
|
coordsm{nullptr},
|
|
M{nullptr},
|
|
dM{nullptr}
|
|
{
|
|
// 1. Parse command-line options.
|
|
mesh_file1 = "meshes/block1.mesh";
|
|
mesh_file2 = "meshes/rotatedblock2.mesh";
|
|
const char *mf1 = mesh_file1.c_str();
|
|
const char *mf2 = mesh_file2.c_str();
|
|
|
|
mesh1 = new Mesh(mf1, 1, 1);
|
|
mesh2 = new Mesh(mf2, 1, 1);
|
|
{
|
|
for(int l = 0; l < ref_levels; l++)
|
|
{
|
|
mesh1->UniformRefinement();
|
|
mesh2->UniformRefinement();
|
|
}
|
|
}
|
|
|
|
dim = mesh1->Dimension();
|
|
MFEM_VERIFY(dim == mesh2->Dimension(), "");
|
|
|
|
// boundary attribute 2 is the potential contact surface of nodes
|
|
attr.Append(3);
|
|
// boundary attribute 2 is the potential contact surface for master surface
|
|
m_attr.Append(3);
|
|
|
|
fec1 = new H1_FECollection(1, dim);
|
|
fespace1 = new FiniteElementSpace(mesh1, fec1, dim, Ordering::byVDIM);
|
|
ndof_1 = fespace1->GetTrueVSize();
|
|
cout << "Number of finite element unknowns for mesh1: " << ndof_1 << endl;
|
|
mesh1->SetNodalFESpace(fespace1);
|
|
|
|
fec2 = new H1_FECollection(1, dim);
|
|
fespace2 = new FiniteElementSpace(mesh2, fec2, dim, Ordering::byVDIM);
|
|
ndof_2 = fespace2->GetTrueVSize();
|
|
cout << "Number of finite element unknowns for mesh2: " << ndof_2 << endl;
|
|
mesh2->SetNodalFESpace(fespace2);
|
|
|
|
// degrees of freedom of both meshes
|
|
ndofs = ndof_1 + ndof_2;
|
|
|
|
// number of nodes for each mesh
|
|
nnd_1 = mesh1->GetNV();
|
|
nnd_2 = mesh2->GetNV();
|
|
nnd = nnd_1 + nnd_2;
|
|
|
|
nodes0.SetSpace(mesh1->GetNodes()->FESpace());
|
|
nodes0 = *mesh1->GetNodes();
|
|
nodes1 = mesh1->GetNodes();
|
|
|
|
|
|
Array<int> ess_bdr1(mesh1->bdr_attributes.Max());
|
|
ess_bdr1 = 0;
|
|
Array<int> ess_bdr2(mesh2->bdr_attributes.Max());
|
|
ess_bdr2 = 0;
|
|
|
|
for (int b=0; b<mesh2->GetNBE(); ++b)
|
|
{
|
|
if (mesh2->GetBdrAttribute(b)== 3)
|
|
{
|
|
Array<int> vert;
|
|
mesh2->GetBdrElementVertices(b, vert);
|
|
for (auto v : vert)
|
|
{
|
|
bdryVerts2.insert(v);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::set<int> dirbdryv2;
|
|
for (int b=0; b<mesh2->GetNBE(); ++b)
|
|
{
|
|
if (mesh2->GetBdrAttribute(b) == 2)
|
|
{
|
|
Array<int> vert;
|
|
mesh2->GetBdrElementVertices(b, vert);
|
|
for (auto v : vert)
|
|
{
|
|
dirbdryv2.insert(v);
|
|
}
|
|
}
|
|
}
|
|
std::set<int> dirbdryv1;
|
|
for (int b=0; b<mesh1->GetNBE(); ++b)
|
|
{
|
|
if (mesh1->GetBdrAttribute(b) == 2)
|
|
{
|
|
Array<int> vert;
|
|
mesh1->GetBdrElementVertices(b, vert);
|
|
for (auto v : vert)
|
|
{
|
|
dirbdryv1.insert(v);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto v : dirbdryv2)
|
|
{
|
|
for (int i=0; i<dim; ++i)
|
|
{
|
|
Dirichlet_dof.Append(v*dim + i + ndof_1);
|
|
Dirichlet_val.Append(0.);
|
|
}
|
|
}
|
|
double delta = 0.1;
|
|
for (auto v : dirbdryv1)
|
|
{
|
|
Dirichlet_dof.Append(v*dim + 0);
|
|
Dirichlet_val.Append(delta);
|
|
Dirichlet_dof.Append(v*dim + 1);
|
|
Dirichlet_val.Append(0.);
|
|
Dirichlet_dof.Append(v*dim + 2);
|
|
Dirichlet_val.Append(0.);
|
|
}
|
|
|
|
x1 = new GridFunction(fespace1);
|
|
x2 = new GridFunction(fespace2);
|
|
(*x1) = 0.0;
|
|
(*x2) = 0.0;
|
|
for(int i = 0; i < Dirichlet_dof.Size(); i++)
|
|
{
|
|
if(Dirichlet_dof[i] >= ndof_1)
|
|
{
|
|
(*x2)(Dirichlet_dof[i] - ndof_1) = Dirichlet_val[i];
|
|
ess_tdof_list2.Append(Dirichlet_dof[i] - ndof_1);
|
|
}
|
|
else
|
|
{
|
|
(*x1)(Dirichlet_dof[i]) = Dirichlet_val[i];
|
|
ess_tdof_list1.Append(Dirichlet_dof[i]);
|
|
}
|
|
}
|
|
|
|
b1 = new LinearForm(fespace1);
|
|
b2 = new LinearForm(fespace2);
|
|
b1->Assemble();
|
|
b2->Assemble();
|
|
|
|
lambda1.SetSize(mesh1->attributes.Max());
|
|
mu1.SetSize(mesh1->attributes.Max());
|
|
lambda1 = 57.6923076923;
|
|
mu1 = 38.4615384615;
|
|
lambda1_func = new PWConstCoefficient(lambda1);
|
|
mu1_func = new PWConstCoefficient(mu1);
|
|
|
|
lambda2.SetSize(mesh2->attributes.Max());
|
|
mu2.SetSize(mesh2->attributes.Max());
|
|
lambda2 = 57.6923076923;
|
|
mu2 = 38.4615384615;
|
|
lambda2_func = new PWConstCoefficient(lambda2);
|
|
mu2_func = new PWConstCoefficient(mu2);
|
|
|
|
a1 = new BilinearForm(fespace1);
|
|
a1->SetDiagonalPolicy(Operator::DIAG_ONE);
|
|
a1->AddDomainIntegrator(new ElasticityIntegrator(*lambda1_func, *mu1_func));
|
|
a1->Assemble();
|
|
// a1->EliminateVDofs(ess_tdof_list1);
|
|
// a1->Finalize();
|
|
// A1 = a1->SpMat();
|
|
|
|
// B1.SetSize(ndof_1); B1 = 0.0;
|
|
// a1->EliminateVDofsInRHS(ess_tdof_list1, (*x1), B1);
|
|
a1->FormLinearSystem(ess_tdof_list1,*x1,*b1,A1,X1,B1);
|
|
|
|
a2 = new BilinearForm(fespace2);
|
|
a2->SetDiagonalPolicy(Operator::DIAG_ONE);
|
|
a2->AddDomainIntegrator(new ElasticityIntegrator(*lambda2_func, *mu2_func));
|
|
a2->Assemble();
|
|
// a2->EliminateVDofs(ess_tdof_list2);
|
|
// a2->Finalize();
|
|
// A2 = a2->SpMat();
|
|
|
|
// B2.SetSize(ndof_2); B2 = 0.0;
|
|
// a2->EliminateVDofsInRHS(ess_tdof_list2, (*x2), B2);
|
|
|
|
a2->FormLinearSystem(ess_tdof_list2,*x2,*b2,A2,X2,B2);
|
|
|
|
Array<int> offs(3);
|
|
offs[0] = 0;
|
|
offs[1] = ndof_1;
|
|
offs[2] = ndof_2;
|
|
offs.PartialSum();
|
|
BlockMatrix Kblock(offs);
|
|
Kblock.SetBlock(0,0,&A1);
|
|
Kblock.SetBlock(1,1,&A2);
|
|
K = Kblock.CreateMonolithic();
|
|
|
|
// K = new SparseMatrix(ndofs, ndofs);
|
|
// for (int i=0; i<A1.Height(); i++) // 1,1 block
|
|
// {
|
|
// Array<int> col_tmp;
|
|
// mfem::Vector v_tmp;
|
|
// // col_tmp = 0;
|
|
// // v_tmp = 0.0;
|
|
// A1.GetRow(i, col_tmp, v_tmp);
|
|
// K->SetRow(i, col_tmp, v_tmp);
|
|
// }
|
|
// for (int i=0; i<A2.Height(); i++) // 2, 2 block
|
|
// {
|
|
// Array<int> col_tmp;
|
|
// mfem::Vector v_tmp;
|
|
// // col_tmp = 0;
|
|
// // v_tmp = 0.0;
|
|
// A2.GetRow(i, col_tmp, v_tmp);
|
|
// for (int j=0; j<col_tmp.Size(); j++)
|
|
// {
|
|
// col_tmp[j] += ndof_1;
|
|
// }
|
|
// K->SetRow(i+ndof_1, col_tmp, v_tmp); // mesh1 top left corner
|
|
// }
|
|
// K->Finalize(1,false);
|
|
K->Threshold(0.0);
|
|
K->SortColumnIndices();
|
|
// Construct node to segment contact constraint.
|
|
attr.Sort();
|
|
|
|
npoints = bdryVerts2.size();
|
|
s_conn.SetSize(npoints);
|
|
xyz.SetSize(dim * npoints);
|
|
xyz = 0.0;
|
|
|
|
cout << "Boundary vertices for contact surface vertices in mesh 2" << endl;
|
|
|
|
// construct the nodal coordinates on mesh2 to be projected, including displacement
|
|
int count = 0;
|
|
for (auto v : bdryVerts2)
|
|
{
|
|
for (int i=0; i<dim; ++i)
|
|
{
|
|
xyz(count*dim + i) = mesh2->GetVertex(v)[i] + (*x2)[v*dim+i];
|
|
}
|
|
s_conn[count] = v + nnd_1; // dof1 is the master
|
|
count++;
|
|
}
|
|
|
|
MFEM_VERIFY(count == npoints, "");
|
|
|
|
// segment reference coordinates of the closest point
|
|
m_xi.SetSize(npoints*(dim-1));
|
|
m_xi = -1.0;
|
|
// xs.SetSize(dim*npoints);
|
|
// xs = 0.0;
|
|
// for (int i=0; i<npoints; i++)
|
|
// {
|
|
// for (int j=0; j<dim; j++)
|
|
// {
|
|
// xs[i*dim+j] = xyz[i + (j*npoints)];
|
|
// }
|
|
// }
|
|
|
|
m_conn.SetSize(4*npoints); // only works for linear elements that have 4 vertices!
|
|
coordsm = new DenseMatrix(4*npoints, dim);
|
|
|
|
// adding displacement to mesh1 using a fixed grid function from mesh1
|
|
// Tucker modification, removing the nullifying of x1...
|
|
(*x1) = 0.0; // x1 order: [xyz xyz... xyz]
|
|
add(nodes0, *x1, *nodes1); // issues with moving the mesh nodes?
|
|
|
|
FindPointsInMesh(*mesh1, xyz, m_conn, m_xi); // memory is leaked when this function is called
|
|
|
|
for (int i=0; i<npoints; i++)
|
|
{
|
|
for (int j=0; j<4; j++)
|
|
{
|
|
for (int k=0; k<dim; k++)
|
|
{
|
|
(*coordsm)(i*4+j,k) = mesh1->GetVertex(m_conn[i*4+j])[k]+
|
|
(*x1)[dim*m_conn[i*4+j]+k];
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- enforcing compatibility with the contactproblem structure
|
|
dimD = ndofs;
|
|
dimS = nnd;
|
|
InitializeParentData(dimD, dimS);
|
|
// ---
|
|
|
|
M = new SparseMatrix(nnd,ndofs);
|
|
dM = new std::vector<SparseMatrix>(nnd, SparseMatrix(ndofs,ndofs));
|
|
|
|
// Assemble_Contact(nnd, npoints, ndofs, xs, m_xi, *coordsm, s_conn, m_conn, gapv, *M,
|
|
// *dM);
|
|
Assemble_Contact(nnd, npoints, ndofs, xyz, m_xi, *coordsm, s_conn, m_conn, gapv, *M,
|
|
*dM);
|
|
assert(M);
|
|
}
|
|
|
|
|
|
|
|
ExContactBlockTL::~ExContactBlockTL()
|
|
{
|
|
delete mesh1;
|
|
delete mesh2;
|
|
delete fec1;
|
|
delete fec2;
|
|
delete fespace1;
|
|
delete fespace2;
|
|
delete x1;
|
|
delete x2;
|
|
delete b1;
|
|
delete b2;
|
|
delete lambda1_func;
|
|
delete lambda2_func;
|
|
delete mu1_func;
|
|
delete mu2_func;
|
|
delete a1;
|
|
delete a2;
|
|
delete K;
|
|
delete coordsm;
|
|
delete M;
|
|
delete dM;
|
|
}
|
|
|
|
FiniteElementSpace ExContactBlockTL::GetVh1()
|
|
{
|
|
return *fespace1;
|
|
}
|
|
|
|
FiniteElementSpace ExContactBlockTL::GetVh2()
|
|
{
|
|
return *fespace2;
|
|
}
|
|
|
|
|
|
// update gap function based on a current configuration
|
|
// how is that data fed here
|
|
void ExContactBlockTL::update_g() const
|
|
{
|
|
int count = 0;
|
|
for (auto v : bdryVerts2)
|
|
{
|
|
for (int i=0; i<dim; ++i)
|
|
{
|
|
// xyz[count + (i * npoints)] = mesh2->GetVertex(v)[i] + (*x2)[v*dim+i];
|
|
xyz(count*dim + i) = mesh2->GetVertex(v)[i] + (*x2)[v*dim+i];
|
|
}
|
|
count++;
|
|
}
|
|
MFEM_VERIFY(count == npoints, "");
|
|
|
|
// xs = 0.0;
|
|
// for (int i=0; i<npoints; i++)
|
|
// {
|
|
// for (int j=0; j<dim; j++)
|
|
// {
|
|
// xs[i*dim+j] = xyz[i + (j*npoints)];
|
|
// }
|
|
// }
|
|
|
|
add(nodes0, *x1, *nodes1);
|
|
FindPointsInMesh(*mesh1, xyz, m_conn, m_xi);
|
|
|
|
for (int i=0; i<npoints; i++)
|
|
{
|
|
for (int j=0; j<4; j++)
|
|
{
|
|
for (int k=0; k<dim; k++)
|
|
{
|
|
(*coordsm)(i*4+j,k) = mesh1->GetVertex(m_conn[i*4+j])[k]+
|
|
(*x1)[dim*m_conn[i*4+j]+k];
|
|
}
|
|
}
|
|
}
|
|
M->Clear();
|
|
delete M;
|
|
M = nullptr;
|
|
M = new SparseMatrix(nnd,ndofs);
|
|
for (int i=0; i<nnd; i++)
|
|
{
|
|
(*dM)[i].Clear();
|
|
}
|
|
delete dM;
|
|
dM = new std::vector<SparseMatrix>(nnd, SparseMatrix(ndofs,ndofs));
|
|
|
|
Assemble_Contact(nnd, npoints, ndofs, xyz, m_xi, *coordsm, s_conn, m_conn, gapv, *M,
|
|
*dM);
|
|
}
|
|
|
|
void ExContactBlockTL::update_jac()
|
|
{
|
|
assert(0 && "cannot reach here");
|
|
update_g();
|
|
}
|
|
|
|
void ExContactBlockTL::update_hess()
|
|
{
|
|
assert(0 && "cannot reach here");
|
|
update_g();
|
|
}
|
|
|
|
|
|
|
|
double ExContactBlockTL::E(const Vector &d) const
|
|
{
|
|
double obj_val = 0.0;
|
|
Number * x = new Number[ndofs];
|
|
|
|
for(int i = 0; i < ndofs; i++)
|
|
{
|
|
x[i] = d(i);
|
|
}
|
|
|
|
// bool boolreturned;
|
|
// boolreturned = eval_f(0, x, true, obj_val);
|
|
eval_f(0, x, true, obj_val);
|
|
delete [] x;
|
|
return obj_val;
|
|
}
|
|
|
|
void ExContactBlockTL::DdE(const Vector &d, Vector &gradE) const
|
|
{
|
|
Number * x = new Number[ndofs];
|
|
Number * grad_obj = new Number[ndofs];
|
|
|
|
for(int i = 0; i < ndofs; i++)
|
|
{
|
|
x[i] = d(i);
|
|
}
|
|
|
|
// bool boolreturned;
|
|
// boolreturned = eval_grad_f(0, x, true, grad_obj);
|
|
|
|
eval_grad_f(0, x, true, grad_obj);
|
|
|
|
for(int i = 0; i < ndofs; i++)
|
|
{
|
|
gradE(i) = grad_obj[i];
|
|
}
|
|
|
|
delete [] x;
|
|
delete [] grad_obj;
|
|
}
|
|
|
|
SparseMatrix* ExContactBlockTL::DddE(const Vector &d)
|
|
{
|
|
return new SparseMatrix(*K);
|
|
}
|
|
|
|
|
|
|
|
// g(d) = d >= 0
|
|
void ExContactBlockTL::g(const Vector &d, Vector &gd) const
|
|
{
|
|
Number * x = new Number[ndofs];
|
|
Number * gapx = new Number[nnd];
|
|
|
|
for(int i = 0; i < ndofs; i++)
|
|
{
|
|
x[i] = d(i);
|
|
}
|
|
|
|
// bool boolreturned;
|
|
// boolreturned = eval_g(ndofs, x, true, nnd, gapx);
|
|
eval_g(ndofs, x, true, nnd, gapx);
|
|
for(int i = 0; i < nnd; i++)
|
|
{
|
|
gd(i) = gapx[i];
|
|
}
|
|
|
|
delete [] x;
|
|
delete [] gapx;
|
|
}
|
|
|
|
SparseMatrix* ExContactBlockTL::Ddg(const Vector &d)
|
|
{
|
|
// !!!!!!!!!!!!!!!!TO DO: call eval_jac_g ....
|
|
// only do so after eval_jac_g has been updated in order
|
|
// that the gap function Jacobian data that is stored in
|
|
// the SparseMatrix member data M is updated
|
|
for(int i = 0; i < ndof_1; i++)
|
|
{
|
|
(*x1)(i) = d(i);
|
|
}
|
|
for(int i = ndof_1; i < ndofs; i++)
|
|
{
|
|
(*x2)(i-ndof_1) = d(i);
|
|
}
|
|
//update_g();
|
|
return new SparseMatrix(*M);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//bool ExContactBlockTL::get_nlp_info(
|
|
// Index& n,
|
|
// Index& m,
|
|
// Index& nnz_jac_g,
|
|
// Index& nnz_h_lag,
|
|
// IndexStyleEnum& index_style
|
|
//)
|
|
//{
|
|
// // The problem described in ExContactBlockTL.hpp has 2 variables, x1, & x2,
|
|
// n = ndofs;
|
|
//
|
|
// // one equality constraint,
|
|
// m = nnd;
|
|
//
|
|
// nnz_jac_g = nnd*ndofs; // treat it as a dense matrix for now
|
|
//
|
|
// // treat it as a dense matrix for now. only need lower-triangular part
|
|
// nnz_h_lag = (ndofs*ndofs + ndofs)/2;
|
|
//
|
|
// // We use the standard fortran index style for row/col entries
|
|
// index_style = C_STYLE;
|
|
//
|
|
// return true;
|
|
//}
|
|
//
|
|
//bool ExContactBlockTL::get_bounds_info(
|
|
// Index n,
|
|
// Number* x_l,
|
|
// Number* x_u,
|
|
// Index m,
|
|
// Number* g_l,
|
|
// Number* g_u
|
|
//)
|
|
//{
|
|
// assert(n == ndofs);
|
|
// assert(m == nnd);
|
|
//
|
|
// for (auto i=0; i<n; i++)
|
|
// {
|
|
// x_l[i] = -1.0e20;
|
|
// x_u[i] = +1.0e20;
|
|
// }
|
|
// for (auto i=0; i<Dirichlet_dof.Size(); i++)
|
|
// {
|
|
// x_l[Dirichlet_dof[i]] = Dirichlet_val[i];
|
|
// x_u[Dirichlet_dof[i]] = Dirichlet_val[i];
|
|
// }
|
|
//
|
|
// // we only have equality constraints
|
|
// for (auto i=0; i<m; i++)
|
|
// {
|
|
// g_l[i] = 0.0;
|
|
// g_u[i] = +1.0e20;
|
|
// }
|
|
//
|
|
// return true;
|
|
//}
|
|
//
|
|
//bool ExContactBlockTL::get_starting_point(
|
|
// Index n,
|
|
// bool init_x,
|
|
// Number* x,
|
|
// bool init_z,
|
|
// Number* z_L,
|
|
// Number* z_U,
|
|
// Index m,
|
|
// bool init_lambda,
|
|
// Number* lambda
|
|
//)
|
|
//{
|
|
// assert(init_x == true);
|
|
// assert(init_z == false);
|
|
// assert(init_lambda == false);
|
|
//
|
|
// for (auto i=0; i<n; i++)
|
|
// {
|
|
// x[i] = 0;
|
|
// }
|
|
// for (auto i=0; i<ndof_1; i++)
|
|
// {
|
|
// x[i] = (*x1)[i];
|
|
// }
|
|
// for (auto i=ndof_1; i<n; i++)
|
|
// {
|
|
// x[i] = (*x2)[i-ndof_1] ;
|
|
// }
|
|
//
|
|
// return true;
|
|
//}
|
|
//
|
|
bool ExContactBlockTL::eval_f(
|
|
Index n,
|
|
const Number* x,
|
|
bool new_x,
|
|
Number& obj_value
|
|
) const
|
|
{
|
|
{
|
|
for (auto i=0; i<ndof_1; i++)
|
|
{
|
|
(*x1)[i] = x[i];
|
|
}
|
|
for (auto i=ndof_1; i<n; i++)
|
|
{
|
|
(*x2)[i-ndof_1] = x[i];
|
|
}
|
|
}
|
|
|
|
obj_value = 0;
|
|
obj_value += A1.InnerProduct(*x1, *x1);
|
|
obj_value += A2.InnerProduct(*x2, *x2);
|
|
obj_value *= 0.5;
|
|
|
|
// --- addition
|
|
obj_value -= InnerProduct(*x1, B1);
|
|
obj_value -= InnerProduct(*x2, B2);
|
|
// ---
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ExContactBlockTL::eval_grad_f(
|
|
Index n,
|
|
const Number* x,
|
|
bool new_x,
|
|
Number* grad_f
|
|
) const
|
|
{
|
|
// if(new_x)
|
|
{
|
|
for (auto i=0; i<ndof_1; i++)
|
|
{
|
|
(*x1)[i] = x[i];
|
|
}
|
|
for (auto i=ndof_1; i<n; i++)
|
|
{
|
|
(*x2)[i-ndof_1] = x[i];
|
|
}
|
|
}
|
|
|
|
// return the gradient of the objective function grad_{x} f(x)
|
|
mfem::Vector temp1(ndof_1);
|
|
mfem::Vector temp2(ndof_2);
|
|
|
|
A1.Mult(*x1, temp1);
|
|
A2.Mult(*x2, temp2);
|
|
|
|
// --- addition
|
|
temp1.Add(-1.0, B1);
|
|
temp2.Add(-1.0, B2);
|
|
// ---
|
|
|
|
for (auto i=0; i<ndof_1; i++)
|
|
{
|
|
grad_f[i] = temp1[i];
|
|
}
|
|
for (auto i=0; i<ndof_2; i++)
|
|
{
|
|
grad_f[i+ndof_1] = temp2[i];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ExContactBlockTL::eval_g(
|
|
Index n,
|
|
const Number* x,
|
|
bool new_x,
|
|
Index m,
|
|
Number* cons
|
|
) const
|
|
{
|
|
assert(n == ndofs);
|
|
assert(m == nnd);
|
|
|
|
// if(new_x)
|
|
{
|
|
for (auto i=0; i<ndof_1; i++)
|
|
{
|
|
(*x1)[i] = x[i];
|
|
}
|
|
for (auto i=ndof_1; i<n; i++)
|
|
{
|
|
(*x2)[i-ndof_1] = x[i];
|
|
}
|
|
update_g();
|
|
}
|
|
|
|
for (auto i=0; i<m; i++)
|
|
{
|
|
cons[i] = gapv[i];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ExContactBlockTL::eval_jac_g(
|
|
Index n,
|
|
const Number* x,
|
|
bool new_x,
|
|
Index m,
|
|
Index nele_jac,
|
|
Index* iRow,
|
|
Index* jCol,
|
|
Number* values
|
|
) const
|
|
{
|
|
assert(n == ndofs);
|
|
assert(m == nnd);
|
|
assert(n*m == nele_jac); // TODO: dense matrix for now
|
|
if (new_x)
|
|
{
|
|
for (auto i=0; i<ndof_1; i++)
|
|
{
|
|
(*x1)[i] = x[i];
|
|
}
|
|
for (auto i=ndof_1; i<n; i++)
|
|
{
|
|
(*x2)[i-ndof_1] = x[i];
|
|
}
|
|
// TODO: do something here to update jac
|
|
}
|
|
|
|
// TODO: we use dense Jac for now
|
|
if ( values == nullptr )
|
|
{
|
|
// return the structure of the jacobian of the constraints
|
|
for (auto i=0; i<m; i++)
|
|
{
|
|
for (auto j=0; j<n; j++)
|
|
{
|
|
iRow[i*n+j] = i;
|
|
jCol[i*n+j] = j;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int *M_i = M->GetI();
|
|
const int *M_j = M->GetJ();
|
|
const double *M_data = M->GetData();
|
|
|
|
for (auto i=0; i<nele_jac; i++)
|
|
{
|
|
values[i] = 0.0;
|
|
}
|
|
for (auto i=0; i<m; i++)
|
|
{
|
|
for (auto k=M_i[i]; k<M_i[i+1]; k++)
|
|
{
|
|
values[i*n+M_j[k]] = M_data[k];
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ExContactBlockTL::eval_h(
|
|
Index n,
|
|
const Number* x,
|
|
bool new_x,
|
|
Number obj_factor,
|
|
Index m,
|
|
const Number* lambda,
|
|
bool new_lambda,
|
|
Index nele_hess,
|
|
Index* iRow,
|
|
Index* jCol,
|
|
Number* values
|
|
)
|
|
{
|
|
assert(n == ndofs);
|
|
assert(m == nnd);
|
|
assert((n*n+n)/2 == nele_hess); // TODO: dense matrix for now
|
|
|
|
if (new_x)
|
|
{
|
|
for (auto i=0; i<ndof_1; i++)
|
|
{
|
|
(*x1)[i] = x[i];
|
|
}
|
|
for (auto i=ndof_1; i<n; i++)
|
|
{
|
|
(*x2)[i-ndof_1] = x[i];
|
|
}
|
|
// TODO: do something here to update hes
|
|
}
|
|
|
|
// TODO: we use dense Hes for now
|
|
if ( values == nullptr )
|
|
{
|
|
// return the structure. This is a symmetric matrix, fill the lower left triangle only.
|
|
int k = 0;
|
|
for (auto i=0; i<n; i++)
|
|
{
|
|
for (auto j=0; j<=i; j++)
|
|
{
|
|
iRow[k] = i;
|
|
jCol[k] = j;
|
|
k++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// return the values
|
|
for (auto k=0; k<nele_hess; k++)
|
|
{
|
|
values[k] = 0.0;
|
|
}
|
|
|
|
const int *K_i = K->GetI();
|
|
const int *K_j = K->GetJ();
|
|
const double *K_data = K->GetData();
|
|
for (auto i=0; i<n; i++)
|
|
{
|
|
for (auto k=K_i[i]; k<K_i[i+1]; k++)
|
|
{
|
|
if (K_j[k]<=i)
|
|
{
|
|
values[(i*i+i)/2+K_j[k]] += K_data[k] * obj_factor;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto con_idx=0; con_idx<m; con_idx++)
|
|
{
|
|
const int *dM_i = dM->at(con_idx).GetI();
|
|
const int *dM_j = dM->at(con_idx).GetJ();
|
|
const double *dM_data = dM->at(con_idx).GetData();
|
|
for (auto i=0; i<n; i++)
|
|
{
|
|
for (auto k=dM_i[i]; k<dM_i[i+1]; k++)
|
|
{
|
|
if (dM_j[k]<=i)
|
|
{
|
|
values[(i*i+i)/2+dM_j[k]] += dM_data[k] * lambda[con_idx];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//void ExContactBlockTL::finalize_solution(
|
|
// SolverReturn status,
|
|
// Index n,
|
|
// const Number* x,
|
|
// const Number* z_L,
|
|
// const Number* z_U,
|
|
// Index m,
|
|
// const Number* g,
|
|
// const Number* lambda,
|
|
// Number obj_value,
|
|
// const IpoptData* ip_data,
|
|
// IpoptCalculatedQuantities* ip_cq
|
|
//)
|
|
//{}
|