xref: /libCEED/examples/rust/ex2-surface-vector/src/main.rs (revision 3eb59678ecb8a0fe884fb9297a6048221d053835)
1*3eb59678SJeremy L Thompson // Copyright (c) 2017-2025, Lawrence Livermore National Security, LLC and other CEED contributors.
2*3eb59678SJeremy L Thompson // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3*3eb59678SJeremy L Thompson //
4*3eb59678SJeremy L Thompson // SPDX-License-Identifier: BSD-2-Clause
5*3eb59678SJeremy L Thompson //
6*3eb59678SJeremy L Thompson // This file is part of CEED:  http://github.com/ceed
7*3eb59678SJeremy L Thompson //
8*3eb59678SJeremy L Thompson //                             libCEED Example 4
9*3eb59678SJeremy L Thompson //
10*3eb59678SJeremy L Thompson // This example illustrates a simple usage of libCEED to compute the surface
11*3eb59678SJeremy L Thompson // area of a 3D body using matrix-free application of a diffusion operator.
12*3eb59678SJeremy L Thompson // Arbitrary mesh and solution degrees in 1D, 2D and 3D are supported from the
13*3eb59678SJeremy L Thompson // same code. This calculation is executed in triplicate with a 3 component
14*3eb59678SJeremy L Thompson // vector system.
15*3eb59678SJeremy L Thompson //
16*3eb59678SJeremy L Thompson // The example has no dependencies, and is designed to be self-contained. For
17*3eb59678SJeremy L Thompson // additional examples that use external discretization libraries (MFEM, PETSc,
18*3eb59678SJeremy L Thompson // etc.) see the subdirectories in libceed/examples.
19*3eb59678SJeremy L Thompson //
20*3eb59678SJeremy L Thompson // All libCEED objects use a Ceed device object constructed based on a command
21*3eb59678SJeremy L Thompson // line argument (-ceed).
22*3eb59678SJeremy L Thompson 
23*3eb59678SJeremy L Thompson use clap::Parser;
24*3eb59678SJeremy L Thompson use libceed::{
25*3eb59678SJeremy L Thompson     BasisOpt, Ceed, ElemRestrictionOpt, QFunctionInputs, QFunctionOpt, QFunctionOutputs, VectorOpt,
26*3eb59678SJeremy L Thompson };
27*3eb59678SJeremy L Thompson mod opt;
28*3eb59678SJeremy L Thompson mod transform;
29*3eb59678SJeremy L Thompson 
30*3eb59678SJeremy L Thompson // ----------------------------------------------------------------------------
31*3eb59678SJeremy L Thompson // Example 4
32*3eb59678SJeremy L Thompson // ----------------------------------------------------------------------------
33*3eb59678SJeremy L Thompson fn main() -> libceed::Result<()> {
34*3eb59678SJeremy L Thompson     let options = opt::Opt::parse();
35*3eb59678SJeremy L Thompson     example_2_vector(options)
36*3eb59678SJeremy L Thompson }
37*3eb59678SJeremy L Thompson 
38*3eb59678SJeremy L Thompson #[allow(clippy::erasing_op)]
39*3eb59678SJeremy L Thompson #[allow(clippy::identity_op)]
40*3eb59678SJeremy L Thompson fn example_2_vector(options: opt::Opt) -> libceed::Result<()> {
41*3eb59678SJeremy L Thompson     // Process command line arguments
42*3eb59678SJeremy L Thompson     let opt::Opt {
43*3eb59678SJeremy L Thompson         ceed_spec,
44*3eb59678SJeremy L Thompson         dim,
45*3eb59678SJeremy L Thompson         mesh_degree,
46*3eb59678SJeremy L Thompson         solution_degree,
47*3eb59678SJeremy L Thompson         num_qpts,
48*3eb59678SJeremy L Thompson         problem_size_requested,
49*3eb59678SJeremy L Thompson         test,
50*3eb59678SJeremy L Thompson         quiet,
51*3eb59678SJeremy L Thompson         gallery,
52*3eb59678SJeremy L Thompson     } = options;
53*3eb59678SJeremy L Thompson     assert!((0..=3).contains(&dim));
54*3eb59678SJeremy L Thompson     assert!(mesh_degree >= 1);
55*3eb59678SJeremy L Thompson     assert!(solution_degree >= 1);
56*3eb59678SJeremy L Thompson     assert!(num_qpts >= 1);
57*3eb59678SJeremy L Thompson     let ncomp_x = dim;
58*3eb59678SJeremy L Thompson     let problem_size: i64 = if problem_size_requested < 0 {
59*3eb59678SJeremy L Thompson         if test {
60*3eb59678SJeremy L Thompson             16 * 16 * (dim * dim) as i64
61*3eb59678SJeremy L Thompson         } else {
62*3eb59678SJeremy L Thompson             256 * 1024
63*3eb59678SJeremy L Thompson         }
64*3eb59678SJeremy L Thompson     } else {
65*3eb59678SJeremy L Thompson         problem_size_requested
66*3eb59678SJeremy L Thompson     };
67*3eb59678SJeremy L Thompson     let ncomp_u = 3;
68*3eb59678SJeremy L Thompson 
69*3eb59678SJeremy L Thompson     // Summary output
70*3eb59678SJeremy L Thompson     if !quiet {
71*3eb59678SJeremy L Thompson         println!("Selected options: [command line option] : <current value>");
72*3eb59678SJeremy L Thompson         println!("    Ceed specification [-c] : {}", ceed_spec);
73*3eb59678SJeremy L Thompson         println!("    Mesh dimension     [-d] : {}", dim);
74*3eb59678SJeremy L Thompson         println!("    Mesh degree        [-m] : {}", mesh_degree);
75*3eb59678SJeremy L Thompson         println!("    Solution degree    [-p] : {}", solution_degree);
76*3eb59678SJeremy L Thompson         println!("    Num. 1D quadr. pts [-q] : {}", num_qpts);
77*3eb59678SJeremy L Thompson         println!("    Approx. # unknowns [-s] : {}", problem_size);
78*3eb59678SJeremy L Thompson         println!(
79*3eb59678SJeremy L Thompson             "    QFunction source   [-g] : {}",
80*3eb59678SJeremy L Thompson             if gallery { "gallery" } else { "user closure" }
81*3eb59678SJeremy L Thompson         );
82*3eb59678SJeremy L Thompson     }
83*3eb59678SJeremy L Thompson 
84*3eb59678SJeremy L Thompson     // Initalize ceed context
85*3eb59678SJeremy L Thompson     let ceed = Ceed::init(&ceed_spec);
86*3eb59678SJeremy L Thompson 
87*3eb59678SJeremy L Thompson     // Mesh and solution bases
88*3eb59678SJeremy L Thompson     let basis_mesh = ceed.basis_tensor_H1_Lagrange(
89*3eb59678SJeremy L Thompson         dim,
90*3eb59678SJeremy L Thompson         ncomp_x,
91*3eb59678SJeremy L Thompson         mesh_degree + 1,
92*3eb59678SJeremy L Thompson         num_qpts,
93*3eb59678SJeremy L Thompson         libceed::QuadMode::Gauss,
94*3eb59678SJeremy L Thompson     )?;
95*3eb59678SJeremy L Thompson     let basis_solution = ceed.basis_tensor_H1_Lagrange(
96*3eb59678SJeremy L Thompson         dim,
97*3eb59678SJeremy L Thompson         ncomp_u,
98*3eb59678SJeremy L Thompson         solution_degree + 1,
99*3eb59678SJeremy L Thompson         num_qpts,
100*3eb59678SJeremy L Thompson         libceed::QuadMode::Gauss,
101*3eb59678SJeremy L Thompson     )?;
102*3eb59678SJeremy L Thompson 
103*3eb59678SJeremy L Thompson     // Determine mesh size from approximate problem size
104*3eb59678SJeremy L Thompson     let num_xyz = mesh::cartesian_mesh_size(dim, solution_degree, problem_size);
105*3eb59678SJeremy L Thompson     if !quiet {
106*3eb59678SJeremy L Thompson         print!("\nMesh size                   : nx = {}", num_xyz[0]);
107*3eb59678SJeremy L Thompson         if dim > 1 {
108*3eb59678SJeremy L Thompson             print!(", ny = {}", num_xyz[1]);
109*3eb59678SJeremy L Thompson         }
110*3eb59678SJeremy L Thompson         if dim > 2 {
111*3eb59678SJeremy L Thompson             print!(", nz = {}", num_xyz[2]);
112*3eb59678SJeremy L Thompson         }
113*3eb59678SJeremy L Thompson         println!();
114*3eb59678SJeremy L Thompson     }
115*3eb59678SJeremy L Thompson 
116*3eb59678SJeremy L Thompson     // Build ElemRestriction objects describing the mesh and solution discrete
117*3eb59678SJeremy L Thompson     // representations
118*3eb59678SJeremy L Thompson     let (rstr_mesh, _) =
119*3eb59678SJeremy L Thompson         mesh::build_cartesian_restriction(&ceed, dim, num_xyz, mesh_degree, ncomp_x, num_qpts)?;
120*3eb59678SJeremy L Thompson     let (_, rstr_qdata) = mesh::build_cartesian_restriction(
121*3eb59678SJeremy L Thompson         &ceed,
122*3eb59678SJeremy L Thompson         dim,
123*3eb59678SJeremy L Thompson         num_xyz,
124*3eb59678SJeremy L Thompson         solution_degree,
125*3eb59678SJeremy L Thompson         dim * (dim + 1) / 2,
126*3eb59678SJeremy L Thompson         num_qpts,
127*3eb59678SJeremy L Thompson     )?;
128*3eb59678SJeremy L Thompson 
129*3eb59678SJeremy L Thompson     let (rstr_solution, _) =
130*3eb59678SJeremy L Thompson         mesh::build_cartesian_restriction(&ceed, dim, num_xyz, solution_degree, ncomp_u, num_qpts)?;
131*3eb59678SJeremy L Thompson     let mesh_size = rstr_mesh.lvector_size();
132*3eb59678SJeremy L Thompson     let solution_size = rstr_solution.lvector_size();
133*3eb59678SJeremy L Thompson     if !quiet {
134*3eb59678SJeremy L Thompson         println!("Number of mesh nodes        : {}", mesh_size / dim);
135*3eb59678SJeremy L Thompson         println!("Number of solution nodes    : {}", solution_size);
136*3eb59678SJeremy L Thompson     }
137*3eb59678SJeremy L Thompson 
138*3eb59678SJeremy L Thompson     // Create a Vector with the mesh coordinates
139*3eb59678SJeremy L Thompson     let mut mesh_coords = mesh::cartesian_mesh_coords(&ceed, dim, num_xyz, mesh_degree, mesh_size)?;
140*3eb59678SJeremy L Thompson 
141*3eb59678SJeremy L Thompson     // Apply a transformation to the mesh coordinates
142*3eb59678SJeremy L Thompson     let exact_area = transform::transform_mesh_coordinates(dim, &mut mesh_coords)?;
143*3eb59678SJeremy L Thompson 
144*3eb59678SJeremy L Thompson     // QFunction that builds the quadrature data for the diff operator
145*3eb59678SJeremy L Thompson     // -- QFunction from user closure
146*3eb59678SJeremy L Thompson     let build_diff = move |[jacobian, weights, ..]: QFunctionInputs,
147*3eb59678SJeremy L Thompson                            [qdata, ..]: QFunctionOutputs| {
148*3eb59678SJeremy L Thompson         // Build quadrature data
149*3eb59678SJeremy L Thompson         match dim {
150*3eb59678SJeremy L Thompson             1 => qdata
151*3eb59678SJeremy L Thompson                 .iter_mut()
152*3eb59678SJeremy L Thompson                 .zip(jacobian.iter().zip(weights.iter()))
153*3eb59678SJeremy L Thompson                 .for_each(|(qdata, (j, weight))| *qdata = weight / j),
154*3eb59678SJeremy L Thompson             2 => {
155*3eb59678SJeremy L Thompson                 let q = qdata.len() / 3;
156*3eb59678SJeremy L Thompson                 for i in 0..q {
157*3eb59678SJeremy L Thompson                     let j11 = jacobian[i + q * 0];
158*3eb59678SJeremy L Thompson                     let j21 = jacobian[i + q * 1];
159*3eb59678SJeremy L Thompson                     let j12 = jacobian[i + q * 2];
160*3eb59678SJeremy L Thompson                     let j22 = jacobian[i + q * 3];
161*3eb59678SJeremy L Thompson                     let qw = weights[i] / (j11 * j22 - j21 * j12);
162*3eb59678SJeremy L Thompson                     qdata[i + q * 0] = qw * (j12 * j12 + j22 * j22);
163*3eb59678SJeremy L Thompson                     qdata[i + q * 1] = qw * (j11 * j11 + j21 * j21);
164*3eb59678SJeremy L Thompson                     qdata[i + q * 2] = -qw * (j11 * j12 + j21 * j22);
165*3eb59678SJeremy L Thompson                 }
166*3eb59678SJeremy L Thompson             }
167*3eb59678SJeremy L Thompson             3 => {
168*3eb59678SJeremy L Thompson                 let q = qdata.len() / 6;
169*3eb59678SJeremy L Thompson                 for i in 0..q {
170*3eb59678SJeremy L Thompson                     let mut a = [0.0; 9];
171*3eb59678SJeremy L Thompson                     for j in 0..3 {
172*3eb59678SJeremy L Thompson                         for k in 0..3 {
173*3eb59678SJeremy L Thompson                             a[k * 3 + j] = jacobian[i + q * ((j + 1) % 3 + 3 * ((k + 1) % 3))]
174*3eb59678SJeremy L Thompson                                 * jacobian[i + q * ((j + 2) % 3 + 3 * ((k + 2) % 3))]
175*3eb59678SJeremy L Thompson                                 - jacobian[i + q * ((j + 1) % 3 + 3 * ((k + 2) % 3))]
176*3eb59678SJeremy L Thompson                                     * jacobian[i + q * ((j + 2) % 3 + 3 * ((k + 1) % 3))];
177*3eb59678SJeremy L Thompson                         }
178*3eb59678SJeremy L Thompson                     }
179*3eb59678SJeremy L Thompson                     let qw = weights[i]
180*3eb59678SJeremy L Thompson                         / (jacobian[i + q * 0] * a[0 * 3 + 0]
181*3eb59678SJeremy L Thompson                             + jacobian[i + q * 1] * a[0 * 3 + 1]
182*3eb59678SJeremy L Thompson                             + jacobian[i + q * 2] * a[0 * 3 + 2]);
183*3eb59678SJeremy L Thompson                     qdata[i + q * 0] = qw
184*3eb59678SJeremy L Thompson                         * (a[0 * 3 + 0] * a[0 * 3 + 0]
185*3eb59678SJeremy L Thompson                             + a[0 * 3 + 1] * a[0 * 3 + 1]
186*3eb59678SJeremy L Thompson                             + a[0 * 3 + 2] * a[0 * 3 + 2]);
187*3eb59678SJeremy L Thompson                     qdata[i + q * 1] = qw
188*3eb59678SJeremy L Thompson                         * (a[1 * 3 + 0] * a[1 * 3 + 0]
189*3eb59678SJeremy L Thompson                             + a[1 * 3 + 1] * a[1 * 3 + 1]
190*3eb59678SJeremy L Thompson                             + a[1 * 3 + 2] * a[1 * 3 + 2]);
191*3eb59678SJeremy L Thompson                     qdata[i + q * 2] = qw
192*3eb59678SJeremy L Thompson                         * (a[2 * 3 + 0] * a[2 * 3 + 0]
193*3eb59678SJeremy L Thompson                             + a[2 * 3 + 1] * a[2 * 3 + 1]
194*3eb59678SJeremy L Thompson                             + a[2 * 3 + 2] * a[2 * 3 + 2]);
195*3eb59678SJeremy L Thompson                     qdata[i + q * 3] = qw
196*3eb59678SJeremy L Thompson                         * (a[1 * 3 + 0] * a[2 * 3 + 0]
197*3eb59678SJeremy L Thompson                             + a[1 * 3 + 1] * a[2 * 3 + 1]
198*3eb59678SJeremy L Thompson                             + a[1 * 3 + 2] * a[2 * 3 + 2]);
199*3eb59678SJeremy L Thompson                     qdata[i + q * 4] = qw
200*3eb59678SJeremy L Thompson                         * (a[0 * 3 + 0] * a[2 * 3 + 0]
201*3eb59678SJeremy L Thompson                             + a[0 * 3 + 1] * a[2 * 3 + 1]
202*3eb59678SJeremy L Thompson                             + a[0 * 3 + 2] * a[2 * 3 + 2]);
203*3eb59678SJeremy L Thompson                     qdata[i + q * 5] = qw
204*3eb59678SJeremy L Thompson                         * (a[0 * 3 + 0] * a[1 * 3 + 0]
205*3eb59678SJeremy L Thompson                             + a[0 * 3 + 1] * a[1 * 3 + 1]
206*3eb59678SJeremy L Thompson                             + a[0 * 3 + 2] * a[1 * 3 + 2]);
207*3eb59678SJeremy L Thompson                 }
208*3eb59678SJeremy L Thompson             }
209*3eb59678SJeremy L Thompson             _ => unreachable!(),
210*3eb59678SJeremy L Thompson         };
211*3eb59678SJeremy L Thompson 
212*3eb59678SJeremy L Thompson         // Return clean error code
213*3eb59678SJeremy L Thompson         0
214*3eb59678SJeremy L Thompson     };
215*3eb59678SJeremy L Thompson     let qf_build_closure = ceed
216*3eb59678SJeremy L Thompson         .q_function_interior(1, Box::new(build_diff))?
217*3eb59678SJeremy L Thompson         .input("dx", ncomp_x * dim, libceed::EvalMode::Grad)?
218*3eb59678SJeremy L Thompson         .input("weights", 1, libceed::EvalMode::Weight)?
219*3eb59678SJeremy L Thompson         .output("qdata", dim * (dim + 1) / 2, libceed::EvalMode::None)?;
220*3eb59678SJeremy L Thompson     // -- QFunction from gallery
221*3eb59678SJeremy L Thompson     let qf_build_named = {
222*3eb59678SJeremy L Thompson         let name = format!("Poisson{}DBuild", dim);
223*3eb59678SJeremy L Thompson         ceed.q_function_interior_by_name(&name)?
224*3eb59678SJeremy L Thompson     };
225*3eb59678SJeremy L Thompson     // -- QFunction for use with Operator
226*3eb59678SJeremy L Thompson     let qf_build = if gallery {
227*3eb59678SJeremy L Thompson         QFunctionOpt::SomeQFunctionByName(&qf_build_named)
228*3eb59678SJeremy L Thompson     } else {
229*3eb59678SJeremy L Thompson         QFunctionOpt::SomeQFunction(&qf_build_closure)
230*3eb59678SJeremy L Thompson     };
231*3eb59678SJeremy L Thompson 
232*3eb59678SJeremy L Thompson     // Operator that build the quadrature data for the diff operator
233*3eb59678SJeremy L Thompson     let op_build = ceed
234*3eb59678SJeremy L Thompson         .operator(qf_build, QFunctionOpt::None, QFunctionOpt::None)?
235*3eb59678SJeremy L Thompson         .name("build qdata")?
236*3eb59678SJeremy L Thompson         .field("dx", &rstr_mesh, &basis_mesh, VectorOpt::Active)?
237*3eb59678SJeremy L Thompson         .field(
238*3eb59678SJeremy L Thompson             "weights",
239*3eb59678SJeremy L Thompson             ElemRestrictionOpt::None,
240*3eb59678SJeremy L Thompson             &basis_mesh,
241*3eb59678SJeremy L Thompson             VectorOpt::None,
242*3eb59678SJeremy L Thompson         )?
243*3eb59678SJeremy L Thompson         .field("qdata", &rstr_qdata, BasisOpt::None, VectorOpt::Active)?
244*3eb59678SJeremy L Thompson         .check()?;
245*3eb59678SJeremy L Thompson 
246*3eb59678SJeremy L Thompson     // Compute the quadrature data for the diff operator
247*3eb59678SJeremy L Thompson     let elem_qpts = num_qpts.pow(dim as u32);
248*3eb59678SJeremy L Thompson     let num_elem: usize = num_xyz.iter().take(dim).product();
249*3eb59678SJeremy L Thompson     let mut qdata = ceed.vector(num_elem * elem_qpts * dim * (dim + 1) / 2)?;
250*3eb59678SJeremy L Thompson     op_build.apply(&mesh_coords, &mut qdata)?;
251*3eb59678SJeremy L Thompson 
252*3eb59678SJeremy L Thompson     // QFunction that applies the diff operator
253*3eb59678SJeremy L Thompson     // -- QFunction from user closure
254*3eb59678SJeremy L Thompson     let apply_diff = move |[ug, qdata, ..]: QFunctionInputs, [vg, ..]: QFunctionOutputs| {
255*3eb59678SJeremy L Thompson         // Apply diffusion operator
256*3eb59678SJeremy L Thompson         match dim {
257*3eb59678SJeremy L Thompson             1 => {
258*3eb59678SJeremy L Thompson                 let q = qdata.len();
259*3eb59678SJeremy L Thompson                 for c in 0..ncomp_u {
260*3eb59678SJeremy L Thompson                     vg.iter_mut()
261*3eb59678SJeremy L Thompson                         .skip(c * q)
262*3eb59678SJeremy L Thompson                         .zip(ug.iter().skip(c * q).zip(qdata.iter()))
263*3eb59678SJeremy L Thompson                         .for_each(|(vg, (ug, w))| *vg = ug * w)
264*3eb59678SJeremy L Thompson                 }
265*3eb59678SJeremy L Thompson             }
266*3eb59678SJeremy L Thompson             2 => {
267*3eb59678SJeremy L Thompson                 let q = qdata.len() / 3;
268*3eb59678SJeremy L Thompson                 for i in 0..q {
269*3eb59678SJeremy L Thompson                     let dxdxdxdx_t = [
270*3eb59678SJeremy L Thompson                         [qdata[i + 0 * q], qdata[i + 2 * q]],
271*3eb59678SJeremy L Thompson                         [qdata[i + 2 * q], qdata[i + 1 * q]],
272*3eb59678SJeremy L Thompson                     ];
273*3eb59678SJeremy L Thompson                     for c in 0..ncomp_u {
274*3eb59678SJeremy L Thompson                         let du = [ug[i + (c + 0 * ncomp_u) * q], ug[i + (c + 1 * ncomp_u) * q]];
275*3eb59678SJeremy L Thompson                         for j in 0..dim {
276*3eb59678SJeremy L Thompson                             vg[i + (c + j * ncomp_u) * q] =
277*3eb59678SJeremy L Thompson                                 du[0] * dxdxdxdx_t[0][j] + du[1] * dxdxdxdx_t[1][j];
278*3eb59678SJeremy L Thompson                         }
279*3eb59678SJeremy L Thompson                     }
280*3eb59678SJeremy L Thompson                 }
281*3eb59678SJeremy L Thompson             }
282*3eb59678SJeremy L Thompson             3 => {
283*3eb59678SJeremy L Thompson                 let q = qdata.len() / 6;
284*3eb59678SJeremy L Thompson                 for i in 0..q {
285*3eb59678SJeremy L Thompson                     let dxdxdxdx_t = [
286*3eb59678SJeremy L Thompson                         [qdata[i + 0 * q], qdata[i + 5 * q], qdata[i + 4 * q]],
287*3eb59678SJeremy L Thompson                         [qdata[i + 5 * q], qdata[i + 1 * q], qdata[i + 3 * q]],
288*3eb59678SJeremy L Thompson                         [qdata[i + 4 * q], qdata[i + 3 * q], qdata[i + 2 * q]],
289*3eb59678SJeremy L Thompson                     ];
290*3eb59678SJeremy L Thompson                     for c in 0..ncomp_u {
291*3eb59678SJeremy L Thompson                         let du = [
292*3eb59678SJeremy L Thompson                             ug[i + (c + 0 * ncomp_u) * q],
293*3eb59678SJeremy L Thompson                             ug[i + (c + 1 * ncomp_u) * q],
294*3eb59678SJeremy L Thompson                             ug[i + (c + 2 * ncomp_u) * q],
295*3eb59678SJeremy L Thompson                         ];
296*3eb59678SJeremy L Thompson                         for j in 0..dim {
297*3eb59678SJeremy L Thompson                             vg[i + (c + j * ncomp_u) * q] = du[0] * dxdxdxdx_t[0][j]
298*3eb59678SJeremy L Thompson                                 + du[1] * dxdxdxdx_t[1][j]
299*3eb59678SJeremy L Thompson                                 + du[2] * dxdxdxdx_t[2][j];
300*3eb59678SJeremy L Thompson                         }
301*3eb59678SJeremy L Thompson                     }
302*3eb59678SJeremy L Thompson                 }
303*3eb59678SJeremy L Thompson             }
304*3eb59678SJeremy L Thompson             _ => unreachable!(),
305*3eb59678SJeremy L Thompson         };
306*3eb59678SJeremy L Thompson 
307*3eb59678SJeremy L Thompson         // Return clean error code
308*3eb59678SJeremy L Thompson         0
309*3eb59678SJeremy L Thompson     };
310*3eb59678SJeremy L Thompson     let qf_diff_closure = ceed
311*3eb59678SJeremy L Thompson         .q_function_interior(1, Box::new(apply_diff))?
312*3eb59678SJeremy L Thompson         .input("du", dim * ncomp_u, libceed::EvalMode::Grad)?
313*3eb59678SJeremy L Thompson         .input("qdata", dim * (dim + 1) / 2, libceed::EvalMode::None)?
314*3eb59678SJeremy L Thompson         .output("dv", dim * ncomp_u, libceed::EvalMode::Grad)?;
315*3eb59678SJeremy L Thompson     // -- QFunction from gallery
316*3eb59678SJeremy L Thompson     let qf_diff_named = {
317*3eb59678SJeremy L Thompson         let name = format!("Vector3Poisson{}DApply", dim);
318*3eb59678SJeremy L Thompson         ceed.q_function_interior_by_name(&name)?
319*3eb59678SJeremy L Thompson     };
320*3eb59678SJeremy L Thompson     // -- QFunction for use with Operator
321*3eb59678SJeremy L Thompson     let qf_diff = if gallery {
322*3eb59678SJeremy L Thompson         QFunctionOpt::SomeQFunctionByName(&qf_diff_named)
323*3eb59678SJeremy L Thompson     } else {
324*3eb59678SJeremy L Thompson         QFunctionOpt::SomeQFunction(&qf_diff_closure)
325*3eb59678SJeremy L Thompson     };
326*3eb59678SJeremy L Thompson 
327*3eb59678SJeremy L Thompson     // Diff Operator
328*3eb59678SJeremy L Thompson     let op_diff = ceed
329*3eb59678SJeremy L Thompson         .operator(qf_diff, QFunctionOpt::None, QFunctionOpt::None)?
330*3eb59678SJeremy L Thompson         .name("Poisson")?
331*3eb59678SJeremy L Thompson         .field("du", &rstr_solution, &basis_solution, VectorOpt::Active)?
332*3eb59678SJeremy L Thompson         .field("qdata", &rstr_qdata, BasisOpt::None, &qdata)?
333*3eb59678SJeremy L Thompson         .field("dv", &rstr_solution, &basis_solution, VectorOpt::Active)?
334*3eb59678SJeremy L Thompson         .check()?;
335*3eb59678SJeremy L Thompson 
336*3eb59678SJeremy L Thompson     // Solution vectors
337*3eb59678SJeremy L Thompson     let mut u = ceed.vector(solution_size)?;
338*3eb59678SJeremy L Thompson     let mut v = ceed.vector(solution_size)?;
339*3eb59678SJeremy L Thompson 
340*3eb59678SJeremy L Thompson     // Initialize u with sum of node coordinates
341*3eb59678SJeremy L Thompson     let coords = mesh_coords.view()?;
342*3eb59678SJeremy L Thompson     u.set_value(0.0)?;
343*3eb59678SJeremy L Thompson     for c in 0..ncomp_u {
344*3eb59678SJeremy L Thompson         let q = solution_size / ncomp_u;
345*3eb59678SJeremy L Thompson         u.view_mut()?
346*3eb59678SJeremy L Thompson             .iter_mut()
347*3eb59678SJeremy L Thompson             .skip(c * q)
348*3eb59678SJeremy L Thompson             .take(q)
349*3eb59678SJeremy L Thompson             .enumerate()
350*3eb59678SJeremy L Thompson             .for_each(|(i, u)| {
351*3eb59678SJeremy L Thompson                 *u = (0..dim).map(|d| coords[i + d * q]).sum::<libceed::Scalar>()
352*3eb59678SJeremy L Thompson                     * (c + 1) as libceed::Scalar;
353*3eb59678SJeremy L Thompson             });
354*3eb59678SJeremy L Thompson     }
355*3eb59678SJeremy L Thompson 
356*3eb59678SJeremy L Thompson     // Apply the diff operator
357*3eb59678SJeremy L Thompson     op_diff.apply(&u, &mut v)?;
358*3eb59678SJeremy L Thompson 
359*3eb59678SJeremy L Thompson     // Compute the mesh surface area
360*3eb59678SJeremy L Thompson     let area: libceed::Scalar = v
361*3eb59678SJeremy L Thompson         .view()?
362*3eb59678SJeremy L Thompson         .iter()
363*3eb59678SJeremy L Thompson         .map(|v| (*v).abs())
364*3eb59678SJeremy L Thompson         .sum::<libceed::Scalar>()
365*3eb59678SJeremy L Thompson         / ((ncomp_u * (ncomp_u + 1)) / 2) as libceed::Scalar;
366*3eb59678SJeremy L Thompson 
367*3eb59678SJeremy L Thompson     // Output results
368*3eb59678SJeremy L Thompson     if !quiet {
369*3eb59678SJeremy L Thompson         println!("Exact mesh surface area     : {:.12}", exact_area);
370*3eb59678SJeremy L Thompson         println!("Computed mesh surface_area  : {:.12}", area);
371*3eb59678SJeremy L Thompson         println!("Surface area error          : {:.12e}", area - exact_area);
372*3eb59678SJeremy L Thompson     }
373*3eb59678SJeremy L Thompson     let tolerance = match dim {
374*3eb59678SJeremy L Thompson         1 => 1E-5,
375*3eb59678SJeremy L Thompson         _ => 1E-1,
376*3eb59678SJeremy L Thompson     };
377*3eb59678SJeremy L Thompson     let error = (area - exact_area).abs();
378*3eb59678SJeremy L Thompson     if error > tolerance {
379*3eb59678SJeremy L Thompson         println!("Volume error too large: {:.12e}", error);
380*3eb59678SJeremy L Thompson         return Err(libceed::Error {
381*3eb59678SJeremy L Thompson             message: format!(
382*3eb59678SJeremy L Thompson                 "Volume error too large - expected: {:.12e}, actual: {:.12e}",
383*3eb59678SJeremy L Thompson                 tolerance, error
384*3eb59678SJeremy L Thompson             ),
385*3eb59678SJeremy L Thompson         });
386*3eb59678SJeremy L Thompson     }
387*3eb59678SJeremy L Thompson     Ok(())
388*3eb59678SJeremy L Thompson }
389*3eb59678SJeremy L Thompson 
390*3eb59678SJeremy L Thompson // ----------------------------------------------------------------------------
391*3eb59678SJeremy L Thompson // Tests
392*3eb59678SJeremy L Thompson // ----------------------------------------------------------------------------
393*3eb59678SJeremy L Thompson #[cfg(test)]
394*3eb59678SJeremy L Thompson mod tests {
395*3eb59678SJeremy L Thompson     use super::*;
396*3eb59678SJeremy L Thompson 
397*3eb59678SJeremy L Thompson     #[test]
398*3eb59678SJeremy L Thompson     fn example_2_vector_1d() {
399*3eb59678SJeremy L Thompson         let options = opt::Opt {
400*3eb59678SJeremy L Thompson             ceed_spec: "/cpu/self/ref/serial".to_string(),
401*3eb59678SJeremy L Thompson             dim: 1,
402*3eb59678SJeremy L Thompson             mesh_degree: 4,
403*3eb59678SJeremy L Thompson             solution_degree: 4,
404*3eb59678SJeremy L Thompson             num_qpts: 6,
405*3eb59678SJeremy L Thompson             problem_size_requested: -1,
406*3eb59678SJeremy L Thompson             test: true,
407*3eb59678SJeremy L Thompson             quiet: true,
408*3eb59678SJeremy L Thompson             gallery: false,
409*3eb59678SJeremy L Thompson         };
410*3eb59678SJeremy L Thompson         assert!(example_2_vector(options).is_ok());
411*3eb59678SJeremy L Thompson     }
412*3eb59678SJeremy L Thompson 
413*3eb59678SJeremy L Thompson     #[test]
414*3eb59678SJeremy L Thompson     fn example_2_vector_2d() {
415*3eb59678SJeremy L Thompson         let options = opt::Opt {
416*3eb59678SJeremy L Thompson             ceed_spec: "/cpu/self/ref/serial".to_string(),
417*3eb59678SJeremy L Thompson             dim: 2,
418*3eb59678SJeremy L Thompson             mesh_degree: 4,
419*3eb59678SJeremy L Thompson             solution_degree: 4,
420*3eb59678SJeremy L Thompson             num_qpts: 6,
421*3eb59678SJeremy L Thompson             problem_size_requested: -1,
422*3eb59678SJeremy L Thompson             test: true,
423*3eb59678SJeremy L Thompson             quiet: true,
424*3eb59678SJeremy L Thompson             gallery: false,
425*3eb59678SJeremy L Thompson         };
426*3eb59678SJeremy L Thompson         assert!(example_2_vector(options).is_ok());
427*3eb59678SJeremy L Thompson     }
428*3eb59678SJeremy L Thompson 
429*3eb59678SJeremy L Thompson     #[test]
430*3eb59678SJeremy L Thompson     fn example_2_vector_3d() {
431*3eb59678SJeremy L Thompson         let options = opt::Opt {
432*3eb59678SJeremy L Thompson             ceed_spec: "/cpu/self/ref/serial".to_string(),
433*3eb59678SJeremy L Thompson             dim: 3,
434*3eb59678SJeremy L Thompson             mesh_degree: 4,
435*3eb59678SJeremy L Thompson             solution_degree: 4,
436*3eb59678SJeremy L Thompson             num_qpts: 6,
437*3eb59678SJeremy L Thompson             problem_size_requested: -1,
438*3eb59678SJeremy L Thompson             test: true,
439*3eb59678SJeremy L Thompson             quiet: false,
440*3eb59678SJeremy L Thompson             gallery: false,
441*3eb59678SJeremy L Thompson         };
442*3eb59678SJeremy L Thompson         assert!(example_2_vector(options).is_ok());
443*3eb59678SJeremy L Thompson     }
444*3eb59678SJeremy L Thompson 
445*3eb59678SJeremy L Thompson     #[test]
446*3eb59678SJeremy L Thompson     fn example_2_vector_1d_gallery() {
447*3eb59678SJeremy L Thompson         let options = opt::Opt {
448*3eb59678SJeremy L Thompson             ceed_spec: "/cpu/self/ref/serial".to_string(),
449*3eb59678SJeremy L Thompson             dim: 1,
450*3eb59678SJeremy L Thompson             mesh_degree: 4,
451*3eb59678SJeremy L Thompson             solution_degree: 4,
452*3eb59678SJeremy L Thompson             num_qpts: 6,
453*3eb59678SJeremy L Thompson             problem_size_requested: -1,
454*3eb59678SJeremy L Thompson             test: true,
455*3eb59678SJeremy L Thompson             quiet: true,
456*3eb59678SJeremy L Thompson             gallery: true,
457*3eb59678SJeremy L Thompson         };
458*3eb59678SJeremy L Thompson         assert!(example_2_vector(options).is_ok());
459*3eb59678SJeremy L Thompson     }
460*3eb59678SJeremy L Thompson 
461*3eb59678SJeremy L Thompson     #[test]
462*3eb59678SJeremy L Thompson     fn example_2_vector_2d_gallery() {
463*3eb59678SJeremy L Thompson         let options = opt::Opt {
464*3eb59678SJeremy L Thompson             ceed_spec: "/cpu/self/ref/serial".to_string(),
465*3eb59678SJeremy L Thompson             dim: 2,
466*3eb59678SJeremy L Thompson             mesh_degree: 4,
467*3eb59678SJeremy L Thompson             solution_degree: 4,
468*3eb59678SJeremy L Thompson             num_qpts: 6,
469*3eb59678SJeremy L Thompson             problem_size_requested: -1,
470*3eb59678SJeremy L Thompson             test: true,
471*3eb59678SJeremy L Thompson             quiet: true,
472*3eb59678SJeremy L Thompson             gallery: true,
473*3eb59678SJeremy L Thompson         };
474*3eb59678SJeremy L Thompson         assert!(example_2_vector(options).is_ok());
475*3eb59678SJeremy L Thompson     }
476*3eb59678SJeremy L Thompson 
477*3eb59678SJeremy L Thompson     #[test]
478*3eb59678SJeremy L Thompson     fn example_2_vector_3d_gallery() {
479*3eb59678SJeremy L Thompson         let options = opt::Opt {
480*3eb59678SJeremy L Thompson             ceed_spec: "/cpu/self/ref/serial".to_string(),
481*3eb59678SJeremy L Thompson             dim: 3,
482*3eb59678SJeremy L Thompson             mesh_degree: 4,
483*3eb59678SJeremy L Thompson             solution_degree: 4,
484*3eb59678SJeremy L Thompson             num_qpts: 6,
485*3eb59678SJeremy L Thompson             problem_size_requested: -1,
486*3eb59678SJeremy L Thompson             test: true,
487*3eb59678SJeremy L Thompson             quiet: true,
488*3eb59678SJeremy L Thompson             gallery: true,
489*3eb59678SJeremy L Thompson         };
490*3eb59678SJeremy L Thompson         assert!(example_2_vector(options).is_ok());
491*3eb59678SJeremy L Thompson     }
492*3eb59678SJeremy L Thompson }
493*3eb59678SJeremy L Thompson 
494*3eb59678SJeremy L Thompson // ----------------------------------------------------------------------------
495