diff --git a/generate_all_hyteg_forms.py b/generate_all_hyteg_forms.py index 867d0727a17725f7a74d62af935325e8673fa047..e33e68759cb9302fa4daf6e11ffa8869d52174b5 100644 --- a/generate_all_hyteg_forms.py +++ b/generate_all_hyteg_forms.py @@ -57,7 +57,9 @@ from hog.manifold_forms import ( vector_laplace_beltrami ) from hog.manifold_facet_forms import ( - manifold_epsilon_sip_facet + manifold_epsilon_sip_facet, + manifold_epsilon_nip_facet, + manifold_divergence_facet ) from hog.forms_vectorial import mass_n1e1, curl_curl from hog.quadrature import Quadrature, select_quadrule @@ -136,7 +138,7 @@ class FormInfo: if self.trial_family == "EG" and self.test_family == "Lagrange": return f"eg_to_p{self.test_degree}" if self.trial_family == "Lagrange" and self.test_family == "EG": - return f"p{self.test_degree}_to_eg" + return f"p{self.trial_degree}_to_eg" elif self.trial_degree == self.test_degree: return f"p{self.trial_degree}" else: @@ -697,6 +699,20 @@ for blending in [IdentityMap(), ExternalMap()]: ) ) +form_infos.append( + FormInfo( + "manifold_normal_penalty", + trial_degree=1, + test_degree=1, + test_family="EG", + quad_schemes={2: 3}, + row_dim=1, + col_dim=3, + is_implemented=is_implemented_for_vector_to_vector, + blending=blending, + ) +) + form_infos.append( FormInfo( "manifold_vector_mass", @@ -787,6 +803,8 @@ for trial_deg, test_deg, transpose in [ (2, 1, False), (0, 2, True), (2, 0, False), + (0, 1, True), + (1, 0, False) ]: for blending in [IdentityMap(), ExternalMap()]: if not transpose: @@ -795,7 +813,7 @@ for trial_deg, test_deg, transpose in [ "manifold_vector_div", trial_degree=trial_deg, test_degree=test_deg, - quad_schemes={2: 3}, + quad_schemes={2: 6}, row_dim=1, col_dim=3, is_implemented=is_implemented_for_vector_to_scalar, @@ -808,7 +826,7 @@ for trial_deg, test_deg, transpose in [ "manifold_vector_divt", trial_degree=trial_deg, test_degree=test_deg, - quad_schemes={2: 3}, + quad_schemes={2: 6}, row_dim=3, col_dim=1, is_implemented=is_implemented_for_scalar_to_vector, @@ -816,6 +834,34 @@ for trial_deg, test_deg, transpose in [ ) ) +form_infos.append( + FormInfo( + "manifold_vector_div", + trial_degree=1, + test_degree=0, + trial_family="EG", + quad_schemes={2: 6}, + row_dim=1, + col_dim=1, + is_implemented=is_implemented_for_vector_to_scalar, + blending=ExternalMap(), + ) +) + +form_infos.append( + FormInfo( + "manifold_vector_divt", + trial_degree=0, + test_degree=1, + test_family="EG", + quad_schemes={2: 6}, + row_dim=1, + col_dim=1, + is_implemented=is_implemented_for_scalar_to_vector, + blending=ExternalMap(), + ) +) + form_infos.append( FormInfo( "manifold_epsilon", @@ -872,6 +918,8 @@ form_infos.append( ) ) +# --- Manifold facet epsilon forms --- + form_infos.append( FormInfo( "manifold_epsilon_sip_inner_facet", @@ -958,6 +1006,202 @@ form_infos.append( ) ) +form_infos.append( + FormInfo( + "manifold_epsilon_nip_inner_facet", + trial_degree=1, + test_degree=1, + trial_family="EG", + quad_schemes={2: 3}, + row_dim=3, + col_dim=1, + is_implemented=is_implemented_for_vector_to_vector, + blending=blending, + ) +) + +form_infos.append( + FormInfo( + "manifold_epsilon_nip_inner_facet", + trial_degree=1, + test_degree=1, + test_family="EG", + quad_schemes={2: 3}, + row_dim=1, + col_dim=3, + is_implemented=is_implemented_for_vector_to_vector, + blending=blending, + ) +) + +form_infos.append( + FormInfo( + "manifold_epsilon_nip_inner_facet", + trial_degree=1, + test_degree=1, + trial_family="EG", + test_family="EG", + quad_schemes={2: 3}, + row_dim=1, + col_dim=1, + is_implemented=is_implemented_for_vector_to_vector, + blending=blending, + ) +) + +form_infos.append( + FormInfo( + "manifold_epsilon_nip_outer_facet", + trial_degree=1, + test_degree=1, + trial_family="EG", + quad_schemes={2: 3}, + row_dim=3, + col_dim=1, + is_implemented=is_implemented_for_vector_to_vector, + blending=blending, + ) +) + +form_infos.append( + FormInfo( + "manifold_epsilon_nip_outer_facet", + trial_degree=1, + test_degree=1, + test_family="EG", + quad_schemes={2: 3}, + row_dim=1, + col_dim=3, + is_implemented=is_implemented_for_vector_to_vector, + blending=blending, + ) +) + +form_infos.append( + FormInfo( + "manifold_epsilon_nip_outer_facet", + trial_degree=1, + test_degree=1, + trial_family="EG", + test_family="EG", + quad_schemes={2: 3}, + row_dim=1, + col_dim=1, + is_implemented=is_implemented_for_vector_to_vector, + blending=blending, + ) +) + +# --- Manifold facet divergence forms --- + +form_infos.append( + FormInfo( + "manifold_div_inner_facet", + trial_degree=1, + test_degree=0, + quad_schemes={2: 6}, + row_dim=3, + col_dim=1, + is_implemented=is_implemented_for_vector_to_scalar, + blending=ExternalMap() + ) +) + +form_infos.append( + FormInfo( + "manifold_div_outer_facet", + trial_degree=1, + test_degree=0, + quad_schemes={2: 6}, + row_dim=3, + col_dim=1, + is_implemented=is_implemented_for_vector_to_scalar, + blending=ExternalMap() + ) +) + +form_infos.append( + FormInfo( + "manifold_divt_inner_facet", + trial_degree=0, + test_degree=1, + quad_schemes={2: 6}, + row_dim=1, + col_dim=3, + is_implemented=is_implemented_for_scalar_to_vector, + blending=ExternalMap() + ) +) + +form_infos.append( + FormInfo( + "manifold_divt_outer_facet", + trial_degree=0, + test_degree=1, + quad_schemes={2: 6}, + row_dim=1, + col_dim=3, + is_implemented=is_implemented_for_scalar_to_vector, + blending=ExternalMap() + ) +) + +form_infos.append( + FormInfo( + "manifold_div_inner_facet", + trial_degree=1, + test_degree=0, + trial_family="EG", + quad_schemes={2: 6}, + row_dim=1, + col_dim=1, + is_implemented=is_implemented_for_vector_to_scalar, + blending=ExternalMap() + ) +) + +form_infos.append( + FormInfo( + "manifold_div_outer_facet", + trial_degree=1, + test_degree=0, + trial_family="EG", + quad_schemes={2: 6}, + row_dim=1, + col_dim=1, + is_implemented=is_implemented_for_vector_to_scalar, + blending=ExternalMap() + ) +) + +form_infos.append( + FormInfo( + "manifold_divt_inner_facet", + trial_degree=0, + test_degree=1, + test_family="EG", + quad_schemes={2: 6}, + row_dim=1, + col_dim=1, + is_implemented=is_implemented_for_scalar_to_vector, + blending=ExternalMap() + ) +) + +form_infos.append( + FormInfo( + "manifold_divt_outer_facet", + trial_degree=0, + test_degree=1, + test_family="EG", + quad_schemes={2: 6}, + row_dim=1, + col_dim=1, + is_implemented=is_implemented_for_scalar_to_vector, + blending=ExternalMap() + ) +) + def form_func( name: str, row: int, @@ -1094,6 +1338,54 @@ def form_func( component_trial=col, component_test=row, ).integrate(quad, symbolizer) + elif name.startswith("manifold_divt_inner_facet"): + return manifold_divergence_facet( + "inner", + test, + trial, + geometry, + quad, + symbolizer, + transpose=True, + blending=blending, + component_index=col + ) + elif name.startswith("manifold_divt_outer_facet"): + return manifold_divergence_facet( + "outer", + test, + trial, + geometry, + quad, + symbolizer, + transpose=True, + blending=blending, + component_index=col + ) + elif name.startswith("manifold_div_inner_facet"): + return manifold_divergence_facet( + "inner", + test, + trial, + geometry, + quad, + symbolizer, + transpose=False, + blending=blending, + component_index=row + ) + elif name.startswith("manifold_div_outer_facet"): + return manifold_divergence_facet( + "outer", + test, + trial, + geometry, + quad, + symbolizer, + transpose=False, + blending=blending, + component_index=row + ) elif name.startswith("manifold_divt"): return manifold_divergence( trial, @@ -1158,6 +1450,30 @@ def form_func( trial_component=col, test_component=row, ) + elif name.startswith("manifold_epsilon_nip_inner_facet"): + return manifold_epsilon_nip_facet( + "inner", + test, + trial, + geometry, + quad, + symbolizer, + blending, + trial_component=col, + test_component=row, + ) + elif name.startswith("manifold_epsilon_nip_outer_facet"): + return manifold_epsilon_nip_facet( + "outer", + test, + trial, + geometry, + quad, + symbolizer, + blending, + trial_component=col, + test_component=row, + ) elif name.startswith("manifold_epsilon"): return manifold_epsilon( trial, diff --git a/hog/__init__.py b/hog/__init__.py index 3ab7a424466b0205f0a3eb903ca2bba6beea202a..5de3b234acab2511ecabb820269b3ab52e132386 100644 --- a/hog/__init__.py +++ b/hog/__init__.py @@ -32,6 +32,7 @@ __all__ = [ "hyteg_code_generation", "hyteg_form_template", "logger", + "manifold_facet_forms", "math_helpers", "multi_assignment", "operator_generation", diff --git a/hog/fem_helpers.py b/hog/fem_helpers.py index 94b039ba1cdf64f076558c4dc74d850404f00d94..d476e3e2410443bbb3d01e90fe2dd63936a1579a 100644 --- a/hog/fem_helpers.py +++ b/hog/fem_helpers.py @@ -126,7 +126,7 @@ def trafo_shifted_ref_to_affine( if affine_points is None: affine_points = symbolizer.affine_vertices_as_vectors( - geometry.dimensions, geometry.num_vertices + geometry.space_dimension, geometry.num_vertices ) else: if len(affine_points) != geometry.num_vertices: @@ -284,6 +284,60 @@ def trafo_ref_to_physical( return phy +def trafo_shifted_ref_to_physical( #TODO: Remove? + geometry: ElementGeometry, symbolizer: Symbolizer, shift: sp.Matrix, blending: GeometryMap, affine_points: Optional[List[sp.Matrix]] = None +) -> sp.Matrix: + """Returns the transformation of a point in the reference space to the physical element.""" + + if geometry not in blending.supported_geometries(): + raise HOGException("Geometry not supported by blending map.") + + t = trafo_shifted_ref_to_affine(geometry, symbolizer, shift, affine_points=affine_points) + + if isinstance(blending, ExternalMap): + blending_class: type[MultiAssignment] + if isinstance(geometry, LineElement) and geometry.space_dimension == 3: + blending_class = BlendingFEmbeddedLine + if isinstance(geometry, TriangleElement): + blending_class = BlendingFTriangle + if geometry.space_dimension == 3: + blending_class = BlendingDFEmbeddedTriangle + elif isinstance(geometry, TetrahedronElement): + blending_class = BlendingFTetrahedron + else: + raise HOGException( + "Blending not implemented for the passed element geometry." + ) + + phy = sp.zeros(geometry.dimensions, 1) + for coord in range(geometry.dimensions): + phy[coord] = blending_class(sp.Symbol("blend"), coord, *t) + + else: + phy = blending.evaluate(t) + + return phy + +def jac_shifted_ref_to_physical( + geometry: ElementGeometry, + symbolizer: Symbolizer, + shift: sp.Matrix, + affine_points: Optional[List[sp.Matrix]] = None, +) -> sp.Matrix: + """Returns the Jacobian of the transformation from the shifted reference to the affine (computational) element. + + :param geometry: the element geometry + :param symbolizer: a symbolizer object + :param shift: a shift on the reference space + :param affine_points: the symbols of the vertices of the affine element - if omitted, they default to the first d+1 + vector symbols, useful for example if the trafo of two or more different elements is required + """ + + jac_affine = jac_ref_to_affine(geometry, symbolizer, affine_points=affine_points) + jac_blending = jac_shifted_affine_to_physical(geometry, symbolizer, shift, affine_points=affine_points) + + return (jac_blending * jac_affine) + def jac_affine_to_physical( geometry: ElementGeometry, symbolizer: Symbolizer, affine_points: Optional[List[sp.Matrix]] = None @@ -319,7 +373,7 @@ def jac_affine_to_physical( return jac def jac_shifted_affine_to_physical( - geometry: ElementGeometry, symbolizer: Symbolizer, shift: sp.Matrix + geometry: ElementGeometry, symbolizer: Symbolizer, shift: sp.Matrix, affine_points: Optional[List[sp.Matrix]] = None ) -> sp.Matrix: """Returns the Jacobian of the transformation from the affine (computational) to the physical element.""" blending_class: type[MultiAssignment] @@ -332,7 +386,7 @@ def jac_shifted_affine_to_physical( else: raise HOGException("Blending not implemented for the passed element geometry.") - t = trafo_ref_to_affine(geometry, symbolizer) + t = trafo_shifted_ref_to_affine(geometry, symbolizer, shift, affine_points=affine_points) jac = sp.zeros(geometry.space_dimension, geometry.space_dimension) rows, cols = jac.shape for row in range(rows): diff --git a/hog/function_space.py b/hog/function_space.py index 3e1b048daa84cb68385b61d24c05eaab31f46144..0f3a76d596c9f3941d36fd4f5d52c682f8bbcfff 100644 --- a/hog/function_space.py +++ b/hog/function_space.py @@ -493,7 +493,8 @@ class EnrichedGalerkinFunctionSpace(FunctionSpace): dof_map: Optional[List[int]] = None, ) -> List[sp.MatrixBase]: # return [self._affine] - return sp.Matrix([[1, 0], [0, 1]]) + result = sp.Matrix([[1, 0], [0, 1]]) + return [result] def num_dofs(self, geometry: ElementGeometry) -> int: """Returns the number of DoFs per element.""" diff --git a/hog/manifold_facet_forms.py b/hog/manifold_facet_forms.py index 5217af977b37ea98203839566e03a3114644888c..99edb4c7c4af932d24694da49f9f66a1fc318d5c 100644 --- a/hog/manifold_facet_forms.py +++ b/hog/manifold_facet_forms.py @@ -30,14 +30,17 @@ from hog.fem_helpers import ( element_matrix_iterator, ) from hog.function_space import FunctionSpace, EnrichedGalerkinFunctionSpace, LagrangianFunctionSpace -from hog.math_helpers import dot, inv, abs, det, double_contraction, e_vec +from hog.math_helpers import dot, inv, abs, det, double_contraction, e_vec, vol from hog.quadrature import Quadrature from hog.symbolizer import Symbolizer from hog.logger import TimedLogger from hog.blending import GeometryMap, ExternalMap, IdentityMap from hog.forms import Form +from hog.external_functions import ( + ScalarVariableCoefficient2D, +) -from hog.manifold_helpers import face_projection, embedded_normal, embedded_normal_grad +from hog.manifold_helpers import face_projection, embedded_normal, embedded_normal_grad, grad_jac_ref_to_physical from hog.forms_facets import _affine_element_vertices, trafo_ref_interface_to_ref_element, vol @@ -144,15 +147,26 @@ def manifold_epsilon_sip_facet( jac_total_I = jac_blending_I * jac_affine_I fundamental_I = jac_total_I.T * jac_total_I volume_interface = abs(det(fundamental_I))**0.5 - discontinuity_penalty = 3.0 + + #TODO: Make this from affine to physical + trafo_ref_interface_to_affine_interface = trafo_ref_to_affine( + volume_element_geometry, + symbolizer, + affine_points=affine_points_I + [affine_point_E_opposite], + ) + penalty_coeff_class = ScalarVariableCoefficient2D + discontinuity_penalty = penalty_coeff_class( + sp.Symbol("discontinuity_penalty"), 0, *trafo_ref_interface_to_affine_interface + ) interface_affine_tangential = affine_points_I[1] - affine_points_I[0] interface_tangential = jac_blending_I * interface_affine_tangential - manifold_normal = embedded_normal(volume_element_geometry, symbolizer, blending) + manifold_normal_E1 = embedded_normal(volume_element_geometry, symbolizer, blending, affine_points=affine_points_E1) + manifold_normal_E2 = embedded_normal(volume_element_geometry, symbolizer, blending, affine_points=affine_points_E2) outward_normal = sp.Matrix([ - interface_tangential[1] * manifold_normal[2] - interface_tangential[2] * manifold_normal[1], - interface_tangential[2] * manifold_normal[0] - interface_tangential[0] * manifold_normal[2], - interface_tangential[0] * manifold_normal[1] - interface_tangential[1] * manifold_normal[0], + interface_tangential[1] * manifold_normal_E1[2] - interface_tangential[2] * manifold_normal_E1[1], + interface_tangential[2] * manifold_normal_E1[0] - interface_tangential[0] * manifold_normal_E1[2], + interface_tangential[0] * manifold_normal_E1[1] - interface_tangential[1] * manifold_normal_E1[0], ]) outward_normal_orientation = -sp.sign(dot(external_outward_normal, affine_points_E1[0] + affine_points_E1[1] + affine_points_E1[2] - 1.5 * affine_points_I[0] - 1.5 * affine_points_I[1])[0, 0]) @@ -172,7 +186,13 @@ def manifold_epsilon_sip_facet( #TODO: Implement variable viscousity - projection_mat = face_projection(volume_element_geometry, symbolizer, blending) + projection_mat_E1 = face_projection(volume_element_geometry, symbolizer, blending, affine_points_E1) + projection_mat_E2 = face_projection(volume_element_geometry, symbolizer, blending, affine_points_E2) + # normal = embedded_normal(volume_element_geometry, symbolizer, blending) + normal_grad_E1 = embedded_normal_grad(volume_element_geometry, symbolizer, blending, affine_points=affine_points_E1) + normal_grad_E2 = embedded_normal_grad(volume_element_geometry, symbolizer, blending, affine_points=affine_points_E2) + grad_jac_total_E1 = grad_jac_ref_to_physical(volume_element_geometry, symbolizer, blending, affine_points_E1) + grad_jac_total_E2 = grad_jac_ref_to_physical(volume_element_geometry, symbolizer, blending, affine_points_E2) with TimedLogger( f"integrating {mat.shape[0] * mat.shape[1]} expressions", @@ -230,33 +250,82 @@ def manifold_epsilon_sip_facet( if isinstance(trial_element_2, EnrichedGalerkinFunctionSpace): if interface_type == "outer": + #grad_phi = jac_total_E2 * grad_phi + grad_phi_x = projection_mat_E2 * (jac_total_E2 * grad_phi.col(0) + grad_jac_total_E2[0] * phi) + grad_phi_y = projection_mat_E2 * (jac_total_E2 * grad_phi.col(1) + grad_jac_total_E2[1] * phi) + grad_phi = ( + grad_phi_x.row_join(grad_phi_y) + ).T phi = jac_total_E2 * phi - grad_phi = jac_total_E2 * grad_phi else: + #grad_phi = jac_total_E1 * grad_phi + grad_phi_x = projection_mat_E1 * (jac_total_E1 * grad_phi.col(0) + grad_jac_total_E1[0] * phi) + grad_phi_y = projection_mat_E1 * (jac_total_E1 * grad_phi.col(1) + grad_jac_total_E1[1] * phi) + grad_phi = ( + grad_phi_x.row_join(grad_phi_y) + ).T phi = jac_total_E1 * phi - grad_phi = jac_total_E1 * grad_phi elif isinstance(trial_element_2, LagrangianFunctionSpace): - phi = projection_mat * phi - grad_phi = projection_mat * grad_phi + #grad_phi = projection_mat * grad_phi + if interface_type == "outer": + grad_phi_x = projection_mat_E2 * ( + grad_phi.col(0) - normal_grad_E2[0] * manifold_normal_E2.T * phi + ) + grad_phi_y = projection_mat_E2 * ( + grad_phi.col(1) - normal_grad_E2[1] * manifold_normal_E2.T * phi + ) + grad_phi = ( + grad_phi_x.row_join(grad_phi_y) + ).T + phi = projection_mat_E2 * phi + else: + grad_phi_x = projection_mat_E1 * ( + grad_phi.col(0) - normal_grad_E1[0] * manifold_normal_E1.T * phi + ) + grad_phi_y = projection_mat_E1 * ( + grad_phi.col(1) - normal_grad_E1[1] * manifold_normal_E1.T * phi + ) + grad_phi = ( + grad_phi_x.row_join(grad_phi_y) + ).T + phi = projection_mat_E1 * phi if isinstance(test_element_1, EnrichedGalerkinFunctionSpace): + #grad_psi = jac_total_E1 * grad_psi + grad_psi_x = projection_mat_E1 * (jac_total_E1 * grad_psi.col(0) + grad_jac_total_E1[0] * psi) + grad_psi_y = projection_mat_E1 * (jac_total_E1 * grad_psi.col(1) + grad_jac_total_E1[1] * psi) + grad_psi = ( + grad_psi_x.row_join(grad_psi_y) + ).T psi = jac_total_E1 * psi - grad_psi = jac_total_E1 * grad_psi elif isinstance(test_element_1, LagrangianFunctionSpace): - psi = projection_mat * psi - grad_psi = projection_mat * grad_psi + #grad_psi = projection_mat * grad_psi + grad_psi_x = projection_mat_E1 * ( + grad_psi.col(0) - normal_grad_E1[0] * manifold_normal_E1.T * psi + ) + grad_psi_y = projection_mat_E1 * ( + grad_psi.col(1) - normal_grad_E1[1] * manifold_normal_E1.T * psi + ) + grad_psi = ( + grad_psi_x.row_join(grad_psi_y) + ).T + psi = projection_mat_E1 * psi if interface_type == "inner": + scaled_grad_psi = jac_total_E1 * fundamental_inv_E1 * grad_psi + scaled_grad_phi = jac_total_E1 * fundamental_inv_E1 * grad_phi + epsilon_psi = 0.5 * (scaled_grad_psi + scaled_grad_psi.T) + epsilon_phi = 0.5 * (scaled_grad_phi + scaled_grad_phi.T) form = ( ( - -1.0 + -0.5 * dot( - manifold_symm_grad(grad_psi, jac_total_E1, fundamental_inv_E1) * outward_normal, + epsilon_psi * outward_normal, phi, )[0, 0] - - 1.0 + - 0.5 * dot( - manifold_symm_grad(grad_phi, jac_total_E1, fundamental_inv_E1) * outward_normal, + epsilon_phi * outward_normal, psi, )[0, 0] + (discontinuity_penalty / volume_interface) @@ -265,19 +334,23 @@ def manifold_epsilon_sip_facet( * volume_interface ) elif interface_type == "outer": + scaled_grad_psi = jac_total_E1 * fundamental_inv_E1 * grad_psi + scaled_grad_phi = jac_total_E2 * fundamental_inv_E2 * grad_phi + epsilon_psi = 0.5 * (scaled_grad_psi + scaled_grad_psi.T) + epsilon_phi = 0.5 * (scaled_grad_phi + scaled_grad_phi.T) form = ( ( - 1.0 + 0.5 * dot( - manifold_symm_grad(grad_psi, jac_total_E1, fundamental_inv_E1) * outward_normal, + epsilon_psi * outward_normal, phi, )[0, 0] - - 1.0 + - 0.5 * dot( - manifold_symm_grad(grad_phi, jac_total_E2, fundamental_inv_E2) * outward_normal, + epsilon_phi * outward_normal, psi, )[0, 0] - + (discontinuity_penalty / volume_interface) + - (discontinuity_penalty / volume_interface) * dot(phi, psi)[0, 0] ) * volume_interface @@ -288,4 +361,510 @@ def manifold_epsilon_sip_facet( mat[data.row, data.col] = facet_quad.integrate(form, symbolizer).subs(reference_symbols[volume_element_geometry.dimensions - 1], 0) - return mat \ No newline at end of file + return mat + +def manifold_epsilon_nip_facet( + interface_type: str, + test_element_1: FunctionSpace, + trial_element_2: FunctionSpace, + volume_element_geometry: ElementGeometry, + facet_quad: Quadrature, + symbolizer: Symbolizer, + blending: GeometryMap = IdentityMap(), + trial_component: Optional[int] = None, + test_component: Optional[int] = None +) -> sp.Matrix: + """ + See; diffusion_sip_facet_vectorial + """ + + if not (interface_type == "inner" or interface_type == "outer"): + raise HOGException("The manifold facet form is only implemented for interfaces of type inner and outer.") + + if not isinstance(volume_element_geometry, TriangleElement) or not volume_element_geometry.space_dimension == 3: + raise HOGException("The manifold facet form is only implemented for embedded triangles.") + + if not (isinstance(test_element_1, LagrangianFunctionSpace) or isinstance(test_element_1, EnrichedGalerkinFunctionSpace)): + raise HOGException("The manifold facet form is only implemented for Lagrangian function spaces or enriched Galerkin function spaces.") + + if not (isinstance(trial_element_2, LagrangianFunctionSpace) or isinstance(trial_element_2, EnrichedGalerkinFunctionSpace)): + raise HOGException("The manifold facet form is only implemented for Lagrangian function spaces or enriched Galerkin function spaces.") + + if isinstance(test_element_1, LagrangianFunctionSpace) and test_component is None: + raise HOGException("The manifold facet form needs a component index when working with Lagrangian basis functions.") + + if isinstance(trial_element_2, LagrangianFunctionSpace) and trial_component is None: + raise HOGException("The manifold facet form needs a component index when working with Lagrangian basis functions.") + + if isinstance(test_element_1, EnrichedGalerkinFunctionSpace) and test_component is not None: + logging.info("INFO: Manifold_epsilon_facet form received EG basis functions and a component index. The index will be ignored.") + + if isinstance(trial_element_2, EnrichedGalerkinFunctionSpace) and trial_component is not None: + logging.info("INFO: Manifold_epsilon_facet form received EG basis functions and a component index. The index will be ignored.") + + logging.info("INFO: Manifold_epsilon_facet_form form ignores the gradient of the projection matrix for simplicity.") + + with TimedLogger( + "assembling interface diffusion matrix with SIP", level=logging.DEBUG + ): + # Grabbing the symbols for the vertices of both elements, the interface, and the outward normal. + ( + affine_points_E1, + affine_points_E2, + affine_points_I, + affine_point_E_opposite, + _, + external_outward_normal, + ) = _affine_element_vertices(volume_element_geometry, symbolizer) + + # These Jacobians are for the trafo from the element-local reference space to the affine element space. + # Both are required for application of the chain rule. + + jac_affine_E1 = jac_ref_to_affine( + volume_element_geometry, symbolizer, affine_points=affine_points_E1 + ) + jac_affine_E2 = jac_ref_to_affine( + volume_element_geometry, symbolizer, affine_points=affine_points_E2 + ) + + jac_blending_E1 = jac_affine_to_physical( + volume_element_geometry, symbolizer, affine_points=affine_points_E1 + ) + jac_blending_E2 = jac_affine_to_physical( + volume_element_geometry, symbolizer, affine_points=affine_points_E2 + ) + + jac_total_E1 = jac_blending_E1 * jac_affine_E1 + jac_total_E2 = jac_blending_E2 * jac_affine_E2 + + # These trafos are required to evaluate correct points in the reference space of the elements. + # The reference coordinates of the interface (where the quadrature happens) are mapped to element ref space. + + trafo_ref_interface_to_ref_element_E1 = trafo_ref_interface_to_ref_element( + volume_element_geometry, symbolizer, "inner" + ) + + trafo_ref_interface_to_ref_element_E2 = trafo_ref_interface_to_ref_element( + volume_element_geometry, symbolizer, "outer" + ) + + jac_affine_I = jac_ref_to_affine( + LineElement(space_dimension=3), symbolizer, affine_points=affine_points_I + ) + jac_blending_I = jac_affine_to_physical( + LineElement(space_dimension=3), symbolizer, affine_points=affine_points_I + ) + jac_total_I = jac_blending_I * jac_affine_I + fundamental_I = jac_total_I.T * jac_total_I + volume_interface = abs(det(fundamental_I))**0.5 + #volume_interface = abs(vol(affine_points_I)) #TODO: Add blending + discontinuity_penalty = 100.0 + + #TODO: Make this from affine to physical + trafo_ref_interface_to_affine_interface = trafo_ref_to_affine( + volume_element_geometry, + symbolizer, + affine_points=affine_points_I + [affine_point_E_opposite], + ) + penalty_coeff_class = ScalarVariableCoefficient2D + discontinuity_penalty = penalty_coeff_class( + sp.Symbol("discontinuity_penalty"), 0, *trafo_ref_interface_to_affine_interface + ) + + interface_affine_tangential = affine_points_I[1] - affine_points_I[0] + interface_tangential = jac_blending_I * interface_affine_tangential + manifold_normal = embedded_normal(volume_element_geometry, symbolizer, blending) + outward_normal = sp.Matrix([ + interface_tangential[1] * manifold_normal[2] - interface_tangential[2] * manifold_normal[1], + interface_tangential[2] * manifold_normal[0] - interface_tangential[0] * manifold_normal[2], + interface_tangential[0] * manifold_normal[1] - interface_tangential[1] * manifold_normal[0], + ]) + + outward_normal_orientation = -sp.sign(dot(external_outward_normal, affine_points_E1[0] + affine_points_E1[1] + affine_points_E1[2] - 1.5 * affine_points_I[0] - 1.5 * affine_points_I[1])[0, 0]) + outward_normal_norm = outward_normal_orientation * (outward_normal[0]**2 + outward_normal[1]**2 + outward_normal[2]**2)**0.5 + outward_normal = outward_normal / outward_normal_norm + + mat = create_empty_element_matrix( + trial_element_2, test_element_1, volume_element_geometry + ) + it = element_matrix_iterator( + trial_element_2, test_element_1, volume_element_geometry + ) + + reference_symbols = symbolizer.ref_coords_as_list( + volume_element_geometry.dimensions + ) + + #TODO: Implement variable viscousity + + projection_mat = face_projection(volume_element_geometry, symbolizer, blending) + normal = embedded_normal(volume_element_geometry, symbolizer, blending) + normal_grad = embedded_normal_grad(volume_element_geometry, symbolizer, blending) + grad_jac_total_E1 = grad_jac_ref_to_physical(volume_element_geometry, symbolizer, blending, affine_points_E1) + grad_jac_total_E2 = grad_jac_ref_to_physical(volume_element_geometry, symbolizer, blending, affine_points_E2) + + with TimedLogger( + f"integrating {mat.shape[0] * mat.shape[1]} expressions", + level=logging.DEBUG, + ): + for data in it: + # TODO: fix this by introducing extra symbols for the shape functions + if isinstance(trial_element_2, EnrichedGalerkinFunctionSpace): + phi = data.trial_shape + grad_phi = data.trial_shape_grad + elif isinstance(trial_element_2, LagrangianFunctionSpace): + phi_val = data.trial_shape + phi = e_vec(volume_element_geometry.space_dimension, trial_component) * phi_val + grad_phi = (e_vec(volume_element_geometry.space_dimension, trial_component) * phi_val).jacobian(reference_symbols) + + if isinstance(test_element_1, EnrichedGalerkinFunctionSpace): + psi = data.test_shape + grad_psi = data.test_shape_grad + elif isinstance(test_element_1, LagrangianFunctionSpace): + psi_val = data.test_shape + psi = e_vec(volume_element_geometry.space_dimension, test_component) * psi_val + grad_psi = (e_vec(volume_element_geometry.space_dimension, test_component) * psi_val).jacobian(reference_symbols) + + shape_symbols = ["xi_shape_0", "xi_shape_1", "xi_shape_2"][ + : volume_element_geometry.dimensions + ] + phi = phi.subs(zip(reference_symbols, shape_symbols)) + psi = psi.subs(zip(reference_symbols, shape_symbols)) + grad_phi = grad_phi.subs(zip(reference_symbols, shape_symbols)) + grad_psi = grad_psi.subs(zip(reference_symbols, shape_symbols)) + + # The evaluation is performed on the reference space of the _elements_ and _not_ in the reference space + # of the interface. This way we do not need to sort DoFs. So apply the trafo to the trial functions. + if interface_type == "outer": + phi = phi.subs( + zip(shape_symbols, trafo_ref_interface_to_ref_element_E2) + ) + grad_phi = grad_phi.subs( + zip(shape_symbols, trafo_ref_interface_to_ref_element_E2) + ) + else: + phi = phi.subs( + zip(shape_symbols, trafo_ref_interface_to_ref_element_E1) + ) + grad_phi = grad_phi.subs( + zip(shape_symbols, trafo_ref_interface_to_ref_element_E1) + ) + + psi = psi.subs( + zip(shape_symbols, trafo_ref_interface_to_ref_element_E1) + ) + grad_psi = grad_psi.subs( + zip(shape_symbols, trafo_ref_interface_to_ref_element_E1) + ) + + if isinstance(trial_element_2, EnrichedGalerkinFunctionSpace): + if interface_type == "outer": + #grad_phi = jac_total_E2 * grad_phi + grad_phi_x = projection_mat * (jac_total_E2 * grad_phi.col(0) + grad_jac_total_E2[0] * phi) + grad_phi_y = projection_mat * (jac_total_E2 * grad_phi.col(1) + grad_jac_total_E2[1] * phi) + grad_phi = ( + grad_phi_x.row_join(grad_phi_y) + ).T + grad_phi = (jac_total_E2 * grad_phi).T #DEBUG + phi = jac_total_E2 * phi + else: + #grad_phi = jac_total_E1 * grad_phi + grad_phi_x = projection_mat * (jac_total_E1 * grad_phi.col(0) + grad_jac_total_E1[0] * phi) + grad_phi_y = projection_mat * (jac_total_E1 * grad_phi.col(1) + grad_jac_total_E1[1] * phi) + grad_phi = ( + grad_phi_x.row_join(grad_phi_y) + ).T + grad_phi = (jac_total_E1 * grad_phi).T #DEBUG + phi = jac_total_E1 * phi + elif isinstance(trial_element_2, LagrangianFunctionSpace): + #grad_phi = projection_mat * grad_phi + grad_phi_x = projection_mat * ( + grad_phi.col(0) - normal_grad[0] * normal.T * phi + ) + grad_phi_y = projection_mat * ( + grad_phi.col(1) - normal_grad[1] * normal.T * phi + ) + grad_phi = ( + grad_phi_x.row_join(grad_phi_y) + ).T + phi = projection_mat * phi + + if isinstance(test_element_1, EnrichedGalerkinFunctionSpace): + #grad_psi = jac_total_E1 * grad_psi + grad_psi_x = projection_mat * (jac_total_E1 * grad_psi.col(0) + grad_jac_total_E1[0] * psi) + grad_psi_y = projection_mat * (jac_total_E1 * grad_psi.col(1) + grad_jac_total_E1[1] * psi) + grad_psi = ( + grad_psi_x.row_join(grad_psi_y) + ).T + grad_psi = (jac_total_E1 * grad_psi).T #DEBUG + psi = jac_total_E1 * psi + elif isinstance(test_element_1, LagrangianFunctionSpace): + #grad_psi = projection_mat * grad_psi + grad_psi_x = projection_mat * ( + grad_psi.col(0) - normal_grad[0] * normal.T * psi + ) + grad_psi_y = projection_mat * ( + grad_psi.col(1) - normal_grad[1] * normal.T * psi + ) + grad_psi = ( + grad_psi_x.row_join(grad_psi_y) + ).T + psi = projection_mat * psi + + if interface_type == "inner": + form = ( + ( + + (discontinuity_penalty / volume_interface**0.5) + * dot(phi, psi)[0, 0] + ) + * volume_interface + ) + elif interface_type == "outer": + form = ( + ( + - (discontinuity_penalty / volume_interface**0.5) + * dot(phi, psi)[0, 0] + ) + * volume_interface + ) + + elif interface_type == "dirichlet": + raise HOGException("Not implemented") + + mat[data.row, data.col] = facet_quad.integrate(form, symbolizer).subs(reference_symbols[volume_element_geometry.dimensions - 1], 0) + return mat + +def manifold_divergence_facet( + interface_type: str, + test_element_1: FunctionSpace, + trial_element_2: FunctionSpace, + volume_element_geometry: ElementGeometry, + facet_quad: Quadrature, + symbolizer: Symbolizer, + transpose: bool, + blending: GeometryMap = IdentityMap(), + component_index: Optional[int] = None, +) -> sp.Matrix: + """ + See; diffusion_sip_facet_vectorial + """ + + #TODO: Make sure all these checks are really necessary and correct. + + if not (interface_type == "inner" or interface_type == "outer"): + raise HOGException("The manifold facet form is only implemented for interfaces of type inner and outer.") + + if not isinstance(volume_element_geometry, TriangleElement) or not volume_element_geometry.space_dimension == 3: + raise HOGException("The manifold facet form is only implemented for embedded triangles.") + + if not (isinstance(test_element_1, LagrangianFunctionSpace) or isinstance(test_element_1, EnrichedGalerkinFunctionSpace)): + raise HOGException("The manifold facet form is only implemented for Lagrangian function spaces or enriched Galerkin function spaces.") + + if not (isinstance(trial_element_2, LagrangianFunctionSpace) or isinstance(trial_element_2, EnrichedGalerkinFunctionSpace)): + raise HOGException("The manifold facet form is only implemented for Lagrangian function spaces or enriched Galerkin function spaces.") + + if isinstance(test_element_1, LagrangianFunctionSpace) and component_index is None: + raise HOGException("The manifold facet form needs a component index when working with Lagrangian basis functions.") + + if isinstance(trial_element_2, LagrangianFunctionSpace) and component_index is None: + raise HOGException("The manifold facet form needs a component index when working with Lagrangian basis functions.") + + if isinstance(trial_element_2, EnrichedGalerkinFunctionSpace) and transpose: + raise HOGException("The transposed form can only use scalar valued functions for trial space.") + + if isinstance(test_element_1, EnrichedGalerkinFunctionSpace) and not transpose: + raise HOGException("The non-transposed form can only use sclar valued functions for test space.") + + logging.info("INFO: Manifold_epsilon_facet_form form ignores the gradient of the projection matrix for simplicity.") + + with TimedLogger( + "assembling interface diffusion matrix with SIP", level=logging.DEBUG + ): + # Grabbing the symbols for the vertices of both elements, the interface, and the outward normal. + ( + affine_points_E1, + affine_points_E2, + affine_points_I, + affine_point_E_opposite, + _, + external_outward_normal, + ) = _affine_element_vertices(volume_element_geometry, symbolizer) + + # These Jacobians are for the trafo from the element-local reference space to the affine element space. + # Both are required for application of the chain rule. + + jac_affine_E1 = jac_ref_to_affine( + volume_element_geometry, symbolizer, affine_points=affine_points_E1 + ) + jac_affine_E2 = jac_ref_to_affine( + volume_element_geometry, symbolizer, affine_points=affine_points_E2 + ) + + jac_blending_E1 = jac_affine_to_physical( + volume_element_geometry, symbolizer, affine_points=affine_points_E1 + ) + jac_blending_E2 = jac_affine_to_physical( + volume_element_geometry, symbolizer, affine_points=affine_points_E2 + ) + + jac_total_E1 = jac_blending_E1 * jac_affine_E1 + jac_total_E2 = jac_blending_E2 * jac_affine_E2 + + # These trafos are required to evaluate correct points in the reference space of the elements. + # The reference coordinates of the interface (where the quadrature happens) are mapped to element ref space. + + trafo_ref_interface_to_ref_element_E1 = trafo_ref_interface_to_ref_element( + volume_element_geometry, symbolizer, "inner" + ) + + trafo_ref_interface_to_ref_element_E2 = trafo_ref_interface_to_ref_element( + volume_element_geometry, symbolizer, "outer" + ) + + jac_affine_I = jac_ref_to_affine( + LineElement(space_dimension=3), symbolizer, affine_points=affine_points_I + ) + jac_blending_I = jac_affine_to_physical( + LineElement(space_dimension=3), symbolizer, affine_points=affine_points_I + ) + jac_total_I = jac_blending_I * jac_affine_I + fundamental_I = jac_total_I.T * jac_total_I + volume_interface = abs(det(fundamental_I))**0.5 + #volume_interface = abs(vol(affine_points_I)) #TODO: Add blending + + interface_affine_tangential = affine_points_I[1] - affine_points_I[0] + interface_tangential = jac_blending_I * interface_affine_tangential + manifold_normal = embedded_normal(volume_element_geometry, symbolizer, blending) + outward_normal = sp.Matrix([ + interface_tangential[1] * manifold_normal[2] - interface_tangential[2] * manifold_normal[1], + interface_tangential[2] * manifold_normal[0] - interface_tangential[0] * manifold_normal[2], + interface_tangential[0] * manifold_normal[1] - interface_tangential[1] * manifold_normal[0], + ]) + + outward_normal_orientation = -sp.sign(dot(external_outward_normal, affine_points_E1[0] + affine_points_E1[1] + affine_points_E1[2] - 1.5 * affine_points_I[0] - 1.5 * affine_points_I[1])[0, 0]) + outward_normal_norm = outward_normal_orientation * (outward_normal[0]**2 + outward_normal[1]**2 + outward_normal[2]**2)**0.5 + outward_normal = outward_normal / outward_normal_norm + + mat = create_empty_element_matrix( + trial_element_2, test_element_1, volume_element_geometry + ) + it = element_matrix_iterator( + trial_element_2, test_element_1, volume_element_geometry + ) + + reference_symbols = symbolizer.ref_coords_as_list( + volume_element_geometry.dimensions + ) + + #TODO: Implement variable viscousity + + projection_mat_E1 = face_projection(volume_element_geometry, symbolizer, blending, affine_points=affine_points_E1) + projection_mat_E2 = face_projection(volume_element_geometry, symbolizer, blending, affine_points=affine_points_E2) + + with TimedLogger( + f"integrating {mat.shape[0] * mat.shape[1]} expressions", + level=logging.DEBUG, + ): + for data in it: + # TODO: fix this by introducing extra symbols for the shape functions + if isinstance(trial_element_2, EnrichedGalerkinFunctionSpace): + phi = data.trial_shape + elif isinstance(trial_element_2, LagrangianFunctionSpace): + if not transpose: + phi = e_vec(volume_element_geometry.space_dimension, component_index) * data.trial_shape + else: + phi = data.trial_shape + + if isinstance(test_element_1, EnrichedGalerkinFunctionSpace): + psi = data.test_shape + elif isinstance(test_element_1, LagrangianFunctionSpace): + if transpose: + psi = e_vec(volume_element_geometry.space_dimension, component_index) * data.test_shape + else: + psi = data.test_shape + + + shape_symbols = ["xi_shape_0", "xi_shape_1", "xi_shape_2"][ + : volume_element_geometry.dimensions + ] + phi = phi.subs(zip(reference_symbols, shape_symbols)) + psi = psi.subs(zip(reference_symbols, shape_symbols)) + + # The evaluation is performed on the reference space of the _elements_ and _not_ in the reference space + # of the interface. This way we do not need to sort DoFs. So apply the trafo to the trial functions. + if interface_type == "outer": + phi = phi.subs( + zip(shape_symbols, trafo_ref_interface_to_ref_element_E2) + ) + else: + phi = phi.subs( + zip(shape_symbols, trafo_ref_interface_to_ref_element_E1) + ) + + psi = psi.subs( + zip(shape_symbols, trafo_ref_interface_to_ref_element_E1) + ) + + if isinstance(trial_element_2, EnrichedGalerkinFunctionSpace): + if interface_type == "outer": + phi = jac_total_E2 * phi + else: + phi = jac_total_E1 * phi + elif isinstance(trial_element_2, LagrangianFunctionSpace) and not transpose: + if interface_type == "outer": + phi = projection_mat_E2 * phi + else: + phi = projection_mat_E1 * phi + + if isinstance(test_element_1, EnrichedGalerkinFunctionSpace): + psi = jac_total_E1 * psi + elif isinstance(test_element_1, LagrangianFunctionSpace) and transpose: + psi = projection_mat_E1 * psi + + #TODO: Signs should be flipped, but manifold_vector_divergence also has "wrong" signs so this is necessary to make everything compatible. + if interface_type == "inner": + if not transpose: + form = ( + -0.5 * dot( + phi, + outward_normal + ) + * psi + * volume_interface + ) + else: + form = ( + -0.5 * dot( + psi, + outward_normal + ) + * phi + * volume_interface + ) + elif interface_type == "outer": + if not transpose: + form = ( + 0.5 * dot( + phi, + outward_normal + ) + * psi + * volume_interface + ) + else: + form = ( + -0.5 * dot( + psi, + outward_normal + ) + * phi + * volume_interface + ) + + elif interface_type == "dirichlet": + raise HOGException("Not implemented") + + mat[data.row, data.col] = facet_quad.integrate(form, symbolizer, blending).subs(reference_symbols[volume_element_geometry.dimensions - 1], 0) + + return mat + diff --git a/hog/manifold_forms.py b/hog/manifold_forms.py index aba0aa9bbd41a67c3d705429e5eeebde6d7809d2..6575af9c347fdf71ebc12a86b0973d1039da849e 100644 --- a/hog/manifold_forms.py +++ b/hog/manifold_forms.py @@ -35,7 +35,7 @@ from hog.logger import TimedLogger from hog.blending import GeometryMap, ExternalMap, IdentityMap from hog.forms import Form -from hog.manifold_helpers import face_projection, embedded_normal, embedded_normal_grad +from hog.manifold_helpers import face_projection, embedded_normal, embedded_normal_grad, grad_jac_ref_to_physical def laplace_beltrami( @@ -251,10 +251,7 @@ Weak formulation projected_psi = projection * psi_vec form = sp.Matrix( - tabulation.register_factor( - "manifold_vector_mass_symbol", - dot(projected_phi, projected_psi) * fundamental_form_det**0.5, - ) + dot(projected_phi, projected_psi) * fundamental_form_det**0.5, )[0] mat[data.row, data.col] = form @@ -325,10 +322,7 @@ Weak formulation psi_normal = dot(psi_vec, normal) form = sp.Matrix( - tabulation.register_factor( - "manifold_normal_penalty_symbol", - phi_normal * psi_normal * fundamental_form_det**0.5, - ) + phi_normal * psi_normal * fundamental_form_det**0.5, )[0] mat[data.row, data.col] = form @@ -452,6 +446,7 @@ Weak formulation projection_mat = face_projection(geometry, symbolizer, blending) normal = embedded_normal(geometry, symbolizer, blending) normal_grad = embedded_normal_grad(geometry, symbolizer, blending) + grad_jac_total = grad_jac_ref_to_physical(geometry, symbolizer, blending) jac_affine = jac_ref_to_affine(geometry, symbolizer) @@ -469,40 +464,87 @@ Weak formulation it = element_matrix_iterator(trial, test, geometry) for data in it: - ref_symbols_list = symbolizer.ref_coords_as_list(geometry.dimensions - 1) + ref_symbols_list = symbolizer.ref_coords_as_list(geometry.dimensions) if not transpose: - phi_vec_jac = ( - e_vec(geometry.dimensions, component_index) * data.trial_shape - ).jacobian(ref_symbols_list) - phi_vec_raw = ( - e_vec(geometry.dimensions, component_index) * data.trial_shape - ) - phi = data.test_shape + if isinstance(trial, EnrichedGalerkinFunctionSpace): + raw_phi_grad = data.trial_shape_grad + raw_phi = data.trial_shape + phi_grad_x = projection_mat * ( + jac_total * raw_phi_grad.col(0) + grad_jac_total[0] * raw_phi + ) + phi_grad_y = projection_mat * ( + jac_total * raw_phi_grad.col(1) + grad_jac_total[1] * raw_phi + ) + phi = ( + phi_grad_x.row_join(phi_grad_y) + ).T + + phi = (jac_total * raw_phi_grad).T #DEBUG + # phi = (jac_total * raw_phi_grad).T + else: + raw_phi_grad = ( + e_vec(geometry.space_dimension, component_index) * data.trial_shape + ).jacobian(ref_symbols_list) + raw_phi = ( + e_vec(geometry.space_dimension, component_index) * data.trial_shape + ) + phi_grad_x = projection_mat * ( + raw_phi_grad.col(0) - normal_grad[0] * normal.T * raw_phi #- normal_grad[0] * normal.T * raw_phi + ) + phi_grad_y = projection_mat * ( + raw_phi_grad.col(1) - normal_grad[1] * normal.T * raw_phi #- normal_grad[1] * normal.T * raw_phi + ) + phi = ( + phi_grad_x.row_join(phi_grad_y) + ).T + psi = data.test_shape else: phi = data.trial_shape - phi_vec_jac = ( - e_vec(geometry.dimensions, component_index) * data.test_shape - ).jacobian(ref_symbols_list) - phi_vec_raw = ( - e_vec(geometry.dimensions, component_index) * data.test_shape - ) - - phi_vec_x = projection_mat * ( - phi_vec_jac.col(0) - normal_grad[0] * normal.T * phi_vec_raw - ) - phi_vec_y = projection_mat * ( - phi_vec_jac.col(1) - normal_grad[1] * normal.T * phi_vec_raw - ) - - phi_vec = ( - phi_vec_x.row_join(phi_vec_y) - ).T + if isinstance(test, EnrichedGalerkinFunctionSpace): + raw_psi_grad = data.test_shape_grad + raw_psi = data.test_shape + psi_grad_x = projection_mat * ( + jac_total * raw_psi_grad.col(0) + grad_jac_total[0] * raw_psi + ) + psi_grad_y = projection_mat * ( + jac_total * raw_psi_grad.col(1) + grad_jac_total[1] * raw_psi + ) + psi = ( + psi_grad_x.row_join(psi_grad_y) + ).T + + psi = (jac_total * raw_psi_grad).T # DEBUG + #psi = (jac_total * raw_psi_grad).T + else: + raw_psi_grad = ( + e_vec(geometry.space_dimension, component_index) * data.test_shape + ).jacobian(ref_symbols_list) + raw_psi = ( + e_vec(geometry.space_dimension, component_index) * data.test_shape + ) + psi_grad_x = projection_mat * ( + raw_psi_grad.col(0) - normal_grad[0] * normal.T * raw_psi + ) + psi_grad_y = projection_mat * ( + raw_psi_grad.col(1) - normal_grad[1] * normal.T * raw_psi + ) + psi = ( + psi_grad_x.row_join(psi_grad_y) + ).T - form = ( - (jac_total * fundamental_form_inv * phi_vec).trace() - * phi - * fundamental_form_det**0.5 - ) + if not transpose: + form = ( + (jac_total * fundamental_form_inv * phi).trace() + * psi + * fundamental_form_det**0.5 + ) + else: + form = ( + (jac_total * fundamental_form_inv * psi).trace() + * phi + * fundamental_form_det**0.5 + ) + #form = normal_grad[0][0] + normal_grad[0][1] + normal_grad[0][2] mat[data.row, data.col] = form @@ -606,8 +648,7 @@ Weak formulation phi_epsilon = 0.5 * (phi_projected_grad + phi_projected_grad.T) psi_epsilon = 0.5 * (psi_projected_grad + psi_projected_grad.T) - form = tabulation.register_factor( - "epsilon_epsilon_prod", + form = ( double_contraction(phi_epsilon, psi_epsilon) * fundamental_form_det**0.5, )[0] diff --git a/hog/manifold_helpers.py b/hog/manifold_helpers.py index 1e09a8f02bce431aeb7ab675fc7462de64aea5ac..6193ae5fbf103611dc8fd8b2946e943fa64ae727 100644 --- a/hog/manifold_helpers.py +++ b/hog/manifold_helpers.py @@ -18,11 +18,12 @@ from typing import List import sympy as sp from hog.exception import HOGException +from typing import Optional from hog.element_geometry import ElementGeometry, TriangleElement from hog.symbolizer import Symbolizer from hog.blending import GeometryMap, IdentityMap -from hog.fem_helpers import jac_affine_to_physical, jac_shifted_affine_to_physical +from hog.fem_helpers import jac_affine_to_physical, jac_shifted_affine_to_physical, jac_shifted_ref_to_physical from hog.external_functions import BlendingFEmbeddedTriangle @@ -30,6 +31,7 @@ def embedded_normal( geometry: ElementGeometry, symbolizer: Symbolizer, blending: GeometryMap = IdentityMap(), + affine_points: Optional[List[sp.Matrix]] = None ) -> sp.Matrix: """Returns an unoriented unit normal vector for an embedded triangle.""" @@ -38,16 +40,19 @@ def embedded_normal( "Embedded normal vectors are only defined for triangles embedded in 3D space." ) - vert_points = symbolizer.affine_vertices_as_vectors( - geometry.space_dimension, geometry.num_vertices - ) + if affine_points is None: + vert_points = symbolizer.affine_vertices_as_vectors( + geometry.space_dimension, geometry.num_vertices + ) + else: + vert_points = affine_points span0 = vert_points[1] - vert_points[0] span1 = vert_points[2] - vert_points[0] if not isinstance(blending, IdentityMap): - blending_jac = jac_affine_to_physical(geometry, symbolizer) - span0 = blending_jac.T * span0 - span1 = blending_jac.T * span1 + blending_jac = jac_affine_to_physical(geometry, symbolizer, affine_points=vert_points) + span0 = blending_jac * span0 + span1 = blending_jac * span1 normal = sp.Matrix( [ @@ -59,7 +64,11 @@ def embedded_normal( normal = normal / (normal[0] ** 2 + normal[1] ** 2 + normal[2] ** 2) ** 0.5 return normal -def embedded_normal_grad(geometry: ElementGeometry, symbolizer: Symbolizer, blending: GeometryMap = IdentityMap()) -> List[sp.Matrix]: +def embedded_normal_grad( + geometry: ElementGeometry, + symbolizer: Symbolizer, + blending: GeometryMap = IdentityMap(), + affine_points: Optional[List[sp.Matrix]] = None) -> List[sp.Matrix]: """Returns an approximation for partial_x and partial_y of the unoriented unit normal vector for an embedded triangle.""" if not (isinstance(geometry, TriangleElement) and geometry.space_dimension == 3): @@ -72,20 +81,23 @@ def embedded_normal_grad(geometry: ElementGeometry, symbolizer: Symbolizer, blen shiftX = sp.Matrix([[shiftSize],[0.0]]) shiftY = sp.Matrix([[0.0],[shiftSize]]) - vert_points = symbolizer.affine_vertices_as_vectors(geometry.dimensions, geometry.num_vertices) + if affine_points is None: + vert_points = symbolizer.affine_vertices_as_vectors(geometry.space_dimension, geometry.num_vertices) + else: + vert_points = affine_points span0 = vert_points[1] - vert_points[0] span1 = vert_points[2] - vert_points[0] - blending_jac_here = jac_affine_to_physical(geometry, symbolizer) - blending_jac_x = jac_shifted_affine_to_physical(geometry, symbolizer, shiftX) - blending_jac_y = jac_shifted_affine_to_physical(geometry, symbolizer, shiftY) + blending_jac_here = jac_affine_to_physical(geometry, symbolizer, affine_points=vert_points) + blending_jac_x = jac_shifted_affine_to_physical(geometry, symbolizer, shiftX, affine_points=vert_points) + blending_jac_y = jac_shifted_affine_to_physical(geometry, symbolizer, shiftY, affine_points=vert_points) - span0_here = blending_jac_here.T * span0 - span1_here = blending_jac_here.T * span1 - span0_x = blending_jac_x.T * span0 - span1_x = blending_jac_x.T * span1 - span0_y = blending_jac_y.T * span0 - span1_y = blending_jac_y.T * span1 + span0_here = blending_jac_here * span0 + span1_here = blending_jac_here * span1 + span0_x = blending_jac_x * span0 + span1_x = blending_jac_x * span1 + span0_y = blending_jac_y * span0 + span1_y = blending_jac_y * span1 normal_here = sp.Matrix([ span0_here[1] * span1_here[2] - span0_here[2] * span1_here[1], @@ -117,6 +129,7 @@ def face_projection( geometry: ElementGeometry, symbolizer: Symbolizer, blending: GeometryMap = IdentityMap(), + affine_points: Optional[List[sp.Matrix]] = None ) -> sp.Matrix: """Returns a projection matrix for an embedded triangle.""" @@ -125,7 +138,7 @@ def face_projection( "Projection matrices are only defined for triangles embedded in 3D space." ) - normal = embedded_normal(geometry, symbolizer, blending=blending) + normal = embedded_normal(geometry, symbolizer, blending=blending, affine_points=affine_points) projection = sp.Matrix( [ [1.0 - normal[0] ** 2, -normal[0] * normal[1], -normal[0] * normal[2]], @@ -134,3 +147,29 @@ def face_projection( ] ) return projection + +def grad_jac_ref_to_physical( + geometry: ElementGeometry, + symbolizer: Symbolizer, + blending: GeometryMap, + affine_points: Optional[List[sp.Matrix]] = None + ) -> List[sp.Matrix]: + """Returns an approximation for partial_x and partial_y of the Jacobi Matrix for the trafo from reference space to physical space.""" + + if not (isinstance(geometry, TriangleElement) and geometry.space_dimension == 3): + pass + #TODO: Check other cases or output warning. + + shiftSize = 0.0001 + null_shift = sp.Matrix([[0.0],[0.0]]) + x_shift = sp.Matrix([[shiftSize],[0.0]]) + y_shift = sp.Matrix([[0.0],[shiftSize]]) + + jac_here = jac_shifted_ref_to_physical(geometry, symbolizer, null_shift, affine_points=affine_points) + jac_x = jac_shifted_ref_to_physical(geometry, symbolizer, x_shift, affine_points=affine_points) + jac_y = jac_shifted_ref_to_physical(geometry, symbolizer, y_shift, affine_points=affine_points) + + grad_x = (jac_x - jac_here) / shiftSize + grad_y = (jac_y - jac_here) / shiftSize + + return [grad_x, grad_y]