from oasismove.problems.NSfracStep import *
from oasismove.problems.NSfracStep.MovingCommon import (
get_coordinate_map,
get_visualization_writers,
)
comm = MPI.comm_world
[docs]def problem_parameters(NS_parameters, **NS_namespace):
"""
Problem file for running CFD simulation for the MovingWall problem inspired by the Wall-induced channel flow
presented by Chnafa [1]. The problem considers flow in a long, straight, time-dependent domain, where the flow is
induced by a moving wall at y=h(t). The motion is mainly controlled by the pulsation (sigma) and the ampliture
of oscillations (eps), with an initial height of h0. The flow has an analytic solution for small Reynolds numbers,
and may be used as a validation case.
[1] Chnafa, C. (2014). Using image-based large-eddy simulations to investigate the intracardiac flow and its
turbulent nature (Doctoral dissertation, Université Montpellier II-Sciences et Techniques du Languedoc).
"""
NS_parameters.update(
# Problem specifiic parameters
h0=0.001, # Initial height of wall
eps=0.05, # Amplitude
sigma=2 * np.pi, # Pulsation of the movement
u_max=0.01, # Maximum velocity
# Mesh parameters
scale=25, # Ratio between computational length of x-direction and y-direction
Nx=250, # Resolution on x-direction
Ny=20, # Resolution on x-direction
# Fluid parameters
nu=8 * 10 ** (-7), # Kinematic viscosity
# Simulation properties
T=1.0, # End time
dt=0.001, # Time step
folder="results_moving_wall",
# Oasis parameters
max_iter=2,
dynamic_mesh=True,
save_solution_frequency=1,
checkpoint=500,
print_intermediate_info=100,
velocity_degree=1,
pressure_degree=1,
use_krylov_solvers=True,
max_error=1e-8,
)
[docs]def mesh(eps, h0, scale, dt, Nx, Ny, u_max, **NS_namespace):
X = scale * h0
Y = h0 * (1 + eps)
mesh = RectangleMesh(Point(0, 0), Point(X, Y), Nx, Ny)
print_mesh_information(mesh, dt, u_max, dim=2)
return mesh
[docs]def pre_boundary_condition(mesh, h0, eps, scale, **NS_namespace):
# Mark geometry
movingwall = AutoSubDomain(lambda x: near(x[1], h0 * (1 + eps)))
outlet = AutoSubDomain(lambda x: near(x[0], scale * h0))
leftwall = AutoSubDomain(lambda x: near(x[0], 0))
bottomwall = AutoSubDomain(lambda x: near(x[1], 0))
boundary = MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
boundary.set_all(0)
movingwall.mark(boundary, 1)
leftwall.mark(boundary, 2)
bottomwall.mark(boundary, 3)
outlet.mark(boundary, 4)
return dict(boundary=boundary)
[docs]def create_bcs(
V, Q, w_, sigma, h0, eps, sys_comp, boundary, NS_expressions, **NS_namespace
):
counter_left, left_coordinate_map = get_coordinate_map(V, boundary, w_, 2)
counter_right, right_coordinate_map = get_coordinate_map(V, boundary, w_, 4)
# Create inlet expressions
movingwall = MovingWall(0, sigma, h0, eps, element=V.ufl_element())
leftwall = MovingSideWall(
0, sigma, h0, eps, left_coordinate_map, counter_left, element=V.ufl_element()
)
rightwall = MovingSideWall(
0, sigma, h0, eps, right_coordinate_map, counter_right, element=V.ufl_element()
)
noslip = Constant(0.0)
# Store expressions
NS_expressions["movingwall"] = movingwall
NS_expressions["leftwall"] = leftwall
NS_expressions["rightwall"] = rightwall
NS_expressions["bottom"] = noslip
# Velocity
bcu_movingwall_x = DirichletBC(V, noslip, boundary, 1)
bcu_movingwall_y = DirichletBC(V, movingwall, boundary, 1)
bcu_leftwall_x = DirichletBC(V, noslip, boundary, 2)
bcu_leftwall_y = DirichletBC(V, leftwall, boundary, 2)
bcu_bottomwall = DirichletBC(V, noslip, boundary, 3)
# Pressure
bcp_out = DirichletBC(Q, Constant(0), boundary, 4)
bcs = dict((ui, []) for ui in sys_comp)
bcs["u0"] = [bcu_movingwall_x, bcu_leftwall_x]
bcs["u1"] = [bcu_movingwall_y, bcu_bottomwall, bcu_leftwall_y]
bcs["p"] = [bcp_out]
return bcs
[docs]def pre_solve_hook(
V, mesh, newfolder, velocity_degree, u_components, boundary, **NS_namespace
):
# Visualization files
viz_p, viz_u = get_visualization_writers(newfolder, ["pressure", "velocity"])
# Extract dof map and coordinates
VV = VectorFunctionSpace(mesh, "CG", velocity_degree)
u_vec = Function(VV, name="Velocity")
coordinates = mesh.coordinates()
if velocity_degree == 1:
dof_map = vertex_to_dof_map(V)
else:
dof_map = V.dofmap().entity_dofs(mesh, 0)
# Mesh velocity conditions
noslip = Constant(0.0)
bcu_walls_x = DirichletBC(V, noslip, boundary, 1)
bcu_walls_y = DirichletBC(V, NS_expressions["movingwall"], boundary, 1)
bc_left_wall_x = DirichletBC(V, noslip, boundary, 2)
bc_left_wall_y = DirichletBC(V, NS_expressions["leftwall"], boundary, 2)
bc_bottom_wall = DirichletBC(V, noslip, boundary, 3)
bc_out_x = DirichletBC(V, noslip, boundary, 4)
bc_out_y = DirichletBC(V, NS_expressions["rightwall"], boundary, 4)
bc_mesh = dict((ui, []) for ui in u_components)
rigid_bc_x = [bc_bottom_wall, bc_out_x]
rigid_bc_y = [bc_bottom_wall, bc_out_y]
bc_mesh["u0"] = [bcu_walls_x, bc_left_wall_x] + rigid_bc_x
bc_mesh["u1"] = [bcu_walls_y, bc_left_wall_y] + rigid_bc_y
return dict(
viz_p=viz_p,
viz_u=viz_u,
u_vec=u_vec,
bc_mesh=bc_mesh,
dof_map=dof_map,
coordinates=coordinates,
)
[docs]class MovingWall(UserExpression):
def __init__(self, t, sigma, h0, eps, **kwargs):
self.t = t
self.sigma = sigma
self.h0 = h0
self.eps = eps
self.value = 0
super().__init__(**kwargs)
[docs] def update(self, t):
"""
Sinusidal wall motion from [1]
[1] Chnafa, C. (2014). Using image-based large-eddy simulations to investigate the intracardiac flow and its
turbulent nature (Doctoral dissertation, Université Montpellier II-Sciences et Techniques du Languedoc).
"""
self.t = t
self.value = -self.sigma * self.h0 * self.eps * np.sin(self.sigma * t)
[docs] def eval(self, value, _):
value[:] = self.value
[docs]class MovingSideWall(UserExpression):
def __init__(self, t, sigma, h0, eps, x_hat_map, counter_max, **kwargs):
self.t = t
self.map = x_hat_map
self.max = counter_max
self.counter = -1
self.sigma = sigma
self.h0 = h0
self.eps = eps
self.value = 0
super().__init__(**kwargs)
[docs] def update(self, t):
self.t = t
[docs] def eval(self, values, _, **kwargs):
self.counter += 1
scaling = self.map[self.counter][1] / (self.h0 * (1 + self.eps))
values[:] = (
-scaling * self.sigma * self.h0 * self.eps * np.sin(self.sigma * self.t)
)
if self.counter == self.max:
self.counter = -1
[docs]def update_boundary_conditions(t, NS_expressions, **NS_namespace):
# Update time
for key, value in NS_expressions.items():
if "wall" in key:
value.update(t)
[docs]def temporal_hook(
mesh,
dt,
h0,
eps,
nu,
t,
tstep,
save_solution_frequency,
p_,
u_,
viz_u,
viz_p,
u_vec,
**NS_namespace
):
# Write solution at time t
if tstep % save_solution_frequency == 0:
assign(u_vec.sub(0), u_[0])
assign(u_vec.sub(1), u_[1])
viz_u.write(u_vec, t)
viz_p.write(p_, t)
# Compute mean velocity and Reynolds number at inlet
if tstep % 10 == 0:
h = mesh.hmin()
L = h0 * (1 + eps)
compute_flow_quantities(
u_,
L,
nu,
mesh,
t,
tstep,
dt,
h,
outlet_area=1,
boundary=None,
outlet_ids=[],
inlet_ids=[],
id_wall=0,
period=1.0,
newfolder=None,
dynamic_mesh=False,
write_to_file=False,
)