1*9ba83ac0SJeremy L Thompson# Copyright (c) 2017-2026, Lawrence Livermore National Security, LLC and other CEED contributors 27b3ff069SJeremy L Thompson# All Rights Reserved. See the top-level LICENSE and NOTICE files for details. 37b3ff069SJeremy L Thompson# 47b3ff069SJeremy L Thompson# SPDX-License-Identifier: BSD-2-Clause 57b3ff069SJeremy L Thompson# 67b3ff069SJeremy L Thompson# This file is part of CEED: http://github.com/ceed 77b3ff069SJeremy L Thompson 87b3ff069SJeremy L Thompsonimport sys 97b3ff069SJeremy L Thompsonimport os 107b3ff069SJeremy L Thompsonfrom sysconfig import get_config_var 117b3ff069SJeremy L Thompsonimport argparse 127b3ff069SJeremy L Thompsonimport math 137b3ff069SJeremy L Thompsonimport numpy as np 147b3ff069SJeremy L Thompsonimport libceed 157b3ff069SJeremy L Thompsonimport ctypes 167b3ff069SJeremy L Thompson 177b3ff069SJeremy L Thompson 187b3ff069SJeremy L Thompsondef parse_arguments(): 197b3ff069SJeremy L Thompson """Parse command line arguments for surface area computation 207b3ff069SJeremy L Thompson 217b3ff069SJeremy L Thompson Returns: 227b3ff069SJeremy L Thompson Namespace: Parsed arguments with fields: 237b3ff069SJeremy L Thompson ceed: CEED resource specifier 247b3ff069SJeremy L Thompson dim: Problem dimension (1-3) 257b3ff069SJeremy L Thompson mesh_degree: Mesh polynomial degree 267b3ff069SJeremy L Thompson solution_degree: Solution polynomial degree 277b3ff069SJeremy L Thompson num_qpts: Number of quadrature points 287b3ff069SJeremy L Thompson problem_size: Approximate problem size 297b3ff069SJeremy L Thompson test: Test mode flag 307b3ff069SJeremy L Thompson quiet: Suppress output flag 317b3ff069SJeremy L Thompson gallery: Use gallery QFunctions flag 327b3ff069SJeremy L Thompson """ 337b3ff069SJeremy L Thompson parser = argparse.ArgumentParser(description="libCEED surface area example") 347b3ff069SJeremy L Thompson parser.add_argument("-c", "--ceed", default="/cpu/self", 357b3ff069SJeremy L Thompson help="libCEED resource specifier (default: /cpu/self)") 367b3ff069SJeremy L Thompson parser.add_argument("-d", "--dim", type=int, default=3, 377b3ff069SJeremy L Thompson help="Problem dimension (1-3) (default: 3)") 387b3ff069SJeremy L Thompson parser.add_argument("-m", "--mesh-degree", type=int, default=4, 397b3ff069SJeremy L Thompson help="Mesh polynomial degree (default: 4)") 407b3ff069SJeremy L Thompson parser.add_argument("-p", "--solution-degree", type=int, default=4, 417b3ff069SJeremy L Thompson help="Solution polynomial degree (default: 4)") 427b3ff069SJeremy L Thompson parser.add_argument("-q", "--quadrature-points", type=int, default=6, 437b3ff069SJeremy L Thompson help="Number of quadrature points (default: 6)") 447b3ff069SJeremy L Thompson parser.add_argument("-s", "--problem-size", type=int, default=-1, 457b3ff069SJeremy L Thompson help="Approximate problem size (default: ~256k)") 467b3ff069SJeremy L Thompson parser.add_argument("-t", "--test", action="store_true", 477b3ff069SJeremy L Thompson help="Test mode (reduced problem size)") 487b3ff069SJeremy L Thompson parser.add_argument("--quiet", action="store_true", 497b3ff069SJeremy L Thompson help="Suppress output") 507b3ff069SJeremy L Thompson parser.add_argument("-g", "--gallery", action="store_true", 517b3ff069SJeremy L Thompson help="Use gallery QFunctions") 527b3ff069SJeremy L Thompson 537b3ff069SJeremy L Thompson args = parser.parse_args() 547b3ff069SJeremy L Thompson if args.dim not in [1, 2, 3]: 557b3ff069SJeremy L Thompson parser.error("Dimension must be 1, 2, or 3") 567b3ff069SJeremy L Thompson return args 577b3ff069SJeremy L Thompson 587b3ff069SJeremy L Thompson 597b3ff069SJeremy L Thompsondef get_cartesian_mesh_size(dim, degree, prob_size): 607b3ff069SJeremy L Thompson """Determine Cartesian mesh size for given problem size 617b3ff069SJeremy L Thompson 627b3ff069SJeremy L Thompson Args: 637b3ff069SJeremy L Thompson dim: Spatial dimension (1-3) 647b3ff069SJeremy L Thompson degree: Polynomial degree 657b3ff069SJeremy L Thompson prob_size: Target problem size 667b3ff069SJeremy L Thompson 677b3ff069SJeremy L Thompson Returns: 687b3ff069SJeremy L Thompson list: Number of elements in each dimension 697b3ff069SJeremy L Thompson """ 707b3ff069SJeremy L Thompson # Calculate number of elements needed 717b3ff069SJeremy L Thompson num_elem = prob_size // (degree ** dim) 727b3ff069SJeremy L Thompson 737b3ff069SJeremy L Thompson # Find smallest power of 2 >= num_elem 747b3ff069SJeremy L Thompson s = 0 757b3ff069SJeremy L Thompson while num_elem > 1: 767b3ff069SJeremy L Thompson num_elem = num_elem / 2 777b3ff069SJeremy L Thompson s += 1 787b3ff069SJeremy L Thompson 797b3ff069SJeremy L Thompson # Distribute across dimensions 807b3ff069SJeremy L Thompson r = s % dim 817b3ff069SJeremy L Thompson num_xyz = [] 827b3ff069SJeremy L Thompson for d in range(dim): 837b3ff069SJeremy L Thompson sd = s // dim 847b3ff069SJeremy L Thompson if r > 0: 857b3ff069SJeremy L Thompson sd += 1 867b3ff069SJeremy L Thompson r -= 1 877b3ff069SJeremy L Thompson num_xyz.append(1 << sd) 887b3ff069SJeremy L Thompson return num_xyz 897b3ff069SJeremy L Thompson 907b3ff069SJeremy L Thompson 917b3ff069SJeremy L Thompsondef build_cartesian_restriction(ceed, dim, num_xyz, degree, num_comp, num_q_comp, num_qpts, create_qdata=False): 927b3ff069SJeremy L Thompson """Build element restriction for Cartesian grid 937b3ff069SJeremy L Thompson 947b3ff069SJeremy L Thompson Args: 957b3ff069SJeremy L Thompson ceed: libCEED context 967b3ff069SJeremy L Thompson dim: Spatial dimension 977b3ff069SJeremy L Thompson num_xyz: Elements per dimension 987b3ff069SJeremy L Thompson degree: Polynomial degree 997b3ff069SJeremy L Thompson num_comp: Number of components 1007b3ff069SJeremy L Thompson num_q_comp: Number of quadrature data components 1017b3ff069SJeremy L Thompson num_qpts: Quadrature points per dimension 1027b3ff069SJeremy L Thompson build_qdata: Flag to build restriction for quadrature data 1037b3ff069SJeremy L Thompson 1047b3ff069SJeremy L Thompson Returns: 1057b3ff069SJeremy L Thompson tuple: (elem_restriction, size, q_data_restriction, num_elem, elem_qpts) 1067b3ff069SJeremy L Thompson """ 1077b3ff069SJeremy L Thompson p = degree + 1 # Nodes per element per dimension 1087b3ff069SJeremy L Thompson num_nodes = p ** dim 1097b3ff069SJeremy L Thompson elem_qpts = num_qpts ** dim 1107b3ff069SJeremy L Thompson 1117b3ff069SJeremy L Thompson # Calculate grid parameters 1127b3ff069SJeremy L Thompson nd = [] 1137b3ff069SJeremy L Thompson num_elem = 1 1147b3ff069SJeremy L Thompson scalar_size = 1 1157b3ff069SJeremy L Thompson for d in range(dim): 1167b3ff069SJeremy L Thompson num_elem *= num_xyz[d] 1177b3ff069SJeremy L Thompson nd.append(num_xyz[d] * (p - 1) + 1) # Nodes per dimension 1187b3ff069SJeremy L Thompson scalar_size *= nd[d] 1197b3ff069SJeremy L Thompson 1207b3ff069SJeremy L Thompson size = scalar_size * num_comp 1217b3ff069SJeremy L Thompson 1227b3ff069SJeremy L Thompson # Create element connectivity 1237b3ff069SJeremy L Thompson elem_nodes = np.zeros(num_elem * num_nodes, dtype=np.int32) 1247b3ff069SJeremy L Thompson for e in range(num_elem): 1257b3ff069SJeremy L Thompson # Get element coordinates 1267b3ff069SJeremy L Thompson e_xyz = [0] * dim 1277b3ff069SJeremy L Thompson re = e 1287b3ff069SJeremy L Thompson for d in range(dim): 1297b3ff069SJeremy L Thompson e_xyz[d] = re % num_xyz[d] 1307b3ff069SJeremy L Thompson re //= num_xyz[d] 1317b3ff069SJeremy L Thompson 1327b3ff069SJeremy L Thompson # Calculate global node numbers 1337b3ff069SJeremy L Thompson for n in range(num_nodes): 1347b3ff069SJeremy L Thompson g_node = 0 1357b3ff069SJeremy L Thompson g_stride = 1 1367b3ff069SJeremy L Thompson r_node = n 1377b3ff069SJeremy L Thompson for d in range(dim): 1387b3ff069SJeremy L Thompson g_node += (e_xyz[d] * (p - 1) + r_node % p) * g_stride 1397b3ff069SJeremy L Thompson g_stride *= nd[d] 1407b3ff069SJeremy L Thompson r_node //= p 1417b3ff069SJeremy L Thompson elem_nodes[e * num_nodes + n] = g_node 1427b3ff069SJeremy L Thompson 1437b3ff069SJeremy L Thompson # Create restrictions 1447b3ff069SJeremy L Thompson elem_restriction = ceed.ElemRestriction( 1457b3ff069SJeremy L Thompson num_elem, num_nodes, num_comp, scalar_size, size, elem_nodes) 1467b3ff069SJeremy L Thompson 1477b3ff069SJeremy L Thompson q_data_restriction = None 1487b3ff069SJeremy L Thompson if create_qdata: 1497b3ff069SJeremy L Thompson strides = np.array([1, elem_qpts, elem_qpts * num_q_comp], dtype=np.int32) 1507b3ff069SJeremy L Thompson q_data_restriction = ceed.StridedElemRestriction( 1517b3ff069SJeremy L Thompson num_elem, elem_qpts, num_q_comp, num_elem * elem_qpts * num_q_comp, strides) 1527b3ff069SJeremy L Thompson 1537b3ff069SJeremy L Thompson return elem_restriction, size, q_data_restriction, num_elem, elem_qpts 1547b3ff069SJeremy L Thompson 1557b3ff069SJeremy L Thompson 1567b3ff069SJeremy L Thompsondef set_cartesian_mesh_coords(ceed, dim, num_xyz, mesh_degree, mesh_coords): 1577b3ff069SJeremy L Thompson """Create Cartesian mesh coordinates 1587b3ff069SJeremy L Thompson 1597b3ff069SJeremy L Thompson Args: 1607b3ff069SJeremy L Thompson ceed: libCEED context 1617b3ff069SJeremy L Thompson dim: Spatial dimension 1627b3ff069SJeremy L Thompson num_xyz: Elements per dimension 1637b3ff069SJeremy L Thompson mesh_degree: Mesh polynomial degree 1647b3ff069SJeremy L Thompson mesh_coords: CeedVector to hold mesh coordinates 1657b3ff069SJeremy L Thompson 1667b3ff069SJeremy L Thompson Returns: 1677b3ff069SJeremy L Thompson Vector: Mesh coordinates 1687b3ff069SJeremy L Thompson """ 1697b3ff069SJeremy L Thompson p = mesh_degree + 1 1707b3ff069SJeremy L Thompson nd = [] 1717b3ff069SJeremy L Thompson scalar_size = 1 1727b3ff069SJeremy L Thompson for d in range(dim): 1737b3ff069SJeremy L Thompson nd.append(num_xyz[d] * (p - 1) + 1) 1747b3ff069SJeremy L Thompson scalar_size *= nd[d] 1757b3ff069SJeremy L Thompson 1767b3ff069SJeremy L Thompson # Get Lobatto nodes (quadrature points) 1777b3ff069SJeremy L Thompson nodes, _ = ceed.lobatto_quadrature(p) 1787b3ff069SJeremy L Thompson nodes = 0.5 + 0.5 * nodes # Map from [-1,1] to [0,1] 1797b3ff069SJeremy L Thompson 1807b3ff069SJeremy L Thompson # Create coordinates 1817b3ff069SJeremy L Thompson coords = np.zeros(scalar_size * dim) 1827b3ff069SJeremy L Thompson for gs_node in range(scalar_size): 1837b3ff069SJeremy L Thompson r_node = gs_node 1847b3ff069SJeremy L Thompson for d in range(dim): 1857b3ff069SJeremy L Thompson d_1d = r_node % nd[d] 1867b3ff069SJeremy L Thompson elem_id = d_1d // (p - 1) 1877b3ff069SJeremy L Thompson node_id = d_1d % (p - 1) 1887b3ff069SJeremy L Thompson coords[gs_node + scalar_size * d] = (elem_id + nodes[node_id]) / num_xyz[d] 1897b3ff069SJeremy L Thompson r_node //= nd[d] 1907b3ff069SJeremy L Thompson 1917b3ff069SJeremy L Thompson mesh_coords.set_array(coords, cmode=libceed.COPY_VALUES) 1927b3ff069SJeremy L Thompson return mesh_coords 1937b3ff069SJeremy L Thompson 1947b3ff069SJeremy L Thompson 1957b3ff069SJeremy L Thompsondef transform_mesh_coords(dim, mesh_size, mesh_coords, use_sin=True): 1967b3ff069SJeremy L Thompson """Transform mesh coordinates and return exact surface area 1977b3ff069SJeremy L Thompson 1987b3ff069SJeremy L Thompson Args: 1997b3ff069SJeremy L Thompson dim: Spatial dimension 2007b3ff069SJeremy L Thompson mesh_size: Total mesh vector size 2017b3ff069SJeremy L Thompson mesh_coords: Mesh coordinates vector 2027b3ff069SJeremy L Thompson use_sin: Use sinusoidal transformation 2037b3ff069SJeremy L Thompson 2047b3ff069SJeremy L Thompson Returns: 2057b3ff069SJeremy L Thompson float: Tuple with exact volume and surface area for transformed mesh 2067b3ff069SJeremy L Thompson """ 2077b3ff069SJeremy L Thompson exact_volume = {1: 1.0, 2: 3. / 4. * np.pi, 3: 3. / 4. * np.pi}[dim] 2087b3ff069SJeremy L Thompson exact_area = {1: 2.0, 2: 4.0, 3: 6.0}[dim] 2097b3ff069SJeremy L Thompson 2107b3ff069SJeremy L Thompson # Apply transformation to coordinates 2117b3ff069SJeremy L Thompson num_nodes = mesh_size // dim 2127b3ff069SJeremy L Thompson with mesh_coords.array_write() as coords: 2137b3ff069SJeremy L Thompson if dim == 1: 2147b3ff069SJeremy L Thompson for i in range(num_nodes): 2157b3ff069SJeremy L Thompson x = coords[i] - 0.5 2167b3ff069SJeremy L Thompson coords[i] = 0.5 + (1.0 / np.sqrt(3.0)) * np.sin((2.0 / 3.0) * np.pi * x) 2177b3ff069SJeremy L Thompson else: 2187b3ff069SJeremy L Thompson if use_sin: 2197b3ff069SJeremy L Thompson for i in range(num_nodes): 2207b3ff069SJeremy L Thompson u = 1. + coords[i] 2217b3ff069SJeremy L Thompson v = np.pi / 2. * coords[i + num_nodes] 2227b3ff069SJeremy L Thompson coords[i] = u * np.cos(v) 2237b3ff069SJeremy L Thompson coords[i + num_nodes] = u * np.sin(v) 2247b3ff069SJeremy L Thompson else: 2257b3ff069SJeremy L Thompson for i in range(num_nodes): 2267b3ff069SJeremy L Thompson x = coords[i] - 0.5 2277b3ff069SJeremy L Thompson coords[i] = 0.5 + (1.0 / np.sqrt(3.0)) * np.sin((2.0 / 3.0) * np.pi * x) 2287b3ff069SJeremy L Thompson 2297b3ff069SJeremy L Thompson return (exact_volume, exact_area) 2307b3ff069SJeremy L Thompson 2317b3ff069SJeremy L Thompson 2327b3ff069SJeremy L Thompsondef find_qfs_so(name, path): 2337b3ff069SJeremy L Thompson """Find the QFunctions shared library. 2347b3ff069SJeremy L Thompson Returns: 2357b3ff069SJeremy L Thompson Filepath to shared library object 2367b3ff069SJeremy L Thompson """ 2377b3ff069SJeremy L Thompson for root, dirs, files in os.walk(path): 2387b3ff069SJeremy L Thompson if name in files: 2397b3ff069SJeremy L Thompson return os.path.join(root, name) 2407b3ff069SJeremy L Thompson 2417b3ff069SJeremy L Thompson 2427b3ff069SJeremy L Thompsondef load_qfs_so(): 2437b3ff069SJeremy L Thompson """Load the QFunctions shared library. 2447b3ff069SJeremy L Thompson Returns: 2457b3ff069SJeremy L Thompson Loaded shared library object 2467b3ff069SJeremy L Thompson """ 2477b3ff069SJeremy L Thompson file_dir = os.path.join( 2487b3ff069SJeremy L Thompson os.path.dirname(os.path.abspath(__file__)), 2497b3ff069SJeremy L Thompson "build") 2507b3ff069SJeremy L Thompson qfs_so = find_qfs_so( 2517b3ff069SJeremy L Thompson "libceed_c_qfunctions" + get_config_var("EXT_SUFFIX"), 2527b3ff069SJeremy L Thompson file_dir) 2537b3ff069SJeremy L Thompson 2547b3ff069SJeremy L Thompson # Load library 2557b3ff069SJeremy L Thompson return ctypes.cdll.LoadLibrary(qfs_so) 256