Files
mfem/fem/integ/bilininteg_interp_pa.cpp
T

1954 lines
57 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 "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../qfunction.hpp"
namespace mfem
{
// Apply to x corresponding to DOFs in H^1 (domain) the (topological) gradient
// to get a dof in H(curl) (range). You can think of the range as the "test" space
// and the domain as the "trial" space, but there's no integration.
static void PAHcurlApplyGradient2D(const int c_dofs1D,
const int o_dofs1D,
const int NE,
const Array<real_t> &B_,
const Array<real_t> &G_,
const Vector &x_,
Vector &y_)
{
auto B = Reshape(B_.Read(), c_dofs1D, c_dofs1D);
auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
auto x = Reshape(x_.Read(), c_dofs1D, c_dofs1D, NE);
auto y = Reshape(y_.ReadWrite(), 2 * c_dofs1D * o_dofs1D, NE);
MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().MAX_D1D &&
o_dofs1D <= c_dofs1D, "");
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
real_t w[MAX_D1D][MAX_D1D];
// horizontal part
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
w[dx][ey] = 0.0;
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w[dx][ey] += B(ey, dy) * x(dx, dy, e);
}
}
}
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int ex = 0; ex < o_dofs1D; ++ex)
{
real_t s = 0.0;
for (int dx = 0; dx < c_dofs1D; ++dx)
{
s += G(ex, dx) * w[dx][ey];
}
const int local_index = ey*o_dofs1D + ex;
y(local_index, e) += s;
}
}
// vertical part
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int ey = 0; ey < o_dofs1D; ++ey)
{
w[dx][ey] = 0.0;
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w[dx][ey] += G(ey, dy) * x(dx, dy, e);
}
}
}
for (int ey = 0; ey < o_dofs1D; ++ey)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
real_t s = 0.0;
for (int dx = 0; dx < c_dofs1D; ++dx)
{
s += B(ex, dx) * w[dx][ey];
}
const int local_index = c_dofs1D * o_dofs1D + ey*c_dofs1D + ex;
y(local_index, e) += s;
}
}
});
}
// Specialization of PAHcurlApplyGradient2D to the case where B is identity
static void PAHcurlApplyGradient2DBId(const int c_dofs1D,
const int o_dofs1D,
const int NE,
const Array<real_t> &G_,
const Vector &x_,
Vector &y_)
{
auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
auto x = Reshape(x_.Read(), c_dofs1D, c_dofs1D, NE);
auto y = Reshape(y_.ReadWrite(), 2 * c_dofs1D * o_dofs1D, NE);
MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().MAX_D1D &&
o_dofs1D <= c_dofs1D, "");
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
real_t w[MAX_D1D][MAX_D1D];
// horizontal part
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
const int dy = ey;
w[dx][ey] = x(dx, dy, e);
}
}
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int ex = 0; ex < o_dofs1D; ++ex)
{
real_t s = 0.0;
for (int dx = 0; dx < c_dofs1D; ++dx)
{
s += G(ex, dx) * w[dx][ey];
}
const int local_index = ey*o_dofs1D + ex;
y(local_index, e) += s;
}
}
// vertical part
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int ey = 0; ey < o_dofs1D; ++ey)
{
w[dx][ey] = 0.0;
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w[dx][ey] += G(ey, dy) * x(dx, dy, e);
}
}
}
for (int ey = 0; ey < o_dofs1D; ++ey)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
const int dx = ex;
const real_t s = w[dx][ey];
const int local_index = c_dofs1D * o_dofs1D + ey*c_dofs1D + ex;
y(local_index, e) += s;
}
}
});
}
static void PAHcurlApplyGradientTranspose2D(
const int c_dofs1D, const int o_dofs1D, const int NE,
const Array<real_t> &B_, const Array<real_t> &G_,
const Vector &x_, Vector &y_)
{
auto B = Reshape(B_.Read(), c_dofs1D, c_dofs1D);
auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
auto x = Reshape(x_.Read(), 2 * c_dofs1D * o_dofs1D, NE);
auto y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, NE);
MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
o_dofs1D <= c_dofs1D, "");
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
real_t w[MAX_D1D][MAX_D1D];
// horizontal part (open x, closed y)
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int ex = 0; ex < o_dofs1D; ++ex)
{
w[dy][ex] = 0.0;
for (int ey = 0; ey < c_dofs1D; ++ey)
{
const int local_index = ey*o_dofs1D + ex;
w[dy][ex] += B(ey, dy) * x(local_index, e);
}
}
}
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
real_t s = 0.0;
for (int ex = 0; ex < o_dofs1D; ++ex)
{
s += G(ex, dx) * w[dy][ex];
}
y(dx, dy, e) += s;
}
}
// vertical part (open y, closed x)
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
w[dy][ex] = 0.0;
for (int ey = 0; ey < o_dofs1D; ++ey)
{
const int local_index = c_dofs1D * o_dofs1D + ey*c_dofs1D + ex;
w[dy][ex] += G(ey, dy) * x(local_index, e);
}
}
}
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
real_t s = 0.0;
for (int ex = 0; ex < c_dofs1D; ++ex)
{
s += B(ex, dx) * w[dy][ex];
}
y(dx, dy, e) += s;
}
}
});
}
// Specialization of PAHcurlApplyGradientTranspose2D to the case where
// B is identity
static void PAHcurlApplyGradientTranspose2DBId(
const int c_dofs1D, const int o_dofs1D, const int NE,
const Array<real_t> &G_,
const Vector &x_, Vector &y_)
{
auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
auto x = Reshape(x_.Read(), 2 * c_dofs1D * o_dofs1D, NE);
auto y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, NE);
MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
o_dofs1D <= c_dofs1D, "");
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
real_t w[MAX_D1D][MAX_D1D];
// horizontal part (open x, closed y)
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int ex = 0; ex < o_dofs1D; ++ex)
{
const int ey = dy;
const int local_index = ey*o_dofs1D + ex;
w[dy][ex] = x(local_index, e);
}
}
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
real_t s = 0.0;
for (int ex = 0; ex < o_dofs1D; ++ex)
{
s += G(ex, dx) * w[dy][ex];
}
y(dx, dy, e) += s;
}
}
// vertical part (open y, closed x)
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
w[dy][ex] = 0.0;
for (int ey = 0; ey < o_dofs1D; ++ey)
{
const int local_index = c_dofs1D * o_dofs1D + ey*c_dofs1D + ex;
w[dy][ex] += G(ey, dy) * x(local_index, e);
}
}
}
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
const int ex = dx;
const real_t s = w[dy][ex];
y(dx, dy, e) += s;
}
}
});
}
static void PAHcurlApplyGradient3D(const int c_dofs1D,
const int o_dofs1D,
const int NE,
const Array<real_t> &B_,
const Array<real_t> &G_,
const Vector &x_,
Vector &y_)
{
auto B = Reshape(B_.Read(), c_dofs1D, c_dofs1D);
auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
auto x = Reshape(x_.Read(), c_dofs1D, c_dofs1D, c_dofs1D, NE);
auto y = Reshape(y_.ReadWrite(), (3 * c_dofs1D * c_dofs1D * o_dofs1D), NE);
MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
o_dofs1D <= c_dofs1D, "");
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
real_t w1[MAX_D1D][MAX_D1D][MAX_D1D];
real_t w2[MAX_D1D][MAX_D1D][MAX_D1D];
// ---
// dofs that point parallel to x-axis (open in x, closed in y, z)
// ---
// contract in z
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w1[dx][dy][ez] = 0.0;
for (int dz = 0; dz < c_dofs1D; ++dz)
{
w1[dx][dy][ez] += B(ez, dz) * x(dx, dy, dz, e);
}
}
}
}
// contract in y
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
w2[dx][ey][ez] = 0.0;
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w2[dx][ey][ez] += B(ey, dy) * w1[dx][dy][ez];
}
}
}
}
// contract in x
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int ex = 0; ex < o_dofs1D; ++ex)
{
real_t s = 0.0;
for (int dx = 0; dx < c_dofs1D; ++dx)
{
s += G(ex, dx) * w2[dx][ey][ez];
}
const int local_index = ez*c_dofs1D*o_dofs1D + ey*o_dofs1D + ex;
y(local_index, e) += s;
}
}
}
// ---
// dofs that point parallel to y-axis (open in y, closed in x, z)
// ---
// contract in z
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w1[dx][dy][ez] = 0.0;
for (int dz = 0; dz < c_dofs1D; ++dz)
{
w1[dx][dy][ez] += B(ez, dz) * x(dx, dy, dz, e);
}
}
}
}
// contract in y
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < o_dofs1D; ++ey)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
w2[dx][ey][ez] = 0.0;
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w2[dx][ey][ez] += G(ey, dy) * w1[dx][dy][ez];
}
}
}
}
// contract in x
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < o_dofs1D; ++ey)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
real_t s = 0.0;
for (int dx = 0; dx < c_dofs1D; ++dx)
{
s += B(ex, dx) * w2[dx][ey][ez];
}
const int local_index = c_dofs1D*c_dofs1D*o_dofs1D +
ez*c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
y(local_index, e) += s;
}
}
}
// ---
// dofs that point parallel to z-axis (open in z, closed in x, y)
// ---
// contract in z
for (int ez = 0; ez < o_dofs1D; ++ez)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w1[dx][dy][ez] = 0.0;
for (int dz = 0; dz < c_dofs1D; ++dz)
{
w1[dx][dy][ez] += G(ez, dz) * x(dx, dy, dz, e);
}
}
}
}
// contract in y
for (int ez = 0; ez < o_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
w2[dx][ey][ez] = 0.0;
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w2[dx][ey][ez] += B(ey, dy) * w1[dx][dy][ez];
}
}
}
}
// contract in x
for (int ez = 0; ez < o_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
real_t s = 0.0;
for (int dx = 0; dx < c_dofs1D; ++dx)
{
s += B(ex, dx) * w2[dx][ey][ez];
}
const int local_index = 2*c_dofs1D*c_dofs1D*o_dofs1D +
ez*c_dofs1D*c_dofs1D + ey*c_dofs1D + ex;
y(local_index, e) += s;
}
}
}
});
}
// Specialization of PAHcurlApplyGradient3D to the case where B is identity
static void PAHcurlApplyGradient3DBId(const int c_dofs1D,
const int o_dofs1D,
const int NE,
const Array<real_t> &G_,
const Vector &x_,
Vector &y_)
{
auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
auto x = Reshape(x_.Read(), c_dofs1D, c_dofs1D, c_dofs1D, NE);
auto y = Reshape(y_.ReadWrite(), (3 * c_dofs1D * c_dofs1D * o_dofs1D), NE);
MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
o_dofs1D <= c_dofs1D, "");
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
real_t w1[MAX_D1D][MAX_D1D][MAX_D1D];
real_t w2[MAX_D1D][MAX_D1D][MAX_D1D];
// ---
// dofs that point parallel to x-axis (open in x, closed in y, z)
// ---
// contract in z
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
const int dz = ez;
w1[dx][dy][ez] = x(dx, dy, dz, e);
}
}
}
// contract in y
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
const int dy = ey;
w2[dx][ey][ez] = w1[dx][dy][ez];
}
}
}
// contract in x
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int ex = 0; ex < o_dofs1D; ++ex)
{
real_t s = 0.0;
for (int dx = 0; dx < c_dofs1D; ++dx)
{
s += G(ex, dx) * w2[dx][ey][ez];
}
const int local_index = ez*c_dofs1D*o_dofs1D + ey*o_dofs1D + ex;
y(local_index, e) += s;
}
}
}
// ---
// dofs that point parallel to y-axis (open in y, closed in x, z)
// ---
// contract in z
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
const int dz = ez;
w1[dx][dy][ez] = x(dx, dy, dz, e);
}
}
}
// contract in y
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < o_dofs1D; ++ey)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
w2[dx][ey][ez] = 0.0;
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w2[dx][ey][ez] += G(ey, dy) * w1[dx][dy][ez];
}
}
}
}
// contract in x
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < o_dofs1D; ++ey)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
const int dx = ex;
const real_t s = w2[dx][ey][ez];
const int local_index = c_dofs1D*c_dofs1D*o_dofs1D +
ez*c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
y(local_index, e) += s;
}
}
}
// ---
// dofs that point parallel to z-axis (open in z, closed in x, y)
// ---
// contract in z
for (int ez = 0; ez < o_dofs1D; ++ez)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w1[dx][dy][ez] = 0.0;
for (int dz = 0; dz < c_dofs1D; ++dz)
{
w1[dx][dy][ez] += G(ez, dz) * x(dx, dy, dz, e);
}
}
}
}
// contract in y
for (int ez = 0; ez < o_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
const int dy = ey;
w2[dx][ey][ez] = w1[dx][dy][ez];
}
}
}
// contract in x
for (int ez = 0; ez < o_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
const int dx = ex;
const real_t s = w2[dx][ey][ez];
const int local_index = 2*c_dofs1D*c_dofs1D*o_dofs1D +
ez*c_dofs1D*c_dofs1D + ey*c_dofs1D + ex;
y(local_index, e) += s;
}
}
}
});
}
static void PAHcurlApplyGradientTranspose3D(
const int c_dofs1D, const int o_dofs1D, const int NE,
const Array<real_t> &B_, const Array<real_t> &G_,
const Vector &x_, Vector &y_)
{
auto B = Reshape(B_.Read(), c_dofs1D, c_dofs1D);
auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
auto x = Reshape(x_.Read(), (3 * c_dofs1D * c_dofs1D * o_dofs1D), NE);
auto y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, c_dofs1D, NE);
MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
o_dofs1D <= c_dofs1D, "");
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
real_t w1[MAX_D1D][MAX_D1D][MAX_D1D];
real_t w2[MAX_D1D][MAX_D1D][MAX_D1D];
// ---
// dofs that point parallel to x-axis (open in x, closed in y, z)
// ---
// contract in z
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int ex = 0; ex < o_dofs1D; ++ex)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
w1[ex][ey][dz] = 0.0;
for (int ez = 0; ez < c_dofs1D; ++ez)
{
const int local_index = ez*c_dofs1D*o_dofs1D + ey*o_dofs1D + ex;
w1[ex][ey][dz] += B(ez, dz) * x(local_index, e);
}
}
}
}
// contract in y
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int ex = 0; ex < o_dofs1D; ++ex)
{
w2[ex][dy][dz] = 0.0;
for (int ey = 0; ey < c_dofs1D; ++ey)
{
w2[ex][dy][dz] += B(ey, dy) * w1[ex][ey][dz];
}
}
}
}
// contract in x
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
real_t s = 0.0;
for (int ex = 0; ex < o_dofs1D; ++ex)
{
s += G(ex, dx) * w2[ex][dy][dz];
}
y(dx, dy, dz, e) += s;
}
}
}
// ---
// dofs that point parallel to y-axis (open in y, closed in x, z)
// ---
// contract in z
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
for (int ey = 0; ey < o_dofs1D; ++ey)
{
w1[ex][ey][dz] = 0.0;
for (int ez = 0; ez < c_dofs1D; ++ez)
{
const int local_index = c_dofs1D*c_dofs1D*o_dofs1D +
ez*c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
w1[ex][ey][dz] += B(ez, dz) * x(local_index, e);
}
}
}
}
// contract in y
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
w2[ex][dy][dz] = 0.0;
for (int ey = 0; ey < o_dofs1D; ++ey)
{
w2[ex][dy][dz] += G(ey, dy) * w1[ex][ey][dz];
}
}
}
}
// contract in x
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
real_t s = 0.0;
for (int ex = 0; ex < c_dofs1D; ++ex)
{
s += B(ex, dx) * w2[ex][dy][dz];
}
y(dx, dy, dz, e) += s;
}
}
}
// ---
// dofs that point parallel to z-axis (open in z, closed in x, y)
// ---
// contract in z
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
w1[ex][ey][dz] = 0.0;
for (int ez = 0; ez < o_dofs1D; ++ez)
{
const int local_index = 2*c_dofs1D*c_dofs1D*o_dofs1D +
ez*c_dofs1D*c_dofs1D + ey*c_dofs1D + ex;
w1[ex][ey][dz] += G(ez, dz) * x(local_index, e);
}
}
}
}
// contract in y
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
w2[ex][dy][dz] = 0.0;
for (int ey = 0; ey < c_dofs1D; ++ey)
{
w2[ex][dy][dz] += B(ey, dy) * w1[ex][ey][dz];
}
}
}
}
// contract in x
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
real_t s = 0.0;
for (int ex = 0; ex < c_dofs1D; ++ex)
{
s += B(ex, dx) * w2[ex][dy][dz];
}
y(dx, dy, dz, e) += s;
}
}
}
});
}
// Specialization of PAHcurlApplyGradientTranspose3D to the case where
// B is identity
static void PAHcurlApplyGradientTranspose3DBId(
const int c_dofs1D, const int o_dofs1D, const int NE,
const Array<real_t> &G_,
const Vector &x_, Vector &y_)
{
auto G = Reshape(G_.Read(), o_dofs1D, c_dofs1D);
auto x = Reshape(x_.Read(), (3 * c_dofs1D * c_dofs1D * o_dofs1D), NE);
auto y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, c_dofs1D, NE);
MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
o_dofs1D <= c_dofs1D, "");
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
real_t w1[MAX_D1D][MAX_D1D][MAX_D1D];
real_t w2[MAX_D1D][MAX_D1D][MAX_D1D];
// ---
// dofs that point parallel to x-axis (open in x, closed in y, z)
// ---
// contract in z
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int ex = 0; ex < o_dofs1D; ++ex)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
const int ez = dz;
const int local_index = ez*c_dofs1D*o_dofs1D + ey*o_dofs1D + ex;
w1[ex][ey][dz] = x(local_index, e);
}
}
}
// contract in y
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int ex = 0; ex < o_dofs1D; ++ex)
{
const int ey = dy;
w2[ex][dy][dz] = w1[ex][ey][dz];
}
}
}
// contract in x
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
real_t s = 0.0;
for (int ex = 0; ex < o_dofs1D; ++ex)
{
s += G(ex, dx) * w2[ex][dy][dz];
}
y(dx, dy, dz, e) += s;
}
}
}
// ---
// dofs that point parallel to y-axis (open in y, closed in x, z)
// ---
// contract in z
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
for (int ey = 0; ey < o_dofs1D; ++ey)
{
const int ez = dz;
const int local_index = c_dofs1D*c_dofs1D*o_dofs1D +
ez*c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
w1[ex][ey][dz] = x(local_index, e);
}
}
}
// contract in y
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
w2[ex][dy][dz] = 0.0;
for (int ey = 0; ey < o_dofs1D; ++ey)
{
w2[ex][dy][dz] += G(ey, dy) * w1[ex][ey][dz];
}
}
}
}
// contract in x
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
const int ex = dx;
real_t s = w2[ex][dy][dz];
y(dx, dy, dz, e) += s;
}
}
}
// ---
// dofs that point parallel to z-axis (open in z, closed in x, y)
// ---
// contract in z
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
w1[ex][ey][dz] = 0.0;
for (int ez = 0; ez < o_dofs1D; ++ez)
{
const int local_index = 2*c_dofs1D*c_dofs1D*o_dofs1D +
ez*c_dofs1D*c_dofs1D + ey*c_dofs1D + ex;
w1[ex][ey][dz] += G(ez, dz) * x(local_index, e);
}
}
}
}
// contract in y
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
const int ey = dy;
w2[ex][dy][dz] = w1[ex][ey][dz];
}
}
}
// contract in x
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
const int ex = dx;
real_t s = w2[ex][dy][dz];
y(dx, dy, dz, e) += s;
}
}
}
});
}
void GradientInterpolator::AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes)
{
// Assumes tensor-product elements, with a vector test space and H^1 trial space.
Mesh *mesh = trial_fes.GetMesh();
const FiniteElement *trial_fel = trial_fes.GetTypicalFE();
const FiniteElement *test_fel = test_fes.GetTypicalFE();
const NodalTensorFiniteElement *trial_el =
dynamic_cast<const NodalTensorFiniteElement*>(trial_fel);
MFEM_VERIFY(trial_el != NULL, "Only NodalTensorFiniteElement is supported!");
const VectorTensorFiniteElement *test_el =
dynamic_cast<const VectorTensorFiniteElement*>(test_fel);
MFEM_VERIFY(test_el != NULL, "Only VectorTensorFiniteElement is supported!");
const int dims = trial_el->GetDim();
MFEM_VERIFY(dims == 2 || dims == 3, "Bad dimension!");
dim = mesh->Dimension();
MFEM_VERIFY(dim == 2 || dim == 3, "Bad dimension!");
MFEM_VERIFY(trial_el->GetOrder() == test_el->GetOrder(),
"Orders do not match!");
ne = trial_fes.GetNE();
const int order = trial_el->GetOrder();
dofquad_fe = new H1_SegmentElement(order, trial_el->GetBasisType());
mfem::QuadratureFunctions1D qf1d;
mfem::IntegrationRule closed_ir;
closed_ir.SetSize(order + 1);
qf1d.GaussLobatto(order + 1, &closed_ir);
mfem::IntegrationRule open_ir;
open_ir.SetSize(order);
qf1d.GaussLegendre(order, &open_ir);
maps_O_C = &dofquad_fe->GetDofToQuad(open_ir, DofToQuad::TENSOR);
o_dofs1D = maps_O_C->nqpt;
if (trial_el->GetBasisType() == BasisType::GaussLobatto)
{
B_id = true;
c_dofs1D = maps_O_C->ndof;
}
else
{
B_id = false;
maps_C_C = &dofquad_fe->GetDofToQuad(closed_ir, DofToQuad::TENSOR);
c_dofs1D = maps_C_C->nqpt;
}
}
void GradientInterpolator::AddMultPA(const Vector &x, Vector &y) const
{
if (dim == 3)
{
if (B_id)
{
PAHcurlApplyGradient3DBId(c_dofs1D, o_dofs1D, ne,
maps_O_C->G, x, y);
}
else
{
PAHcurlApplyGradient3D(c_dofs1D, o_dofs1D, ne, maps_C_C->B,
maps_O_C->G, x, y);
}
}
else if (dim == 2)
{
if (B_id)
{
PAHcurlApplyGradient2DBId(c_dofs1D, o_dofs1D, ne,
maps_O_C->G, x, y);
}
else
{
PAHcurlApplyGradient2D(c_dofs1D, o_dofs1D, ne, maps_C_C->B, maps_O_C->G,
x, y);
}
}
else
{
mfem_error("Bad dimension!");
}
}
void GradientInterpolator::AddMultTransposePA(const Vector &x, Vector &y) const
{
if (dim == 3)
{
if (B_id)
{
PAHcurlApplyGradientTranspose3DBId(c_dofs1D, o_dofs1D, ne,
maps_O_C->G, x, y);
}
else
{
PAHcurlApplyGradientTranspose3D(c_dofs1D, o_dofs1D, ne, maps_C_C->B,
maps_O_C->G, x, y);
}
}
else if (dim == 2)
{
if (B_id)
{
PAHcurlApplyGradientTranspose2DBId(c_dofs1D, o_dofs1D, ne,
maps_O_C->G, x, y);
}
else
{
PAHcurlApplyGradientTranspose2D(c_dofs1D, o_dofs1D, ne, maps_C_C->B,
maps_O_C->G, x, y);
}
}
else
{
mfem_error("Bad dimension!");
}
}
static void PAHcurlVecH1IdentityApply2D(const int c_dofs1D,
const int o_dofs1D,
const int NE,
const Array<real_t> &Bclosed,
const Array<real_t> &Bopen,
const Vector &pa_data,
const Vector &x_,
Vector &y_)
{
auto Bc = Reshape(Bclosed.Read(), c_dofs1D, c_dofs1D);
auto Bo = Reshape(Bopen.Read(), o_dofs1D, c_dofs1D);
auto x = Reshape(x_.Read(), c_dofs1D, c_dofs1D, 2, NE);
auto y = Reshape(y_.ReadWrite(), (2 * c_dofs1D * o_dofs1D), NE);
auto vk = Reshape(pa_data.Read(), 2, (2 * c_dofs1D * o_dofs1D), NE);
MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
o_dofs1D <= c_dofs1D, "");
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
real_t w[2][MAX_D1D][MAX_D1D];
// dofs that point parallel to x-axis (open in x, closed in y)
// contract in y
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int j=0; j<2; ++j)
{
w[j][dx][ey] = 0.0;
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w[j][dx][ey] += Bc(ey, dy) * x(dx, dy, j, e);
}
}
}
}
// contract in x
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int ex = 0; ex < o_dofs1D; ++ex)
{
for (int j=0; j<2; ++j)
{
real_t s = 0.0;
for (int dx = 0; dx < c_dofs1D; ++dx)
{
s += Bo(ex, dx) * w[j][dx][ey];
}
const int local_index = ey*o_dofs1D + ex;
y(local_index, e) += s * vk(j, local_index, e);
}
}
}
// dofs that point parallel to y-axis (open in y, closed in x)
// contract in y
for (int ey = 0; ey < o_dofs1D; ++ey)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int j=0; j<2; ++j)
{
w[j][dx][ey] = 0.0;
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w[j][dx][ey] += Bo(ey, dy) * x(dx, dy, j, e);
}
}
}
}
// contract in x
for (int ey = 0; ey < o_dofs1D; ++ey)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
for (int j=0; j<2; ++j)
{
real_t s = 0.0;
for (int dx = 0; dx < c_dofs1D; ++dx)
{
s += Bc(ex, dx) * w[j][dx][ey];
}
const int local_index = c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
y(local_index, e) += s * vk(j, local_index, e);
}
}
}
});
}
static void PAHcurlVecH1IdentityApplyTranspose2D(const int c_dofs1D,
const int o_dofs1D,
const int NE,
const Array<real_t> &Bclosed,
const Array<real_t> &Bopen,
const Vector &pa_data,
const Vector &x_,
Vector &y_)
{
auto Bc = Reshape(Bclosed.Read(), c_dofs1D, c_dofs1D);
auto Bo = Reshape(Bopen.Read(), o_dofs1D, c_dofs1D);
auto x = Reshape(x_.Read(), (2 * c_dofs1D * o_dofs1D), NE);
auto y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, 2, NE);
auto vk = Reshape(pa_data.Read(), 2, (2 * c_dofs1D * o_dofs1D), NE);
MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D &&
o_dofs1D <= c_dofs1D, "");
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
real_t w[2][MAX_D1D][MAX_D1D];
// dofs that point parallel to x-axis (open in x, closed in y)
// contract in x
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int j=0; j<2; ++j) { w[j][dx][ey] = 0.0; }
}
for (int ex = 0; ex < o_dofs1D; ++ex)
{
const int local_index = ey*o_dofs1D + ex;
const real_t xd = x(local_index, e);
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int j=0; j<2; ++j)
{
w[j][dx][ey] += Bo(ex, dx) * xd * vk(j, local_index, e);
}
}
}
}
// contract in y
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int j=0; j<2; ++j)
{
real_t s = 0.0;
for (int ey = 0; ey < c_dofs1D; ++ey)
{
s += w[j][dx][ey] * Bc(ey, dy);
}
y(dx, dy, j, e) += s;
}
}
}
// dofs that point parallel to y-axis (open in y, closed in x)
// contract in x
for (int ey = 0; ey < o_dofs1D; ++ey)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int j=0; j<2; ++j) { w[j][dx][ey] = 0.0; }
}
for (int ex = 0; ex < c_dofs1D; ++ex)
{
const int local_index = c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
const real_t xd = x(local_index, e);
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int j=0; j<2; ++j)
{
w[j][dx][ey] += Bc(ex, dx) * xd * vk(j, local_index, e);
}
}
}
}
// contract in y
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int j=0; j<2; ++j)
{
real_t s = 0.0;
for (int ey = 0; ey < o_dofs1D; ++ey)
{
s += w[j][dx][ey] * Bo(ey, dy);
}
y(dx, dy, j, e) += s;
}
}
}
});
}
static void PAHcurlVecH1IdentityApply3D(const int c_dofs1D,
const int o_dofs1D,
const int NE,
const Array<real_t> &Bclosed,
const Array<real_t> &Bopen,
const Vector &pa_data,
const Vector &x_,
Vector &y_)
{
auto Bc = Reshape(Bclosed.Read(), c_dofs1D, c_dofs1D);
auto Bo = Reshape(Bopen.Read(), o_dofs1D, c_dofs1D);
auto x = Reshape(x_.Read(), c_dofs1D, c_dofs1D, c_dofs1D, 3, NE);
auto y = Reshape(y_.ReadWrite(), (3 * c_dofs1D * c_dofs1D * o_dofs1D), NE);
auto vk = Reshape(pa_data.Read(), 3, (3 * c_dofs1D * c_dofs1D * o_dofs1D),
NE);
MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().MAX_D1D &&
o_dofs1D <= c_dofs1D, "");
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
real_t w1[3][MAX_D1D][MAX_D1D][MAX_D1D];
real_t w2[3][MAX_D1D][MAX_D1D][MAX_D1D];
// dofs that point parallel to x-axis (open in x, closed in y, z)
// contract in z
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int j=0; j<3; ++j)
{
w1[j][dx][dy][ez] = 0.0;
for (int dz = 0; dz < c_dofs1D; ++dz)
{
w1[j][dx][dy][ez] += Bc(ez, dz) * x(dx, dy, dz, j, e);
}
}
}
}
}
// contract in y
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int j=0; j<3; ++j)
{
w2[j][dx][ey][ez] = 0.0;
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w2[j][dx][ey][ez] += Bc(ey, dy) * w1[j][dx][dy][ez];
}
}
}
}
}
// contract in x
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int ex = 0; ex < o_dofs1D; ++ex)
{
for (int j=0; j<3; ++j)
{
real_t s = 0.0;
for (int dx = 0; dx < c_dofs1D; ++dx)
{
s += Bo(ex, dx) * w2[j][dx][ey][ez];
}
const int local_index = ez*c_dofs1D*o_dofs1D + ey*o_dofs1D + ex;
y(local_index, e) += s * vk(j, local_index, e);
}
}
}
}
// dofs that point parallel to y-axis (open in y, closed in x, z)
// contract in z
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int j=0; j<3; ++j)
{
w1[j][dx][dy][ez] = 0.0;
for (int dz = 0; dz < c_dofs1D; ++dz)
{
w1[j][dx][dy][ez] += Bc(ez, dz) * x(dx, dy, dz, j, e);
}
}
}
}
}
// contract in y
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < o_dofs1D; ++ey)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int j=0; j<3; ++j)
{
w2[j][dx][ey][ez] = 0.0;
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w2[j][dx][ey][ez] += Bo(ey, dy) * w1[j][dx][dy][ez];
}
}
}
}
}
// contract in x
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < o_dofs1D; ++ey)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
for (int j=0; j<3; ++j)
{
real_t s = 0.0;
for (int dx = 0; dx < c_dofs1D; ++dx)
{
s += Bc(ex, dx) * w2[j][dx][ey][ez];
}
const int local_index = c_dofs1D*c_dofs1D*o_dofs1D +
ez*c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
y(local_index, e) += s * vk(j, local_index, e);
}
}
}
}
// dofs that point parallel to z-axis (open in z, closed in x, y)
// contract in z
for (int ez = 0; ez < o_dofs1D; ++ez)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int j=0; j<3; ++j)
{
w1[j][dx][dy][ez] = 0.0;
for (int dz = 0; dz < c_dofs1D; ++dz)
{
w1[j][dx][dy][ez] += Bo(ez, dz) * x(dx, dy, dz, j, e);
}
}
}
}
}
// contract in y
for (int ez = 0; ez < o_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int j=0; j<3; ++j)
{
w2[j][dx][ey][ez] = 0.0;
for (int dy = 0; dy < c_dofs1D; ++dy)
{
w2[j][dx][ey][ez] += Bc(ey, dy) * w1[j][dx][dy][ez];
}
}
}
}
}
// contract in x
for (int ez = 0; ez < o_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int ex = 0; ex < c_dofs1D; ++ex)
{
for (int j=0; j<3; ++j)
{
real_t s = 0.0;
for (int dx = 0; dx < c_dofs1D; ++dx)
{
s += Bc(ex, dx) * w2[j][dx][ey][ez];
}
const int local_index = 2*c_dofs1D*c_dofs1D*o_dofs1D +
ez*c_dofs1D*c_dofs1D + ey*c_dofs1D + ex;
y(local_index, e) += s * vk(j, local_index, e);
}
}
}
}
});
}
static void PAHcurlVecH1IdentityApplyTranspose3D(const int c_dofs1D,
const int o_dofs1D,
const int NE,
const Array<real_t> &Bclosed,
const Array<real_t> &Bopen,
const Vector &pa_data,
const Vector &x_,
Vector &y_)
{
auto Bc = Reshape(Bclosed.Read(), c_dofs1D, c_dofs1D);
auto Bo = Reshape(Bopen.Read(), o_dofs1D, c_dofs1D);
auto x = Reshape(x_.Read(), (3 * c_dofs1D * c_dofs1D * o_dofs1D), NE);
auto y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, c_dofs1D, 3, NE);
auto vk = Reshape(pa_data.Read(), 3, (3 * c_dofs1D * c_dofs1D * o_dofs1D),
NE);
MFEM_VERIFY(c_dofs1D <= DeviceDofQuadLimits::Get().MAX_D1D &&
o_dofs1D <= c_dofs1D, "");
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
constexpr static int MAX_D1D = DofQuadLimits::HCURL_MAX_D1D;
real_t w1[3][MAX_D1D][MAX_D1D][MAX_D1D];
real_t w2[3][MAX_D1D][MAX_D1D][MAX_D1D];
// dofs that point parallel to x-axis (open in x, closed in y, z)
// contract in x
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int j=0; j<3; ++j)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
w2[j][dx][ey][ez] = 0.0;
}
for (int ex = 0; ex < o_dofs1D; ++ex)
{
const int local_index = ez*c_dofs1D*o_dofs1D + ey*o_dofs1D + ex;
const real_t xv = x(local_index, e) * vk(j, local_index, e);
for (int dx = 0; dx < c_dofs1D; ++dx)
{
w2[j][dx][ey][ez] += xv * Bo(ex, dx);
}
}
}
}
}
// contract in y
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int j=0; j<3; ++j)
{
w1[j][dx][dy][ez] = 0.0;
for (int ey = 0; ey < c_dofs1D; ++ey)
{
w1[j][dx][dy][ez] += w2[j][dx][ey][ez] * Bc(ey, dy);
}
}
}
}
}
// contract in z
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int j=0; j<3; ++j)
{
real_t s = 0.0;
for (int ez = 0; ez < c_dofs1D; ++ez)
{
s += w1[j][dx][dy][ez] * Bc(ez, dz);
}
y(dx, dy, dz, j, e) += s;
}
}
}
}
// dofs that point parallel to y-axis (open in y, closed in x, z)
// contract in x
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int ey = 0; ey < o_dofs1D; ++ey)
{
for (int j=0; j<3; ++j)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
w2[j][dx][ey][ez] = 0.0;
}
for (int ex = 0; ex < c_dofs1D; ++ex)
{
const int local_index = c_dofs1D*c_dofs1D*o_dofs1D +
ez*c_dofs1D*o_dofs1D + ey*c_dofs1D + ex;
const real_t xv = x(local_index, e) * vk(j, local_index, e);
for (int dx = 0; dx < c_dofs1D; ++dx)
{
w2[j][dx][ey][ez] += xv * Bc(ex, dx);
}
}
}
}
}
// contract in y
for (int ez = 0; ez < c_dofs1D; ++ez)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int j=0; j<3; ++j)
{
w1[j][dx][dy][ez] = 0.0;
for (int ey = 0; ey < o_dofs1D; ++ey)
{
w1[j][dx][dy][ez] += w2[j][dx][ey][ez] * Bo(ey, dy);
}
}
}
}
}
// contract in z
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int j=0; j<3; ++j)
{
real_t s = 0.0;
for (int ez = 0; ez < c_dofs1D; ++ez)
{
s += w1[j][dx][dy][ez] * Bc(ez, dz);
}
y(dx, dy, dz, j, e) += s;
}
}
}
}
// dofs that point parallel to z-axis (open in z, closed in x, y)
// contract in x
for (int ez = 0; ez < o_dofs1D; ++ez)
{
for (int ey = 0; ey < c_dofs1D; ++ey)
{
for (int j=0; j<3; ++j)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
w2[j][dx][ey][ez] = 0.0;
}
for (int ex = 0; ex < c_dofs1D; ++ex)
{
const int local_index = 2*c_dofs1D*c_dofs1D*o_dofs1D +
ez*c_dofs1D*c_dofs1D + ey*c_dofs1D + ex;
const real_t xv = x(local_index, e) * vk(j, local_index, e);
for (int dx = 0; dx < c_dofs1D; ++dx)
{
w2[j][dx][ey][ez] += xv * Bc(ex, dx);
}
}
}
}
}
// contract in y
for (int ez = 0; ez < o_dofs1D; ++ez)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int j=0; j<3; ++j)
{
w1[j][dx][dy][ez] = 0.0;
for (int ey = 0; ey < c_dofs1D; ++ey)
{
w1[j][dx][dy][ez] += w2[j][dx][ey][ez] * Bc(ey, dy);
}
}
}
}
}
// contract in z
for (int dx = 0; dx < c_dofs1D; ++dx)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dz = 0; dz < c_dofs1D; ++dz)
{
for (int j=0; j<3; ++j)
{
real_t s = 0.0;
for (int ez = 0; ez < o_dofs1D; ++ez)
{
s += w1[j][dx][dy][ez] * Bo(ez, dz);
}
y(dx, dy, dz, j, e) += s;
}
}
}
}
});
}
void IdentityInterpolator::AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes)
{
// Assumes tensor-product elements, with a vector test space and H^1 trial space.
Mesh *mesh = trial_fes.GetMesh();
const FiniteElement *trial_fel = trial_fes.GetTypicalFE();
const FiniteElement *test_fel = test_fes.GetTypicalFE();
const NodalTensorFiniteElement *trial_el =
dynamic_cast<const NodalTensorFiniteElement*>(trial_fel);
MFEM_VERIFY(trial_el != NULL, "Only NodalTensorFiniteElement is supported!");
const VectorTensorFiniteElement *test_el =
dynamic_cast<const VectorTensorFiniteElement*>(test_fel);
MFEM_VERIFY(test_el != NULL, "Only VectorTensorFiniteElement is supported!");
const int dims = trial_el->GetDim();
MFEM_VERIFY(dims == 2 || dims == 3, "");
dim = mesh->Dimension();
MFEM_VERIFY(dim == 2 || dim == 3, "");
MFEM_VERIFY(trial_el->GetOrder() == test_el->GetOrder(), "");
MFEM_VERIFY(vdim == 1, "vdim != 1 with PA is not supported yet!");
ne = trial_fes.GetNE();
const int order = trial_el->GetOrder();
dofquad_fe.reset(new H1_SegmentElement(order));
mfem::QuadratureFunctions1D qf1d;
mfem::IntegrationRule closed_ir;
closed_ir.SetSize(order + 1);
qf1d.GaussLobatto(order + 1, &closed_ir);
mfem::IntegrationRule open_ir;
open_ir.SetSize(order);
qf1d.GaussLegendre(order, &open_ir);
maps_C_C = &dofquad_fe->GetDofToQuad(closed_ir, DofToQuad::TENSOR);
maps_O_C = &dofquad_fe->GetDofToQuad(open_ir, DofToQuad::TENSOR);
o_dofs1D = maps_O_C->nqpt;
c_dofs1D = maps_C_C->nqpt;
MFEM_VERIFY(maps_O_C->ndof == c_dofs1D &&
maps_C_C->ndof == c_dofs1D, "Discrepancy in the number of DOFs");
const int ndof_test = (dim == 3) ? 3 * c_dofs1D * c_dofs1D * o_dofs1D
: 2 * c_dofs1D * o_dofs1D;
const IntegrationRule & Nodes = test_el->GetNodes();
pa_data.SetSize(dim * ndof_test * ne, Device::GetMemoryType());
auto op = Reshape(pa_data.HostWrite(), dim, ndof_test, ne);
const Array<int> &dofmap = test_el->GetDofMap();
if (dim == 3)
{
// Note that ND_HexahedronElement uses 6 vectors in tk rather than 3, with
// the last 3 having negative signs. Here the signs are all positive, as
// signs are applied in ElementRestriction.
const real_t tk[9] = { 1.,0.,0., 0.,1.,0., 0.,0.,1. };
for (int c=0; c<3; ++c)
{
for (int i=0; i<ndof_test/3; ++i)
{
const int d = (c*ndof_test/3) + i;
// ND_HexahedronElement sets dof2tk = (dofmap < 0) ? 3+c : c, but here
// no signs should be applied due to ElementRestriction.
const int dof2tk = c;
const int id = (dofmap[d] >= 0) ? dofmap[d] : -1 - dofmap[d];
for (int e=0; e<ne; ++e)
{
real_t v[3];
ElementTransformation *tr = mesh->GetElementTransformation(e);
tr->SetIntPoint(&Nodes.IntPoint(id));
tr->Jacobian().Mult(tk + dof2tk*dim, v);
for (int j=0; j<3; ++j)
{
op(j,d,e) = v[j];
}
}
}
}
}
else // 2D case
{
const real_t tk[4] = { 1.,0., 0.,1. };
for (int c=0; c<2; ++c)
{
for (int i=0; i<ndof_test/2; ++i)
{
const int d = (c*ndof_test/2) + i;
// ND_QuadrilateralElement sets dof2tk = (dofmap < 0) ? 2+c : c, but here
// no signs should be applied due to ElementRestriction.
const int dof2tk = c;
const int id = (dofmap[d] >= 0) ? dofmap[d] : -1 - dofmap[d];
for (int e=0; e<ne; ++e)
{
real_t v[2];
ElementTransformation *tr = mesh->GetElementTransformation(e);
tr->SetIntPoint(&Nodes.IntPoint(id));
tr->Jacobian().Mult(tk + dof2tk*dim, v);
for (int j=0; j<2; ++j)
{
op(j,d,e) = v[j];
}
}
}
}
}
}
void IdentityInterpolator::AddMultPA(const Vector &x, Vector &y) const
{
if (dim == 3)
{
PAHcurlVecH1IdentityApply3D(c_dofs1D, o_dofs1D, ne, maps_C_C->B, maps_O_C->B,
pa_data, x, y);
}
else if (dim == 2)
{
PAHcurlVecH1IdentityApply2D(c_dofs1D, o_dofs1D, ne, maps_C_C->B, maps_O_C->B,
pa_data, x, y);
}
else
{
mfem_error("Bad dimension!");
}
}
void IdentityInterpolator::AddMultTransposePA(const Vector &x, Vector &y) const
{
if (dim == 3)
{
PAHcurlVecH1IdentityApplyTranspose3D(c_dofs1D, o_dofs1D, ne, maps_C_C->B,
maps_O_C->B, pa_data, x, y);
}
else if (dim == 2)
{
PAHcurlVecH1IdentityApplyTranspose2D(c_dofs1D, o_dofs1D, ne, maps_C_C->B,
maps_O_C->B, pa_data, x, y);
}
else
{
mfem_error("Bad dimension!");
}
}
} // namespace mfem