I hope it is more clear now, thank you.
import gmsh
import numpy as np
import tqdm
from mpi4py import MPI
from petsc4py import PETSc
from dolfinx import fem, mesh, io, plot
import ufl
# Define temporal parameters
t = 0 # Start time
T = 0.01 # Final time
num_steps = 1
dt = T / num_steps # time step size
# Define mesh
x0, y0 = -2, -2
L = 4
l = 0.2
gdim = 2
mesh_comm = MPI.COMM_WORLD
model_rank = 0
gmsh.initialize()
fluid_tag, source_tag = 11, 22
source, fluid = [], []
res_min = 0.05
if mesh_comm.rank == model_rank:
box = gmsh.model.occ.addRectangle(x0, y0, 0, L, L)
obstacle = gmsh.model.occ.addRectangle(0.1, 0.1, 0, l, l)
whole = gmsh.model.occ.fragment([(gdim, box)], [(gdim, obstacle)])
gmsh.model.occ.synchronize()
volumes = gmsh.model.occ.getEntities(2)
for dim, tag in volumes:
mass = gmsh.model.occ.getMass(dim, tag)
if np.isclose(mass, l * l):
source.append(tag)
else:
fluid.append(tag)
gmsh.model.addPhysicalGroup(2, fluid, fluid_tag)
gmsh.model.setPhysicalName(2, fluid_tag, "fluid")
gmsh.model.addPhysicalGroup(2, source, source_tag)
gmsh.model.setPhysicalName(2, source_tag, "source")
gmsh.model.occ.synchronize()
edges = gmsh.model.getBoundary([(2, source[0])], oriented=False)
gmsh.model.mesh.field.add("Distance", 1)
gmsh.model.mesh.field.setNumbers(1, "EdgesList", [e[1] for e in edges])
gmsh.model.mesh.field.add("Threshold", 2)
gmsh.model.mesh.field.setNumber(2, "IField", 1)
gmsh.model.mesh.field.setNumber(2, "LcMin", l / 3)
gmsh.model.mesh.field.setNumber(2, "LcMax", 2 * l)
gmsh.model.mesh.field.setNumber(2, "DistMin", 2 * l)
gmsh.model.mesh.field.setNumber(2, "DistMax", 6 * l)
gmsh.model.mesh.field.add("Min", 5)
gmsh.model.mesh.field.setNumbers(5, "FieldsList", [2])
gmsh.model.mesh.field.setAsBackgroundMesh(5)
gmsh.option.setNumber("Mesh.Algorithm", 7)
gmsh.model.mesh.generate(gdim)
gmsh.model.mesh.optimize("Netgen")
domain, ct, ft = io.gmshio.model_to_mesh(gmsh.model, mesh_comm, model_rank, gdim=gdim)
ct.name = 'Cell markers'
ft.name = 'Facet markers'
gmsh.finalize()
# # Function spaces
V = fem.FunctionSpace(domain, ("CG", 1))
W = fem.VectorFunctionSpace(domain, ("CG", 2))
Q = fem.FunctionSpace(domain, ("DG", 0))
# initial condition
def initial_condition(x, a=5):
return np.zeros((1, x.shape[1]), dtype=PETSc.ScalarType)
# Fluid velocity
class FluidVelocity():
def __init__(self, t, max_speed=1.0):
self.t = t
self.max_speed = max_speed
def __call__(self, x):
values = np.zeros((gdim, x.shape[1]), dtype=PETSc.ScalarType)
values[0] = self.max_speed * ((x[1] + 2) / 4)**2
return values
# Function for the "previous" timestep
u_n = fem.Function(V)
u_n.name = "u_n"
u_n.interpolate(initial_condition)
# Function for the present timestep
uh = fem.Function(V)
uh.name = "uh"
uh.interpolate(initial_condition)
# Function for the fluid velocity
v_fluid = fem.Function(W)
v_fluid.name = "v_fluid"
fluid_velocity = FluidVelocity(t)
v_fluid.interpolate(fluid_velocity)
# Trial and Test for the variational problem
u, v = ufl.TrialFunction(V), ufl.TestFunction(V)
# BCs
fdim = domain.topology.dim - 1
facets = mesh.locate_entities_boundary(domain, fdim, lambda x: np.full(x.shape[1], True))
dofs = fem.locate_dofs_topological(V, fdim, facets)
bc = fem.dirichletbc(PETSc.ScalarType(0), dofs, V)
## Source and diffusion coefficient
f = fem.Function(Q)
f.x.array[:] = 0
cells = ct.find(source_tag)
f.x.array[cells] = 0.5
D = fem.Constant(domain, PETSc.ScalarType(0.5))
# Variational problem
a = u * v * ufl.dx + dt *D* ufl.dot(ufl.grad(u), ufl.grad(v)) * ufl.dx + dt * v * ufl.dot(v_fluid, ufl.grad(u)) * ufl.dx
L = (u_n + dt * f) * v * ufl.dx
bilinear_form = fem.form(a)
linear_form = fem.form(L)
A = fem.petsc.assemble_matrix(bilinear_form, bcs=[bc])
A.assemble()
b = fem.petsc.create_vector(linear_form)
# Solver
solver = PETSc.KSP().create(domain.comm)
solver.setOperators(A)
solver.setType(PETSc.KSP.Type.PREONLY)
solver.getPC().setType(PETSc.PC.Type.LU)
xdmf = io.XDMFFile(domain.comm, "result.xdmf", "w")
xdmf.write_mesh(domain)
xdmf.write_function(uh, t)
progress = tqdm.tqdm(total=num_steps)
for i in range(num_steps):
progress.update(1)
t += dt
# Update the right hand side reusing the initial vector
with b.localForm() as loc_b:
loc_b.set(0)
fem.petsc.assemble_vector(b, linear_form)
# Apply Dirichlet boundary condition to the vector
fem.petsc.apply_lifting(b, [bilinear_form], [[bc]])
b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
fem.petsc.set_bc(b, [bc])
# Solve linear problem
solver.solve(b, uh.vector)
uh.x.scatter_forward()
# Update solution at previous time step (u_n)
u_n.x.array[:] = uh.x.array
# Write solution to file
xdmf.write_function(uh, t)
xdmf.close()