402 lines
14 KiB
C++
402 lines
14 KiB
C++
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
|
|
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
|
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
|
//
|
|
// This file is part of the MFEM library. For more information and source code
|
|
// availability visit https://mfem.org.
|
|
//
|
|
// MFEM is free software; you can redistribute it and/or modify it under the
|
|
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
|
// CONTRIBUTING.md for details.
|
|
|
|
#include "ceed.hpp"
|
|
|
|
#ifdef MFEM_USE_CEED
|
|
#include "../../general/device.hpp"
|
|
#include "../../fem/gridfunc.hpp"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#ifndef _WIN32
|
|
typedef struct stat struct_stat;
|
|
#else
|
|
#define stat(dir, buf) _stat(dir, buf)
|
|
#define S_ISDIR(mode) _S_IFDIR(mode)
|
|
typedef struct _stat struct_stat;
|
|
#endif
|
|
|
|
namespace mfem
|
|
{
|
|
|
|
namespace internal
|
|
{
|
|
|
|
extern Ceed ceed;
|
|
|
|
std::string ceed_path;
|
|
|
|
}
|
|
|
|
void InitCeedCoeff(Coefficient* Q, CeedData* ptr)
|
|
{
|
|
if (ConstantCoefficient* coeff = dynamic_cast<ConstantCoefficient*>(Q))
|
|
{
|
|
CeedConstCoeff* ceedCoeff = new CeedConstCoeff{coeff->constant};
|
|
ptr->coeff_type = CeedCoeff::Const;
|
|
ptr->coeff = (void*)ceedCoeff;
|
|
}
|
|
else if (GridFunctionCoefficient* coeff =
|
|
dynamic_cast<GridFunctionCoefficient*>(Q))
|
|
{
|
|
CeedGridCoeff* ceedCoeff = new CeedGridCoeff;
|
|
ceedCoeff->coeff = coeff->GetGridFunction();
|
|
ptr->coeff_type = CeedCoeff::Grid;
|
|
ptr->coeff = (void*)ceedCoeff;
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("This type of Coefficient is not supported.");
|
|
}
|
|
}
|
|
|
|
static CeedElemTopology GetCeedTopology(Geometry::Type geom)
|
|
{
|
|
switch (geom)
|
|
{
|
|
case Geometry::SEGMENT:
|
|
return CEED_LINE;
|
|
case Geometry::TRIANGLE:
|
|
return CEED_TRIANGLE;
|
|
case Geometry::SQUARE:
|
|
return CEED_QUAD;
|
|
case Geometry::TETRAHEDRON:
|
|
return CEED_TET;
|
|
case Geometry::CUBE:
|
|
return CEED_HEX;
|
|
case Geometry::PRISM:
|
|
return CEED_PRISM;
|
|
default:
|
|
MFEM_ABORT("This type of element is not supported");
|
|
return CEED_PRISM;
|
|
}
|
|
}
|
|
|
|
static void InitCeedNonTensorBasisAndRestriction(const FiniteElementSpace &fes,
|
|
const IntegrationRule &ir,
|
|
Ceed ceed, CeedBasis *basis,
|
|
CeedElemRestriction *restr)
|
|
{
|
|
Mesh *mesh = fes.GetMesh();
|
|
const FiniteElement *fe = fes.GetFE(0);
|
|
const int dim = mesh->Dimension();
|
|
const int P = fe->GetDof();
|
|
const int Q = ir.GetNPoints();
|
|
DenseMatrix shape(P, Q);
|
|
Vector grad(P*dim*Q);
|
|
DenseMatrix qref(dim, Q);
|
|
Vector qweight(Q);
|
|
Vector shape_i(P);
|
|
DenseMatrix grad_i(P, dim);
|
|
const Table &el_dof = fes.GetElementToDofTable();
|
|
Array<int> tp_el_dof(el_dof.Size_of_connections());
|
|
const TensorBasisElement * tfe =
|
|
dynamic_cast<const TensorBasisElement *>(fe);
|
|
if (tfe) // Lexicographic ordering using dof_map
|
|
{
|
|
const Array<int>& dof_map = tfe->GetDofMap();
|
|
for (int i = 0; i < Q; i++)
|
|
{
|
|
const IntegrationPoint &ip = ir.IntPoint(i);
|
|
qref(0,i) = ip.x;
|
|
if (dim>1) { qref(1,i) = ip.y; }
|
|
if (dim>2) { qref(2,i) = ip.z; }
|
|
qweight(i) = ip.weight;
|
|
fe->CalcShape(ip, shape_i);
|
|
fe->CalcDShape(ip, grad_i);
|
|
for (int j = 0; j < P; j++)
|
|
{
|
|
shape(j, i) = shape_i(dof_map[j]);
|
|
for (int d = 0; d < dim; ++d)
|
|
{
|
|
grad(j+i*P+d*Q*P) = grad_i(dof_map[j], d);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < mesh->GetNE(); i++)
|
|
{
|
|
const int el_offset = fe->GetDof() * i;
|
|
for (int j = 0; j < fe->GetDof(); j++)
|
|
{
|
|
tp_el_dof[j + el_offset] = el_dof.GetJ()[dof_map[j] + el_offset];
|
|
}
|
|
}
|
|
}
|
|
else // Native ordering
|
|
{
|
|
for (int i = 0; i < Q; i++)
|
|
{
|
|
const IntegrationPoint &ip = ir.IntPoint(i);
|
|
qref(0,i) = ip.x;
|
|
if (dim>1) { qref(1,i) = ip.y; }
|
|
if (dim>2) { qref(2,i) = ip.z; }
|
|
qweight(i) = ip.weight;
|
|
fe->CalcShape(ip, shape_i);
|
|
fe->CalcDShape(ip, grad_i);
|
|
for (int j = 0; j < P; j++)
|
|
{
|
|
shape(j, i) = shape_i(j);
|
|
for (int d = 0; d < dim; ++d)
|
|
{
|
|
grad(j+i*P+d*Q*P) = grad_i(j, d);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int e = 0; e < mesh->GetNE(); e++)
|
|
{
|
|
for (int i = 0; i < P; i++)
|
|
{
|
|
tp_el_dof[i + e*P] = el_dof.GetJ()[i + e*P];
|
|
}
|
|
}
|
|
}
|
|
CeedBasisCreateH1(ceed, GetCeedTopology(fe->GetGeomType()), fes.GetVDim(),
|
|
fe->GetDof(), ir.GetNPoints(), shape.GetData(),
|
|
grad.GetData(), qref.GetData(), qweight.GetData(), basis);
|
|
CeedInterlaceMode imode = CEED_NONINTERLACED;
|
|
if (fes.GetOrdering()==Ordering::byVDIM)
|
|
{
|
|
imode = CEED_INTERLACED;
|
|
}
|
|
CeedElemRestrictionCreate(ceed, imode, mesh->GetNE(), fe->GetDof(),
|
|
fes.GetNDofs(), fes.GetVDim(), CEED_MEM_HOST, CEED_COPY_VALUES,
|
|
tp_el_dof.GetData(), restr);
|
|
}
|
|
|
|
static void InitCeedTensorBasisAndRestriction(const FiniteElementSpace &fes,
|
|
const IntegrationRule &ir,
|
|
Ceed ceed, CeedBasis *basis,
|
|
CeedElemRestriction *restr)
|
|
{
|
|
Mesh *mesh = fes.GetMesh();
|
|
const FiniteElement *fe = fes.GetFE(0);
|
|
const int order = fes.GetOrder(0);
|
|
const TensorBasisElement * tfe =
|
|
dynamic_cast<const TensorBasisElement *>(fe);
|
|
MFEM_VERIFY(tfe, "invalid FE");
|
|
const Array<int>& dof_map = tfe->GetDofMap();
|
|
const FiniteElement *fe1d =
|
|
fes.FEColl()->FiniteElementForGeometry(Geometry::SEGMENT);
|
|
DenseMatrix shape1d(fe1d->GetDof(), ir.GetNPoints());
|
|
DenseMatrix grad1d(fe1d->GetDof(), ir.GetNPoints());
|
|
Vector qref1d(ir.GetNPoints()), qweight1d(ir.GetNPoints());
|
|
Vector shape_i(shape1d.Height());
|
|
DenseMatrix grad_i(grad1d.Height(), 1);
|
|
const H1_SegmentElement *h1_fe1d =
|
|
dynamic_cast<const H1_SegmentElement *>(fe1d);
|
|
MFEM_VERIFY(h1_fe1d, "invalid FE");
|
|
const Array<int> &dof_map_1d = h1_fe1d->GetDofMap();
|
|
for (int i = 0; i < ir.GetNPoints(); i++)
|
|
{
|
|
const IntegrationPoint &ip = ir.IntPoint(i);
|
|
qref1d(i) = ip.x;
|
|
qweight1d(i) = ip.weight;
|
|
fe1d->CalcShape(ip, shape_i);
|
|
fe1d->CalcDShape(ip, grad_i);
|
|
for (int j = 0; j < shape1d.Height(); j++)
|
|
{
|
|
shape1d(j, i) = shape_i(dof_map_1d[j]);
|
|
grad1d(j, i) = grad_i(dof_map_1d[j], 0);
|
|
}
|
|
}
|
|
CeedBasisCreateTensorH1(ceed, mesh->Dimension(), fes.GetVDim(), order + 1,
|
|
ir.GetNPoints(), shape1d.GetData(),
|
|
grad1d.GetData(), qref1d.GetData(),
|
|
qweight1d.GetData(), basis);
|
|
|
|
const Table &el_dof = fes.GetElementToDofTable();
|
|
Array<int> tp_el_dof(el_dof.Size_of_connections());
|
|
for (int i = 0; i < mesh->GetNE(); i++)
|
|
{
|
|
const int el_offset = fe->GetDof() * i;
|
|
for (int j = 0; j < fe->GetDof(); j++)
|
|
{
|
|
tp_el_dof[j + el_offset] = el_dof.GetJ()[dof_map[j] + el_offset];
|
|
}
|
|
}
|
|
CeedInterlaceMode imode = CEED_NONINTERLACED;
|
|
if (fes.GetOrdering()==Ordering::byVDIM)
|
|
{
|
|
imode = CEED_INTERLACED;
|
|
}
|
|
CeedElemRestrictionCreate(ceed, imode, mesh->GetNE(), fe->GetDof(),
|
|
fes.GetNDofs(), fes.GetVDim(), CEED_MEM_HOST, CEED_COPY_VALUES,
|
|
tp_el_dof.GetData(), restr);
|
|
}
|
|
|
|
void InitCeedBasisAndRestriction(const FiniteElementSpace &fes,
|
|
const IntegrationRule &irm,
|
|
Ceed ceed, CeedBasis *basis,
|
|
CeedElemRestriction *restr)
|
|
{
|
|
if (UsesTensorBasis(fes))
|
|
{
|
|
const IntegrationRule &ir = IntRules.Get(Geometry::SEGMENT, irm.GetOrder());
|
|
InitCeedTensorBasisAndRestriction(fes, ir, ceed, basis, restr);
|
|
}
|
|
else
|
|
{
|
|
InitCeedNonTensorBasisAndRestriction(fes, irm, ceed, basis, restr);
|
|
}
|
|
}
|
|
|
|
const std::string &GetCeedPath()
|
|
{
|
|
if (internal::ceed_path.empty())
|
|
{
|
|
const char *install_dir = MFEM_INSTALL_DIR "/include/mfem/fem/libceed";
|
|
const char *source_dir = MFEM_SOURCE_DIR "/fem/libceed";
|
|
struct_stat m_stat;
|
|
if (stat(install_dir, &m_stat) == 0 && S_ISDIR(m_stat.st_mode))
|
|
{
|
|
internal::ceed_path = install_dir;
|
|
}
|
|
else if (stat(source_dir, &m_stat) == 0 && S_ISDIR(m_stat.st_mode))
|
|
{
|
|
internal::ceed_path = source_dir;
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("Cannot find libCEED kernels in MFEM_INSTALL_DIR or "
|
|
"MFEM_SOURCE_DIR");
|
|
}
|
|
// Could be useful for debugging:
|
|
// mfem::out << "Using libCEED dir: " << internal::ceed_path << std::endl;
|
|
}
|
|
return internal::ceed_path;
|
|
}
|
|
|
|
void CeedPAAssemble(const CeedPAOperator& op,
|
|
CeedData& ceedData)
|
|
{
|
|
const FiniteElementSpace &fes = op.fes;
|
|
const mfem::IntegrationRule &irm = op.ir;
|
|
Ceed ceed(internal::ceed);
|
|
mfem::Mesh *mesh = fes.GetMesh();
|
|
CeedInt nqpts, nelem = mesh->GetNE();
|
|
CeedInt dim = mesh->SpaceDimension(), vdim = fes.GetVDim();
|
|
|
|
mesh->EnsureNodes();
|
|
InitCeedBasisAndRestriction(fes, irm, ceed, &ceedData.basis, &ceedData.restr);
|
|
|
|
const mfem::FiniteElementSpace *mesh_fes = mesh->GetNodalFESpace();
|
|
MFEM_VERIFY(mesh_fes, "the Mesh has no nodal FE space");
|
|
InitCeedBasisAndRestriction(*mesh_fes, irm, ceed, &ceedData.mesh_basis,
|
|
&ceedData.mesh_restr);
|
|
|
|
CeedBasisGetNumQuadraturePoints(ceedData.basis, &nqpts);
|
|
|
|
const int qdatasize = op.qdatasize;
|
|
CeedElemRestrictionCreateStrided(ceed, nelem, nqpts, nelem*nqpts, qdatasize,
|
|
CEED_STRIDES_BACKEND, &ceedData.restr_i);
|
|
|
|
CeedVectorCreate(ceed, mesh->GetNodes()->Size(), &ceedData.node_coords);
|
|
CeedVectorSetArray(ceedData.node_coords, CEED_MEM_HOST, CEED_USE_POINTER,
|
|
mesh->GetNodes()->GetData());
|
|
|
|
CeedVectorCreate(ceed, nelem * nqpts * qdatasize, &ceedData.rho);
|
|
|
|
// Context data to be passed to the 'f_build_diff' Q-function.
|
|
ceedData.build_ctx.dim = mesh->Dimension();
|
|
ceedData.build_ctx.space_dim = mesh->SpaceDimension();
|
|
|
|
std::string qf_file = GetCeedPath() + op.header;
|
|
std::string qf;
|
|
|
|
// Create the Q-function that builds the operator (i.e. computes its
|
|
// quadrature data) and set its context data.
|
|
switch (ceedData.coeff_type)
|
|
{
|
|
case CeedCoeff::Const:
|
|
qf = qf_file + op.const_func;
|
|
CeedQFunctionCreateInterior(ceed, 1, op.const_qf,
|
|
qf.c_str(),
|
|
&ceedData.build_qfunc);
|
|
ceedData.build_ctx.coeff = ((CeedConstCoeff*)ceedData.coeff)->val;
|
|
break;
|
|
case CeedCoeff::Grid:
|
|
qf = qf_file + op.grid_func;
|
|
CeedQFunctionCreateInterior(ceed, 1, op.grid_qf,
|
|
qf.c_str(),
|
|
&ceedData.build_qfunc);
|
|
CeedQFunctionAddInput(ceedData.build_qfunc, "coeff", 1, CEED_EVAL_INTERP);
|
|
break;
|
|
default:
|
|
MFEM_ABORT("This coeff_type is not handled");
|
|
}
|
|
CeedQFunctionAddInput(ceedData.build_qfunc, "dx", dim * dim, CEED_EVAL_GRAD);
|
|
CeedQFunctionAddInput(ceedData.build_qfunc, "weights", 1, CEED_EVAL_WEIGHT);
|
|
CeedQFunctionAddOutput(ceedData.build_qfunc, "qdata", qdatasize,
|
|
CEED_EVAL_NONE);
|
|
CeedQFunctionSetContext(ceedData.build_qfunc, &ceedData.build_ctx,
|
|
sizeof(ceedData.build_ctx));
|
|
|
|
// Create the operator that builds the quadrature data for the operator.
|
|
CeedOperatorCreate(ceed, ceedData.build_qfunc, NULL, NULL,
|
|
&ceedData.build_oper);
|
|
if (ceedData.coeff_type==CeedCoeff::Grid)
|
|
{
|
|
CeedGridCoeff* ceedCoeff = (CeedGridCoeff*)ceedData.coeff;
|
|
InitCeedBasisAndRestriction(*ceedCoeff->coeff->FESpace(), irm, ceed,
|
|
&ceedCoeff->basis,
|
|
&ceedCoeff->restr);
|
|
CeedVectorCreate(ceed, ceedCoeff->coeff->FESpace()->GetNDofs(),
|
|
&ceedCoeff->coeffVector);
|
|
CeedVectorSetArray(ceedCoeff->coeffVector, CEED_MEM_HOST, CEED_USE_POINTER,
|
|
ceedCoeff->coeff->GetData());
|
|
CeedOperatorSetField(ceedData.build_oper, "coeff", ceedCoeff->restr,
|
|
ceedCoeff->basis, ceedCoeff->coeffVector);
|
|
}
|
|
CeedOperatorSetField(ceedData.build_oper, "dx", ceedData.mesh_restr,
|
|
ceedData.mesh_basis, CEED_VECTOR_ACTIVE);
|
|
CeedOperatorSetField(ceedData.build_oper, "weights", CEED_ELEMRESTRICTION_NONE,
|
|
ceedData.mesh_basis, CEED_VECTOR_NONE);
|
|
CeedOperatorSetField(ceedData.build_oper, "qdata", ceedData.restr_i,
|
|
CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
|
|
|
|
// Compute the quadrature data for the operator.
|
|
CeedOperatorApply(ceedData.build_oper, ceedData.node_coords, ceedData.rho,
|
|
CEED_REQUEST_IMMEDIATE);
|
|
|
|
// Create the Q-function that defines the action of the operator.
|
|
qf = qf_file + op.apply_func;//":f_apply_diff";
|
|
CeedQFunctionCreateInterior(ceed, 1, op.apply_qf,
|
|
qf.c_str(),
|
|
&ceedData.apply_qfunc);
|
|
CeedInt dimU = vdim*(op.trial_op==CEED_EVAL_GRAD ? dim : 1);
|
|
CeedInt dimV = vdim*(op.test_op==CEED_EVAL_GRAD ? dim : 1);
|
|
CeedQFunctionAddInput(ceedData.apply_qfunc, "u", dimU, op.trial_op);
|
|
CeedQFunctionAddInput(ceedData.apply_qfunc, "qdata", qdatasize,
|
|
CEED_EVAL_NONE);
|
|
CeedQFunctionAddOutput(ceedData.apply_qfunc, "v", dimV, op.test_op);
|
|
CeedQFunctionSetContext(ceedData.apply_qfunc, &ceedData.build_ctx,
|
|
sizeof(ceedData.build_ctx));
|
|
|
|
// Create the diff operator.
|
|
CeedOperatorCreate(ceed, ceedData.apply_qfunc, NULL, NULL, &ceedData.oper);
|
|
CeedOperatorSetField(ceedData.oper, "u", ceedData.restr, ceedData.basis,
|
|
CEED_VECTOR_ACTIVE);
|
|
CeedOperatorSetField(ceedData.oper, "qdata", ceedData.restr_i,
|
|
CEED_BASIS_COLLOCATED, ceedData.rho);
|
|
CeedOperatorSetField(ceedData.oper, "v", ceedData.restr, ceedData.basis,
|
|
CEED_VECTOR_ACTIVE);
|
|
|
|
CeedVectorCreate(ceed, fes.GetNDofs(), &ceedData.u);
|
|
CeedVectorCreate(ceed, fes.GetNDofs(), &ceedData.v);
|
|
}
|
|
|
|
} // namespace mfem
|
|
|
|
#endif // MFEM_USE_CEED
|