import dolfinx
import numpy as np
import ufl
from dolfinx import fem
from dolfinx.fem import (Function, FunctionSpace, assemble_scalar, form,
locate_dofs_topological, dirichletbc, petsc)
from ufl import grad, inner, dot, nabla_grad, div
from petsc4py import PETSc
[docs]
class tentative_velocity():
def __init__(self, domain: dolfinx.mesh.Mesh, ct: dolfinx.cpp.mesh.MeshTags_int32, ft: dolfinx.cpp.mesh.MeshTags_int32, inlet,
physical_param: dict, bound_markers: dict, time_adv = 'EI'):
self.domain = domain
self.ct = ct
self.ft = ft
self.fdim = domain.geometry.dim - 1
self.bound_markers = bound_markers
metadata = {"quadrature_degree": 4}
self.dx = ufl.Measure('dx', domain=self.domain, subdomain_data=self.ct, metadata=metadata)
self.ds = ufl.Measure('ds', domain=self.domain, subdomain_data=self.ft, metadata=metadata)
# Parameters
self.nu = fem.Constant(self.domain, PETSc.ScalarType(physical_param['nu']))
self.dt = fem.Constant(self.domain, PETSc.ScalarType(physical_param['dt']))
self.t = 0
# Functional Spaces
self.v_cg2 = ufl.VectorElement("Lagrange", self.domain.ufl_cell(), 2)
self.V = FunctionSpace(self.domain, self.v_cg2)
self.Q = FunctionSpace(self.domain, ("Lagrange", 1))
# Trial and test functions
self.u_ = ufl.TrialFunction(self.V)
self.v = ufl.TestFunction(self.V)
# Boundary conditions
fdim = self.domain.topology.dim - 1
## Inlet
self.u_in = Function(self.V)
self.inlet_velocity = inlet(self.t, physical_param['T'])
self.u_in.interpolate(self.inlet_velocity)
self.bcu_inflow = dirichletbc(self.u_in, locate_dofs_topological(self.V, fdim, self.ft.find(bound_markers['inlet'])))
## Walls
self.u_nonslip = np.array((0,) * self.domain.geometry.dim, dtype=PETSc.ScalarType)
self.bcu_walls = dirichletbc(self.u_nonslip, locate_dofs_topological(self.V, fdim, self.ft.find(bound_markers['walls'])), self.V)
## Obstacle
self.bcu_obs = dirichletbc(self.u_nonslip, locate_dofs_topological(self.V, fdim, self.ft.find(bound_markers['obstacle'])), self.V)
self.bcu = [self.bcu_inflow, self.bcu_walls, self.bcu_obs]
# Define functions
self.uOld = Function(self.V) # solution at t_n-1
if time_adv != 'EI':
self.u_n = Function(self.V) # solution at t_n-2
self.time_adv = time_adv
self.u_tilde = Function(self.V)
self.pOld = Function(self.Q)
self.pOld.name = "p"
# self.solver.setType(PETSc.KSP.Type.GMRES)
# self.solver.getPC().setType(PETSc.PC.Type.ILU)
[docs]
def advance(self, t):
self.t = t
self.inlet_velocity.t = t
self.u_in.interpolate(self.inlet_velocity)
# Assemble matrix
self.A.zeroEntries()
fem.petsc.assemble_matrix(self.A, self.a, self.bcu)
self.A.assemble()
# Assemble rhs vector
with self.b.localForm() as loc:
loc.set(0.)
fem.petsc.assemble_vector(self.b, self.L)
fem.petsc.apply_lifting(self.b, [self.a], [self.bcu])
self.b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
fem.petsc.set_bc(self.b, self.bcu)
self.solver.solve(self.b, self.u_tilde.vector)
self.u_tilde.x.scatter_forward()
[docs]
class pressure_projection():
def __init__(self, domain: dolfinx.mesh.Mesh, ct: dolfinx.cpp.mesh.MeshTags_int32, ft: dolfinx.cpp.mesh.MeshTags_int32,
physical_param: dict, bound_markers: dict, time_adv = 'EI'):
self.domain = domain
self.ct = ct
self.ft = ft
self.fdim = domain.geometry.dim - 1
self.bound_markers = bound_markers
metadata = {"quadrature_degree": 4}
self.dx = ufl.Measure('dx', domain=self.domain, subdomain_data=self.ct, metadata=metadata)
self.ds = ufl.Measure('ds', domain=self.domain, subdomain_data=self.ft, metadata=metadata)
# Parameters
self.dt = fem.Constant(self.domain, PETSc.ScalarType(physical_param['dt']))
# Functional Spaces
self.v_cg2 = ufl.VectorElement("Lagrange", self.domain.ufl_cell(), 2)
self.V = FunctionSpace(self.domain, self.v_cg2)
self.Q = FunctionSpace(self.domain, ("Lagrange", 1))
# Trial and test functions
self.p_ = ufl.TrialFunction(self.Q)
self.q = ufl.TestFunction(self.Q)
# Boundary conditions
fdim = self.domain.topology.dim - 1
## Inlet
self.bcp = [dirichletbc(PETSc.ScalarType(0), locate_dofs_topological(self.Q, fdim, self.ft.find(bound_markers['outlet'])), self.Q)]
# Define functions
self.u_tilde = Function(self.V)
self.phi = Function(self.Q)
if time_adv == 'BDF2':
self.alpha = 3 / 2
else:
self.alpha = 1.
[docs]
def advance(self, u_tilde: dolfinx.fem.Function):
assert(len(u_tilde.x.array[:]) == len(self.u_tilde.x.array))
self.u_tilde.x.array[:] = u_tilde.x.array
# Assemble rhs vector
with self.b.localForm() as loc:
loc.set(0)
fem.petsc.assemble_vector(self.b, self.L)
fem.petsc.apply_lifting(self.b, [self.a], [self.bcp])
self.b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
fem.petsc.set_bc(self.b, self.bcp)
self.solver.solve(self.b, self.phi.vector)
self.phi.x.scatter_forward()
[docs]
class update_velocity():
def __init__(self, domain: dolfinx.mesh.Mesh, ct: dolfinx.cpp.mesh.MeshTags_int32, ft: dolfinx.cpp.mesh.MeshTags_int32, physical_param: dict,
time_adv='EI'):
self.domain = domain
self.ct = ct
self.ft = ft
self.fdim = domain.geometry.dim - 1
metadata = {"quadrature_degree": 4}
self.dx = ufl.Measure('dx', domain=self.domain, subdomain_data=self.ct, metadata=metadata)
self.ds = ufl.Measure('ds', domain=self.domain, subdomain_data=self.ft, metadata=metadata)
# Parameters
self.dt = fem.Constant(self.domain, PETSc.ScalarType(physical_param['dt']))
# Functional Spaces
self.v_cg2 = ufl.VectorElement("Lagrange", self.domain.ufl_cell(), 2)
self.V = FunctionSpace(self.domain, self.v_cg2)
self.Q = FunctionSpace(self.domain, ("Lagrange", 1))
# Trial and test functions
self.u_ = ufl.TrialFunction(self.V)
self.v = ufl.TestFunction(self.V)
# Define functions
self.u_tilde = Function(self.V)
self.phi = Function(self.Q)
self.u_new = Function(self.V)
self.u_new.name = "U"
if time_adv == 'BDF2':
self.alpha = 3 / 2
else:
self.alpha = 1.
[docs]
def advance(self, u_tilde: dolfinx.fem.Function, phi):
assert(len(u_tilde.x.array[:]) == len(self.u_tilde.x.array))
self.u_tilde.x.array[:] = u_tilde.x.array
assert(len(phi.x.array[:]) == len(self.phi.x.array))
self.phi.x.array[:] = phi.x.array
# Assemble rhs vector
with self.b.localForm() as loc:
loc.set(0)
fem.petsc.assemble_vector(self.b, self.L)
self.b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
self.solver.solve(self.b, self.u_new.vector)
self.u_new.x.scatter_forward()
[docs]
class drag_lift():
def __init__(self, domain: dolfinx.mesh.Mesh, ft: dolfinx.cpp.mesh.MeshTags_int32, physical_param: dict, obstacle_mark: int,
points = [[0.15, 0.2, 0], [0.25, 0.2, 0]]):
self.normal = -ufl.FacetNormal(domain)
self.dObs = ufl.Measure("ds", domain=domain, subdomain_data=ft, subdomain_id=obstacle_mark)
self.nu = physical_param['nu']
self.rhoLU2 = physical_param['rhoLU2']
# Functional Spaces
self.v_cg2 = ufl.VectorElement("Lagrange", domain.ufl_cell(), 2)
self.V = FunctionSpace(domain, self.v_cg2)
self.Q = FunctionSpace(domain, ("Lagrange", 1))
# Define functions
self.u = Function(self.V)
self.u_t = inner(ufl.as_vector((self.normal[1], -self.normal[0])), self.u)
self.p = Function(self.Q)
self.drag = form( 2 / self.rhoLU2 * (self.nu * inner(grad(self.u_t), self.normal) * self.normal[1] - self.p * self.normal[0]) * self.dObs)
self.lift = form(-2 / self.rhoLU2 * (self.nu * inner(grad(self.u_t), self.normal) * self.normal[0] + self.p * self.normal[1]) * self.dObs)
if domain.comm.rank == 0:
self.C_D = []
self.C_L = []
self.t_u = []
self.t_p = []
self.points = np.array(points)
self.tree = dolfinx.geometry.BoundingBoxTree(domain, domain.geometry.dim)
self.cell_candidates = dolfinx.geometry.compute_collisions(self.tree, self.points)
# self.tree = dolfinx.geometry.bb_tree(domain, domain.geometry.dim)
# self.cell_candidates = dolfinx.geometry.compute_collisions_points(self.tree, self.points)
self.colliding_cells = dolfinx.geometry.compute_colliding_cells(domain, self.cell_candidates, self.points)
self.front_cells = self.colliding_cells.links(0)
self.back_cells = self.colliding_cells.links(1)
if domain.comm.rank == 0:
self.p_diff = []
self.domain = domain
[docs]
def compute(self, t: float, dt: float, u_new: dolfinx.fem.Function, p_new: dolfinx.fem.Function):
# Update velocity and pressure
self.u.x.array[:] = u_new.x.array
self.p.x.array[:] = p_new.x.array
drag_coeff = self.domain.comm.gather(assemble_scalar(self.drag), root=0)
lift_coeff = self.domain.comm.gather(assemble_scalar(self.lift), root=0)
p_front = None
if len(self.front_cells) > 0:
p_front = self.p.eval(self.points[0], self.front_cells[:1])
p_front = self.domain.comm.gather(p_front, root=0)
p_back = None
if len(self.back_cells) > 0:
p_back = self.p.eval(self.points[1], self.back_cells[:1])
p_back = self.domain.comm.gather(p_back, root=0)
if self.domain.comm.rank == 0:
self.t_u.append(t)
self.t_p.append(t - dt / 2)
self.C_D.append( sum(drag_coeff) )
self.C_L.append( sum(lift_coeff) )
# Choose first pressure that is found from the different processors
for pressure in p_front:
if pressure is not None:
p_diff_t = pressure[0]
break
for pressure in p_back:
if pressure is not None:
p_diff_t -= pressure[0]
break
self.p_diff.append(p_diff_t)