581 lines
22 KiB
C++
581 lines
22 KiB
C++
// Copyright (c) 2010-2025, 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 "../bilininteg.hpp"
|
|
#include "../../general/forall.hpp"
|
|
#include "../ceed/integrators/diffusion/diffusion.hpp"
|
|
|
|
#include "./bilininteg_vecdiffusion_pa.hpp" // IWYU pragma: keep
|
|
|
|
// #include "bilininteg_vecdiffusion_kernels.hpp"
|
|
// #include "bilininteg_vecdiffusion_pa.hpp"
|
|
|
|
namespace mfem
|
|
{
|
|
|
|
VectorDiffusionIntegrator::VectorDiffusionIntegrator(const IntegrationRule *ir)
|
|
: BilinearFormIntegrator(ir)
|
|
{
|
|
// static Kernels kernels;
|
|
}
|
|
|
|
VectorDiffusionIntegrator::VectorDiffusionIntegrator(Coefficient &q)
|
|
: VectorDiffusionIntegrator()
|
|
{
|
|
Q = &q;
|
|
}
|
|
|
|
VectorDiffusionIntegrator::VectorDiffusionIntegrator(int vector_dimension)
|
|
: VectorDiffusionIntegrator()
|
|
{
|
|
vdim = vector_dimension;
|
|
}
|
|
|
|
VectorDiffusionIntegrator::VectorDiffusionIntegrator(Coefficient &q,
|
|
const IntegrationRule *ir)
|
|
: VectorDiffusionIntegrator(ir)
|
|
{
|
|
Q = &q;
|
|
}
|
|
|
|
VectorDiffusionIntegrator::VectorDiffusionIntegrator(Coefficient &q,
|
|
int vector_dimension)
|
|
: VectorDiffusionIntegrator()
|
|
{
|
|
Q = &q;
|
|
vdim = vector_dimension;
|
|
}
|
|
|
|
VectorDiffusionIntegrator::VectorDiffusionIntegrator(VectorCoefficient &vq)
|
|
: VectorDiffusionIntegrator()
|
|
{
|
|
VQ = &vq;
|
|
vdim = vq.GetVDim();
|
|
}
|
|
|
|
VectorDiffusionIntegrator::VectorDiffusionIntegrator(MatrixCoefficient &mq)
|
|
: VectorDiffusionIntegrator()
|
|
{
|
|
MQ = &mq;
|
|
vdim = mq.GetVDim();
|
|
}
|
|
|
|
void VectorDiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
|
{
|
|
Mesh *mesh = fes.GetMesh();
|
|
const FiniteElement &el = *fes.GetTypicalFE();
|
|
const auto *ir = IntRule ? IntRule : &DiffusionIntegrator::GetRule(el, el);
|
|
|
|
if (DeviceCanUseCeed())
|
|
{
|
|
delete ceedOp;
|
|
const bool mixed =
|
|
mesh->GetNumGeometries(mesh->Dimension()) > 1 || fes.IsVariableOrder();
|
|
if (mixed) { ceedOp = new ceed::MixedPADiffusionIntegrator(*this, fes, Q); }
|
|
else { ceedOp = new ceed::PADiffusionIntegrator(fes, *ir, Q); }
|
|
return;
|
|
}
|
|
|
|
// If vdim is not set, set it to the space dimension
|
|
vdim = (vdim == -1) ? fes.GetVDim() : vdim;
|
|
MFEM_VERIFY(vdim == fes.GetVDim(), "vdim != fes.GetVDim()");
|
|
|
|
const MemoryType mt = pa_mt == MemoryType::DEFAULT
|
|
? Device::GetDeviceMemoryType()
|
|
: pa_mt;
|
|
|
|
ne = fes.GetNE();
|
|
dim = mesh->Dimension();
|
|
sdim = mesh->SpaceDimension();
|
|
const int nq = ir->GetNPoints();
|
|
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS, mt);
|
|
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
|
|
dofs1D = maps->ndof;
|
|
quad1D = maps->nqpt;
|
|
const int q1d = quad1D;
|
|
|
|
if (!(dim == 2 || dim == 3)) { MFEM_ABORT("Dimension not supported."); }
|
|
|
|
QuadratureSpace qs(*mesh, *ir);
|
|
CoefficientVector coeff(qs, CoefficientStorage::FULL);
|
|
|
|
if (Q)
|
|
{
|
|
coeff.Project(*Q);
|
|
}
|
|
else if (VQ)
|
|
{
|
|
coeff.Project(*VQ);
|
|
MFEM_VERIFY(VQ->GetVDim() == vdim, "VQ vdim vs. vdim error");
|
|
}
|
|
else if (MQ)
|
|
{
|
|
coeff.ProjectTranspose(*MQ);
|
|
MFEM_VERIFY(MQ->GetVDim() == vdim, "MQ dimension vs. vdim error");
|
|
MFEM_VERIFY(coeff.Size() == (vdim*vdim) * ne * nq, "MQ size error");
|
|
}
|
|
else { coeff.SetConstant(1.0); }
|
|
|
|
coeff_vdim = coeff.GetVDim();
|
|
const bool scalar_coeff = coeff_vdim == 1;
|
|
const bool vector_coeff = coeff_vdim == vdim;
|
|
const bool matrix_coeff = coeff_vdim == vdim * vdim;
|
|
MFEM_VERIFY(scalar_coeff + vector_coeff + matrix_coeff == 1, "");
|
|
|
|
const int pa_size = dim * dim;
|
|
pa_data.SetSize(nq * pa_size * vdim * (matrix_coeff ? dim : 1) * ne, mt);
|
|
|
|
if (dim == 2 && sdim == 3)
|
|
{
|
|
MFEM_VERIFY(scalar_coeff, "");
|
|
const int nc = vdim;
|
|
const auto W = Reshape(ir->GetWeights().Read(), q1d, q1d);
|
|
const auto J = Reshape(geom->J.Read(), q1d, q1d, sdim, dim, ne);
|
|
const auto C = Reshape(coeff.Read(), coeff_vdim, q1d, q1d, ne);
|
|
auto D = Reshape(pa_data.Write(), q1d, q1d, pa_size,
|
|
vdim * (matrix_coeff ? dim : 1), ne);
|
|
|
|
mfem::forall_2D(ne, q1d, q1d, [=] MFEM_HOST_DEVICE(int e)
|
|
{
|
|
MFEM_FOREACH_THREAD(qy, y, q1d)
|
|
{
|
|
MFEM_FOREACH_THREAD(qx, x, q1d)
|
|
{
|
|
for (int i = 0; i < nc; ++i)
|
|
{
|
|
const real_t wq = W(qx, qy);
|
|
const real_t J11 = J(qx, qy, 0, 0, e);
|
|
const real_t J21 = J(qx, qy, 1, 0, e);
|
|
const real_t J31 = J(qx, qy, 2, 0, e);
|
|
const real_t J12 = J(qx, qy, 0, 1, e);
|
|
const real_t J22 = J(qx, qy, 1, 1, e);
|
|
const real_t J32 = J(qx, qy, 2, 1, e);
|
|
const real_t E = J11*J11 + J21*J21 + J31*J31;
|
|
const real_t G = J12*J12 + J22*J22 + J32*J32;
|
|
const real_t F = J11*J12 + J21*J22 + J31*J32;
|
|
const real_t iw = 1.0 / sqrt(E*G - F*F);
|
|
const auto C0 = C(0, qx, qy, e);
|
|
const real_t alpha = wq * C0 * iw;
|
|
D(qx, qy, 0, i, e) = alpha * G; // 1,1
|
|
D(qx, qy, 1, i, e) = -alpha * F; // 1,2
|
|
D(qx, qy, 2, i, e) = -alpha * F; // 2,1 == 1,2
|
|
D(qx, qy, 3, i, e) = alpha * E; // 2,2
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
else if (dim == 2 && sdim == 2)
|
|
{
|
|
const int nc = vdim, cvdim = coeff_vdim;
|
|
const auto W = Reshape(ir->GetWeights().Read(), q1d, q1d);
|
|
const auto J = Reshape(geom->J.Read(), q1d, q1d, sdim, dim, ne);
|
|
const auto C = Reshape(coeff.Read(), coeff_vdim, q1d, q1d, ne);
|
|
auto DE = Reshape(pa_data.Write(), q1d, q1d, pa_size,
|
|
vdim * (matrix_coeff ? dim : 1), ne);
|
|
mfem::forall_2D(ne, q1d, q1d, [=] MFEM_HOST_DEVICE(int e)
|
|
{
|
|
MFEM_FOREACH_THREAD(qy, y, q1d)
|
|
{
|
|
MFEM_FOREACH_THREAD(qx, x, q1d)
|
|
{
|
|
const real_t J11 = J(qx, qy, 0, 0, e);
|
|
const real_t J21 = J(qx, qy, 1, 0, e);
|
|
const real_t J12 = J(qx, qy, 0, 1, e);
|
|
const real_t J22 = J(qx, qy, 1, 1, e);
|
|
const real_t w_detJ = W(qx, qy) / ((J11*J22)-(J21*J12));
|
|
const real_t D0 = w_detJ * (J12*J12 + J22*J22);
|
|
const real_t D1 = -w_detJ * (J12*J11 + J22*J21);
|
|
const real_t D2 = w_detJ * (J11*J11 + J21*J21);
|
|
const int map[4] = {0, 2, 1, 3};
|
|
|
|
for (int i = 0; i < (matrix_coeff ? cvdim : nc); ++i)
|
|
{
|
|
const auto k = matrix_coeff ? map[i] : (vector_coeff ? i : 0);
|
|
const auto Cc = C(k, qx, qy, e);
|
|
DE(qx, qy, 0, i, e) = D0 * Cc;
|
|
DE(qx, qy, 1, i, e) = D1 * Cc;
|
|
DE(qx, qy, 2, i, e) = D1 * Cc;
|
|
DE(qx, qy, 3, i, e) = D2 * Cc;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
else if (dim == 3 && sdim == 3)
|
|
{
|
|
const int nc = vdim, cvdim = coeff_vdim;
|
|
const auto W = Reshape(ir->GetWeights().Read(), q1d, q1d, q1d);
|
|
const auto J = Reshape(geom->J.Read(), q1d, q1d, q1d, sdim, dim, ne);
|
|
const auto C = Reshape(coeff.Read(), coeff_vdim, q1d, q1d, q1d, ne);
|
|
auto DE = Reshape(pa_data.Write(), q1d, q1d, q1d, pa_size,
|
|
vdim * (matrix_coeff ? dim : 1), ne);
|
|
|
|
mfem::forall_3D(ne, q1d, q1d, q1d, [=] MFEM_HOST_DEVICE(int e)
|
|
{
|
|
MFEM_FOREACH_THREAD(qz, z, q1d)
|
|
{
|
|
MFEM_FOREACH_THREAD(qy, y, q1d)
|
|
{
|
|
MFEM_FOREACH_THREAD(qx, x, q1d)
|
|
{
|
|
const real_t J11 = J(qx, qy, qz, 0, 0, e);
|
|
const real_t J21 = J(qx, qy, qz, 1, 0, e);
|
|
const real_t J31 = J(qx, qy, qz, 2, 0, e);
|
|
const real_t J12 = J(qx, qy, qz, 0, 1, e);
|
|
const real_t J22 = J(qx, qy, qz, 1, 1, e);
|
|
const real_t J32 = J(qx, qy, qz, 2, 1, e);
|
|
const real_t J13 = J(qx, qy, qz, 0, 2, e);
|
|
const real_t J23 = J(qx, qy, qz, 1, 2, e);
|
|
const real_t J33 = J(qx, qy, qz, 2, 2, e);
|
|
const real_t detJ = J11 * (J22 * J33 - J32 * J23) -
|
|
J21 * (J12 * J33 - J32 * J13) +
|
|
J31 * (J12 * J23 - J22 * J13);
|
|
const real_t c_detJ = W(qx, qy, qz) / detJ;
|
|
// adj(J)
|
|
const real_t A11 = (J22 * J33) - (J23 * J32);
|
|
const real_t A12 = (J32 * J13) - (J12 * J33);
|
|
const real_t A13 = (J12 * J23) - (J22 * J13);
|
|
const real_t A21 = (J31 * J23) - (J21 * J33);
|
|
const real_t A22 = (J11 * J33) - (J13 * J31);
|
|
const real_t A23 = (J21 * J13) - (J11 * J23);
|
|
const real_t A31 = (J21 * J32) - (J31 * J22);
|
|
const real_t A32 = (J31 * J12) - (J11 * J32);
|
|
const real_t A33 = (J11 * J22) - (J12 * J21);
|
|
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
|
|
const real_t D11 = c_detJ * (A11*A11 + A12*A12 + A13*A13); // 1,1
|
|
const real_t D21 = c_detJ * (A11*A21 + A12*A22 + A13*A23); // 2,1
|
|
const real_t D31 = c_detJ * (A11*A31 + A12*A32 + A13*A33); // 3,1
|
|
const real_t D22 = c_detJ * (A21*A21 + A22*A22 + A23*A23); // 2,2
|
|
const real_t D32 = c_detJ * (A21*A31 + A22*A32 + A23*A33); // 3,2
|
|
const real_t D33 = c_detJ * (A31*A31 + A32*A32 + A33*A33); // 3,3
|
|
const int map[9] = {0, 3, 6, 1, 4, 7, 2, 5, 8};
|
|
|
|
for (int i = 0; i < (matrix_coeff ? cvdim : nc); ++i)
|
|
{
|
|
const auto k = matrix_coeff ? map[i] : vector_coeff ? i : 0;
|
|
const auto Ck = C(k, qx, qy, qz, e);
|
|
DE(qx, qy, qz, 0, i, e) = D11 * Ck;
|
|
DE(qx, qy, qz, 1, i, e) = D21 * Ck;
|
|
DE(qx, qy, qz, 2, i, e) = D31 * Ck;
|
|
DE(qx, qy, qz, 3, i, e) = D22 * Ck;
|
|
DE(qx, qy, qz, 4, i, e) = D32 * Ck;
|
|
DE(qx, qy, qz, 5, i, e) = D33 * Ck;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("Unknown VectorDiffusionIntegrator::AssemblePA kernel for"
|
|
<< " dim:" << dim << ", vdim:" << vdim << ", sdim:" << sdim);
|
|
}
|
|
}
|
|
|
|
// PA Diffusion Apply kernel
|
|
void VectorDiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
|
{
|
|
// Use CEED backend if available
|
|
if (DeviceCanUseCeed()) { return ceedOp->AddMult(x, y); }
|
|
|
|
// Add the VectorDiffusionAddMultPA specializations
|
|
static const auto vector_diffusion_kernel_specializations =
|
|
(
|
|
// 2D, SDIM = 2
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 2,2>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 3,3>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 4,4>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 5,5>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 6,6>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 7,7>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 8,8>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 9,9>::Add(),
|
|
// 2D, SDIM = 3
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,3, 2,2>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,3, 3,3>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,3, 4,4>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,3, 5,5>::Add(),
|
|
// 3D, SDIM = 3
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 2,2>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 2,3>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 3,4>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 4,5>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 4,6>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 5,6>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 5,8>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 6,7>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 7,8>::Add(),
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 8,9>::Add(),
|
|
true);
|
|
MFEM_CONTRACT_VAR(vector_diffusion_kernel_specializations);
|
|
|
|
ApplyPAKernels::Run(dim, sdim, dofs1D, quad1D,
|
|
ne, coeff_vdim, maps->B, maps->G, pa_data, x, y,
|
|
sdim, dofs1D, quad1D);
|
|
|
|
}
|
|
|
|
template<int T_D1D = 0, int T_Q1D = 0>
|
|
static void PAVectorDiffusionDiagonal2D(const int NE,
|
|
const Array<real_t> &b,
|
|
const Array<real_t> &g,
|
|
const Vector &d,
|
|
Vector &y,
|
|
const int d1d = 0,
|
|
const int q1d = 0)
|
|
{
|
|
const int D1D = T_D1D ? T_D1D : d1d;
|
|
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
|
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
|
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
|
|
|
const auto B = Reshape(b.Read(), Q1D, D1D);
|
|
const auto G = Reshape(g.Read(), Q1D, D1D);
|
|
// note the different shape for D, this is a (symmetric) matrix so we only
|
|
// store necessary entries
|
|
MFEM_VERIFY(d.Size() == Q1D*Q1D*4*2*NE, "");
|
|
const auto D = Reshape(d.Read(), Q1D*Q1D, /*3*/4, 2, NE);
|
|
auto Y = Reshape(y.ReadWrite(), D1D, D1D, 2, NE);
|
|
|
|
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
|
|
{
|
|
const int D1D = T_D1D ? T_D1D : d1d;
|
|
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
|
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
|
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
|
// gradphi \cdot Q \gradphi has four terms
|
|
real_t QD0[MQ1][MD1];
|
|
real_t QD1[MQ1][MD1];
|
|
real_t QD2[MQ1][MD1];
|
|
for (int qx = 0; qx < Q1D; ++qx)
|
|
{
|
|
for (int dy = 0; dy < D1D; ++dy)
|
|
{
|
|
QD0[qx][dy] = 0.0;
|
|
QD1[qx][dy] = 0.0;
|
|
QD2[qx][dy] = 0.0;
|
|
for (int qy = 0; qy < Q1D; ++qy)
|
|
{
|
|
const int q = qx + qy * Q1D;
|
|
const real_t D0 = D(q,0,0,e);
|
|
const real_t D1 = D(q,1,0,e);
|
|
const real_t D2 = D(q,3/*2*/,0,e); // size from 3 (symmetric) to 4 (dims x dims)
|
|
QD0[qx][dy] += B(qy, dy) * B(qy, dy) * D0;
|
|
QD1[qx][dy] += B(qy, dy) * G(qy, dy) * D1;
|
|
QD2[qx][dy] += G(qy, dy) * G(qy, dy) * D2;
|
|
}
|
|
}
|
|
}
|
|
for (int dy = 0; dy < D1D; ++dy)
|
|
{
|
|
for (int dx = 0; dx < D1D; ++dx)
|
|
{
|
|
real_t temp = 0.0;
|
|
for (int qx = 0; qx < Q1D; ++qx)
|
|
{
|
|
temp += G(qx, dx) * G(qx, dx) * QD0[qx][dy];
|
|
temp += G(qx, dx) * B(qx, dx) * QD1[qx][dy];
|
|
temp += B(qx, dx) * G(qx, dx) * QD1[qx][dy];
|
|
temp += B(qx, dx) * B(qx, dx) * QD2[qx][dy];
|
|
}
|
|
Y(dx,dy,0,e) += temp;
|
|
Y(dx,dy,1,e) += temp;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
template<int T_D1D = 0, int T_Q1D = 0>
|
|
static void PAVectorDiffusionDiagonal3D(const int NE,
|
|
const Array<real_t> &b,
|
|
const Array<real_t> &g,
|
|
const Vector &d,
|
|
Vector &y,
|
|
const int d1d = 0,
|
|
const int q1d = 0)
|
|
{
|
|
constexpr int DIM = 3;
|
|
const int D1D = T_D1D ? T_D1D : d1d;
|
|
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
|
const int max_q1d = T_Q1D ? T_Q1D : DeviceDofQuadLimits::Get().MAX_Q1D;
|
|
const int max_d1d = T_D1D ? T_D1D : DeviceDofQuadLimits::Get().MAX_D1D;
|
|
MFEM_VERIFY(D1D <= max_d1d, "");
|
|
MFEM_VERIFY(Q1D <= max_q1d, "");
|
|
auto B = Reshape(b.Read(), Q1D, D1D);
|
|
auto G = Reshape(g.Read(), Q1D, D1D);
|
|
MFEM_VERIFY(d.Size() == Q1D*Q1D*Q1D*9*3*NE, "");
|
|
auto Q = Reshape(d.Read(), Q1D*Q1D*Q1D, 9/*PA_SIZE:dims*dims*/, 3/*VDIM*/, NE);
|
|
auto Y = Reshape(y.ReadWrite(), D1D, D1D, D1D, 3, NE);
|
|
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
|
|
{
|
|
const int D1D = T_D1D ? T_D1D : d1d;
|
|
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
|
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
|
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
|
real_t QQD[MQ1][MQ1][MD1];
|
|
real_t QDD[MQ1][MD1][MD1];
|
|
for (int i = 0; i < DIM; ++i)
|
|
{
|
|
for (int j = 0; j < DIM; ++j)
|
|
{
|
|
// first tensor contraction, along z direction
|
|
for (int qx = 0; qx < Q1D; ++qx)
|
|
{
|
|
for (int qy = 0; qy < Q1D; ++qy)
|
|
{
|
|
for (int dz = 0; dz < D1D; ++dz)
|
|
{
|
|
QQD[qx][qy][dz] = 0.0;
|
|
for (int qz = 0; qz < Q1D; ++qz)
|
|
{
|
|
const int q = qx + (qy + qz * Q1D) * Q1D;
|
|
const int k = j >= i ?
|
|
3 - (3-i)*(2-i)/2 + j:
|
|
3 - (3-j)*(2-j)/2 + i;
|
|
// using 6 symmetric values
|
|
const real_t O = Q(q,k,0,e);
|
|
const real_t Bz = B(qz,dz);
|
|
const real_t Gz = G(qz,dz);
|
|
const real_t L = i==2 ? Gz : Bz;
|
|
const real_t R = j==2 ? Gz : Bz;
|
|
QQD[qx][qy][dz] += L * O * R;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// second tensor contraction, along y direction
|
|
for (int qx = 0; qx < Q1D; ++qx)
|
|
{
|
|
for (int dz = 0; dz < D1D; ++dz)
|
|
{
|
|
for (int dy = 0; dy < D1D; ++dy)
|
|
{
|
|
QDD[qx][dy][dz] = 0.0;
|
|
for (int qy = 0; qy < Q1D; ++qy)
|
|
{
|
|
const real_t By = B(qy,dy);
|
|
const real_t Gy = G(qy,dy);
|
|
const real_t L = i==1 ? Gy : By;
|
|
const real_t R = j==1 ? Gy : By;
|
|
QDD[qx][dy][dz] += L * QQD[qx][qy][dz] * R;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// third tensor contraction, along x direction
|
|
for (int dz = 0; dz < D1D; ++dz)
|
|
{
|
|
for (int dy = 0; dy < D1D; ++dy)
|
|
{
|
|
for (int dx = 0; dx < D1D; ++dx)
|
|
{
|
|
real_t temp = 0.0;
|
|
for (int qx = 0; qx < Q1D; ++qx)
|
|
{
|
|
const real_t Bx = B(qx,dx);
|
|
const real_t Gx = G(qx,dx);
|
|
const real_t L = i==0 ? Gx : Bx;
|
|
const real_t R = j==0 ? Gx : Bx;
|
|
temp += L * QDD[qx][dy][dz] * R;
|
|
}
|
|
Y(dx, dy, dz, 0, e) += temp;
|
|
Y(dx, dy, dz, 1, e) += temp;
|
|
Y(dx, dy, dz, 2, e) += temp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
static void PAVectorDiffusionAssembleDiagonal(const int dim,
|
|
const int D1D,
|
|
const int Q1D,
|
|
const int NE,
|
|
const Array<real_t> &B,
|
|
const Array<real_t> &G,
|
|
const Vector &op,
|
|
Vector &y)
|
|
{
|
|
if (dim == 2)
|
|
{
|
|
return PAVectorDiffusionDiagonal2D(NE, B, G, op, y, D1D, Q1D);
|
|
}
|
|
else if (dim == 3)
|
|
{
|
|
return PAVectorDiffusionDiagonal3D(NE, B, G, op, y, D1D, Q1D);
|
|
}
|
|
MFEM_ABORT("Dimension not implemented.");
|
|
}
|
|
|
|
void VectorDiffusionIntegrator::AssembleDiagonalPA(Vector &diag)
|
|
{
|
|
if (DeviceCanUseCeed())
|
|
{
|
|
ceedOp->GetDiagonal(diag);
|
|
}
|
|
else
|
|
{
|
|
MFEM_VERIFY(!VQ && !MQ, "VQ and MQ not supported.");
|
|
PAVectorDiffusionAssembleDiagonal(dim, dofs1D, quad1D, ne,
|
|
maps->B, maps->G,
|
|
pa_data, diag);
|
|
}
|
|
}
|
|
|
|
/*
|
|
// PA Diffusion Apply kernel
|
|
void VectorDiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
|
{
|
|
if (DeviceCanUseCeed())
|
|
{
|
|
ceedOp->AddMult(x, y);
|
|
}
|
|
else
|
|
{
|
|
const int D1D = dofs1D;
|
|
const int Q1D = quad1D;
|
|
const Array<real_t> &B = maps->B;
|
|
const Array<real_t> &G = maps->G;
|
|
const Array<real_t> &Bt = maps->Bt;
|
|
const Array<real_t> &Gt = maps->Gt;
|
|
const Vector &D = pa_data;
|
|
ApplyPAKernels::Run(dim, sdim, D1D, Q1D, ne, B, G, Bt, Gt, D, x, y, D1D,
|
|
Q1D, sdim);
|
|
}
|
|
}
|
|
|
|
/// \cond DO_NOT_DOCUMENT
|
|
|
|
VectorDiffusionIntegrator::ApplyKernelType
|
|
VectorDiffusionIntegrator::ApplyPAKernels::Fallback(int DIM, int, int, int)
|
|
{
|
|
if (DIM == 2) { return internal::PAVectorDiffusionApply2D; }
|
|
else if (DIM == 3) { return internal::PAVectorDiffusionApply3D; }
|
|
else { MFEM_ABORT(""); }
|
|
}
|
|
|
|
VectorDiffusionIntegrator::Kernels::Kernels()
|
|
{
|
|
VectorDiffusionIntegrator::AddSpecialization<2, 3, 2, 2>();
|
|
VectorDiffusionIntegrator::AddSpecialization<2, 3, 3, 3>();
|
|
VectorDiffusionIntegrator::AddSpecialization<2, 3, 4, 4>();
|
|
VectorDiffusionIntegrator::AddSpecialization<2, 3, 5, 5>();
|
|
}
|
|
|
|
/// \endcond DO_NOT_DOCUMENT
|
|
*/
|
|
|
|
} // namespace mfem
|