xref: /libCEED/examples/python/ex1_volume.py (revision 9ba83ac0e4b1fca39d6fa6737a318a9f0cbc172d)
17b3ff069SJeremy L Thompson#!/usr/bin/env python3
2*9ba83ac0SJeremy L Thompson# Copyright (c) 2017-2026, Lawrence Livermore National Security, LLC and other CEED contributors.
37b3ff069SJeremy L Thompson# All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
47b3ff069SJeremy L Thompson#
57b3ff069SJeremy L Thompson# SPDX-License-Identifier: BSD-2-Clause
67b3ff069SJeremy L Thompson#
77b3ff069SJeremy L Thompson# This file is part of CEED:  http://github.com/ceed
87b3ff069SJeremy L Thompson#
97b3ff069SJeremy L Thompson# libCEED example using diffusion operator to compute surface area
107b3ff069SJeremy L Thompson#
117b3ff069SJeremy L Thompson# Sample runs:
127b3ff069SJeremy L Thompson#
137b3ff069SJeremy L Thompson#     python ex1_volume.py
147b3ff069SJeremy L Thompson#     python ex1_volume -c /cpu/self
157b3ff069SJeremy L Thompson#     python ex1_volume -c /gpu/cuda
167b3ff069SJeremy L Thompson
177b3ff069SJeremy L Thompsonimport sys
187b3ff069SJeremy L Thompsonimport os
197b3ff069SJeremy L Thompsonimport numpy as np
207b3ff069SJeremy L Thompsonimport libceed
217b3ff069SJeremy L Thompsonimport ex_common as common
227b3ff069SJeremy L Thompson
237b3ff069SJeremy L Thompson
247b3ff069SJeremy L Thompsondef main():
257b3ff069SJeremy L Thompson    """Main function for volume example"""
267b3ff069SJeremy L Thompson    args = common.parse_arguments()
277b3ff069SJeremy L Thompson    return example_1(args)
287b3ff069SJeremy L Thompson
297b3ff069SJeremy L Thompson
307b3ff069SJeremy L Thompsondef example_1(args):
317b3ff069SJeremy L Thompson    """Compute volume using mass operator
327b3ff069SJeremy L Thompson
337b3ff069SJeremy L Thompson    Args:
347b3ff069SJeremy L Thompson        args: Parsed command line arguments
357b3ff069SJeremy L Thompson
367b3ff069SJeremy L Thompson    Returns:
377b3ff069SJeremy L Thompson        int: 0 on success, error code on failure
387b3ff069SJeremy L Thompson    """
397b3ff069SJeremy L Thompson    # Process arguments
407b3ff069SJeremy L Thompson    dim = args.dim
417b3ff069SJeremy L Thompson    mesh_degree = max(args.mesh_degree, args.solution_degree)
427b3ff069SJeremy L Thompson    sol_degree = args.solution_degree
437b3ff069SJeremy L Thompson    num_qpts = args.quadrature_points
447b3ff069SJeremy L Thompson    problem_size = args.problem_size if args.problem_size > 0 else (8 * 16 if args.test else 256 * 1024)
457b3ff069SJeremy L Thompson    ncomp_x = dim  # Number of coordinate components
467b3ff069SJeremy L Thompson
477b3ff069SJeremy L Thompson    # Print configuration
487b3ff069SJeremy L Thompson    if not args.quiet:
497b3ff069SJeremy L Thompson        print("Selected options: [command line option] : <current value>")
507b3ff069SJeremy L Thompson        print(f"    Ceed specification [-c] : {args.ceed}")
517b3ff069SJeremy L Thompson        print(f"    Mesh dimension     [-d] : {dim}")
527b3ff069SJeremy L Thompson        print(f"    Mesh degree        [-m] : {mesh_degree}")
537b3ff069SJeremy L Thompson        print(f"    Solution degree    [-p] : {sol_degree}")
547b3ff069SJeremy L Thompson        print(f"    Num. 1D quadr. pts [-q] : {num_qpts}")
557b3ff069SJeremy L Thompson        print(f"    Approx. # unknowns [-s] : {problem_size}")
567b3ff069SJeremy L Thompson        print(f"    QFunction source   [-g] : {'gallery' if args.gallery else 'user'}")
577b3ff069SJeremy L Thompson
587b3ff069SJeremy L Thompson    # Initialize CEED
597b3ff069SJeremy L Thompson    ceed = libceed.Ceed(args.ceed)
607b3ff069SJeremy L Thompson
617b3ff069SJeremy L Thompson    # Create bases
627b3ff069SJeremy L Thompson    # Tensor-product Lagrange basis for mesh coordinates
637b3ff069SJeremy L Thompson    mesh_basis = ceed.BasisTensorH1Lagrange(
647b3ff069SJeremy L Thompson        dim, ncomp_x, mesh_degree + 1, num_qpts, libceed.GAUSS)
657b3ff069SJeremy L Thompson
667b3ff069SJeremy L Thompson    # Tensor-product Lagrange basis for solution
677b3ff069SJeremy L Thompson    solution_basis = ceed.BasisTensorH1Lagrange(
687b3ff069SJeremy L Thompson        dim, 1, sol_degree + 1, num_qpts, libceed.GAUSS)
697b3ff069SJeremy L Thompson
707b3ff069SJeremy L Thompson    # Create mesh
717b3ff069SJeremy L Thompson    # Determine mesh size
727b3ff069SJeremy L Thompson    num_xyz = common.get_cartesian_mesh_size(dim, sol_degree, problem_size)
737b3ff069SJeremy L Thompson    if not args.quiet:
747b3ff069SJeremy L Thompson        print("\nMesh size                   : nx = %d" % num_xyz[0], end="")
757b3ff069SJeremy L Thompson        if dim > 1:
767b3ff069SJeremy L Thompson            print(", ny = %d" % num_xyz[1], end="")
777b3ff069SJeremy L Thompson        if dim > 2:
787b3ff069SJeremy L Thompson            print(", nz = %d" % num_xyz[2], end="")
797b3ff069SJeremy L Thompson        print()
807b3ff069SJeremy L Thompson
817b3ff069SJeremy L Thompson    # Create element restrictions
827b3ff069SJeremy L Thompson    num_q_comp = 1
837b3ff069SJeremy L Thompson    mesh_restriction, mesh_size, _, _, _ = common.build_cartesian_restriction(
847b3ff069SJeremy L Thompson        ceed, dim, num_xyz, mesh_degree, ncomp_x, num_q_comp, num_qpts, create_qdata=False)
857b3ff069SJeremy L Thompson    solution_restriction, sol_size, q_data_restriction, num_elem, elem_qpts = common.build_cartesian_restriction(
867b3ff069SJeremy L Thompson        ceed, dim, num_xyz, sol_degree, 1, num_q_comp, num_qpts, create_qdata=True)
877b3ff069SJeremy L Thompson
887b3ff069SJeremy L Thompson    if not args.quiet:
897b3ff069SJeremy L Thompson        print("Number of mesh nodes        : %d" % (mesh_size // dim))
907b3ff069SJeremy L Thompson        print("Number of solution nodes    : %d" % sol_size)
917b3ff069SJeremy L Thompson
927b3ff069SJeremy L Thompson    # Create and transform mesh coordinates
937b3ff069SJeremy L Thompson    mesh_coords = ceed.Vector(mesh_size)
947b3ff069SJeremy L Thompson    common.set_cartesian_mesh_coords(ceed, dim, num_xyz, mesh_degree, mesh_coords)
957b3ff069SJeremy L Thompson    exact_volume, _ = common.transform_mesh_coords(dim, mesh_size, mesh_coords)
967b3ff069SJeremy L Thompson
977b3ff069SJeremy L Thompson    # Create the QFunction that builds the mass operator (i.e. computes its quadrature data) and set its context data
987b3ff069SJeremy L Thompson    qf_build = None
997b3ff069SJeremy L Thompson    if args.gallery:
1007b3ff069SJeremy L Thompson        qf_build = ceed.QFunctionByName(f"Mass{dim}DBuild")
1017b3ff069SJeremy L Thompson    else:
1027b3ff069SJeremy L Thompson        build_ctx = ceed.QFunctionContext()
1037b3ff069SJeremy L Thompson        ctx_data = np.array([dim, dim], dtype=np.int32)
1047b3ff069SJeremy L Thompson        build_ctx.set_data(ctx_data)
1057b3ff069SJeremy L Thompson
1067b3ff069SJeremy L Thompson        qfs_so = common.load_qfs_so()
1077b3ff069SJeremy L Thompson        file_dir = os.path.dirname(os.path.abspath(__file__))
1087b3ff069SJeremy L Thompson
1097b3ff069SJeremy L Thompson        qf_build = ceed.QFunction(1, qfs_so.build_mass,
1107b3ff069SJeremy L Thompson                                  os.path.join(file_dir, "ex1-volume.h:build_mass"))
1117b3ff069SJeremy L Thompson        qf_build.add_input("dx", dim * dim, libceed.EVAL_GRAD)
1127b3ff069SJeremy L Thompson        qf_build.add_input("weights", 1, libceed.EVAL_WEIGHT)
1137b3ff069SJeremy L Thompson        qf_build.add_output("qdata", num_q_comp, libceed.EVAL_NONE)
1147b3ff069SJeremy L Thompson        qf_build.set_context(build_ctx)
1157b3ff069SJeremy L Thompson
1167b3ff069SJeremy L Thompson    # Create the operator that builds the quadrature data for the mass operator
1177b3ff069SJeremy L Thompson    op_build = ceed.Operator(qf_build)
1187b3ff069SJeremy L Thompson    op_build.set_field("dx", mesh_restriction, mesh_basis, libceed.VECTOR_ACTIVE)
1197b3ff069SJeremy L Thompson    op_build.set_field("weights", libceed.ELEMRESTRICTION_NONE, mesh_basis, libceed.VECTOR_NONE)
1207b3ff069SJeremy L Thompson    op_build.set_field("qdata", q_data_restriction, libceed.BASIS_NONE, libceed.VECTOR_ACTIVE)
1217b3ff069SJeremy L Thompson
1227b3ff069SJeremy L Thompson    # Compute the quadrature data for the mass operator
1237b3ff069SJeremy L Thompson    q_data = ceed.Vector(num_elem * elem_qpts * num_q_comp)
1247b3ff069SJeremy L Thompson    op_build.apply(mesh_coords, q_data)
1257b3ff069SJeremy L Thompson
1267b3ff069SJeremy L Thompson    # Setup QFunction for applying the mass operator
1277b3ff069SJeremy L Thompson    qf_mass = None
1287b3ff069SJeremy L Thompson    if args.gallery:
1297b3ff069SJeremy L Thompson        qf_mass = ceed.QFunctionByName("MassApply")
1307b3ff069SJeremy L Thompson    else:
1317b3ff069SJeremy L Thompson        build_ctx = ceed.QFunctionContext()
1327b3ff069SJeremy L Thompson        ctx_data = np.array([dim, dim], dtype=np.int32)
1337b3ff069SJeremy L Thompson        build_ctx.set_data(ctx_data)
1347b3ff069SJeremy L Thompson
1357b3ff069SJeremy L Thompson        qfs_so = common.load_qfs_so()
1367b3ff069SJeremy L Thompson        file_dir = os.path.dirname(os.path.abspath(__file__))
1377b3ff069SJeremy L Thompson
1387b3ff069SJeremy L Thompson        qf_mass = ceed.QFunction(1, qfs_so.apply_mass,
1397b3ff069SJeremy L Thompson                                 os.path.join(file_dir, "ex1-volume.h:apply_mass"))
1407b3ff069SJeremy L Thompson        qf_mass.add_input("u", 1, libceed.EVAL_INTERP)
1417b3ff069SJeremy L Thompson        qf_mass.add_input("qdata", num_q_comp, libceed.EVAL_NONE)
1427b3ff069SJeremy L Thompson        qf_mass.add_output("v", 1, libceed.EVAL_INTERP)
1437b3ff069SJeremy L Thompson        qf_mass.set_context(build_ctx)
1447b3ff069SJeremy L Thompson
1457b3ff069SJeremy L Thompson    # Create the mass operator
1467b3ff069SJeremy L Thompson    op_mass = ceed.Operator(qf_mass)
1477b3ff069SJeremy L Thompson    op_mass.set_field("u", solution_restriction, solution_basis, libceed.VECTOR_ACTIVE)
1487b3ff069SJeremy L Thompson    op_mass.set_field("qdata", q_data_restriction, libceed.BASIS_NONE, q_data)
1497b3ff069SJeremy L Thompson    op_mass.set_field("v", solution_restriction, solution_basis, libceed.VECTOR_ACTIVE)
1507b3ff069SJeremy L Thompson
1517b3ff069SJeremy L Thompson    # Create solution vectors
1527b3ff069SJeremy L Thompson    u = ceed.Vector(sol_size)
1537b3ff069SJeremy L Thompson    v = ceed.Vector(sol_size)
1547b3ff069SJeremy L Thompson    u.set_value(1.0)  # Set all entries of u to 1.0
1557b3ff069SJeremy L Thompson
1567b3ff069SJeremy L Thompson    # Apply mass operator: v = M * u
1577b3ff069SJeremy L Thompson    op_mass.apply(u, v)
1587b3ff069SJeremy L Thompson
1597b3ff069SJeremy L Thompson    # Compute volume by summing all entries in v
1607b3ff069SJeremy L Thompson    volume = 0.0
1617b3ff069SJeremy L Thompson    with v.array_read() as v_array:
1627b3ff069SJeremy L Thompson        # Simply sum all values to compute the volume
1637b3ff069SJeremy L Thompson        volume = np.sum(v_array)
1647b3ff069SJeremy L Thompson
1657b3ff069SJeremy L Thompson    if not args.test:
1667b3ff069SJeremy L Thompson        print()
1677b3ff069SJeremy L Thompson        print(f"Exact mesh volume    : {exact_volume:.14g}")
1687b3ff069SJeremy L Thompson        print(f"Computed mesh volume : {volume:.14g}")
1697b3ff069SJeremy L Thompson        print(f"Volume error         : {volume - exact_volume:.14g}")
1707b3ff069SJeremy L Thompson    else:
1717b3ff069SJeremy L Thompson        # Test mode - check if error is within tolerance
1727b3ff069SJeremy L Thompson        tol = 200 * libceed.EPSILON if dim == 1 else 1e-5
1737b3ff069SJeremy L Thompson        if abs(volume - exact_volume) > tol:
1747b3ff069SJeremy L Thompson            print(f"Volume error : {volume - exact_volume:.14g}")
1757b3ff069SJeremy L Thompson            sys.exit(1)
1767b3ff069SJeremy L Thompson
1777b3ff069SJeremy L Thompson    return 0
1787b3ff069SJeremy L Thompson
1797b3ff069SJeremy L Thompson
1807b3ff069SJeremy L Thompsonif __name__ == "__main__":
1817b3ff069SJeremy L Thompson    sys.exit(main())
182