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