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 1 9*3eb59678SJeremy L Thompson // 10*3eb59678SJeremy L Thompson // This example illustrates a simple usage of libCEED to compute the volume of a 11*3eb59678SJeremy L Thompson // 3D body using matrix-free application of a mass + diff operator. Arbitrary 12*3eb59678SJeremy L Thompson // mesh and solution orders in 1D, 2D and 3D are supported from the same code. 13*3eb59678SJeremy L Thompson // 14*3eb59678SJeremy L Thompson // The example has no dependencies, and is designed to be self-contained. For 15*3eb59678SJeremy L Thompson // additional examples that use external discretization libraries (MFEM, PETSc, 16*3eb59678SJeremy L Thompson // etc.) see the subdirectories in libceed/examples. 17*3eb59678SJeremy L Thompson // 18*3eb59678SJeremy L Thompson // All libCEED objects use a Ceed device object constructed based on a command 19*3eb59678SJeremy L Thompson // line argument (-ceed). 20*3eb59678SJeremy L Thompson 21*3eb59678SJeremy L Thompson use clap::Parser; 22*3eb59678SJeremy L Thompson use libceed::{ 23*3eb59678SJeremy L Thompson BasisOpt, Ceed, ElemRestrictionOpt, QFunctionInputs, QFunctionOpt, QFunctionOutputs, VectorOpt, 24*3eb59678SJeremy L Thompson }; 25*3eb59678SJeremy L Thompson mod opt; 26*3eb59678SJeremy L Thompson mod transform; 27*3eb59678SJeremy L Thompson 28*3eb59678SJeremy L Thompson // ---------------------------------------------------------------------------- 29*3eb59678SJeremy L Thompson // Example 1 30*3eb59678SJeremy L Thompson // ---------------------------------------------------------------------------- 31*3eb59678SJeremy L Thompson fn main() -> libceed::Result<()> { 32*3eb59678SJeremy L Thompson let options = opt::Opt::parse(); 33*3eb59678SJeremy L Thompson example_3(options) 34*3eb59678SJeremy L Thompson } 35*3eb59678SJeremy L Thompson 36*3eb59678SJeremy L Thompson #[allow(clippy::erasing_op)] 37*3eb59678SJeremy L Thompson #[allow(clippy::identity_op)] 38*3eb59678SJeremy L Thompson fn example_3(options: opt::Opt) -> libceed::Result<()> { 39*3eb59678SJeremy L Thompson // Process command line arguments 40*3eb59678SJeremy L Thompson let opt::Opt { 41*3eb59678SJeremy L Thompson ceed_spec, 42*3eb59678SJeremy L Thompson dim, 43*3eb59678SJeremy L Thompson mesh_degree, 44*3eb59678SJeremy L Thompson solution_degree, 45*3eb59678SJeremy L Thompson num_qpts, 46*3eb59678SJeremy L Thompson problem_size_requested, 47*3eb59678SJeremy L Thompson test, 48*3eb59678SJeremy L Thompson quiet, 49*3eb59678SJeremy L Thompson } = options; 50*3eb59678SJeremy L Thompson assert!((1..=3).contains(&dim)); 51*3eb59678SJeremy L Thompson assert!(mesh_degree >= 1); 52*3eb59678SJeremy L Thompson assert!(solution_degree >= 1); 53*3eb59678SJeremy L Thompson assert!(num_qpts >= 1); 54*3eb59678SJeremy L Thompson let ncomp_x = dim; 55*3eb59678SJeremy L Thompson let problem_size: i64 = if problem_size_requested < 0 { 56*3eb59678SJeremy L Thompson if test { 57*3eb59678SJeremy L Thompson 8 * 16 58*3eb59678SJeremy L Thompson } else { 59*3eb59678SJeremy L Thompson 256 * 1024 60*3eb59678SJeremy L Thompson } 61*3eb59678SJeremy L Thompson } else { 62*3eb59678SJeremy L Thompson problem_size_requested 63*3eb59678SJeremy L Thompson }; 64*3eb59678SJeremy L Thompson 65*3eb59678SJeremy L Thompson // Summary output 66*3eb59678SJeremy L Thompson if !quiet { 67*3eb59678SJeremy L Thompson println!("Selected options: [command line option] : <current value>"); 68*3eb59678SJeremy L Thompson println!(" Ceed specification [-c] : {}", ceed_spec); 69*3eb59678SJeremy L Thompson println!(" Mesh dimension [-d] : {}", dim); 70*3eb59678SJeremy L Thompson println!(" Mesh degree [-m] : {}", mesh_degree); 71*3eb59678SJeremy L Thompson println!(" Solution degree [-p] : {}", solution_degree); 72*3eb59678SJeremy L Thompson println!(" Num. 1D quadr. pts [-q] : {}", num_qpts); 73*3eb59678SJeremy L Thompson println!(" Approx. # unknowns [-s] : {}", problem_size); 74*3eb59678SJeremy L Thompson println!(" QFunction source : user closure"); 75*3eb59678SJeremy L Thompson } 76*3eb59678SJeremy L Thompson 77*3eb59678SJeremy L Thompson // Initalize ceed context 78*3eb59678SJeremy L Thompson let ceed = Ceed::init(&ceed_spec); 79*3eb59678SJeremy L Thompson 80*3eb59678SJeremy L Thompson // Mesh and solution bases 81*3eb59678SJeremy L Thompson let basis_mesh = ceed.basis_tensor_H1_Lagrange( 82*3eb59678SJeremy L Thompson dim, 83*3eb59678SJeremy L Thompson ncomp_x, 84*3eb59678SJeremy L Thompson mesh_degree + 1, 85*3eb59678SJeremy L Thompson num_qpts, 86*3eb59678SJeremy L Thompson libceed::QuadMode::Gauss, 87*3eb59678SJeremy L Thompson )?; 88*3eb59678SJeremy L Thompson let basis_solution = ceed.basis_tensor_H1_Lagrange( 89*3eb59678SJeremy L Thompson dim, 90*3eb59678SJeremy L Thompson 1, 91*3eb59678SJeremy L Thompson solution_degree + 1, 92*3eb59678SJeremy L Thompson num_qpts, 93*3eb59678SJeremy L Thompson libceed::QuadMode::Gauss, 94*3eb59678SJeremy L Thompson )?; 95*3eb59678SJeremy L Thompson 96*3eb59678SJeremy L Thompson // Determine mesh size from approximate problem size 97*3eb59678SJeremy L Thompson let num_xyz = mesh::cartesian_mesh_size(dim, solution_degree, problem_size); 98*3eb59678SJeremy L Thompson if !quiet { 99*3eb59678SJeremy L Thompson print!("\nMesh size : nx = {}", num_xyz[0]); 100*3eb59678SJeremy L Thompson if dim > 1 { 101*3eb59678SJeremy L Thompson print!(", ny = {}", num_xyz[1]); 102*3eb59678SJeremy L Thompson } 103*3eb59678SJeremy L Thompson if dim > 2 { 104*3eb59678SJeremy L Thompson print!(", nz = {}", num_xyz[2]); 105*3eb59678SJeremy L Thompson } 106*3eb59678SJeremy L Thompson println!(); 107*3eb59678SJeremy L Thompson } 108*3eb59678SJeremy L Thompson 109*3eb59678SJeremy L Thompson // Build ElemRestriction objects describing the mesh and solution discrete 110*3eb59678SJeremy L Thompson // representations 111*3eb59678SJeremy L Thompson let (rstr_mesh, _) = 112*3eb59678SJeremy L Thompson mesh::build_cartesian_restriction(&ceed, dim, num_xyz, mesh_degree, ncomp_x, num_qpts)?; 113*3eb59678SJeremy L Thompson let (_, rstr_qdata) = mesh::build_cartesian_restriction( 114*3eb59678SJeremy L Thompson &ceed, 115*3eb59678SJeremy L Thompson dim, 116*3eb59678SJeremy L Thompson num_xyz, 117*3eb59678SJeremy L Thompson solution_degree, 118*3eb59678SJeremy L Thompson 1 + dim * (dim + 1) / 2, 119*3eb59678SJeremy L Thompson num_qpts, 120*3eb59678SJeremy L Thompson )?; 121*3eb59678SJeremy L Thompson let (rstr_solution, _) = 122*3eb59678SJeremy L Thompson mesh::build_cartesian_restriction(&ceed, dim, num_xyz, solution_degree, 1, num_qpts)?; 123*3eb59678SJeremy L Thompson let mesh_size = rstr_mesh.lvector_size(); 124*3eb59678SJeremy L Thompson let solution_size = rstr_solution.lvector_size(); 125*3eb59678SJeremy L Thompson if !quiet { 126*3eb59678SJeremy L Thompson println!("Number of mesh nodes : {}", mesh_size / dim); 127*3eb59678SJeremy L Thompson println!("Number of solution nodes : {}", solution_size); 128*3eb59678SJeremy L Thompson } 129*3eb59678SJeremy L Thompson 130*3eb59678SJeremy L Thompson // Create a Vector with the mesh coordinates 131*3eb59678SJeremy L Thompson let mut mesh_coords = mesh::cartesian_mesh_coords(&ceed, dim, num_xyz, mesh_degree, mesh_size)?; 132*3eb59678SJeremy L Thompson 133*3eb59678SJeremy L Thompson // Apply a transformation to the mesh coordinates 134*3eb59678SJeremy L Thompson let exact_volume = transform::transform_mesh_coordinates(dim, mesh_size, &mut mesh_coords)?; 135*3eb59678SJeremy L Thompson 136*3eb59678SJeremy L Thompson // QFunction that builds the quadrature data for the mass + diff operator 137*3eb59678SJeremy L Thompson // -- QFunction from user closure 138*3eb59678SJeremy L Thompson let build_mass_diff = move |[jacobian, weights, ..]: QFunctionInputs, 139*3eb59678SJeremy L Thompson [qdata, ..]: QFunctionOutputs| { 140*3eb59678SJeremy L Thompson // Build quadrature data 141*3eb59678SJeremy L Thompson match dim { 142*3eb59678SJeremy L Thompson 1 => { 143*3eb59678SJeremy L Thompson let q = qdata.len() / 2; 144*3eb59678SJeremy L Thompson for i in 0..q { 145*3eb59678SJeremy L Thompson // Mass 146*3eb59678SJeremy L Thompson qdata[i + q * 0] = weights[i] * jacobian[i]; 147*3eb59678SJeremy L Thompson // Diff 148*3eb59678SJeremy L Thompson qdata[i + q * 1] = weights[i] / jacobian[i]; 149*3eb59678SJeremy L Thompson } 150*3eb59678SJeremy L Thompson } 151*3eb59678SJeremy L Thompson 2 => { 152*3eb59678SJeremy L Thompson let q = qdata.len() / 4; 153*3eb59678SJeremy L Thompson for i in 0..q { 154*3eb59678SJeremy L Thompson let j11 = jacobian[i + q * 0]; 155*3eb59678SJeremy L Thompson let j21 = jacobian[i + q * 1]; 156*3eb59678SJeremy L Thompson let j12 = jacobian[i + q * 2]; 157*3eb59678SJeremy L Thompson let j22 = jacobian[i + q * 3]; 158*3eb59678SJeremy L Thompson // Mass 159*3eb59678SJeremy L Thompson qdata[i + q * 0] = weights[i] * (j11 * j22 - j21 * j12); 160*3eb59678SJeremy L Thompson // Diff 161*3eb59678SJeremy L Thompson let qw = weights[i] / (j11 * j22 - j21 * j12); 162*3eb59678SJeremy L Thompson qdata[i + q * 1] = qw * (j12 * j12 + j22 * j22); 163*3eb59678SJeremy L Thompson qdata[i + q * 2] = qw * (j11 * j11 + j21 * j21); 164*3eb59678SJeremy L Thompson qdata[i + q * 3] = -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() / 7; 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 // Mass 180*3eb59678SJeremy L Thompson qdata[i + q * 0] = weights[i] 181*3eb59678SJeremy L Thompson * (jacobian[i + q * 0] * a[0 * 3 + 0] 182*3eb59678SJeremy L Thompson + jacobian[i + q * 1] * a[0 * 3 + 1] 183*3eb59678SJeremy L Thompson + jacobian[i + q * 2] * a[0 * 3 + 2]); 184*3eb59678SJeremy L Thompson let qw = weights[i] 185*3eb59678SJeremy L Thompson / (jacobian[i + q * 0] * a[0 * 3 + 0] 186*3eb59678SJeremy L Thompson + jacobian[i + q * 1] * a[0 * 3 + 1] 187*3eb59678SJeremy L Thompson + jacobian[i + q * 2] * a[0 * 3 + 2]); 188*3eb59678SJeremy L Thompson // Diff 189*3eb59678SJeremy L Thompson qdata[i + q * 1] = qw 190*3eb59678SJeremy L Thompson * (a[0 * 3 + 0] * a[0 * 3 + 0] 191*3eb59678SJeremy L Thompson + a[0 * 3 + 1] * a[0 * 3 + 1] 192*3eb59678SJeremy L Thompson + a[0 * 3 + 2] * a[0 * 3 + 2]); 193*3eb59678SJeremy L Thompson qdata[i + q * 2] = qw 194*3eb59678SJeremy L Thompson * (a[1 * 3 + 0] * a[1 * 3 + 0] 195*3eb59678SJeremy L Thompson + a[1 * 3 + 1] * a[1 * 3 + 1] 196*3eb59678SJeremy L Thompson + a[1 * 3 + 2] * a[1 * 3 + 2]); 197*3eb59678SJeremy L Thompson qdata[i + q * 3] = qw 198*3eb59678SJeremy L Thompson * (a[2 * 3 + 0] * a[2 * 3 + 0] 199*3eb59678SJeremy L Thompson + a[2 * 3 + 1] * a[2 * 3 + 1] 200*3eb59678SJeremy L Thompson + a[2 * 3 + 2] * a[2 * 3 + 2]); 201*3eb59678SJeremy L Thompson qdata[i + q * 4] = qw 202*3eb59678SJeremy L Thompson * (a[1 * 3 + 0] * a[2 * 3 + 0] 203*3eb59678SJeremy L Thompson + a[1 * 3 + 1] * a[2 * 3 + 1] 204*3eb59678SJeremy L Thompson + a[1 * 3 + 2] * a[2 * 3 + 2]); 205*3eb59678SJeremy L Thompson qdata[i + q * 5] = qw 206*3eb59678SJeremy L Thompson * (a[0 * 3 + 0] * a[2 * 3 + 0] 207*3eb59678SJeremy L Thompson + a[0 * 3 + 1] * a[2 * 3 + 1] 208*3eb59678SJeremy L Thompson + a[0 * 3 + 2] * a[2 * 3 + 2]); 209*3eb59678SJeremy L Thompson qdata[i + q * 6] = qw 210*3eb59678SJeremy L Thompson * (a[0 * 3 + 0] * a[1 * 3 + 0] 211*3eb59678SJeremy L Thompson + a[0 * 3 + 1] * a[1 * 3 + 1] 212*3eb59678SJeremy L Thompson + a[0 * 3 + 2] * a[1 * 3 + 2]); 213*3eb59678SJeremy L Thompson } 214*3eb59678SJeremy L Thompson } 215*3eb59678SJeremy L Thompson _ => unreachable!(), 216*3eb59678SJeremy L Thompson }; 217*3eb59678SJeremy L Thompson 218*3eb59678SJeremy L Thompson // Return clean error code 219*3eb59678SJeremy L Thompson 0 220*3eb59678SJeremy L Thompson }; 221*3eb59678SJeremy L Thompson let qf_build_closure = ceed 222*3eb59678SJeremy L Thompson .q_function_interior(1, Box::new(build_mass_diff))? 223*3eb59678SJeremy L Thompson .input("dx", ncomp_x * dim, libceed::EvalMode::Grad)? 224*3eb59678SJeremy L Thompson .input("weights", 1, libceed::EvalMode::Weight)? 225*3eb59678SJeremy L Thompson .output("qdata", 1 + dim * (dim + 1) / 2, libceed::EvalMode::None)?; 226*3eb59678SJeremy L Thompson // -- QFunction for use with Operator 227*3eb59678SJeremy L Thompson let qf_build = QFunctionOpt::SomeQFunction(&qf_build_closure); 228*3eb59678SJeremy L Thompson 229*3eb59678SJeremy L Thompson // Operator that build the quadrature data for the mass + diff operator 230*3eb59678SJeremy L Thompson let op_build = ceed 231*3eb59678SJeremy L Thompson .operator(qf_build, QFunctionOpt::None, QFunctionOpt::None)? 232*3eb59678SJeremy L Thompson .name("build qdata")? 233*3eb59678SJeremy L Thompson .field("dx", &rstr_mesh, &basis_mesh, VectorOpt::Active)? 234*3eb59678SJeremy L Thompson .field( 235*3eb59678SJeremy L Thompson "weights", 236*3eb59678SJeremy L Thompson ElemRestrictionOpt::None, 237*3eb59678SJeremy L Thompson &basis_mesh, 238*3eb59678SJeremy L Thompson VectorOpt::None, 239*3eb59678SJeremy L Thompson )? 240*3eb59678SJeremy L Thompson .field("qdata", &rstr_qdata, BasisOpt::None, VectorOpt::Active)? 241*3eb59678SJeremy L Thompson .check()?; 242*3eb59678SJeremy L Thompson 243*3eb59678SJeremy L Thompson // Compute the quadrature data for the mass + diff operator 244*3eb59678SJeremy L Thompson let elem_qpts = num_qpts.pow(dim as u32); 245*3eb59678SJeremy L Thompson let num_elem: usize = num_xyz.iter().take(dim).product(); 246*3eb59678SJeremy L Thompson let mut qdata = ceed.vector(num_elem * elem_qpts * (1 + dim * (dim + 1) / 2))?; 247*3eb59678SJeremy L Thompson op_build.apply(&mesh_coords, &mut qdata)?; 248*3eb59678SJeremy L Thompson 249*3eb59678SJeremy L Thompson // QFunction that applies the mass + diff operator 250*3eb59678SJeremy L Thompson // -- QFunction from user closure 251*3eb59678SJeremy L Thompson let apply_mass_diff = move |[u, ug, qdata, ..]: QFunctionInputs, 252*3eb59678SJeremy L Thompson [v, vg, ..]: QFunctionOutputs| { 253*3eb59678SJeremy L Thompson // Apply diffusion operator 254*3eb59678SJeremy L Thompson match dim { 255*3eb59678SJeremy L Thompson 1 => { 256*3eb59678SJeremy L Thompson let q = qdata.len() / 2; 257*3eb59678SJeremy L Thompson for i in 0..q { 258*3eb59678SJeremy L Thompson // Mass 259*3eb59678SJeremy L Thompson v[i] = u[i] * qdata[i + 0 * q]; 260*3eb59678SJeremy L Thompson // Diff 261*3eb59678SJeremy L Thompson vg[i] = ug[i] * qdata[i + 1 * q]; 262*3eb59678SJeremy L Thompson } 263*3eb59678SJeremy L Thompson } 264*3eb59678SJeremy L Thompson 2 => { 265*3eb59678SJeremy L Thompson let q = qdata.len() / 4; 266*3eb59678SJeremy L Thompson for i in 0..q { 267*3eb59678SJeremy L Thompson // Mass 268*3eb59678SJeremy L Thompson v[i] = u[i] * qdata[i + 0 * q]; 269*3eb59678SJeremy L Thompson // Diff 270*3eb59678SJeremy L Thompson let du = [ug[i + q * 0], ug[i + q * 1]]; 271*3eb59678SJeremy L Thompson let dxdxdxdx_t = [ 272*3eb59678SJeremy L Thompson [qdata[i + 1 * q], qdata[i + 3 * q]], 273*3eb59678SJeremy L Thompson [qdata[i + 3 * q], qdata[i + 2 * q]], 274*3eb59678SJeremy L Thompson ]; 275*3eb59678SJeremy L Thompson for j in 0..2 { 276*3eb59678SJeremy L Thompson vg[i + j * q] = du[0] * dxdxdxdx_t[0][j] + du[1] * dxdxdxdx_t[1][j]; 277*3eb59678SJeremy L Thompson } 278*3eb59678SJeremy L Thompson } 279*3eb59678SJeremy L Thompson } 280*3eb59678SJeremy L Thompson 3 => { 281*3eb59678SJeremy L Thompson let q = qdata.len() / 7; 282*3eb59678SJeremy L Thompson for i in 0..q { 283*3eb59678SJeremy L Thompson // Mass 284*3eb59678SJeremy L Thompson v[i] = u[i] * qdata[i + 0 * q]; 285*3eb59678SJeremy L Thompson // Diff 286*3eb59678SJeremy L Thompson let du = [ug[i + q * 0], ug[i + q * 1], ug[i + q * 2]]; 287*3eb59678SJeremy L Thompson let dxdxdxdx_t = [ 288*3eb59678SJeremy L Thompson [qdata[i + 1 * q], qdata[i + 6 * q], qdata[i + 5 * q]], 289*3eb59678SJeremy L Thompson [qdata[i + 6 * q], qdata[i + 2 * q], qdata[i + 4 * q]], 290*3eb59678SJeremy L Thompson [qdata[i + 5 * q], qdata[i + 4 * q], qdata[i + 3 * q]], 291*3eb59678SJeremy L Thompson ]; 292*3eb59678SJeremy L Thompson for j in 0..3 { 293*3eb59678SJeremy L Thompson vg[i + j * q] = du[0] * dxdxdxdx_t[0][j] 294*3eb59678SJeremy L Thompson + du[1] * dxdxdxdx_t[1][j] 295*3eb59678SJeremy L Thompson + du[2] * dxdxdxdx_t[2][j]; 296*3eb59678SJeremy L Thompson } 297*3eb59678SJeremy L Thompson } 298*3eb59678SJeremy L Thompson } 299*3eb59678SJeremy L Thompson _ => unreachable!(), 300*3eb59678SJeremy L Thompson }; 301*3eb59678SJeremy L Thompson 302*3eb59678SJeremy L Thompson // Return clean error code 303*3eb59678SJeremy L Thompson 0 304*3eb59678SJeremy L Thompson }; 305*3eb59678SJeremy L Thompson let qf_mass_diff_closure = ceed 306*3eb59678SJeremy L Thompson .q_function_interior(1, Box::new(apply_mass_diff))? 307*3eb59678SJeremy L Thompson .input("u", 1, libceed::EvalMode::Interp)? 308*3eb59678SJeremy L Thompson .input("du", dim, libceed::EvalMode::Grad)? 309*3eb59678SJeremy L Thompson .input("qdata", 1 + dim * (dim + 1) / 2, libceed::EvalMode::None)? 310*3eb59678SJeremy L Thompson .output("v", 1, libceed::EvalMode::Interp)? 311*3eb59678SJeremy L Thompson .output("dv", dim, libceed::EvalMode::Grad)?; 312*3eb59678SJeremy L Thompson // -- QFunction for use with Operator 313*3eb59678SJeremy L Thompson let qf_mass_diff = QFunctionOpt::SomeQFunction(&qf_mass_diff_closure); 314*3eb59678SJeremy L Thompson 315*3eb59678SJeremy L Thompson // Mass + diff Operator 316*3eb59678SJeremy L Thompson let op_mass_diff = ceed 317*3eb59678SJeremy L Thompson .operator(qf_mass_diff, QFunctionOpt::None, QFunctionOpt::None)? 318*3eb59678SJeremy L Thompson .name("mass diff")? 319*3eb59678SJeremy L Thompson .field("u", &rstr_solution, &basis_solution, VectorOpt::Active)? 320*3eb59678SJeremy L Thompson .field("du", &rstr_solution, &basis_solution, VectorOpt::Active)? 321*3eb59678SJeremy L Thompson .field("qdata", &rstr_qdata, BasisOpt::None, &qdata)? 322*3eb59678SJeremy L Thompson .field("v", &rstr_solution, &basis_solution, VectorOpt::Active)? 323*3eb59678SJeremy L Thompson .field("dv", &rstr_solution, &basis_solution, VectorOpt::Active)? 324*3eb59678SJeremy L Thompson .check()?; 325*3eb59678SJeremy L Thompson 326*3eb59678SJeremy L Thompson // Solution vectors 327*3eb59678SJeremy L Thompson let u = ceed.vector_from_slice(&vec![1.0; solution_size])?; 328*3eb59678SJeremy L Thompson let mut v = ceed.vector(solution_size)?; 329*3eb59678SJeremy L Thompson 330*3eb59678SJeremy L Thompson // Apply the mass + diff operator 331*3eb59678SJeremy L Thompson op_mass_diff.apply(&u, &mut v)?; 332*3eb59678SJeremy L Thompson 333*3eb59678SJeremy L Thompson // Compute the mesh volume 334*3eb59678SJeremy L Thompson let volume: libceed::Scalar = v.view()?.iter().sum(); 335*3eb59678SJeremy L Thompson 336*3eb59678SJeremy L Thompson // Output results 337*3eb59678SJeremy L Thompson if !quiet { 338*3eb59678SJeremy L Thompson println!("Exact mesh volume : {:.12}", exact_volume); 339*3eb59678SJeremy L Thompson println!("Computed mesh volume : {:.12}", volume); 340*3eb59678SJeremy L Thompson println!( 341*3eb59678SJeremy L Thompson "Volume error : {:.12e}", 342*3eb59678SJeremy L Thompson volume - exact_volume 343*3eb59678SJeremy L Thompson ); 344*3eb59678SJeremy L Thompson } 345*3eb59678SJeremy L Thompson let tolerance = match dim { 346*3eb59678SJeremy L Thompson 1 => 200.0 * libceed::EPSILON, 347*3eb59678SJeremy L Thompson _ => 1E-5, 348*3eb59678SJeremy L Thompson }; 349*3eb59678SJeremy L Thompson let error = (volume - exact_volume).abs(); 350*3eb59678SJeremy L Thompson if error > tolerance { 351*3eb59678SJeremy L Thompson println!("Volume error too large: {:.12e}", error); 352*3eb59678SJeremy L Thompson return Err(libceed::Error { 353*3eb59678SJeremy L Thompson message: format!( 354*3eb59678SJeremy L Thompson "Volume error too large - expected: {:.12e}, actual: {:.12e}", 355*3eb59678SJeremy L Thompson tolerance, error 356*3eb59678SJeremy L Thompson ), 357*3eb59678SJeremy L Thompson }); 358*3eb59678SJeremy L Thompson } 359*3eb59678SJeremy L Thompson Ok(()) 360*3eb59678SJeremy L Thompson } 361*3eb59678SJeremy L Thompson 362*3eb59678SJeremy L Thompson // ---------------------------------------------------------------------------- 363*3eb59678SJeremy L Thompson // Tests 364*3eb59678SJeremy L Thompson // ---------------------------------------------------------------------------- 365*3eb59678SJeremy L Thompson #[cfg(test)] 366*3eb59678SJeremy L Thompson mod tests { 367*3eb59678SJeremy L Thompson use super::*; 368*3eb59678SJeremy L Thompson 369*3eb59678SJeremy L Thompson #[test] 370*3eb59678SJeremy L Thompson fn example_3_1d() { 371*3eb59678SJeremy L Thompson let options = opt::Opt { 372*3eb59678SJeremy L Thompson ceed_spec: "/cpu/self/ref/serial".to_string(), 373*3eb59678SJeremy L Thompson dim: 1, 374*3eb59678SJeremy L Thompson mesh_degree: 4, 375*3eb59678SJeremy L Thompson solution_degree: 4, 376*3eb59678SJeremy L Thompson num_qpts: 6, 377*3eb59678SJeremy L Thompson problem_size_requested: -1, 378*3eb59678SJeremy L Thompson test: true, 379*3eb59678SJeremy L Thompson quiet: true, 380*3eb59678SJeremy L Thompson }; 381*3eb59678SJeremy L Thompson assert!(example_3(options).is_ok()); 382*3eb59678SJeremy L Thompson } 383*3eb59678SJeremy L Thompson 384*3eb59678SJeremy L Thompson #[test] 385*3eb59678SJeremy L Thompson fn example_3_2d() { 386*3eb59678SJeremy L Thompson let options = opt::Opt { 387*3eb59678SJeremy L Thompson ceed_spec: "/cpu/self/ref/serial".to_string(), 388*3eb59678SJeremy L Thompson dim: 2, 389*3eb59678SJeremy L Thompson mesh_degree: 4, 390*3eb59678SJeremy L Thompson solution_degree: 4, 391*3eb59678SJeremy L Thompson num_qpts: 6, 392*3eb59678SJeremy L Thompson problem_size_requested: -1, 393*3eb59678SJeremy L Thompson test: true, 394*3eb59678SJeremy L Thompson quiet: true, 395*3eb59678SJeremy L Thompson }; 396*3eb59678SJeremy L Thompson assert!(example_3(options).is_ok()); 397*3eb59678SJeremy L Thompson } 398*3eb59678SJeremy L Thompson 399*3eb59678SJeremy L Thompson #[test] 400*3eb59678SJeremy L Thompson fn example_3_3d() { 401*3eb59678SJeremy L Thompson let options = opt::Opt { 402*3eb59678SJeremy L Thompson ceed_spec: "/cpu/self/ref/serial".to_string(), 403*3eb59678SJeremy L Thompson dim: 3, 404*3eb59678SJeremy L Thompson mesh_degree: 4, 405*3eb59678SJeremy L Thompson solution_degree: 4, 406*3eb59678SJeremy L Thompson num_qpts: 6, 407*3eb59678SJeremy L Thompson problem_size_requested: -1, 408*3eb59678SJeremy L Thompson test: true, 409*3eb59678SJeremy L Thompson quiet: false, 410*3eb59678SJeremy L Thompson }; 411*3eb59678SJeremy L Thompson assert!(example_3(options).is_ok()); 412*3eb59678SJeremy L Thompson } 413*3eb59678SJeremy L Thompson } 414*3eb59678SJeremy L Thompson 415*3eb59678SJeremy L Thompson // ---------------------------------------------------------------------------- 416