Skip to content

Commit c2bc9a8

Browse files
authored
Merge pull request #1262 from CEED/jeremy/grad-at-points
Add CEED_EVAL_GRAD support for CeedBasisApplyAtPoints
2 parents 0814d5a + 5ea233a commit c2bc9a8

4 files changed

Lines changed: 307 additions & 37 deletions

File tree

doc/sphinx/source/releasenotes.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ For example, `CeedOperatorContextGetFieldLabel` was renamed to `CeedOperatorGetC
1717
- Update `/cpu/self/memcheck/*` backends to help verify `CeedVector` array access assumptions and `CeedQFunction` user output assumptions.
1818
- Update {c:func}`CeedOperatorLinearAssembleDiagonal` to provide default implementation that supports `CeedOperator` with multiple active bases.
1919
- Added Sycl backends `/gpu/sycl/ref` and `/gpu/sycl/shared`
20+
- Added {c:func}`CeedBasisApplyAtPoints` for evalution of values and derivaties at arbitrary points inside elements
2021

2122
### Examples
2223

@@ -60,7 +61,7 @@ This issue will be fixed in a future OCCA release.
6061
- Support for primitive variables for more accurate boundary layers and all-speed flow.
6162
- Added $YZ\beta$ shock capturing scheme and Shock Tube example.
6263
- Added Channel example, with comparison to analytic solutions.
63-
- Added Flat Plate with boundary layer mesh and compressible Blasius inflow condition based on Chebyshev collocation solution of the Blasius equations.
64+
- Added Flat Plate with boundary layer mesh and compressible Blasius inflow condition based on Chebyshev collocation solution of the Blasius equations.
6465
- Added strong and weak synthetic turbulence generation (STG) inflow boundary conditions.
6566
- Added "freestream" boundary conditions based on HLLC Riemann solver.
6667
- Automated stabilization coefficients for different basis degree.

interface/ceed-basis.c

Lines changed: 101 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,53 @@ const CeedBasis CEED_BASIS_COLLOCATED = &ceed_basis_collocated;
3434
/// @addtogroup CeedBasisDeveloper
3535
/// @{
3636

37+
/**
38+
@brief Compute Chebyshev polynomial values at a point
39+
40+
@param[in] x Coordinate to evaluate Chebyshev polynomials at
41+
@param[in] n Number of Chebyshev polynomials to evaluate, n >= 2
42+
@param[out] chebyshev_x Array of Chebyshev polynomial values
43+
44+
@return An error code: 0 - success, otherwise - failure
45+
46+
@ref Developer
47+
**/
48+
static int CeedChebyshevPolynomialsAtPoint(CeedScalar x, CeedInt n, CeedScalar *chebyshev_x) {
49+
chebyshev_x[0] = 1.0;
50+
chebyshev_x[1] = 2 * x;
51+
for (CeedInt i = 2; i < n; i++) chebyshev_x[i] = 2 * x * chebyshev_x[i - 1] - chebyshev_x[i - 2];
52+
53+
return CEED_ERROR_SUCCESS;
54+
}
55+
56+
/**
57+
@brief Compute values of the derivative of Chebyshev polynomials at a point
58+
59+
@param[in] x Coordinate to evaluate derivative of Chebyshev polynomials at
60+
@param[in] n Number of Chebyshev polynomials to evaluate, n >= 2
61+
@param[out] chebyshev_x Array of Chebyshev polynomial derivative values
62+
63+
@return An error code: 0 - success, otherwise - failure
64+
65+
@ref Developer
66+
**/
67+
static int CeedChebyshevDerivativeAtPoint(CeedScalar x, CeedInt n, CeedScalar *chebyshev_dx) {
68+
CeedScalar chebyshev_x[3];
69+
70+
chebyshev_x[1] = 1.0;
71+
chebyshev_x[2] = 2 * x;
72+
chebyshev_dx[0] = 0.0;
73+
chebyshev_dx[1] = 2.0;
74+
for (CeedInt i = 2; i < n; i++) {
75+
chebyshev_x[0] = chebyshev_x[1];
76+
chebyshev_x[1] = chebyshev_x[2];
77+
chebyshev_x[2] = 2 * x * chebyshev_x[1] - chebyshev_x[0];
78+
chebyshev_dx[i] = 2 * x * chebyshev_dx[i - 1] + 2 * chebyshev_x[1] - chebyshev_dx[i - 2];
79+
}
80+
81+
return CEED_ERROR_SUCCESS;
82+
}
83+
3784
/**
3885
@brief Compute Householder reflection
3986
@@ -1438,6 +1485,9 @@ int CeedBasisApplyAtPoints(CeedBasis basis, CeedInt num_points, CeedTransposeMod
14381485
(t_mode == CEED_NOTRANSPOSE && (v_length >= num_points * num_q_comp || u_length >= num_nodes * num_comp)));
14391486
break;
14401487
case CEED_EVAL_GRAD:
1488+
good_dims = ((t_mode == CEED_TRANSPOSE && (u_length >= num_points * num_q_comp * dim || v_length >= num_nodes * num_comp)) ||
1489+
(t_mode == CEED_NOTRANSPOSE && (v_length >= num_points * num_q_comp * dim || u_length >= num_nodes * num_comp)));
1490+
break;
14411491
case CEED_EVAL_NONE:
14421492
case CEED_EVAL_WEIGHT:
14431493
case CEED_EVAL_DIV:
@@ -1456,6 +1506,8 @@ int CeedBasisApplyAtPoints(CeedBasis basis, CeedInt num_points, CeedTransposeMod
14561506

14571507
// Default implementation
14581508
CeedCheck(basis->is_tensor_basis, basis->ceed, CEED_ERROR_UNSUPPORTED, "Evaluation at arbitrary points only supported for tensor product bases");
1509+
CeedCheck(eval_mode == CEED_EVAL_INTERP || t_mode == CEED_NOTRANSPOSE, basis->ceed, CEED_ERROR_UNSUPPORTED, "%s evaluation only supported for %s",
1510+
CeedEvalModes[eval_mode], CeedTransposeModes[CEED_NOTRANSPOSE]);
14591511
if (!basis->basis_chebyshev) {
14601512
// Build matrix mapping from quadrature point values to Chebyshev coefficients
14611513
CeedScalar *tau, *C, *I, *chebyshev_coeffs_1d;
@@ -1466,13 +1518,7 @@ int CeedBasisApplyAtPoints(CeedBasis basis, CeedInt num_points, CeedTransposeMod
14661518
CeedCheck(P_1d > 0 && Q_1d > 0, basis->ceed, CEED_ERROR_INCOMPATIBLE, "Basis dimensions are malformed");
14671519
CeedCall(CeedCalloc(Q_1d * Q_1d, &C));
14681520
CeedCall(CeedBasisGetQRef(basis, &q_ref_1d));
1469-
for (CeedInt i = 0; i < Q_1d; i++) {
1470-
const CeedScalar x = q_ref_1d[i];
1471-
1472-
C[i * Q_1d + 0] = 1.0;
1473-
C[i * Q_1d + 1] = 2 * x;
1474-
for (CeedInt j = 2; j < Q_1d; j++) C[i * Q_1d + j] = 2 * x * C[i * Q_1d + j - 1] - C[i * Q_1d + j - 2];
1475-
}
1521+
for (CeedInt i = 0; i < Q_1d; i++) CeedCall(CeedChebyshevPolynomialsAtPoint(q_ref_1d[i], Q_1d, &C[i * Q_1d]));
14761522

14771523
// Inverse of coefficient matrix
14781524
CeedCall(CeedCalloc(Q_1d * Q_1d, &chebyshev_coeffs_1d));
@@ -1546,36 +1592,61 @@ int CeedBasisApplyAtPoints(CeedBasis basis, CeedInt num_points, CeedTransposeMod
15461592
CeedCall(CeedVectorGetArrayRead(basis->vec_chebyshev, CEED_MEM_HOST, &chebyshev_coeffs));
15471593
CeedCall(CeedVectorGetArrayRead(x_ref, CEED_MEM_HOST, &x_array_read));
15481594
CeedCall(CeedVectorGetArrayWrite(v, CEED_MEM_HOST, &v_array));
1549-
{
1550-
CeedScalar tmp[2][num_comp * CeedIntPow(Q_1d, dim)], chebyshev_x[Q_1d];
1551-
1552-
// ---- Values at point
1553-
for (CeedInt p = 0; p < num_points; p++) {
1554-
CeedInt pre = num_comp * CeedIntPow(Q_1d, dim - 1), post = 1;
1555-
1556-
for (CeedInt d = dim - 1; d >= 0; d--) {
1557-
// ------ Compute Chebyshev polynomial values
1558-
{
1559-
const CeedScalar x = x_array_read[p * dim + d];
1560-
1561-
chebyshev_x[0] = 1.0;
1562-
chebyshev_x[1] = 2 * x;
1563-
for (CeedInt j = 2; j < Q_1d; j++) chebyshev_x[j] = 2 * x * chebyshev_x[j - 1] - chebyshev_x[j - 2];
1595+
switch (eval_mode) {
1596+
case CEED_EVAL_INTERP: {
1597+
CeedScalar tmp[2][num_comp * CeedIntPow(Q_1d, dim)], chebyshev_x[Q_1d];
1598+
1599+
// ---- Values at point
1600+
for (CeedInt p = 0; p < num_points; p++) {
1601+
CeedInt pre = num_comp * CeedIntPow(Q_1d, dim - 1), post = 1;
1602+
1603+
// Note: stepping "backwards" through the tensor contractions to agree with the ordering of the Chebyshev coefficients
1604+
for (CeedInt d = dim - 1; d >= 0; d--) {
1605+
// ------ Tensor contract with current Chebyshev polynomial values
1606+
CeedCall(CeedChebyshevPolynomialsAtPoint(x_array_read[p * dim + d], Q_1d, chebyshev_x));
1607+
CeedCall(CeedTensorContractApply(basis->contract, pre, Q_1d, post, 1, chebyshev_x, t_mode, false,
1608+
d == (dim - 1) ? chebyshev_coeffs : tmp[d % 2], d == 0 ? &v_array[p * num_comp] : tmp[(d + 1) % 2]));
1609+
pre /= Q_1d;
1610+
post *= 1;
15641611
}
1565-
// ------ Tensor contract
1566-
CeedCall(CeedTensorContractApply(basis->contract, pre, Q_1d, post, 1, chebyshev_x, t_mode, false,
1567-
d == (dim - 1) ? chebyshev_coeffs : tmp[d % 2], d == 0 ? &v_array[p * num_comp] : tmp[(d + 1) % 2]));
1568-
pre /= Q_1d;
1569-
post *= 1;
15701612
}
1613+
break;
15711614
}
1615+
case CEED_EVAL_GRAD: {
1616+
CeedScalar tmp[2][num_comp * CeedIntPow(Q_1d, dim)], chebyshev_x[Q_1d];
1617+
1618+
// ---- Values at point
1619+
for (CeedInt p = 0; p < num_points; p++) {
1620+
// Note: stepping "backwards" through the tensor contractions to agree with the ordering of the Chebyshev coefficients
1621+
// Dim**2 contractions, apply grad when pass == dim
1622+
for (CeedInt pass = dim - 1; pass >= 0; pass--) {
1623+
CeedInt pre = num_comp * CeedIntPow(Q_1d, dim - 1), post = 1;
1624+
1625+
for (CeedInt d = dim - 1; d >= 0; d--) {
1626+
// ------ Tensor contract with current Chebyshev polynomial values
1627+
if (pass == d) CeedCall(CeedChebyshevDerivativeAtPoint(x_array_read[p * dim + d], Q_1d, chebyshev_x));
1628+
else CeedCall(CeedChebyshevPolynomialsAtPoint(x_array_read[p * dim + d], Q_1d, chebyshev_x));
1629+
CeedCall(CeedTensorContractApply(basis->contract, pre, Q_1d, post, 1, chebyshev_x, t_mode, false,
1630+
d == (dim - 1) ? chebyshev_coeffs : tmp[d % 2],
1631+
d == 0 ? &v_array[p * num_comp * dim + pass] : tmp[(d + 1) % 2]));
1632+
pre /= Q_1d;
1633+
post *= 1;
1634+
}
1635+
}
1636+
}
1637+
break;
1638+
}
1639+
default:
1640+
// Nothing to do, this won't occur
1641+
break;
15721642
}
15731643
CeedCall(CeedVectorRestoreArrayRead(basis->vec_chebyshev, &chebyshev_coeffs));
15741644
CeedCall(CeedVectorRestoreArrayRead(x_ref, &x_array_read));
15751645
CeedCall(CeedVectorRestoreArray(v, &v_array));
15761646
break;
15771647
}
15781648
case CEED_TRANSPOSE: {
1649+
// Note: No switch on e_mode here because only CEED_EVAL_INTERP is supported at this time
15791650
// Arbitrary points to nodes
15801651
CeedScalar *chebyshev_coeffs;
15811652
const CeedScalar *u_array, *x_array_read;
@@ -1591,16 +1662,10 @@ int CeedBasisApplyAtPoints(CeedBasis basis, CeedInt num_points, CeedTransposeMod
15911662
for (CeedInt p = 0; p < num_points; p++) {
15921663
CeedInt pre = num_comp * 1, post = 1;
15931664

1665+
// Note: stepping "backwards" through the tensor contractions to agree with the ordering of the Chebyshev coefficients
15941666
for (CeedInt d = dim - 1; d >= 0; d--) {
1595-
// ------ Compute Chebyshev polynomial values
1596-
{
1597-
const CeedScalar x = x_array_read[p * dim + d];
1598-
1599-
chebyshev_x[0] = 1.0;
1600-
chebyshev_x[1] = 2 * x;
1601-
for (CeedInt j = 2; j < Q_1d; j++) chebyshev_x[j] = 2 * x * chebyshev_x[j - 1] - chebyshev_x[j - 2];
1602-
}
1603-
// ------ Tensor contract
1667+
// ------ Tensor contract with current Chebyshev polynomial values
1668+
CeedCall(CeedChebyshevPolynomialsAtPoint(x_array_read[p * dim + d], Q_1d, chebyshev_x));
16041669
CeedCall(CeedTensorContractApply(basis->contract, pre, 1, post, Q_1d, chebyshev_x, t_mode, p > 0 && d == 0,
16051670
d == (dim - 1) ? &u_array[p * num_comp] : tmp[d % 2], d == 0 ? chebyshev_coeffs : tmp[(d + 1) % 2]));
16061671
pre /= 1;

tests/t355-basis.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/// @file
2+
/// Test polynomial gradient to arbirtary points in 1D
3+
/// \test Test polynomial gradient to arbitrary points in 1D
4+
#include <ceed.h>
5+
#include <math.h>
6+
#include <stdio.h>
7+
8+
#define ALEN(a) (sizeof(a) / sizeof((a)[0]))
9+
10+
static CeedScalar Eval(CeedScalar x, CeedInt n, const CeedScalar *c) {
11+
CeedScalar y = c[n - 1];
12+
for (CeedInt i = n - 2; i >= 0; i--) y = y * x + c[i];
13+
return y;
14+
}
15+
16+
static CeedScalar EvalGrad(CeedScalar x, CeedInt n, const CeedScalar *c) {
17+
CeedScalar y = (n - 1) * c[n - 1];
18+
for (CeedInt i = n - 2; i >= 1; i--) y = y * x + i * c[i];
19+
return y;
20+
}
21+
22+
int main(int argc, char **argv) {
23+
Ceed ceed;
24+
CeedVector x, x_nodes, x_points, u, v;
25+
CeedBasis basis_x, basis_u;
26+
const CeedInt p = 5, q = 5, num_points = 4;
27+
const CeedScalar c[4] = {1, 2, 3, 4}; // f = 1 + 2x + 3x^2 + ..., df = 2 + 6x + 12x^2 + ...
28+
29+
CeedInit(argv[1], &ceed);
30+
31+
CeedVectorCreate(ceed, 2, &x);
32+
CeedVectorCreate(ceed, p, &x_nodes);
33+
CeedVectorCreate(ceed, num_points, &x_points);
34+
CeedVectorCreate(ceed, p, &u);
35+
CeedVectorCreate(ceed, num_points, &v);
36+
37+
// Get nodal coordinates
38+
CeedBasisCreateTensorH1Lagrange(ceed, 1, 1, 2, p, CEED_GAUSS_LOBATTO, &basis_x);
39+
{
40+
CeedScalar x_array[2];
41+
42+
for (CeedInt i = 0; i < 2; i++) x_array[i] = CeedIntPow(-1, i + 1);
43+
CeedVectorSetArray(x, CEED_MEM_HOST, CEED_COPY_VALUES, x_array);
44+
}
45+
CeedBasisApply(basis_x, 1, CEED_NOTRANSPOSE, CEED_EVAL_INTERP, x, x_nodes);
46+
47+
// Set values of u at nodes
48+
{
49+
const CeedScalar *x_array;
50+
CeedScalar u_array[p];
51+
52+
CeedVectorGetArrayRead(x_nodes, CEED_MEM_HOST, &x_array);
53+
for (CeedInt i = 0; i < p; i++) u_array[i] = Eval(x_array[i], ALEN(c), c);
54+
CeedVectorRestoreArrayRead(x_nodes, &x_array);
55+
CeedVectorSetArray(u, CEED_MEM_HOST, CEED_COPY_VALUES, (CeedScalar *)&u_array);
56+
}
57+
58+
// Interpolate to arbitrary points
59+
CeedBasisCreateTensorH1Lagrange(ceed, 1, 1, p, q, CEED_GAUSS, &basis_u);
60+
{
61+
CeedScalar x_array[4] = {-0.33, -0.65, 0.16, 0.99};
62+
63+
CeedVectorSetArray(x_points, CEED_MEM_HOST, CEED_COPY_VALUES, x_array);
64+
}
65+
CeedBasisApplyAtPoints(basis_u, num_points, CEED_NOTRANSPOSE, CEED_EVAL_GRAD, x_points, u, v);
66+
67+
{
68+
const CeedScalar *x_array, *v_array;
69+
70+
CeedVectorGetArrayRead(x_points, CEED_MEM_HOST, &x_array);
71+
CeedVectorGetArrayRead(v, CEED_MEM_HOST, &v_array);
72+
for (CeedInt i = 0; i < num_points; i++) {
73+
CeedScalar dfx = EvalGrad(x_array[i], ALEN(c), c);
74+
if (fabs(v_array[i] - dfx) > 500. * CEED_EPSILON) printf("%f != %f = df(%f)\n", v_array[i], dfx, x_array[i]);
75+
}
76+
CeedVectorRestoreArrayRead(x_points, &x_array);
77+
CeedVectorRestoreArrayRead(v, &v_array);
78+
}
79+
80+
CeedVectorDestroy(&x);
81+
CeedVectorDestroy(&x_nodes);
82+
CeedVectorDestroy(&x_points);
83+
CeedVectorDestroy(&u);
84+
CeedVectorDestroy(&v);
85+
CeedBasisDestroy(&basis_x);
86+
CeedBasisDestroy(&basis_u);
87+
CeedDestroy(&ceed);
88+
return 0;
89+
}

0 commit comments

Comments
 (0)