1# Copyright (c) 2017-2026, Lawrence Livermore National Security, LLC and other CEED contributors 2# All Rights Reserved. See the top-level LICENSE and NOTICE files for details. 3# 4# SPDX-License-Identifier: BSD-2-Clause 5# 6# This file is part of CEED: http://github.com/ceed 7 8# @file 9# Test Ceed QFunction functionality 10 11import os 12import libceed 13import numpy as np 14import check 15 16TOL = libceed.EPSILON * 256 17 18# ------------------------------------------------------------------------------- 19# Utility 20# ------------------------------------------------------------------------------- 21 22 23def load_qfs_so(): 24 from sysconfig import get_config_var 25 import ctypes 26 27 file_dir = os.path.dirname(os.path.abspath(__file__)) 28 qfs_so = os.path.join( 29 file_dir, 30 "libceed_qfunctions" + get_config_var("EXT_SUFFIX")) 31 32 # Load library 33 return ctypes.cdll.LoadLibrary(qfs_so) 34 35# ------------------------------------------------------------------------------- 36# Test creation, evaluation, and destruction for qfunction 37# ------------------------------------------------------------------------------- 38 39 40def test_400(ceed_resource): 41 ceed = libceed.Ceed(ceed_resource) 42 43 file_dir = os.path.dirname(os.path.abspath(__file__)) 44 qfs = load_qfs_so() 45 46 qf_setup = ceed.QFunction(1, qfs.setup_mass, 47 os.path.join(file_dir, "test-qfunctions.h:setup_mass")) 48 qf_setup.add_input("w", 1, libceed.EVAL_WEIGHT) 49 qf_setup.add_input("dx", 1, libceed.EVAL_GRAD) 50 qf_setup.add_output("qdata", 1, libceed.EVAL_NONE) 51 52 qf_mass = ceed.QFunction(1, qfs.apply_mass, 53 os.path.join(file_dir, "test-qfunctions.h:apply_mass")) 54 qf_mass.add_input("qdata", 1, libceed.EVAL_NONE) 55 qf_mass.add_input("u", 1, libceed.EVAL_INTERP) 56 qf_mass.add_output("v", 1, libceed.EVAL_INTERP) 57 58 q = 8 59 60 w_array = np.zeros(q, dtype=ceed.scalar_type()) 61 u_array = np.zeros(q, dtype=ceed.scalar_type()) 62 v_true = np.zeros(q, dtype=ceed.scalar_type()) 63 for i in range(q): 64 x = 2. * i / (q - 1) - 1 65 w_array[i] = 1 - x * x 66 u_array[i] = 2 + 3 * x + 5 * x * x 67 v_true[i] = w_array[i] * u_array[i] 68 69 dx = ceed.Vector(q) 70 dx.set_value(1) 71 w = ceed.Vector(q) 72 w.set_array(w_array, cmode=libceed.USE_POINTER) 73 u = ceed.Vector(q) 74 u.set_array(u_array, cmode=libceed.USE_POINTER) 75 v = ceed.Vector(q) 76 v.set_value(0) 77 qdata = ceed.Vector(q) 78 qdata.set_value(0) 79 80 inputs = [dx, w] 81 outputs = [qdata] 82 qf_setup.apply(q, inputs, outputs) 83 84 inputs = [qdata, u] 85 outputs = [v] 86 qf_mass.apply(q, inputs, outputs) 87 88 with v.array_read() as v_array: 89 for i in range(q): 90 assert v_array[i] == v_true[i] 91 92# ------------------------------------------------------------------------------- 93# Test creation, evaluation, and destruction for qfunction 94# ------------------------------------------------------------------------------- 95 96 97def test_401(ceed_resource): 98 ceed = libceed.Ceed(ceed_resource) 99 100 file_dir = os.path.dirname(os.path.abspath(__file__)) 101 qfs = load_qfs_so() 102 103 qf_setup = ceed.QFunction(1, qfs.setup_mass, 104 os.path.join(file_dir, "test-qfunctions.h:setup_mass")) 105 qf_setup.add_input("w", 1, libceed.EVAL_WEIGHT) 106 qf_setup.add_input("dx", 1, libceed.EVAL_GRAD) 107 qf_setup.add_output("qdata", 1, libceed.EVAL_NONE) 108 109 qf_mass = ceed.QFunction(1, qfs.apply_mass, 110 os.path.join(file_dir, "test-qfunctions.h:apply_mass")) 111 qf_mass.add_input("qdata", 1, libceed.EVAL_NONE) 112 qf_mass.add_input("u", 1, libceed.EVAL_INTERP) 113 qf_mass.add_output("v", 1, libceed.EVAL_INTERP) 114 115 ctx_data = np.array([1., 2., 3., 4., 5.], dtype=ceed.scalar_type()) 116 ctx = ceed.QFunctionContext() 117 ctx.set_data(ctx_data) 118 qf_mass.set_context(ctx) 119 120 q = 8 121 122 w_array = np.zeros(q, dtype=ceed.scalar_type()) 123 u_array = np.zeros(q, dtype=ceed.scalar_type()) 124 v_true = np.zeros(q, dtype=ceed.scalar_type()) 125 for i in range(q): 126 x = 2. * i / (q - 1) - 1 127 w_array[i] = 1 - x * x 128 u_array[i] = 2 + 3 * x + 5 * x * x 129 v_true[i] = 5 * w_array[i] * u_array[i] 130 131 dx = ceed.Vector(q) 132 dx.set_value(1) 133 w = ceed.Vector(q) 134 w.set_array(w_array, cmode=libceed.USE_POINTER) 135 u = ceed.Vector(q) 136 u.set_array(u_array, cmode=libceed.USE_POINTER) 137 v = ceed.Vector(q) 138 v.set_value(0) 139 qdata = ceed.Vector(q) 140 qdata.set_value(0) 141 142 inputs = [dx, w] 143 outputs = [qdata] 144 qf_setup.apply(q, inputs, outputs) 145 146 inputs = [qdata, u] 147 outputs = [v] 148 qf_mass.apply(q, inputs, outputs) 149 150 with v.array_read() as v_array: 151 for i in range(q): 152 assert abs(v_array[i] - v_true[i]) < TOL 153 154# ------------------------------------------------------------------------------- 155# Test viewing of qfunction 156# ------------------------------------------------------------------------------- 157 158 159def test_402(ceed_resource, capsys): 160 ceed = libceed.Ceed(ceed_resource) 161 162 file_dir = os.path.dirname(os.path.abspath(__file__)) 163 qfs = load_qfs_so() 164 165 qf_setup = ceed.QFunction(1, qfs.setup_mass, 166 os.path.join(file_dir, "test-qfunctions.h:setup_mass")) 167 qf_setup.add_input("w", 1, libceed.EVAL_WEIGHT) 168 qf_setup.add_input("dx", 1, libceed.EVAL_GRAD) 169 qf_setup.add_output("qdata", 1, libceed.EVAL_NONE) 170 171 qf_mass = ceed.QFunction(1, qfs.apply_mass, 172 os.path.join(file_dir, "test-qfunctions.h:apply_mass")) 173 qf_mass.add_input("qdata", 1, libceed.EVAL_NONE) 174 qf_mass.add_input("u", 1, libceed.EVAL_INTERP) 175 qf_mass.add_output("v", 1, libceed.EVAL_INTERP) 176 177 print(qf_setup) 178 print(qf_mass) 179 180 if libceed.lib.CEED_SCALAR_TYPE == libceed.SCALAR_FP64: 181 ctx_data = np.array([1., 2., 3., 4., 5.], dtype="float64") 182 # Make ctx twice as long in fp32, so size will be the same as fp64 output 183 else: 184 ctx_data = np.array([1., 2., 3., 4., 5., 1., 2., 3., 4., 5.], 185 dtype="float32") 186 ctx = ceed.QFunctionContext() 187 ctx.set_data(ctx_data) 188 print(ctx) 189 190 stdout, stderr, ref_stdout = check.output(capsys) 191 assert not stderr 192 assert stdout == ref_stdout 193 194# ------------------------------------------------------------------------------- 195# Test creation, evaluation, and destruction for qfunction by name 196# ------------------------------------------------------------------------------- 197 198 199def test_410(ceed_resource): 200 ceed = libceed.Ceed(ceed_resource) 201 202 qf_setup = ceed.QFunctionByName("Mass1DBuild") 203 qf_mass = ceed.QFunctionByName("MassApply") 204 205 q = 8 206 207 j_array = np.zeros(q, dtype=ceed.scalar_type()) 208 w_array = np.zeros(q, dtype=ceed.scalar_type()) 209 u_array = np.zeros(q, dtype=ceed.scalar_type()) 210 v_true = np.zeros(q, dtype=ceed.scalar_type()) 211 for i in range(q): 212 x = 2. * i / (q - 1) - 1 213 j_array[i] = 1 214 w_array[i] = 1 - x * x 215 u_array[i] = 2 + 3 * x + 5 * x * x 216 v_true[i] = w_array[i] * u_array[i] 217 218 j = ceed.Vector(q) 219 j.set_array(j_array, cmode=libceed.USE_POINTER) 220 w = ceed.Vector(q) 221 w.set_array(w_array, cmode=libceed.USE_POINTER) 222 u = ceed.Vector(q) 223 u.set_array(u_array, cmode=libceed.USE_POINTER) 224 v = ceed.Vector(q) 225 v.set_value(0) 226 qdata = ceed.Vector(q) 227 qdata.set_value(0) 228 229 inputs = [j, w] 230 outputs = [qdata] 231 qf_setup.apply(q, inputs, outputs) 232 233 inputs = [w, u] 234 outputs = [v] 235 qf_mass.apply(q, inputs, outputs) 236 237 with v.array_read() as v_array: 238 for i in range(q): 239 assert v_array[i] == v_true[i] 240 241# ------------------------------------------------------------------------------- 242# Test creation, evaluation, and destruction of identity qfunction 243# ------------------------------------------------------------------------------- 244 245 246def test_411(ceed_resource): 247 ceed = libceed.Ceed(ceed_resource) 248 249 qf = ceed.IdentityQFunction(1, libceed.EVAL_INTERP, libceed.EVAL_INTERP) 250 251 q = 8 252 253 u_array = np.zeros(q, dtype=ceed.scalar_type()) 254 for i in range(q): 255 u_array[i] = i * i 256 257 u = ceed.Vector(q) 258 u.set_array(u_array, cmode=libceed.USE_POINTER) 259 v = ceed.Vector(q) 260 v.set_value(0) 261 262 inputs = [u] 263 outputs = [v] 264 qf.apply(q, inputs, outputs) 265 266 with v.array_read() as v_array: 267 for i in range(q): 268 assert v_array[i] == i * i 269 270# ------------------------------------------------------------------------------- 271# Test creation, evaluation, and destruction of identity qfunction with size>1 272# ------------------------------------------------------------------------------- 273 274 275def test_412(ceed_resource): 276 ceed = libceed.Ceed(ceed_resource) 277 278 size = 3 279 qf = ceed.IdentityQFunction(size, libceed.EVAL_INTERP, libceed.EVAL_INTERP) 280 281 q = 8 282 283 u_array = np.zeros(q * size, dtype=ceed.scalar_type()) 284 for i in range(q * size): 285 u_array[i] = i * i 286 287 u = ceed.Vector(q * size) 288 u.set_array(u_array, cmode=libceed.USE_POINTER) 289 v = ceed.Vector(q * size) 290 v.set_value(0) 291 292 inputs = [u] 293 outputs = [v] 294 qf.apply(q, inputs, outputs) 295 296 with v.array_read() as v_array: 297 for i in range(q * size): 298 assert v_array[i] == i * i 299 300# ------------------------------------------------------------------------------- 301# Test viewing of qfunction by name 302# ------------------------------------------------------------------------------- 303 304 305def test_413(ceed_resource, capsys): 306 ceed = libceed.Ceed(ceed_resource) 307 308 qf_setup = ceed.QFunctionByName("Mass1DBuild") 309 qf_mass = ceed.QFunctionByName("MassApply") 310 311 print(qf_setup) 312 print(qf_mass) 313 314 stdout, stderr, ref_stdout = check.output(capsys) 315 assert not stderr 316 assert stdout == ref_stdout 317 318# ------------------------------------------------------------------------------- 319