def _linear_IMEX_no_DIM(self, v: ProxyFunction, gfu_u: GridFunction, dt: Parameter) -> ngs.LinearForm: """ Linear form when IMEX linearization is being used and the diffuse interface method is not being used. Handles both CG and DG. """ L = dt * (v * self.f - ngs.InnerProduct(ngs.Grad(gfu_u) * gfu_u, v)) * ngs.dx # Define the special DG functions. n, h, alpha = get_special_functions(self.mesh, self.nu) # Dirichlet BC for u if self.DG: for marker in self.BC.get('dirichlet', {}).get('u', {}): g = self.BC['dirichlet']['u'][marker] L += dt * ( -self.kv * ngs.InnerProduct(ngs.Grad( v), ngs.OuterProduct(g, n)) # 1/2 of penalty for u=g + self.kv * alpha * g * v # 1/2 of penalty for u=g # from ∇u^ on 𝚪_D ) * self._ds(marker) # Stress BC for marker in self.BC.get('stress', {}).get('stress', {}): h = self.BC['stress']['stress'][marker] if self.DG: L += dt * v * h * self._ds(marker) else: L += dt * v.Trace() * h * self._ds(marker) return L
def _Linear_no_DIM(self, v: ProxyFunction, dt: Parameter) -> ngs.LinearForm: """ Linear form when the diffuse interface method is not being used. Handles both CG and DG. """ # Define the base linear form L = dt * v * self.f * ngs.dx # Define the special DG functions. n, _, alpha = get_special_functions(self.mesh, self.nu) if self.DG: # Dirichlet BCs for u for marker in self.BC.get('dirichlet', {}).get('u', {}): g = self.BC['dirichlet']['u'][marker] L += dt * self.kv * ( alpha * g * v # 1/2 of penalty for u=g from ∇u^ on 𝚪_D - ngs.InnerProduct(ngs.Grad(v), ngs.OuterProduct( g, n)) # 1/2 of penalty for u=g ) * self._ds(marker) # Stress BCs for marker in self.BC.get('stress', {}).get('stress', {}): h = self.BC['stress']['stress'][marker] if self.DG: L += dt * v * h * self._ds(marker) else: L += dt * v.Trace() * h * self._ds(marker) return L
def _linear_Oseen_DIM(self, v: ProxyFunction, w: GridFunction, dt: Parameter) -> ngs.LinearForm: """ Linear form when the diffuse interface method is being used with Oseen linearization. Handles both CG and DG. """ L = dt * v * self.f * self.DIM_solver.phi_gfu * ngs.dx # Define the special DG functions. n, h, alpha = get_special_functions(self.mesh, self.nu) if self.DG: # Conformal Dirichlet BCs for u. for marker in self.BC.get('dirichlet', {}).get('u', {}): g = self.BC['dirichlet']['u'][marker] L += dt * ( v * (-0.5 * w * n * g + 0.5 * ngs.Norm(w * n) * g ) # 1/2 of uw^ (convection) - self.kv * ngs.InnerProduct(ngs.Grad( v), ngs.OuterProduct(g, n)) # 1/2 of penalty for u=g + self.kv * alpha * g * v # 1/2 of penalty for u=g from ∇u^ on 𝚪_D ) * self._ds(marker) # Penalty term for DIM Dirichlet BCs. This is the Nitsche method. for marker in self.DIM_BC.get('dirichlet', {}).get('u', {}): # Penalty terms for DIM Dirichlet BCs g = self.DIM_BC['dirichlet']['u'][marker] L += dt * ( v * (0.5 * w * self.DIM_solver.grad_phi_gfu * g + 0.5 * ngs.Norm(w * -self.DIM_solver.grad_phi_gfu) * g) + self.kv * ngs.InnerProduct( ngs.Grad(v), ngs.OuterProduct(g, self.DIM_solver.grad_phi_gfu)) + self.kv * alpha * g * v * self.DIM_solver.mag_grad_phi_gfu ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx # Conformal stress BC for marker in self.BC.get('stress', {}).get('stress', {}): h = self.BC['stress']['stress'][marker] if self.DG: L += dt * v * h * self._ds(marker) else: L += dt * v.Trace() * h * self._ds(marker) # TODO: Add non-Dirichlet DIM BCs. return L
def _linear_DIM(self, v: ProxyFunction, dt: Parameter) -> ngs.LinearForm: """ Linear form when the diffuse interface method is being used. Handles both CG and DG. """ # Define the base linear form L = dt * v * self.f * self.DIM_solver.phi_gfu * ngs.dx # Define the special DG functions. n, _, alpha = get_special_functions(self.mesh, self.nu) if self.DG: # Conformal Dirichlet BCs for u for marker in self.BC.get('dirichlet', {}).get('u', {}): g = self.BC['dirichlet']['u'][marker] L += dt * self.kv * ( alpha * g * v # 1/2 of penalty for u=g from ∇u^ on 𝚪_D - ngs.InnerProduct(ngs.Grad(v), ngs.OuterProduct( g, n)) # 1/2 of penalty for u=g ) * self._ds(marker) # Penalty term for DIM Dirichlet BCs. This is the Nitsche method. for marker in self.DIM_BC.get('dirichlet', {}).get('u', {}): # Penalty terms for DIM Dirichlet BCs g = self.DIM_BC['dirichlet']['u'][marker] L += dt * self.kv * ( alpha * g * v * self.DIM_solver.mag_grad_phi_gfu + ngs.InnerProduct( ngs.Grad(v), ngs.OuterProduct(g, self.DIM_solver.grad_phi_gfu)) ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx # Stress BCs for marker in self.BC.get('stress', {}).get('stress', {}): h = self.BC['stress']['stress'][marker] if self.DG: L += dt * v * h * self._ds(marker) else: L += dt * v.Trace() * h * self._ds(marker) # TODO: Add non-Dirichlet DIM BCs. return L
def _bilinear_IMEX_no_DIM(self, u: ProxyFunction, p: ProxyFunction, v: ProxyFunction, q: ProxyFunction, dt: Parameter, explicit_bilinear) -> ngs.BilinearForm: """ Bilinear form when IMEX linearization is being used and the diffuse interface method is not being used. Handles both CG and DG. """ # Define the special DG functions. n, _, alpha = get_special_functions(self.mesh, self.nu) p_I = construct_p_mat(p, self.mesh.dim) a = dt * ( self.kv * ngs.InnerProduct(ngs.Grad(u), ngs.Grad(v)) # Stress, Newtonian - ngs.div(u) * q # Conservation of mass - ngs.div(v) * p # Pressure - 1e-10 * p * q # Stabilization term ) * ngs.dx if self.DG: jump_u = jump(u) avg_grad_u = grad_avg(u) jump_v = jump(v) avg_grad_v = grad_avg(v) if not explicit_bilinear: # Penalty for discontinuities a += dt * ( -self.kv * ngs.InnerProduct( avg_grad_u, ngs.OuterProduct(jump_v, n)) # Stress - self.kv * ngs.InnerProduct( avg_grad_v, ngs.OuterProduct(jump_u, n)) # U + self.kv * alpha * ngs.InnerProduct( jump_u, jump_v) # Penalty term for u+=u- on 𝚪_I # from ∇u^ ) * ngs.dx(skeleton=True) # Penalty for dirichlet BCs if self.dirichlet_names.get('u', None) is not None: a += dt * ( -self.kv * ngs.InnerProduct( ngs.Grad(u), ngs.OuterProduct(v, n)) # ∇u^ = ∇u - self.kv * ngs.InnerProduct(ngs.Grad(v), ngs.OuterProduct( u, n)) # 1/2 of penalty for u=g on + self.kv * alpha * u * v # 1/2 of penalty term for u=g # on 𝚪_D from ∇u^ ) * self._ds(self.dirichlet_names['u']) # Parallel Flow BC for marker in self.BC.get('parallel', {}).get('parallel', {}): if self.DG: a += dt * v * (u - n * ngs.InnerProduct(u, n)) * self._ds(marker) else: a += dt * v.Trace() * ( u.Trace() - n * ngs.InnerProduct(u.Trace(), n)) * self._ds(marker) return a
def _bilinear_IMEX_DIM(self, u: ProxyFunction, p: ProxyFunction, v: ProxyFunction, q: ProxyFunction, dt: Parameter, explicit_bilinear) -> ngs.BilinearForm: """ Bilinear form when the diffuse interface method is being used with IMEX linearization. Handles both CG and DG. """ # Define the special DG functions. n, _, alpha = get_special_functions(self.mesh, self.nu) p_I = construct_p_mat(p, self.mesh.dim) a = dt * ( self.kv * ngs.InnerProduct(ngs.Grad(u), ngs.Grad(v)) # Stress, Newtonian - ngs.div(u) * q # Conservation of mass - ngs.div(v) * p # Pressure - 1e-10 * p * q # Stabilization term ) * self.DIM_solver.phi_gfu * ngs.dx # Force u and grad(p) to zero where phi is zero. a += dt * ( alpha * u * v # Removing the alpha penalty following discussion with James. - p * (ngs.div(v))) * (1.0 - self.DIM_solver.phi_gfu) * ngs.dx if self.DG: jump_u = jump(u) avg_grad_u = grad_avg(u) jump_v = jump(v) avg_grad_v = grad_avg(v) if not explicit_bilinear: # Penalty for discontinuities a += dt * ( -self.kv * ngs.InnerProduct( avg_grad_u, ngs.OuterProduct(jump_v, n)) # Stress - self.kv * ngs.InnerProduct( avg_grad_v, ngs.OuterProduct(jump_u, n)) # U + self.kv * alpha * ngs.InnerProduct( jump_u, jump_v) # Penalty term for u+=u- on 𝚪_I # from ∇u^ ) * self.DIM_solver.phi_gfu * ngs.dx(skeleton=True) if self.dirichlet_names.get('u', None) is not None: # Penalty terms for conformal Dirichlet BCs a += dt * ( -self.kv * ngs.InnerProduct( ngs.Grad(u), ngs.OuterProduct(v, n)) # ∇u^ = ∇u - self.kv * ngs.InnerProduct(ngs.Grad(v), ngs.OuterProduct( u, n)) # 1/2 of penalty for u=g on + self.kv * alpha * u * v # 1/2 of penalty term for u=g on 𝚪_D from ∇u^ ) * self._ds(self.dirichlet_names['u']) # Penalty term for DIM Dirichlet BCs. This is the Nitsche method. for marker in self.DIM_BC.get('dirichlet', {}).get('u', {}): a += dt * (self.kv * ngs.InnerProduct( ngs.Grad(u), ngs.OuterProduct(v, self.DIM_solver.grad_phi_gfu)) + self.kv * ngs.InnerProduct( ngs.Grad(v), ngs.OuterProduct(u, self.DIM_solver.grad_phi_gfu)) + self.kv * alpha * u * v * self.DIM_solver.mag_grad_phi_gfu ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx # Conformal parallel flow BC for marker in self.BC.get('parallel', {}).get('parallel', {}): if self.DG: a += dt * v * (u - n * ngs.InnerProduct(u, n)) * self._ds(marker) else: a += dt * v.Trace() * ( u.Trace() - n * ngs.InnerProduct(u.Trace(), n)) * self._ds(marker) # TODO: Add non-Dirichlet DIM BCs. return a