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. """ # Source term L = dt * self.f * v * ngs.dx # Dirichlet BCs for u, they only get added if using DG if self.DG: # Define the special DG functions. n, _, alpha = get_special_functions(self.mesh, self.nu) for marker in self.BC.get('dirichlet', {}).get('u', {}): g = self.BC['dirichlet']['u'][marker] L += dt * self.dc * ( alpha * g * v # 1/2 of penalty term for u=g on πͺ_D from βu^ - g * n * ngs.Grad(v) # 1/2 of penalty for u=g on πͺ_D ) * self._ds(marker) # Neumann BCs for u for marker in self.BC.get('neumann', {}).get('u', {}): h = self.BC['neumann']['u'][marker] L += dt * self.dc * h * v * self._ds(marker) # Robin BCs for u for marker in self.BC.get('robin', {}).get('u', {}): r, q = self.BC['robin']['u'][marker] L += -dt * self.dc * r * q * v * 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_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 special DG functions. n, _, alpha = get_special_functions(self.mesh, self.nu) # Source term L = dt * self.f * v * self.DIM_solver.phi_gfu * ngs.dx if self.DG: for marker in self.BC.get('dirichlet', {}).get('u', {}): # Penalty terms for conformal Dirichlet BCs g = self.BC['dirichlet']['u'][marker] L += dt * self.dc * ( alpha * g * v # 1/2 of penalty term for u=g on πͺ_D from βu^ - g * n * ngs.Grad(v) # 1/2 of penalty for u=g 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 * self.dc * ( ngs.Grad(v) * self.DIM_solver.grad_phi_gfu * g + alpha * g * v * self.DIM_solver.mag_grad_phi_gfu ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx # DIM Neumann BCs for u. for marker in self.DIM_BC.get('neumann', {}).get('u', {}): h = self.DIM_BC['neumann']['u'][marker] L += dt * self.dc * ( -h * v * self.DIM_solver.mag_grad_phi_gfu ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx # DIM Robin BCs for u. for marker in self.DIM_BC.get('robin', {}).get('u', {}): r, q = self.DIM_BC['robin']['u'][marker] L += dt * self.dc * ( r * q * v * self.DIM_solver.mag_grad_phi_gfu ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx # Conformal Neumann BCs for u for marker in self.BC.get('neumann', {}).get('u', {}): h = self.BC['neumann']['u'][marker] L += dt * self.dc * h * v * self._ds(marker) # Conformal Robin BCs for u for marker in self.BC.get('robin', {}).get('u', {}): r, q = self.BC['robin']['u'][marker] L += -dt * self.dc * r * q * v * 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 _bilinear_no_DIM(self, u: ProxyFunction, p: ProxyFunction, v: ProxyFunction, q: ProxyFunction, dt: Parameter, explicit_bilinear: bool) -> ngs.BilinearForm: """ Bilinear form when the diffuse interface method is not being used. Handles both CG and DG. """ 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 # Define the special DG functions. n, _, alpha = get_special_functions(self.mesh, self.nu) # Bulk of Bilinear form 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 + ngs.InnerProduct(avg_grad_v, ngs.OuterProduct(jump_u, n)) # U - alpha * ngs.InnerProduct( jump_u, jump_v) # 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 + ngs.InnerProduct(ngs.Grad(v), ngs.OuterProduct( u, n)) # 1/2 of penalty for u=g on πͺ_D - alpha * u * v # 1/2 of penalty term for u=g on πͺ_D from βu^ ) * self._ds(self.dirichlet_names['u']) return a
def _bilinear_no_DIM(self, u: ProxyFunction, v: ProxyFunction, dt: Parameter, explicit_bilinear: bool) -> ngs.BilinearForm: """ Bilinear form when the diffuse interface method is not being used. Handles both CG and DG. """ # Laplacian term a = dt * self.dc * ngs.InnerProduct(ngs.Grad(u), ngs.Grad(v)) * ngs.dx # Bulk of Bilinear form if self.DG: # Define the special DG functions. n, _, alpha = get_special_functions(self.mesh, self.nu) 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.dc * ( -jump_u * n * avg_grad_v # U - avg_grad_u * n * jump_v # 1/2 term for u+=u- on πͺ_I from βu^ + alpha * jump_u * jump_v # 1/2 term for u+=u- on πͺ_I from βu^ ) * ngs.dx(skeleton=True) if self.dirichlet_names.get('u', None) is not None: # Penalty terms for Dirichlet BCs a += dt * self.dc * ( -u * n * ngs.Grad(v) # 1/2 of penalty for u=g on πͺ_D - ngs.Grad(u) * n * v # βu^ = βu + alpha * u * v # 1/2 of penalty term for u=g on πͺ_D from βu^ ) * self._ds(self.dirichlet_names['u']) # Robin BCs for u for marker in self.BC.get('robin', {}).get('u', {}): r, q = self.BC['robin']['u'][marker] a += -dt * self.dc * r * u * v * self._ds(marker) return a
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
def _bilinear_DIM(self, u: ProxyFunction, v: ProxyFunction, dt: Parameter, explicit_bilinear: bool) -> ngs.BilinearForm: """ Bilinear form when the diffuse interface method is being used. Handles both CG and DG. """ # Define the special DG functions. n, _, alpha = get_special_functions(self.mesh, self.nu) # Laplacian term a = dt * self.dc * ngs.InnerProduct( ngs.Grad(u), ngs.Grad(v)) * self.DIM_solver.phi_gfu * ngs.dx # Force u to zero where phi is zero. # Applying a penalty can help convergence, but the penalty seems to interfere with Neumann BCs. if self.DIM_BC.get('neumann', {}).get('u', {}): a += dt * u * v * (1.0 - self.DIM_solver.phi_gfu) * ngs.dx else: a += dt * alpha * u * v * (1.0 - self.DIM_solver.phi_gfu) * ngs.dx # Bulk of Bilinear form 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.dc * ( -jump_u * n * avg_grad_v # U - avg_grad_u * jump_v * n # 1/2 term for u+=u- on πͺ_I from βu^ + alpha * jump_u * jump_v # 1/2 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.dc * ( -u * n * ngs.Grad(v) # 1/2 of penalty for u=g on πͺ_D - ngs.Grad(u) * n * v # βu^ = βu + 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.dc * ( ngs.Grad(u) * self.DIM_solver.grad_phi_gfu * v + ngs.Grad(v) * self.DIM_solver.grad_phi_gfu * u + alpha * u * v * self.DIM_solver.mag_grad_phi_gfu ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx # DIM Robin BCs for u. for marker in self.DIM_BC.get('robin', {}).get('u', {}): r, q = self.DIM_BC['robin']['u'][marker] a += dt * self.dc * ( r * u * v * self.DIM_solver.mag_grad_phi_gfu ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx # Conformal Robin BCs for u for marker in self.BC.get('robin', {}).get('u', {}): r, q = self.BC['robin']['u'][marker] a += -dt * self.dc * r * u * v * self._ds(marker) return a
def _bilinear_DIM(self, u: ProxyFunction, p: ProxyFunction, v: ProxyFunction, q: ProxyFunction, dt: Parameter, explicit_bilinear: bool) -> ngs.BilinearForm: """ Bilinear form when the diffuse interface method is being used. Handles both CG and DG. """ # Define the special DG functions. n, _, alpha = get_special_functions(self.mesh, self.nu) 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 - p * ngs.div(v)) * (1.0 - self.DIM_solver.phi_gfu) * ngs.dx # Bulk of Bilinear form 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 + ngs.InnerProduct(avg_grad_v, ngs.OuterProduct(jump_u, n)) # U - alpha * ngs.InnerProduct( jump_u, jump_v) # 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 + ngs.InnerProduct(ngs.Grad(v), ngs.OuterProduct( u, n)) # 1/2 of penalty for u=g on πͺ_D - 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)) + ngs.InnerProduct( ngs.Grad(v), ngs.OuterProduct(u, self.DIM_solver.grad_phi_gfu)) + alpha * u * v * self.DIM_solver.mag_grad_phi_gfu ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx # TODO: Add non-Dirichlet DIM BCs. return a