xref: /libCEED/python/tests/test-4-qfunction.py (revision ebfb1ab346d5a1addc1221edc1d1c7f1a6380df6)
1# Copyright (c) 2017-2025, 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