xref: /libCEED/examples/python/ex3_volume.py (revision 7b3ff0698626cc2e5ce463afc10290072fd55c90)
1*7b3ff069SJeremy L Thompson#!/usr/bin/env python3
2*7b3ff069SJeremy L Thompson# Copyright (c) 2017-2025, Lawrence Livermore National Security, LLC and other CEED contributors.
3*7b3ff069SJeremy L Thompson# All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
4*7b3ff069SJeremy L Thompson#
5*7b3ff069SJeremy L Thompson# SPDX-License-Identifier: BSD-2-Clause
6*7b3ff069SJeremy L Thompson#
7*7b3ff069SJeremy L Thompson# This file is part of CEED:  http://github.com/ceed
8*7b3ff069SJeremy L Thompson#
9*7b3ff069SJeremy L Thompson# libCEED example using diffusion operator to compute surface area
10*7b3ff069SJeremy L Thompson#
11*7b3ff069SJeremy L Thompson# Sample runs:
12*7b3ff069SJeremy L Thompson#
13*7b3ff069SJeremy L Thompson#     python ex3_volume.py
14*7b3ff069SJeremy L Thompson#     python ex3_volume -c /cpu/self
15*7b3ff069SJeremy L Thompson#     python ex3_volume -c /gpu/cuda
16*7b3ff069SJeremy L Thompson
17*7b3ff069SJeremy L Thompsonimport sys
18*7b3ff069SJeremy L Thompsonimport os
19*7b3ff069SJeremy L Thompsonimport numpy as np
20*7b3ff069SJeremy L Thompsonimport libceed
21*7b3ff069SJeremy L Thompsonimport ex_common as common
22*7b3ff069SJeremy L Thompson
23*7b3ff069SJeremy L Thompson
24*7b3ff069SJeremy L Thompsondef main():
25*7b3ff069SJeremy L Thompson    """Main function for volume example"""
26*7b3ff069SJeremy L Thompson    args = common.parse_arguments()
27*7b3ff069SJeremy L Thompson    example_3(args)
28*7b3ff069SJeremy L Thompson
29*7b3ff069SJeremy L Thompson
30*7b3ff069SJeremy L Thompsondef example_3(args):
31*7b3ff069SJeremy L Thompson    """Compute volume using mass and diff operator
32*7b3ff069SJeremy L Thompson
33*7b3ff069SJeremy L Thompson    Args:
34*7b3ff069SJeremy L Thompson        args: Parsed command line arguments
35*7b3ff069SJeremy L Thompson
36*7b3ff069SJeremy L Thompson    Returns:
37*7b3ff069SJeremy L Thompson        int: 0 on success, error code on failure
38*7b3ff069SJeremy L Thompson    """
39*7b3ff069SJeremy L Thompson    # Process arguments
40*7b3ff069SJeremy L Thompson    dim = args.dim
41*7b3ff069SJeremy L Thompson    mesh_degree = max(args.mesh_degree, args.solution_degree)
42*7b3ff069SJeremy L Thompson    sol_degree = args.solution_degree
43*7b3ff069SJeremy L Thompson    num_qpts = args.quadrature_points
44*7b3ff069SJeremy L Thompson    problem_size = args.problem_size if args.problem_size > 0 else (8 * 16 if args.test else 256 * 1024)
45*7b3ff069SJeremy L Thompson    ncomp_x = dim  # Number of coordinate components
46*7b3ff069SJeremy L Thompson
47*7b3ff069SJeremy L Thompson    # Print configuration
48*7b3ff069SJeremy L Thompson    if not args.quiet:
49*7b3ff069SJeremy L Thompson        print("Selected options: [command line option] : <current value>")
50*7b3ff069SJeremy L Thompson        print(f"    Ceed specification [-c] : {args.ceed}")
51*7b3ff069SJeremy L Thompson        print(f"    Mesh dimension     [-d] : {dim}")
52*7b3ff069SJeremy L Thompson        print(f"    Mesh degree        [-m] : {mesh_degree}")
53*7b3ff069SJeremy L Thompson        print(f"    Solution degree    [-p] : {sol_degree}")
54*7b3ff069SJeremy L Thompson        print(f"    Num. 1D quadr. pts [-q] : {num_qpts}")
55*7b3ff069SJeremy L Thompson        print(f"    Approx. # unknowns [-s] : {problem_size}")
56*7b3ff069SJeremy L Thompson        print(f"    QFunction source   [-g] : {'gallery' if args.gallery else 'user'}")
57*7b3ff069SJeremy L Thompson
58*7b3ff069SJeremy L Thompson    # Check - Gallery not supported
59*7b3ff069SJeremy L Thompson    if args.gallery:
60*7b3ff069SJeremy L Thompson        print("Gallery QFunction not supported for example 3")
61*7b3ff069SJeremy L Thompson        sys.exit(1)
62*7b3ff069SJeremy L Thompson
63*7b3ff069SJeremy L Thompson    # Initialize CEED
64*7b3ff069SJeremy L Thompson    ceed = libceed.Ceed(args.ceed)
65*7b3ff069SJeremy L Thompson
66*7b3ff069SJeremy L Thompson    # Create bases
67*7b3ff069SJeremy L Thompson    # Tensor-product Lagrange basis for mesh coordinates
68*7b3ff069SJeremy L Thompson    mesh_basis = ceed.BasisTensorH1Lagrange(
69*7b3ff069SJeremy L Thompson        dim, ncomp_x, mesh_degree + 1, num_qpts, libceed.GAUSS)
70*7b3ff069SJeremy L Thompson
71*7b3ff069SJeremy L Thompson    # Tensor-product Lagrange basis for solution
72*7b3ff069SJeremy L Thompson    solution_basis = ceed.BasisTensorH1Lagrange(
73*7b3ff069SJeremy L Thompson        dim, 1, sol_degree + 1, num_qpts, libceed.GAUSS)
74*7b3ff069SJeremy L Thompson
75*7b3ff069SJeremy L Thompson    # Create mesh
76*7b3ff069SJeremy L Thompson    # Determine mesh size
77*7b3ff069SJeremy L Thompson    num_xyz = common.get_cartesian_mesh_size(dim, sol_degree, problem_size)
78*7b3ff069SJeremy L Thompson    if not args.quiet:
79*7b3ff069SJeremy L Thompson        print("\nMesh size                   : nx = %d" % num_xyz[0], end="")
80*7b3ff069SJeremy L Thompson        if dim > 1:
81*7b3ff069SJeremy L Thompson            print(", ny = %d" % num_xyz[1], end="")
82*7b3ff069SJeremy L Thompson        if dim > 2:
83*7b3ff069SJeremy L Thompson            print(", nz = %d" % num_xyz[2], end="")
84*7b3ff069SJeremy L Thompson        print()
85*7b3ff069SJeremy L Thompson
86*7b3ff069SJeremy L Thompson    # Create element restrictions
87*7b3ff069SJeremy L Thompson    num_q_comp = 1 + dim * (dim + 1) // 2
88*7b3ff069SJeremy L Thompson    mesh_restriction, mesh_size, _, _, _ = common.build_cartesian_restriction(
89*7b3ff069SJeremy L Thompson        ceed, dim, num_xyz, mesh_degree, ncomp_x, num_q_comp, num_qpts, create_qdata=False)
90*7b3ff069SJeremy L Thompson    solution_restriction, sol_size, q_data_restriction, num_elem, elem_qpts = common.build_cartesian_restriction(
91*7b3ff069SJeremy L Thompson        ceed, dim, num_xyz, sol_degree, 1, num_q_comp, num_qpts, create_qdata=True)
92*7b3ff069SJeremy L Thompson
93*7b3ff069SJeremy L Thompson    if not args.quiet:
94*7b3ff069SJeremy L Thompson        print("Number of mesh nodes        : %d" % (mesh_size // dim))
95*7b3ff069SJeremy L Thompson        print("Number of solution nodes    : %d" % sol_size)
96*7b3ff069SJeremy L Thompson
97*7b3ff069SJeremy L Thompson    # Create and transform mesh coordinates
98*7b3ff069SJeremy L Thompson    mesh_coords = ceed.Vector(mesh_size)
99*7b3ff069SJeremy L Thompson    common.set_cartesian_mesh_coords(ceed, dim, num_xyz, mesh_degree, mesh_coords)
100*7b3ff069SJeremy L Thompson    exact_volume, _ = common.transform_mesh_coords(dim, mesh_size, mesh_coords)
101*7b3ff069SJeremy L Thompson
102*7b3ff069SJeremy L Thompson    # Create QFunction context
103*7b3ff069SJeremy L Thompson    build_ctx = ceed.QFunctionContext()
104*7b3ff069SJeremy L Thompson    ctx_data = np.array([dim, dim], dtype=np.int32)
105*7b3ff069SJeremy L Thompson    build_ctx.set_data(ctx_data)
106*7b3ff069SJeremy L Thompson
107*7b3ff069SJeremy L Thompson    # Load QFunctions
108*7b3ff069SJeremy L Thompson    qfs_so = common.load_qfs_so()
109*7b3ff069SJeremy L Thompson    file_dir = os.path.dirname(os.path.abspath(__file__))
110*7b3ff069SJeremy L Thompson
111*7b3ff069SJeremy L Thompson    # Create the QFunction that builds the mass + diffusion operator (i.e.
112*7b3ff069SJeremy L Thompson    # computes its quadrature data) and set its context data
113*7b3ff069SJeremy L Thompson    qf_build = ceed.QFunction(1, qfs_so.build_mass_diff,
114*7b3ff069SJeremy L Thompson                              os.path.join(file_dir, "ex3-volume.h:build_mass_diff"))
115*7b3ff069SJeremy L Thompson    qf_build.add_input("dx", dim * dim, libceed.EVAL_GRAD)
116*7b3ff069SJeremy L Thompson    qf_build.add_input("weights", 1, libceed.EVAL_WEIGHT)
117*7b3ff069SJeremy L Thompson    qf_build.add_output("qdata", num_q_comp, libceed.EVAL_NONE)
118*7b3ff069SJeremy L Thompson    qf_build.set_context(build_ctx)
119*7b3ff069SJeremy L Thompson
120*7b3ff069SJeremy L Thompson    # Create the operator that builds the quadrature data for the mass + diffusion operator
121*7b3ff069SJeremy L Thompson    op_build = ceed.Operator(qf_build)
122*7b3ff069SJeremy L Thompson    op_build.set_field("dx", mesh_restriction, mesh_basis, libceed.VECTOR_ACTIVE)
123*7b3ff069SJeremy L Thompson    op_build.set_field("weights", libceed.ELEMRESTRICTION_NONE, mesh_basis, libceed.VECTOR_NONE)
124*7b3ff069SJeremy L Thompson    op_build.set_field("qdata", q_data_restriction, libceed.BASIS_NONE, libceed.VECTOR_ACTIVE)
125*7b3ff069SJeremy L Thompson
126*7b3ff069SJeremy L Thompson    # Compute the quadrature data for the mass + diffusion operator
127*7b3ff069SJeremy L Thompson    q_data = ceed.Vector(num_elem * elem_qpts * num_q_comp)
128*7b3ff069SJeremy L Thompson    op_build.apply(mesh_coords, q_data)
129*7b3ff069SJeremy L Thompson
130*7b3ff069SJeremy L Thompson    # Create the QFunction that defines the action of the mass + diffusion operator
131*7b3ff069SJeremy L Thompson    qf_apply = ceed.QFunction(1, qfs_so.apply_mass_diff,
132*7b3ff069SJeremy L Thompson                              os.path.join(file_dir, "ex3-volume.h:apply_mass_diff"))
133*7b3ff069SJeremy L Thompson    qf_apply.add_input("u", 1, libceed.EVAL_INTERP)
134*7b3ff069SJeremy L Thompson    qf_apply.add_input("du", dim, libceed.EVAL_GRAD)
135*7b3ff069SJeremy L Thompson    qf_apply.add_input("qdata", num_q_comp, libceed.EVAL_NONE)
136*7b3ff069SJeremy L Thompson    qf_apply.add_output("v", 1, libceed.EVAL_INTERP)
137*7b3ff069SJeremy L Thompson    qf_apply.add_output("dv", dim, libceed.EVAL_GRAD)
138*7b3ff069SJeremy L Thompson    qf_apply.set_context(build_ctx)
139*7b3ff069SJeremy L Thompson
140*7b3ff069SJeremy L Thompson    # Create the mass + diffusion operator
141*7b3ff069SJeremy L Thompson    op_apply = ceed.Operator(qf_apply)
142*7b3ff069SJeremy L Thompson    op_apply.set_field("u", solution_restriction, solution_basis, libceed.VECTOR_ACTIVE)
143*7b3ff069SJeremy L Thompson    op_apply.set_field("du", solution_restriction, solution_basis, libceed.VECTOR_ACTIVE)
144*7b3ff069SJeremy L Thompson    op_apply.set_field("qdata", q_data_restriction, libceed.BASIS_NONE, q_data)
145*7b3ff069SJeremy L Thompson    op_apply.set_field("v", solution_restriction, solution_basis, libceed.VECTOR_ACTIVE)
146*7b3ff069SJeremy L Thompson    op_apply.set_field("dv", solution_restriction, solution_basis, libceed.VECTOR_ACTIVE)
147*7b3ff069SJeremy L Thompson
148*7b3ff069SJeremy L Thompson    # Create solution vectors
149*7b3ff069SJeremy L Thompson    u = ceed.Vector(sol_size)
150*7b3ff069SJeremy L Thompson    v = ceed.Vector(sol_size)
151*7b3ff069SJeremy L Thompson    u.set_value(1.0)  # Set all entries of u to 1.0
152*7b3ff069SJeremy L Thompson
153*7b3ff069SJeremy L Thompson    # Apply mass + diffusion operator: v = (M + K) * u
154*7b3ff069SJeremy L Thompson    op_apply.apply(u, v)
155*7b3ff069SJeremy L Thompson
156*7b3ff069SJeremy L Thompson    # Compute volume by summing all entries in v
157*7b3ff069SJeremy L Thompson    volume = 0.0
158*7b3ff069SJeremy L Thompson    with v.array_read() as v_array:
159*7b3ff069SJeremy L Thompson        # Simply sum all values to compute the volume
160*7b3ff069SJeremy L Thompson        volume = np.sum(v_array)
161*7b3ff069SJeremy L Thompson
162*7b3ff069SJeremy L Thompson    if not args.test:
163*7b3ff069SJeremy L Thompson        print()
164*7b3ff069SJeremy L Thompson        print(f"Exact mesh volume    : {exact_volume:.14g}")
165*7b3ff069SJeremy L Thompson        print(f"Computed mesh volume : {volume:.14g}")
166*7b3ff069SJeremy L Thompson        print(f"Volume error         : {volume - exact_volume:.14g}")
167*7b3ff069SJeremy L Thompson    else:
168*7b3ff069SJeremy L Thompson        # Test mode - check if error is within tolerance
169*7b3ff069SJeremy L Thompson        tol = 200 * libceed.EPSILON if dim == 1 else 1e-5
170*7b3ff069SJeremy L Thompson        if abs(volume - exact_volume) > tol:
171*7b3ff069SJeremy L Thompson            print(f"Volume error : {volume - exact_volume:.14g}")
172*7b3ff069SJeremy L Thompson            sys.exit(1)
173*7b3ff069SJeremy L Thompson
174*7b3ff069SJeremy L Thompson    return 0
175*7b3ff069SJeremy L Thompson
176*7b3ff069SJeremy L Thompson
177*7b3ff069SJeremy L Thompsonif __name__ == "__main__":
178*7b3ff069SJeremy L Thompson    main()
179