xref: /libCEED/examples/rust/ex2-surface-vector/src/main.rs (revision 9ba83ac0e4b1fca39d6fa6737a318a9f0cbc172d)
1*9ba83ac0SJeremy L Thompson // Copyright (c) 2017-2026, Lawrence Livermore National Security, LLC and other CEED contributors.
23eb59678SJeremy L Thompson // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
33eb59678SJeremy L Thompson //
43eb59678SJeremy L Thompson // SPDX-License-Identifier: BSD-2-Clause
53eb59678SJeremy L Thompson //
63eb59678SJeremy L Thompson // This file is part of CEED:  http://github.com/ceed
73eb59678SJeremy L Thompson //
83eb59678SJeremy L Thompson //                             libCEED Example 4
93eb59678SJeremy L Thompson //
103eb59678SJeremy L Thompson // This example illustrates a simple usage of libCEED to compute the surface
113eb59678SJeremy L Thompson // area of a 3D body using matrix-free application of a diffusion operator.
123eb59678SJeremy L Thompson // Arbitrary mesh and solution degrees in 1D, 2D and 3D are supported from the
133eb59678SJeremy L Thompson // same code. This calculation is executed in triplicate with a 3 component
143eb59678SJeremy L Thompson // vector system.
153eb59678SJeremy L Thompson //
163eb59678SJeremy L Thompson // The example has no dependencies, and is designed to be self-contained. For
173eb59678SJeremy L Thompson // additional examples that use external discretization libraries (MFEM, PETSc,
183eb59678SJeremy L Thompson // etc.) see the subdirectories in libceed/examples.
193eb59678SJeremy L Thompson //
203eb59678SJeremy L Thompson // All libCEED objects use a Ceed device object constructed based on a command
213eb59678SJeremy L Thompson // line argument (-ceed).
223eb59678SJeremy L Thompson 
233eb59678SJeremy L Thompson use clap::Parser;
243eb59678SJeremy L Thompson use libceed::{
253eb59678SJeremy L Thompson     BasisOpt, Ceed, ElemRestrictionOpt, QFunctionInputs, QFunctionOpt, QFunctionOutputs, VectorOpt,
263eb59678SJeremy L Thompson };
273eb59678SJeremy L Thompson mod opt;
283eb59678SJeremy L Thompson mod transform;
293eb59678SJeremy L Thompson 
303eb59678SJeremy L Thompson // ----------------------------------------------------------------------------
313eb59678SJeremy L Thompson // Example 4
323eb59678SJeremy L Thompson // ----------------------------------------------------------------------------
333eb59678SJeremy L Thompson fn main() -> libceed::Result<()> {
343eb59678SJeremy L Thompson     let options = opt::Opt::parse();
353eb59678SJeremy L Thompson     example_2_vector(options)
363eb59678SJeremy L Thompson }
373eb59678SJeremy L Thompson 
383eb59678SJeremy L Thompson #[allow(clippy::erasing_op)]
393eb59678SJeremy L Thompson #[allow(clippy::identity_op)]
403eb59678SJeremy L Thompson fn example_2_vector(options: opt::Opt) -> libceed::Result<()> {
413eb59678SJeremy L Thompson     // Process command line arguments
423eb59678SJeremy L Thompson     let opt::Opt {
433eb59678SJeremy L Thompson         ceed_spec,
443eb59678SJeremy L Thompson         dim,
453eb59678SJeremy L Thompson         mesh_degree,
463eb59678SJeremy L Thompson         solution_degree,
473eb59678SJeremy L Thompson         num_qpts,
483eb59678SJeremy L Thompson         problem_size_requested,
493eb59678SJeremy L Thompson         test,
503eb59678SJeremy L Thompson         quiet,
513eb59678SJeremy L Thompson         gallery,
523eb59678SJeremy L Thompson     } = options;
533eb59678SJeremy L Thompson     assert!((0..=3).contains(&dim));
543eb59678SJeremy L Thompson     assert!(mesh_degree >= 1);
553eb59678SJeremy L Thompson     assert!(solution_degree >= 1);
563eb59678SJeremy L Thompson     assert!(num_qpts >= 1);
573eb59678SJeremy L Thompson     let ncomp_x = dim;
583eb59678SJeremy L Thompson     let problem_size: i64 = if problem_size_requested < 0 {
593eb59678SJeremy L Thompson         if test {
603eb59678SJeremy L Thompson             16 * 16 * (dim * dim) as i64
613eb59678SJeremy L Thompson         } else {
623eb59678SJeremy L Thompson             256 * 1024
633eb59678SJeremy L Thompson         }
643eb59678SJeremy L Thompson     } else {
653eb59678SJeremy L Thompson         problem_size_requested
663eb59678SJeremy L Thompson     };
673eb59678SJeremy L Thompson     let ncomp_u = 3;
683eb59678SJeremy L Thompson 
693eb59678SJeremy L Thompson     // Summary output
703eb59678SJeremy L Thompson     if !quiet {
713eb59678SJeremy L Thompson         println!("Selected options: [command line option] : <current value>");
723eb59678SJeremy L Thompson         println!("    Ceed specification [-c] : {}", ceed_spec);
733eb59678SJeremy L Thompson         println!("    Mesh dimension     [-d] : {}", dim);
743eb59678SJeremy L Thompson         println!("    Mesh degree        [-m] : {}", mesh_degree);
753eb59678SJeremy L Thompson         println!("    Solution degree    [-p] : {}", solution_degree);
763eb59678SJeremy L Thompson         println!("    Num. 1D quadr. pts [-q] : {}", num_qpts);
773eb59678SJeremy L Thompson         println!("    Approx. # unknowns [-s] : {}", problem_size);
783eb59678SJeremy L Thompson         println!(
793eb59678SJeremy L Thompson             "    QFunction source   [-g] : {}",
803eb59678SJeremy L Thompson             if gallery { "gallery" } else { "user closure" }
813eb59678SJeremy L Thompson         );
823eb59678SJeremy L Thompson     }
833eb59678SJeremy L Thompson 
843eb59678SJeremy L Thompson     // Initalize ceed context
853eb59678SJeremy L Thompson     let ceed = Ceed::init(&ceed_spec);
863eb59678SJeremy L Thompson 
873eb59678SJeremy L Thompson     // Mesh and solution bases
883eb59678SJeremy L Thompson     let basis_mesh = ceed.basis_tensor_H1_Lagrange(
893eb59678SJeremy L Thompson         dim,
903eb59678SJeremy L Thompson         ncomp_x,
913eb59678SJeremy L Thompson         mesh_degree + 1,
923eb59678SJeremy L Thompson         num_qpts,
933eb59678SJeremy L Thompson         libceed::QuadMode::Gauss,
943eb59678SJeremy L Thompson     )?;
953eb59678SJeremy L Thompson     let basis_solution = ceed.basis_tensor_H1_Lagrange(
963eb59678SJeremy L Thompson         dim,
973eb59678SJeremy L Thompson         ncomp_u,
983eb59678SJeremy L Thompson         solution_degree + 1,
993eb59678SJeremy L Thompson         num_qpts,
1003eb59678SJeremy L Thompson         libceed::QuadMode::Gauss,
1013eb59678SJeremy L Thompson     )?;
1023eb59678SJeremy L Thompson 
1033eb59678SJeremy L Thompson     // Determine mesh size from approximate problem size
1043eb59678SJeremy L Thompson     let num_xyz = mesh::cartesian_mesh_size(dim, solution_degree, problem_size);
1053eb59678SJeremy L Thompson     if !quiet {
1063eb59678SJeremy L Thompson         print!("\nMesh size                   : nx = {}", num_xyz[0]);
1073eb59678SJeremy L Thompson         if dim > 1 {
1083eb59678SJeremy L Thompson             print!(", ny = {}", num_xyz[1]);
1093eb59678SJeremy L Thompson         }
1103eb59678SJeremy L Thompson         if dim > 2 {
1113eb59678SJeremy L Thompson             print!(", nz = {}", num_xyz[2]);
1123eb59678SJeremy L Thompson         }
1133eb59678SJeremy L Thompson         println!();
1143eb59678SJeremy L Thompson     }
1153eb59678SJeremy L Thompson 
1163eb59678SJeremy L Thompson     // Build ElemRestriction objects describing the mesh and solution discrete
1173eb59678SJeremy L Thompson     // representations
1183eb59678SJeremy L Thompson     let (rstr_mesh, _) =
1193eb59678SJeremy L Thompson         mesh::build_cartesian_restriction(&ceed, dim, num_xyz, mesh_degree, ncomp_x, num_qpts)?;
1203eb59678SJeremy L Thompson     let (_, rstr_qdata) = mesh::build_cartesian_restriction(
1213eb59678SJeremy L Thompson         &ceed,
1223eb59678SJeremy L Thompson         dim,
1233eb59678SJeremy L Thompson         num_xyz,
1243eb59678SJeremy L Thompson         solution_degree,
1253eb59678SJeremy L Thompson         dim * (dim + 1) / 2,
1263eb59678SJeremy L Thompson         num_qpts,
1273eb59678SJeremy L Thompson     )?;
1283eb59678SJeremy L Thompson 
1293eb59678SJeremy L Thompson     let (rstr_solution, _) =
1303eb59678SJeremy L Thompson         mesh::build_cartesian_restriction(&ceed, dim, num_xyz, solution_degree, ncomp_u, num_qpts)?;
1313eb59678SJeremy L Thompson     let mesh_size = rstr_mesh.lvector_size();
1323eb59678SJeremy L Thompson     let solution_size = rstr_solution.lvector_size();
1333eb59678SJeremy L Thompson     if !quiet {
1343eb59678SJeremy L Thompson         println!("Number of mesh nodes        : {}", mesh_size / dim);
1353eb59678SJeremy L Thompson         println!("Number of solution nodes    : {}", solution_size);
1363eb59678SJeremy L Thompson     }
1373eb59678SJeremy L Thompson 
1383eb59678SJeremy L Thompson     // Create a Vector with the mesh coordinates
1393eb59678SJeremy L Thompson     let mut mesh_coords = mesh::cartesian_mesh_coords(&ceed, dim, num_xyz, mesh_degree, mesh_size)?;
1403eb59678SJeremy L Thompson 
1413eb59678SJeremy L Thompson     // Apply a transformation to the mesh coordinates
1423eb59678SJeremy L Thompson     let exact_area = transform::transform_mesh_coordinates(dim, &mut mesh_coords)?;
1433eb59678SJeremy L Thompson 
1443eb59678SJeremy L Thompson     // QFunction that builds the quadrature data for the diff operator
1453eb59678SJeremy L Thompson     // -- QFunction from user closure
1463eb59678SJeremy L Thompson     let build_diff = move |[jacobian, weights, ..]: QFunctionInputs,
1473eb59678SJeremy L Thompson                            [qdata, ..]: QFunctionOutputs| {
1483eb59678SJeremy L Thompson         // Build quadrature data
1493eb59678SJeremy L Thompson         match dim {
1503eb59678SJeremy L Thompson             1 => qdata
1513eb59678SJeremy L Thompson                 .iter_mut()
1523eb59678SJeremy L Thompson                 .zip(jacobian.iter().zip(weights.iter()))
1533eb59678SJeremy L Thompson                 .for_each(|(qdata, (j, weight))| *qdata = weight / j),
1543eb59678SJeremy L Thompson             2 => {
1553eb59678SJeremy L Thompson                 let q = qdata.len() / 3;
1563eb59678SJeremy L Thompson                 for i in 0..q {
1573eb59678SJeremy L Thompson                     let j11 = jacobian[i + q * 0];
1583eb59678SJeremy L Thompson                     let j21 = jacobian[i + q * 1];
1593eb59678SJeremy L Thompson                     let j12 = jacobian[i + q * 2];
1603eb59678SJeremy L Thompson                     let j22 = jacobian[i + q * 3];
1613eb59678SJeremy L Thompson                     let qw = weights[i] / (j11 * j22 - j21 * j12);
1623eb59678SJeremy L Thompson                     qdata[i + q * 0] = qw * (j12 * j12 + j22 * j22);
1633eb59678SJeremy L Thompson                     qdata[i + q * 1] = qw * (j11 * j11 + j21 * j21);
1643eb59678SJeremy L Thompson                     qdata[i + q * 2] = -qw * (j11 * j12 + j21 * j22);
1653eb59678SJeremy L Thompson                 }
1663eb59678SJeremy L Thompson             }
1673eb59678SJeremy L Thompson             3 => {
1683eb59678SJeremy L Thompson                 let q = qdata.len() / 6;
1693eb59678SJeremy L Thompson                 for i in 0..q {
1703eb59678SJeremy L Thompson                     let mut a = [0.0; 9];
1713eb59678SJeremy L Thompson                     for j in 0..3 {
1723eb59678SJeremy L Thompson                         for k in 0..3 {
1733eb59678SJeremy L Thompson                             a[k * 3 + j] = jacobian[i + q * ((j + 1) % 3 + 3 * ((k + 1) % 3))]
1743eb59678SJeremy L Thompson                                 * jacobian[i + q * ((j + 2) % 3 + 3 * ((k + 2) % 3))]
1753eb59678SJeremy L Thompson                                 - jacobian[i + q * ((j + 1) % 3 + 3 * ((k + 2) % 3))]
1763eb59678SJeremy L Thompson                                     * jacobian[i + q * ((j + 2) % 3 + 3 * ((k + 1) % 3))];
1773eb59678SJeremy L Thompson                         }
1783eb59678SJeremy L Thompson                     }
1793eb59678SJeremy L Thompson                     let qw = weights[i]
1803eb59678SJeremy L Thompson                         / (jacobian[i + q * 0] * a[0 * 3 + 0]
1813eb59678SJeremy L Thompson                             + jacobian[i + q * 1] * a[0 * 3 + 1]
1823eb59678SJeremy L Thompson                             + jacobian[i + q * 2] * a[0 * 3 + 2]);
1833eb59678SJeremy L Thompson                     qdata[i + q * 0] = qw
1843eb59678SJeremy L Thompson                         * (a[0 * 3 + 0] * a[0 * 3 + 0]
1853eb59678SJeremy L Thompson                             + a[0 * 3 + 1] * a[0 * 3 + 1]
1863eb59678SJeremy L Thompson                             + a[0 * 3 + 2] * a[0 * 3 + 2]);
1873eb59678SJeremy L Thompson                     qdata[i + q * 1] = qw
1883eb59678SJeremy L Thompson                         * (a[1 * 3 + 0] * a[1 * 3 + 0]
1893eb59678SJeremy L Thompson                             + a[1 * 3 + 1] * a[1 * 3 + 1]
1903eb59678SJeremy L Thompson                             + a[1 * 3 + 2] * a[1 * 3 + 2]);
1913eb59678SJeremy L Thompson                     qdata[i + q * 2] = qw
1923eb59678SJeremy L Thompson                         * (a[2 * 3 + 0] * a[2 * 3 + 0]
1933eb59678SJeremy L Thompson                             + a[2 * 3 + 1] * a[2 * 3 + 1]
1943eb59678SJeremy L Thompson                             + a[2 * 3 + 2] * a[2 * 3 + 2]);
1953eb59678SJeremy L Thompson                     qdata[i + q * 3] = qw
1963eb59678SJeremy L Thompson                         * (a[1 * 3 + 0] * a[2 * 3 + 0]
1973eb59678SJeremy L Thompson                             + a[1 * 3 + 1] * a[2 * 3 + 1]
1983eb59678SJeremy L Thompson                             + a[1 * 3 + 2] * a[2 * 3 + 2]);
1993eb59678SJeremy L Thompson                     qdata[i + q * 4] = qw
2003eb59678SJeremy L Thompson                         * (a[0 * 3 + 0] * a[2 * 3 + 0]
2013eb59678SJeremy L Thompson                             + a[0 * 3 + 1] * a[2 * 3 + 1]
2023eb59678SJeremy L Thompson                             + a[0 * 3 + 2] * a[2 * 3 + 2]);
2033eb59678SJeremy L Thompson                     qdata[i + q * 5] = qw
2043eb59678SJeremy L Thompson                         * (a[0 * 3 + 0] * a[1 * 3 + 0]
2053eb59678SJeremy L Thompson                             + a[0 * 3 + 1] * a[1 * 3 + 1]
2063eb59678SJeremy L Thompson                             + a[0 * 3 + 2] * a[1 * 3 + 2]);
2073eb59678SJeremy L Thompson                 }
2083eb59678SJeremy L Thompson             }
2093eb59678SJeremy L Thompson             _ => unreachable!(),
2103eb59678SJeremy L Thompson         };
2113eb59678SJeremy L Thompson 
2123eb59678SJeremy L Thompson         // Return clean error code
2133eb59678SJeremy L Thompson         0
2143eb59678SJeremy L Thompson     };
2153eb59678SJeremy L Thompson     let qf_build_closure = ceed
2163eb59678SJeremy L Thompson         .q_function_interior(1, Box::new(build_diff))?
2173eb59678SJeremy L Thompson         .input("dx", ncomp_x * dim, libceed::EvalMode::Grad)?
2183eb59678SJeremy L Thompson         .input("weights", 1, libceed::EvalMode::Weight)?
2193eb59678SJeremy L Thompson         .output("qdata", dim * (dim + 1) / 2, libceed::EvalMode::None)?;
2203eb59678SJeremy L Thompson     // -- QFunction from gallery
2213eb59678SJeremy L Thompson     let qf_build_named = {
2223eb59678SJeremy L Thompson         let name = format!("Poisson{}DBuild", dim);
2233eb59678SJeremy L Thompson         ceed.q_function_interior_by_name(&name)?
2243eb59678SJeremy L Thompson     };
2253eb59678SJeremy L Thompson     // -- QFunction for use with Operator
2263eb59678SJeremy L Thompson     let qf_build = if gallery {
2273eb59678SJeremy L Thompson         QFunctionOpt::SomeQFunctionByName(&qf_build_named)
2283eb59678SJeremy L Thompson     } else {
2293eb59678SJeremy L Thompson         QFunctionOpt::SomeQFunction(&qf_build_closure)
2303eb59678SJeremy L Thompson     };
2313eb59678SJeremy L Thompson 
2323eb59678SJeremy L Thompson     // Operator that build the quadrature data for the diff operator
2333eb59678SJeremy L Thompson     let op_build = ceed
2343eb59678SJeremy L Thompson         .operator(qf_build, QFunctionOpt::None, QFunctionOpt::None)?
2353eb59678SJeremy L Thompson         .name("build qdata")?
2363eb59678SJeremy L Thompson         .field("dx", &rstr_mesh, &basis_mesh, VectorOpt::Active)?
2373eb59678SJeremy L Thompson         .field(
2383eb59678SJeremy L Thompson             "weights",
2393eb59678SJeremy L Thompson             ElemRestrictionOpt::None,
2403eb59678SJeremy L Thompson             &basis_mesh,
2413eb59678SJeremy L Thompson             VectorOpt::None,
2423eb59678SJeremy L Thompson         )?
2433eb59678SJeremy L Thompson         .field("qdata", &rstr_qdata, BasisOpt::None, VectorOpt::Active)?
2443eb59678SJeremy L Thompson         .check()?;
2453eb59678SJeremy L Thompson 
2463eb59678SJeremy L Thompson     // Compute the quadrature data for the diff operator
2473eb59678SJeremy L Thompson     let elem_qpts = num_qpts.pow(dim as u32);
2483eb59678SJeremy L Thompson     let num_elem: usize = num_xyz.iter().take(dim).product();
2493eb59678SJeremy L Thompson     let mut qdata = ceed.vector(num_elem * elem_qpts * dim * (dim + 1) / 2)?;
2503eb59678SJeremy L Thompson     op_build.apply(&mesh_coords, &mut qdata)?;
2513eb59678SJeremy L Thompson 
2523eb59678SJeremy L Thompson     // QFunction that applies the diff operator
2533eb59678SJeremy L Thompson     // -- QFunction from user closure
2543eb59678SJeremy L Thompson     let apply_diff = move |[ug, qdata, ..]: QFunctionInputs, [vg, ..]: QFunctionOutputs| {
2553eb59678SJeremy L Thompson         // Apply diffusion operator
2563eb59678SJeremy L Thompson         match dim {
2573eb59678SJeremy L Thompson             1 => {
2583eb59678SJeremy L Thompson                 let q = qdata.len();
2593eb59678SJeremy L Thompson                 for c in 0..ncomp_u {
2603eb59678SJeremy L Thompson                     vg.iter_mut()
2613eb59678SJeremy L Thompson                         .skip(c * q)
2623eb59678SJeremy L Thompson                         .zip(ug.iter().skip(c * q).zip(qdata.iter()))
2633eb59678SJeremy L Thompson                         .for_each(|(vg, (ug, w))| *vg = ug * w)
2643eb59678SJeremy L Thompson                 }
2653eb59678SJeremy L Thompson             }
2663eb59678SJeremy L Thompson             2 => {
2673eb59678SJeremy L Thompson                 let q = qdata.len() / 3;
2683eb59678SJeremy L Thompson                 for i in 0..q {
2693eb59678SJeremy L Thompson                     let dxdxdxdx_t = [
2703eb59678SJeremy L Thompson                         [qdata[i + 0 * q], qdata[i + 2 * q]],
2713eb59678SJeremy L Thompson                         [qdata[i + 2 * q], qdata[i + 1 * q]],
2723eb59678SJeremy L Thompson                     ];
2733eb59678SJeremy L Thompson                     for c in 0..ncomp_u {
2743eb59678SJeremy L Thompson                         let du = [ug[i + (c + 0 * ncomp_u) * q], ug[i + (c + 1 * ncomp_u) * q]];
2753eb59678SJeremy L Thompson                         for j in 0..dim {
2763eb59678SJeremy L Thompson                             vg[i + (c + j * ncomp_u) * q] =
2773eb59678SJeremy L Thompson                                 du[0] * dxdxdxdx_t[0][j] + du[1] * dxdxdxdx_t[1][j];
2783eb59678SJeremy L Thompson                         }
2793eb59678SJeremy L Thompson                     }
2803eb59678SJeremy L Thompson                 }
2813eb59678SJeremy L Thompson             }
2823eb59678SJeremy L Thompson             3 => {
2833eb59678SJeremy L Thompson                 let q = qdata.len() / 6;
2843eb59678SJeremy L Thompson                 for i in 0..q {
2853eb59678SJeremy L Thompson                     let dxdxdxdx_t = [
2863eb59678SJeremy L Thompson                         [qdata[i + 0 * q], qdata[i + 5 * q], qdata[i + 4 * q]],
2873eb59678SJeremy L Thompson                         [qdata[i + 5 * q], qdata[i + 1 * q], qdata[i + 3 * q]],
2883eb59678SJeremy L Thompson                         [qdata[i + 4 * q], qdata[i + 3 * q], qdata[i + 2 * q]],
2893eb59678SJeremy L Thompson                     ];
2903eb59678SJeremy L Thompson                     for c in 0..ncomp_u {
2913eb59678SJeremy L Thompson                         let du = [
2923eb59678SJeremy L Thompson                             ug[i + (c + 0 * ncomp_u) * q],
2933eb59678SJeremy L Thompson                             ug[i + (c + 1 * ncomp_u) * q],
2943eb59678SJeremy L Thompson                             ug[i + (c + 2 * ncomp_u) * q],
2953eb59678SJeremy L Thompson                         ];
2963eb59678SJeremy L Thompson                         for j in 0..dim {
2973eb59678SJeremy L Thompson                             vg[i + (c + j * ncomp_u) * q] = du[0] * dxdxdxdx_t[0][j]
2983eb59678SJeremy L Thompson                                 + du[1] * dxdxdxdx_t[1][j]
2993eb59678SJeremy L Thompson                                 + du[2] * dxdxdxdx_t[2][j];
3003eb59678SJeremy L Thompson                         }
3013eb59678SJeremy L Thompson                     }
3023eb59678SJeremy L Thompson                 }
3033eb59678SJeremy L Thompson             }
3043eb59678SJeremy L Thompson             _ => unreachable!(),
3053eb59678SJeremy L Thompson         };
3063eb59678SJeremy L Thompson 
3073eb59678SJeremy L Thompson         // Return clean error code
3083eb59678SJeremy L Thompson         0
3093eb59678SJeremy L Thompson     };
3103eb59678SJeremy L Thompson     let qf_diff_closure = ceed
3113eb59678SJeremy L Thompson         .q_function_interior(1, Box::new(apply_diff))?
3123eb59678SJeremy L Thompson         .input("du", dim * ncomp_u, libceed::EvalMode::Grad)?
3133eb59678SJeremy L Thompson         .input("qdata", dim * (dim + 1) / 2, libceed::EvalMode::None)?
3143eb59678SJeremy L Thompson         .output("dv", dim * ncomp_u, libceed::EvalMode::Grad)?;
3153eb59678SJeremy L Thompson     // -- QFunction from gallery
3163eb59678SJeremy L Thompson     let qf_diff_named = {
3173eb59678SJeremy L Thompson         let name = format!("Vector3Poisson{}DApply", dim);
3183eb59678SJeremy L Thompson         ceed.q_function_interior_by_name(&name)?
3193eb59678SJeremy L Thompson     };
3203eb59678SJeremy L Thompson     // -- QFunction for use with Operator
3213eb59678SJeremy L Thompson     let qf_diff = if gallery {
3223eb59678SJeremy L Thompson         QFunctionOpt::SomeQFunctionByName(&qf_diff_named)
3233eb59678SJeremy L Thompson     } else {
3243eb59678SJeremy L Thompson         QFunctionOpt::SomeQFunction(&qf_diff_closure)
3253eb59678SJeremy L Thompson     };
3263eb59678SJeremy L Thompson 
3273eb59678SJeremy L Thompson     // Diff Operator
3283eb59678SJeremy L Thompson     let op_diff = ceed
3293eb59678SJeremy L Thompson         .operator(qf_diff, QFunctionOpt::None, QFunctionOpt::None)?
3303eb59678SJeremy L Thompson         .name("Poisson")?
3313eb59678SJeremy L Thompson         .field("du", &rstr_solution, &basis_solution, VectorOpt::Active)?
3323eb59678SJeremy L Thompson         .field("qdata", &rstr_qdata, BasisOpt::None, &qdata)?
3333eb59678SJeremy L Thompson         .field("dv", &rstr_solution, &basis_solution, VectorOpt::Active)?
3343eb59678SJeremy L Thompson         .check()?;
3353eb59678SJeremy L Thompson 
3363eb59678SJeremy L Thompson     // Solution vectors
3373eb59678SJeremy L Thompson     let mut u = ceed.vector(solution_size)?;
3383eb59678SJeremy L Thompson     let mut v = ceed.vector(solution_size)?;
3393eb59678SJeremy L Thompson 
3403eb59678SJeremy L Thompson     // Initialize u with sum of node coordinates
3413eb59678SJeremy L Thompson     let coords = mesh_coords.view()?;
3423eb59678SJeremy L Thompson     u.set_value(0.0)?;
3433eb59678SJeremy L Thompson     for c in 0..ncomp_u {
3443eb59678SJeremy L Thompson         let q = solution_size / ncomp_u;
3453eb59678SJeremy L Thompson         u.view_mut()?
3463eb59678SJeremy L Thompson             .iter_mut()
3473eb59678SJeremy L Thompson             .skip(c * q)
3483eb59678SJeremy L Thompson             .take(q)
3493eb59678SJeremy L Thompson             .enumerate()
3503eb59678SJeremy L Thompson             .for_each(|(i, u)| {
3513eb59678SJeremy L Thompson                 *u = (0..dim).map(|d| coords[i + d * q]).sum::<libceed::Scalar>()
3523eb59678SJeremy L Thompson                     * (c + 1) as libceed::Scalar;
3533eb59678SJeremy L Thompson             });
3543eb59678SJeremy L Thompson     }
3553eb59678SJeremy L Thompson 
3563eb59678SJeremy L Thompson     // Apply the diff operator
3573eb59678SJeremy L Thompson     op_diff.apply(&u, &mut v)?;
3583eb59678SJeremy L Thompson 
3593eb59678SJeremy L Thompson     // Compute the mesh surface area
3603eb59678SJeremy L Thompson     let area: libceed::Scalar = v
3613eb59678SJeremy L Thompson         .view()?
3623eb59678SJeremy L Thompson         .iter()
3633eb59678SJeremy L Thompson         .map(|v| (*v).abs())
3643eb59678SJeremy L Thompson         .sum::<libceed::Scalar>()
3653eb59678SJeremy L Thompson         / ((ncomp_u * (ncomp_u + 1)) / 2) as libceed::Scalar;
3663eb59678SJeremy L Thompson 
3673eb59678SJeremy L Thompson     // Output results
3683eb59678SJeremy L Thompson     if !quiet {
3693eb59678SJeremy L Thompson         println!("Exact mesh surface area     : {:.12}", exact_area);
3703eb59678SJeremy L Thompson         println!("Computed mesh surface_area  : {:.12}", area);
3713eb59678SJeremy L Thompson         println!("Surface area error          : {:.12e}", area - exact_area);
3723eb59678SJeremy L Thompson     }
3733eb59678SJeremy L Thompson     let tolerance = match dim {
3743eb59678SJeremy L Thompson         1 => 1E-5,
3753eb59678SJeremy L Thompson         _ => 1E-1,
3763eb59678SJeremy L Thompson     };
3773eb59678SJeremy L Thompson     let error = (area - exact_area).abs();
3783eb59678SJeremy L Thompson     if error > tolerance {
3793eb59678SJeremy L Thompson         println!("Volume error too large: {:.12e}", error);
3803eb59678SJeremy L Thompson         return Err(libceed::Error {
3813eb59678SJeremy L Thompson             message: format!(
3823eb59678SJeremy L Thompson                 "Volume error too large - expected: {:.12e}, actual: {:.12e}",
3833eb59678SJeremy L Thompson                 tolerance, error
3843eb59678SJeremy L Thompson             ),
3853eb59678SJeremy L Thompson         });
3863eb59678SJeremy L Thompson     }
3873eb59678SJeremy L Thompson     Ok(())
3883eb59678SJeremy L Thompson }
3893eb59678SJeremy L Thompson 
3903eb59678SJeremy L Thompson // ----------------------------------------------------------------------------
3913eb59678SJeremy L Thompson // Tests
3923eb59678SJeremy L Thompson // ----------------------------------------------------------------------------
3933eb59678SJeremy L Thompson #[cfg(test)]
3943eb59678SJeremy L Thompson mod tests {
3953eb59678SJeremy L Thompson     use super::*;
3963eb59678SJeremy L Thompson 
3973eb59678SJeremy L Thompson     #[test]
3983eb59678SJeremy L Thompson     fn example_2_vector_1d() {
3993eb59678SJeremy L Thompson         let options = opt::Opt {
4003eb59678SJeremy L Thompson             ceed_spec: "/cpu/self/ref/serial".to_string(),
4013eb59678SJeremy L Thompson             dim: 1,
4023eb59678SJeremy L Thompson             mesh_degree: 4,
4033eb59678SJeremy L Thompson             solution_degree: 4,
4043eb59678SJeremy L Thompson             num_qpts: 6,
4053eb59678SJeremy L Thompson             problem_size_requested: -1,
4063eb59678SJeremy L Thompson             test: true,
4073eb59678SJeremy L Thompson             quiet: true,
4083eb59678SJeremy L Thompson             gallery: false,
4093eb59678SJeremy L Thompson         };
4103eb59678SJeremy L Thompson         assert!(example_2_vector(options).is_ok());
4113eb59678SJeremy L Thompson     }
4123eb59678SJeremy L Thompson 
4133eb59678SJeremy L Thompson     #[test]
4143eb59678SJeremy L Thompson     fn example_2_vector_2d() {
4153eb59678SJeremy L Thompson         let options = opt::Opt {
4163eb59678SJeremy L Thompson             ceed_spec: "/cpu/self/ref/serial".to_string(),
4173eb59678SJeremy L Thompson             dim: 2,
4183eb59678SJeremy L Thompson             mesh_degree: 4,
4193eb59678SJeremy L Thompson             solution_degree: 4,
4203eb59678SJeremy L Thompson             num_qpts: 6,
4213eb59678SJeremy L Thompson             problem_size_requested: -1,
4223eb59678SJeremy L Thompson             test: true,
4233eb59678SJeremy L Thompson             quiet: true,
4243eb59678SJeremy L Thompson             gallery: false,
4253eb59678SJeremy L Thompson         };
4263eb59678SJeremy L Thompson         assert!(example_2_vector(options).is_ok());
4273eb59678SJeremy L Thompson     }
4283eb59678SJeremy L Thompson 
4293eb59678SJeremy L Thompson     #[test]
4303eb59678SJeremy L Thompson     fn example_2_vector_3d() {
4313eb59678SJeremy L Thompson         let options = opt::Opt {
4323eb59678SJeremy L Thompson             ceed_spec: "/cpu/self/ref/serial".to_string(),
4333eb59678SJeremy L Thompson             dim: 3,
4343eb59678SJeremy L Thompson             mesh_degree: 4,
4353eb59678SJeremy L Thompson             solution_degree: 4,
4363eb59678SJeremy L Thompson             num_qpts: 6,
4373eb59678SJeremy L Thompson             problem_size_requested: -1,
4383eb59678SJeremy L Thompson             test: true,
4393eb59678SJeremy L Thompson             quiet: false,
4403eb59678SJeremy L Thompson             gallery: false,
4413eb59678SJeremy L Thompson         };
4423eb59678SJeremy L Thompson         assert!(example_2_vector(options).is_ok());
4433eb59678SJeremy L Thompson     }
4443eb59678SJeremy L Thompson 
4453eb59678SJeremy L Thompson     #[test]
4463eb59678SJeremy L Thompson     fn example_2_vector_1d_gallery() {
4473eb59678SJeremy L Thompson         let options = opt::Opt {
4483eb59678SJeremy L Thompson             ceed_spec: "/cpu/self/ref/serial".to_string(),
4493eb59678SJeremy L Thompson             dim: 1,
4503eb59678SJeremy L Thompson             mesh_degree: 4,
4513eb59678SJeremy L Thompson             solution_degree: 4,
4523eb59678SJeremy L Thompson             num_qpts: 6,
4533eb59678SJeremy L Thompson             problem_size_requested: -1,
4543eb59678SJeremy L Thompson             test: true,
4553eb59678SJeremy L Thompson             quiet: true,
4563eb59678SJeremy L Thompson             gallery: true,
4573eb59678SJeremy L Thompson         };
4583eb59678SJeremy L Thompson         assert!(example_2_vector(options).is_ok());
4593eb59678SJeremy L Thompson     }
4603eb59678SJeremy L Thompson 
4613eb59678SJeremy L Thompson     #[test]
4623eb59678SJeremy L Thompson     fn example_2_vector_2d_gallery() {
4633eb59678SJeremy L Thompson         let options = opt::Opt {
4643eb59678SJeremy L Thompson             ceed_spec: "/cpu/self/ref/serial".to_string(),
4653eb59678SJeremy L Thompson             dim: 2,
4663eb59678SJeremy L Thompson             mesh_degree: 4,
4673eb59678SJeremy L Thompson             solution_degree: 4,
4683eb59678SJeremy L Thompson             num_qpts: 6,
4693eb59678SJeremy L Thompson             problem_size_requested: -1,
4703eb59678SJeremy L Thompson             test: true,
4713eb59678SJeremy L Thompson             quiet: true,
4723eb59678SJeremy L Thompson             gallery: true,
4733eb59678SJeremy L Thompson         };
4743eb59678SJeremy L Thompson         assert!(example_2_vector(options).is_ok());
4753eb59678SJeremy L Thompson     }
4763eb59678SJeremy L Thompson 
4773eb59678SJeremy L Thompson     #[test]
4783eb59678SJeremy L Thompson     fn example_2_vector_3d_gallery() {
4793eb59678SJeremy L Thompson         let options = opt::Opt {
4803eb59678SJeremy L Thompson             ceed_spec: "/cpu/self/ref/serial".to_string(),
4813eb59678SJeremy L Thompson             dim: 3,
4823eb59678SJeremy L Thompson             mesh_degree: 4,
4833eb59678SJeremy L Thompson             solution_degree: 4,
4843eb59678SJeremy L Thompson             num_qpts: 6,
4853eb59678SJeremy L Thompson             problem_size_requested: -1,
4863eb59678SJeremy L Thompson             test: true,
4873eb59678SJeremy L Thompson             quiet: true,
4883eb59678SJeremy L Thompson             gallery: true,
4893eb59678SJeremy L Thompson         };
4903eb59678SJeremy L Thompson         assert!(example_2_vector(options).is_ok());
4913eb59678SJeremy L Thompson     }
4923eb59678SJeremy L Thompson }
4933eb59678SJeremy L Thompson 
4943eb59678SJeremy L Thompson // ----------------------------------------------------------------------------
495