diff --git a/generate_all_hyteg_forms.py b/generate_all_hyteg_forms.py index e2731976571de4544c7147174f04d6116ad3ba56..e21806a73402828daceb6cb44137fefc7160833b 100644 --- a/generate_all_hyteg_forms.py +++ b/generate_all_hyteg_forms.py @@ -24,7 +24,7 @@ import sympy as sp from sympy.core.cache import clear_cache import re -from hog.blending import GeometryMap, IdentityMap, ExternalMap +from hog.blending import GeometryMap, IdentityMap, ExternalMap, AnnulusMap from hog.element_geometry import TriangleElement, TetrahedronElement, ElementGeometry from hog.function_space import FunctionSpace, LagrangianFunctionSpace, N1E1Space from hog.forms import ( @@ -38,9 +38,10 @@ from hog.forms import ( divergence, full_stokes, divdiv, + supg_diffusion, ) from hog.forms_vectorial import mass_n1e1, curl_curl -from hog.quadrature import Quadrature +from hog.quadrature import Quadrature, select_quadrule from hog.exception import HOGException from hog.logger import TimedLogger from hog.symbolizer import Symbolizer @@ -419,6 +420,20 @@ form_infos = [ description="Implements a linear form of type: (k(x), psi) where psi a test function and k = k(x) a vectorial, external function.", integrate_rows=[], ), + FormInfo( + "supg_diffusion", + trial_degree=2, + test_degree=2, + quad_schemes={2: 4, 3: 4}, + blending=ExternalMap(), + ), + FormInfo( + "supg_diffusion", + trial_degree=2, + test_degree=2, + quad_schemes={2: 4, 3: 4}, + blending=AnnulusMap(), + ) ] for d in [1, 2]: @@ -658,6 +673,14 @@ def form_func( transpose=False, blending=blending, ).integrate(quad, symbolizer) + elif name.startswith("supg_d"): + return supg_diffusion( + trial, + test, + geometry, + symbolizer, + blending=blending, + ).integrate(quad, symbolizer) else: raise HOGException(f"Cannot call form function with name {name}.") @@ -821,7 +844,8 @@ def main(): f"- Generating code for class {form_info.class_name(row,col)}, {geometry.dimensions}D" ): quad = Quadrature( - form_info.quad_schemes[geometry.dimensions], + select_quadrule(form_info.quad_schemes[geometry.dimensions], geometry), + # form_info.quad_schemes[geometry.dimensions], geometry, inline_values=form_info.inline_quad, ) diff --git a/generate_all_operators.py b/generate_all_operators.py index a9c58a69a59bba041aa3b4c95e04e84220a26cab..8806bc6ff8746aa8e4c35b944d352248cc9e71e8 100644 --- a/generate_all_operators.py +++ b/generate_all_operators.py @@ -40,6 +40,7 @@ from hog.forms import ( full_stokes, nonlinear_diffusion, nonlinear_diffusion_newton_galerkin, + supg_diffusion, ) from hog.forms_vectorial import curl_curl, curl_curl_plus_mass, mass_n1e1 from hog.function_space import FunctionSpace, LagrangianFunctionSpace, N1E1Space @@ -552,6 +553,10 @@ def all_operators( coefficient_function_space=P1, onlyNewtonGalerkinPartOfForm=False), type_descriptor=type_descriptor, opts=opts, blending=blending)) + ops.append(OperatorInfo(mapping="P2", name="SUPGDiffusion", trial_space=P2, test_space=P2, + form=partial(supg_diffusion, velocity_function_space=P2, diffusivityXdelta_function_space=P2), + type_descriptor=type_descriptor, opts=opts, blending=blending)) + # fmt: on for c in [0, 1, 2]: diff --git a/hog/forms.py b/hog/forms.py index 4c7c6b217e935c2f0fb95554f1c19df80cc35f10..568e0ef3f2e8d583764e68eb72986d1d4bef90ec 100644 --- a/hog/forms.py +++ b/hog/forms.py @@ -20,12 +20,15 @@ import sympy as sp from typing import List, Optional, Tuple from hog.ast import Operations, count_operations -from hog.element_geometry import ElementGeometry +from hog.element_geometry import ElementGeometry, TriangleElement, TetrahedronElement from hog.exception import HOGException from hog.fem_helpers import ( trafo_ref_to_affine, jac_ref_to_affine, jac_affine_to_physical, + hessian_ref_to_affine, + hessian_affine_to_blending, + jacinvjac_affine_to_physical, create_empty_element_matrix, element_matrix_iterator, scalar_space_dependent_coefficient, @@ -1375,6 +1378,180 @@ Weak formulation docstring=docstring, ) +def supg_diffusion( + trial: FunctionSpace, + test: FunctionSpace, + geometry: ElementGeometry, + symbolizer: Symbolizer, + blending: GeometryMap = IdentityMap(), + velocity_function_space: FunctionSpace = None, + diffusivityXdelta_function_space: FunctionSpace = None +) -> Form: + docstring = f""" +Second derivative operator for testing. + +Geometry map: {blending} + +Weak formulation + + T: trial function (space: {trial}) + s: test function (space: {test}) + u: velocity function (space: {velocity_function_space}) + k𝛿: FE function representing k·𝛿 (space: {diffusivityXdelta_function_space}) + + For OpGen, + + ∫ k(ΔT) · 𝛿(u · ∇s) + + ------------------- + + For ExternalMap (only for testing), + + ∫ (ΔT) s +""" + + if trial != test: + raise HOGException( + "Trial space must be equal to test space to assemble second derivative matrix." + ) + + with TimedLogger("assembling second derivative matrix", level=logging.DEBUG): + tabulation = Tabulation(symbolizer) + + jac_affine = symbolizer.jac_ref_to_affine(geometry.dimensions) + jac_affine_inv = symbolizer.jac_ref_to_affine_inv(geometry.dimensions) + jac_affine_det = symbolizer.abs_det_jac_ref_to_affine() + + if isinstance(blending, ExternalMap): + TimedLogger("ExternalMap is not tested well").log() + jac_blending = jac_affine_to_physical(geometry, symbolizer) + jacinvjac_blending = jacinvjac_affine_to_physical(geometry, symbolizer) + else: + affine_coords = trafo_ref_to_affine(geometry, symbolizer) + jac_blending = blending.jacobian(affine_coords) + if not isinstance(blending, IdentityMap): + hessian_blending_map = blending.hessian(affine_coords) + + jac_blending_det = abs(det(jac_blending)) + with TimedLogger("inverting blending Jacobian", level=logging.DEBUG): + jac_blending_inv = inv(jac_blending) + + mat = create_empty_element_matrix(trial, test, geometry) + it = element_matrix_iterator(trial, test, geometry) + + if velocity_function_space != None and diffusivityXdelta_function_space != None: + u_eval_symbols = tabulation.register_phi_evals( + velocity_function_space.shape(geometry) + ) + + ux, _ = fem_function_on_element( + velocity_function_space, + geometry, + symbolizer, + domain="reference", + function_id="ux", + basis_eval=u_eval_symbols, + ) + + uy, _ = fem_function_on_element( + velocity_function_space, + geometry, + symbolizer, + domain="reference", + function_id="uy", + basis_eval=u_eval_symbols, + ) + + if isinstance(geometry, TetrahedronElement): + uz, _ = fem_function_on_element( + velocity_function_space, + geometry, + symbolizer, + domain="reference", + function_id="uz", + basis_eval=u_eval_symbols, + ) + u = sp.Matrix([[ux], [uy], [uz]]) + else: + u = sp.Matrix([[ux], [uy]]) + + kdelta_eval_symbols = tabulation.register_phi_evals( + diffusivityXdelta_function_space.shape(geometry) + ) + + kdelta, _ = fem_function_on_element( + diffusivityXdelta_function_space, + geometry, + symbolizer, + domain="reference", + function_id="kdelta", + basis_eval=kdelta_eval_symbols, + ) + + for data in it: + psi = data.test_shape + grad_phi = data.trial_shape_grad + grad_psi = data.test_shape_grad + hessian_phi = data.trial_shape_hessian + hessian_affine = hessian_ref_to_affine(geometry, hessian_phi, jac_affine_inv) + + hessian_affine_symbols = tabulation.register_factor( + "hessian_affine", + hessian_affine, + ) + + jac_affine_inv_T_grad_phi_symbols = tabulation.register_factor( + "jac_affine_inv_T_grad_phi", + jac_affine_inv.T * grad_phi, + ) + + jac_affine_inv_T_grad_psi_symbols = tabulation.register_factor( + "jac_affine_inv_T_grad_psi", + jac_affine_inv.T * grad_psi, + ) + + jac_blending_inv_T_jac_affine_inv_T_grad_psi_symbols = tabulation.register_factor( + "jac_affine_inv_T_grad_psi", + jac_blending_inv.T * jac_affine_inv_T_grad_psi_symbols, + ) + + if isinstance(blending, ExternalMap): + hessian_blending = hessian_affine_to_blending(geometry, hessian_affine, None, jac_blending_inv.T, jac_affine_inv_T_grad_phi_symbols, jacinvjac_blending) + + laplacian = sum([hessian_blending[i, i] for i in range(geometry.dimensions)]) + + form = ( + laplacian * psi + # * dot(u, jac_blending_inv_T_jac_affine_inv_T_grad_psi_symbols) + # * kdelta + * jac_affine_det + * jac_blending_det + ) + elif isinstance(blending, IdentityMap): + laplacian = sum([hessian_affine_symbols[i, i] for i in range(geometry.dimensions)]) + form = ( + laplacian + * dot(u, jac_affine_inv_T_grad_psi_symbols) + * kdelta + * jac_affine_det + ) + else: + hessian_blending = hessian_affine_to_blending(geometry, hessian_affine, hessian_blending_map, jac_blending_inv.T, jac_affine_inv_T_grad_phi_symbols) + + laplacian = sum([hessian_blending[i, i, 0] for i in range(geometry.dimensions)]) + + form = ( + laplacian + * dot(u, jac_blending_inv_T_jac_affine_inv_T_grad_psi_symbols) + * kdelta + * jac_affine_det + * jac_blending_det + ) + # HOGException("Only for testing with Blending map") + + mat[data.row, data.col] = form + + return Form(mat, tabulation, symmetric=False, docstring=docstring) def zero_form( trial: FunctionSpace, test: FunctionSpace, geometry: ElementGeometry