xref: /libCEED/julia/LibCEED.jl/examples/ex3-volume.jl (revision afeb93e9a539977d805c4dfebb022b4afb833c26)
1*afeb93e9SKartiki Pandeusing LibCEED, Printf
2*afeb93e9SKartiki Pande
3*afeb93e9SKartiki Pandeinclude("common.jl")
4*afeb93e9SKartiki Pande
5*afeb93e9SKartiki Pandefunction transform_mesh_coords!(dim, mesh_size, mesh_coords)
6*afeb93e9SKartiki Pande    @witharray coords = mesh_coords begin
7*afeb93e9SKartiki Pande        if dim == 1
8*afeb93e9SKartiki Pande            for i = 1:mesh_size
9*afeb93e9SKartiki Pande                # map [0,1] to [0,1] varying the mesh density
10*afeb93e9SKartiki Pande                coords[i] = 0.5 + 1.0/sqrt(3.0)*sin((2.0/3.0)*pi*(coords[i] - 0.5))
11*afeb93e9SKartiki Pande            end
12*afeb93e9SKartiki Pande            exact_volume = 1.0
13*afeb93e9SKartiki Pande        else
14*afeb93e9SKartiki Pande            num_nodes = mesh_size÷dim
15*afeb93e9SKartiki Pande            @inbounds @simd for i = 1:num_nodes
16*afeb93e9SKartiki Pande                # map (x,y) from [0,1]x[0,1] to the quarter annulus with polar
17*afeb93e9SKartiki Pande                # coordinates, (r,phi) in [1,2]x[0,pi/2] with area = 3/4*pi
18*afeb93e9SKartiki Pande                u = coords[i]
19*afeb93e9SKartiki Pande                v = coords[i+num_nodes]
20*afeb93e9SKartiki Pande                u = 1.0 + u
21*afeb93e9SKartiki Pande                v = pi/2*v
22*afeb93e9SKartiki Pande                coords[i] = u*cos(v)
23*afeb93e9SKartiki Pande                coords[i+num_nodes] = u*sin(v)
24*afeb93e9SKartiki Pande            end
25*afeb93e9SKartiki Pande            exact_volume = 3.0/4.0*pi
26*afeb93e9SKartiki Pande        end
27*afeb93e9SKartiki Pande        return exact_volume
28*afeb93e9SKartiki Pande    end
29*afeb93e9SKartiki Pandeend
30*afeb93e9SKartiki Pande
31*afeb93e9SKartiki Pandefunction run_ex3(; ceed_spec, dim, mesh_order, sol_order, num_qpts, prob_size)
32*afeb93e9SKartiki Pande    ncompx = dim
33*afeb93e9SKartiki Pande    prob_size < 0 && (prob_size = 256*1024)
34*afeb93e9SKartiki Pande
35*afeb93e9SKartiki Pande    ceed = Ceed(ceed_spec)
36*afeb93e9SKartiki Pande    mesh_basis =
37*afeb93e9SKartiki Pande        create_tensor_h1_lagrange_basis(ceed, dim, ncompx, mesh_order + 1, num_qpts, GAUSS)
38*afeb93e9SKartiki Pande    sol_basis =
39*afeb93e9SKartiki Pande        create_tensor_h1_lagrange_basis(ceed, dim, 1, sol_order + 1, num_qpts, GAUSS)
40*afeb93e9SKartiki Pande
41*afeb93e9SKartiki Pande    # Determine the mesh size based on the given approximate problem size.
42*afeb93e9SKartiki Pande    nxyz = get_cartesian_mesh_size(dim, sol_order, prob_size)
43*afeb93e9SKartiki Pande    println("Mesh size: ", nxyz)
44*afeb93e9SKartiki Pande
45*afeb93e9SKartiki Pande    # Build CeedElemRestriction objects describing the mesh and solution discrete
46*afeb93e9SKartiki Pande    # representations.
47*afeb93e9SKartiki Pande    mesh_size, mesh_rstr, _ =
48*afeb93e9SKartiki Pande        build_cartesian_restriction(ceed, dim, nxyz, mesh_order, ncompx, num_qpts)
49*afeb93e9SKartiki Pande    num_q_comp = 1 + div(dim*(dim + 1), 2)
50*afeb93e9SKartiki Pande    sol_size, _, qdata_rstr_i = build_cartesian_restriction(
51*afeb93e9SKartiki Pande        ceed,
52*afeb93e9SKartiki Pande        dim,
53*afeb93e9SKartiki Pande        nxyz,
54*afeb93e9SKartiki Pande        sol_order,
55*afeb93e9SKartiki Pande        num_q_comp,
56*afeb93e9SKartiki Pande        num_qpts,
57*afeb93e9SKartiki Pande        mode=StridedOnly,
58*afeb93e9SKartiki Pande    )
59*afeb93e9SKartiki Pande    sol_size, sol_rstr, sol_rstr_i = build_cartesian_restriction(
60*afeb93e9SKartiki Pande        ceed,
61*afeb93e9SKartiki Pande        dim,
62*afeb93e9SKartiki Pande        nxyz,
63*afeb93e9SKartiki Pande        sol_order,
64*afeb93e9SKartiki Pande        1,
65*afeb93e9SKartiki Pande        num_qpts,
66*afeb93e9SKartiki Pande        mode=RestrictionAndStrided,
67*afeb93e9SKartiki Pande    )
68*afeb93e9SKartiki Pande    println("Number of mesh nodes     : ", div(mesh_size, dim))
69*afeb93e9SKartiki Pande    println("Number of solution nodes : ", sol_size)
70*afeb93e9SKartiki Pande
71*afeb93e9SKartiki Pande    # Create a CeedVector with the mesh coordinates.
72*afeb93e9SKartiki Pande    mesh_coords = CeedVector(ceed, mesh_size)
73*afeb93e9SKartiki Pande    set_cartesian_mesh_coords!(dim, nxyz, mesh_order, mesh_coords)
74*afeb93e9SKartiki Pande    # Apply a transformation to the mesh.
75*afeb93e9SKartiki Pande    exact_vol = transform_mesh_coords!(dim, mesh_size, mesh_coords)
76*afeb93e9SKartiki Pande
77*afeb93e9SKartiki Pande    #Create the Q-function that builds the mass+diffusion operator ( i.e it computes the quadrature data) and set its context data.
78*afeb93e9SKartiki Pande    @interior_qf build_qfunc = (
79*afeb93e9SKartiki Pande        ceed,
80*afeb93e9SKartiki Pande        dim=dim,
81*afeb93e9SKartiki Pande        (dx, :in, EVAL_GRAD, dim, dim),      # ← THIS LINE: dx input
82*afeb93e9SKartiki Pande        (weights, :in, EVAL_WEIGHT),         # ← weights input
83*afeb93e9SKartiki Pande        (qdata, :out, EVAL_NONE, num_q_comp), # ← qdata output
84*afeb93e9SKartiki Pande        begin
85*afeb93e9SKartiki Pande            # Compute determinant
86*afeb93e9SKartiki Pande            det_J = det(dx)
87*afeb93e9SKartiki Pande
88*afeb93e9SKartiki Pande            # Store mass component
89*afeb93e9SKartiki Pande            qdata[1] = weights*det_J
90*afeb93e9SKartiki Pande
91*afeb93e9SKartiki Pande            # Store diffusion components (J^T * J)
92*afeb93e9SKartiki Pande            idx = 2
93*afeb93e9SKartiki Pande            for i = 1:dim
94*afeb93e9SKartiki Pande                for j = i:dim
95*afeb93e9SKartiki Pande                    qdata[idx] = dx[:, i]'*dx[:, j]
96*afeb93e9SKartiki Pande                    idx += 1
97*afeb93e9SKartiki Pande                end
98*afeb93e9SKartiki Pande            end
99*afeb93e9SKartiki Pande        end,
100*afeb93e9SKartiki Pande    )
101*afeb93e9SKartiki Pande
102*afeb93e9SKartiki Pande    # Create the operator that builds the quadrature data for the mass+diffusion operator.
103*afeb93e9SKartiki Pande    build_oper = Operator(
104*afeb93e9SKartiki Pande        ceed,
105*afeb93e9SKartiki Pande        qf=build_qfunc,
106*afeb93e9SKartiki Pande        fields=[
107*afeb93e9SKartiki Pande            (:dx, mesh_rstr, mesh_basis, CeedVectorActive()),
108*afeb93e9SKartiki Pande            (:weights, ElemRestrictionNone(), mesh_basis, CeedVectorNone()),
109*afeb93e9SKartiki Pande            (:qdata, qdata_rstr_i, BasisNone(), CeedVectorActive()),
110*afeb93e9SKartiki Pande        ],
111*afeb93e9SKartiki Pande    )
112*afeb93e9SKartiki Pande
113*afeb93e9SKartiki Pande    # Compute the quadrature data for the mass+diff operator.
114*afeb93e9SKartiki Pande    elem_qpts = num_qpts^dim
115*afeb93e9SKartiki Pande    num_elem = prod(nxyz)
116*afeb93e9SKartiki Pande    qdata = CeedVector(ceed, num_elem*elem_qpts*num_q_comp)
117*afeb93e9SKartiki Pande    print("Computing the quadrature data for the mass+diffusion operator ...")
118*afeb93e9SKartiki Pande    flush(stdout)
119*afeb93e9SKartiki Pande    apply!(build_oper, mesh_coords, qdata)
120*afeb93e9SKartiki Pande    println(" done.")
121*afeb93e9SKartiki Pande
122*afeb93e9SKartiki Pande    # Create the Q-function that defines the action of the mass+diffusion operator.
123*afeb93e9SKartiki Pande    @interior_qf apply_qfunc = (
124*afeb93e9SKartiki Pande        ceed,
125*afeb93e9SKartiki Pande        dim=dim,
126*afeb93e9SKartiki Pande        (u, :in, EVAL_INTERP),
127*afeb93e9SKartiki Pande        (du, :in, EVAL_GRAD, dim),
128*afeb93e9SKartiki Pande        (qdata, :in, EVAL_NONE, num_q_comp),
129*afeb93e9SKartiki Pande        (v, :out, EVAL_INTERP),
130*afeb93e9SKartiki Pande        (dv, :out, EVAL_GRAD, dim),
131*afeb93e9SKartiki Pande        begin
132*afeb93e9SKartiki Pande            # Apply mass: v = qdata[1] * u
133*afeb93e9SKartiki Pande            v .= qdata[1].*u
134*afeb93e9SKartiki Pande
135*afeb93e9SKartiki Pande            # Apply diffusion: dv = (qdata[2:end]) * du
136*afeb93e9SKartiki Pande            # The qdata contains the symmetric diffusion tensor (J^T*J)
137*afeb93e9SKartiki Pande            # dv_i = sum_j (J^T*J)_{i,j} * du_j
138*afeb93e9SKartiki Pande
139*afeb93e9SKartiki Pande            # For efficiency, rebuild the matrix from stored components
140*afeb93e9SKartiki Pande            idx = 2
141*afeb93e9SKartiki Pande            for i = 1:dim
142*afeb93e9SKartiki Pande                dv_i = 0.0
143*afeb93e9SKartiki Pande                for j = 1:dim
144*afeb93e9SKartiki Pande                    # Reconstruct symmetric matrix element
145*afeb93e9SKartiki Pande                    if j >= i
146*afeb93e9SKartiki Pande                        mat_idx = idx + div((j - 1)*j, 2) + (i - 1)
147*afeb93e9SKartiki Pande                    else
148*afeb93e9SKartiki Pande                        mat_idx = idx + div((i - 1)*i, 2) + (j - 1)
149*afeb93e9SKartiki Pande                    end
150*afeb93e9SKartiki Pande                    dv_i += qdata[mat_idx]*du[j]
151*afeb93e9SKartiki Pande                end
152*afeb93e9SKartiki Pande                dv[i] = dv_i
153*afeb93e9SKartiki Pande            end
154*afeb93e9SKartiki Pande        end,
155*afeb93e9SKartiki Pande    )
156*afeb93e9SKartiki Pande
157*afeb93e9SKartiki Pande    # Create the mass+diffusion operator.
158*afeb93e9SKartiki Pande    oper = Operator(
159*afeb93e9SKartiki Pande        ceed,
160*afeb93e9SKartiki Pande        qf=apply_qfunc,
161*afeb93e9SKartiki Pande        fields=[
162*afeb93e9SKartiki Pande            (:u, sol_rstr, sol_basis, CeedVectorActive()),
163*afeb93e9SKartiki Pande            (:du, sol_rstr, sol_basis, CeedVectorActive()),
164*afeb93e9SKartiki Pande            (:qdata, qdata_rstr_i, BasisNone(), qdata),
165*afeb93e9SKartiki Pande            (:v, sol_rstr, sol_basis, CeedVectorActive()),
166*afeb93e9SKartiki Pande            (:dv, sol_rstr, sol_basis, CeedVectorActive()),
167*afeb93e9SKartiki Pande        ],
168*afeb93e9SKartiki Pande    )
169*afeb93e9SKartiki Pande
170*afeb93e9SKartiki Pande    # Compute the mesh volume using the mass+diffusion operator: vol = 1^T \cdot (M + K) \cdot 1
171*afeb93e9SKartiki Pande    print("Computing the mesh volume using the formula: vol = 1^T * (M + K) * 1...")
172*afeb93e9SKartiki Pande    flush(stdout)
173*afeb93e9SKartiki Pande    # Create auxiliary solution-size vectors.
174*afeb93e9SKartiki Pande    u = CeedVector(ceed, sol_size)
175*afeb93e9SKartiki Pande    v = CeedVector(ceed, sol_size)
176*afeb93e9SKartiki Pande    # Initialize 'u' with ones.
177*afeb93e9SKartiki Pande    u[] = 1.0
178*afeb93e9SKartiki Pande    # Apply the mass+diffusion operator: 'u' -> 'v'.
179*afeb93e9SKartiki Pande    apply!(oper, u, v)
180*afeb93e9SKartiki Pande    # Compute and print the sum of the entries of 'v' giving the mesh volume.
181*afeb93e9SKartiki Pande    vol = witharray_read(sum, v, MEM_HOST)
182*afeb93e9SKartiki Pande
183*afeb93e9SKartiki Pande    println(" done.")
184*afeb93e9SKartiki Pande    @printf("Exact mesh volume    : % .14g\n", exact_vol)
185*afeb93e9SKartiki Pande    @printf("Computed mesh volume : % .14g\n", vol)
186*afeb93e9SKartiki Pande    @printf("Volume error         : % .14g\n", vol - exact_vol)
187*afeb93e9SKartiki Pandeend
188*afeb93e9SKartiki Pande
189*afeb93e9SKartiki Pande# Entry point
190*afeb93e9SKartiki Panderun_ex3(
191*afeb93e9SKartiki Pande    ceed_spec="/cpu/self",
192*afeb93e9SKartiki Pande    dim=3,
193*afeb93e9SKartiki Pande    mesh_order=4,
194*afeb93e9SKartiki Pande    sol_order=4,
195*afeb93e9SKartiki Pande    num_qpts=4 + 2,
196*afeb93e9SKartiki Pande    prob_size=-1,
197*afeb93e9SKartiki Pande)
198