Esempio n. 1
0
coupling_boundary = CouplingBoundary()
remaining_boundary = OuterBoundary()

bcs = [DirichletBC(V, u_D, remaining_boundary)]
# Define initial value
u_n = interpolate(u_D, V)
u_n.rename("Temperature", "")

precice = Adapter(adapter_config_filename,
                  interpolation_strategy=interpolation_strategy)

if problem is ProblemType.DIRICHLET:
    precice_dt = precice.initialize(coupling_subdomain=coupling_boundary,
                                    mesh=mesh,
                                    read_field=u_D_function,
                                    write_field=f_N_function,
                                    u_n=u_n)
elif problem is ProblemType.NEUMANN:
    precice_dt = precice.initialize(coupling_subdomain=coupling_boundary,
                                    mesh=mesh,
                                    read_field=f_N_function,
                                    write_field=u_D_function,
                                    u_n=u_n)

dt = Constant(0)
dt.assign(np.min([fenics_dt, precice_dt]))

# Define variational problem
u = TrialFunction(V)
v = TestFunction(V)
Esempio n. 2
0
class Fluid(object):
    def __init__(self, mesh, coupling_boundary, complementary_boundary, bndry, dt, theta, v_max, 
            mu_f, rho_f, result, *args, **kwargs):

        # initialize meshes and boundaries
        self.mesh  = mesh
        self.coupling_boundary = coupling_boundary
        self.complementary_boundary = complementary_boundary
        bnd_mesh = BoundaryMesh(mesh, 'exterior')
        self.bndry = bndry          # boundary-marking function for integration

        self.fenics_dt  = float(dt)
        self.dt = Constant(dt)
        self.theta = theta
        self.t     = 0.0
        self.v_max = v_max
        self.mu_f     = mu_f
        self.rho_f    = rho_f
        

        # bounding box tree
        self.bb = BoundingBoxTree()
        self.bb.build(self.mesh)

        # Define finite elements
        eV = VectorElement("CG", mesh.ufl_cell(), 2)	    # velocity element
        eU = VectorElement("CG", mesh.ufl_cell(), 2)	    # displacement element
        eP = FiniteElement("CG", mesh.ufl_cell(), 1)	    # pressure element

        eW = MixedElement([eV, eU, eP])                     # mixed element
        W  = FunctionSpace(self.mesh, eW)                   # function space for ALE fluid equation
        self.W = W
        self.U = FunctionSpace(self.mesh, eU)               # function space for projected functions 


        self.W_boundary = VectorFunctionSpace(bnd_mesh, 'CG', 2)    # boundary function space
        self.fun4forces = Function(self.W)                  # function for Variational formulation

        # Set boundary conditions
        self.v_in = Expression(("t<2.0? 0.5*(1.0 - cos(0.5*pi*t))*v_max*4/(gW*gW)*(x[1]*(gW - x[1])): \
                      v_max*4/(gW*gW)*(x[1]*(gW - x[1]))", "0.0"),
                      degree = 2, v_max = Constant(self.v_max), gW = Constant(gW), t = self.t)

        #info("Expression set.")
        bc_v_in     = DirichletBC(self.W.sub(0), self.v_in,            bndry, _INFLOW)
        bc_v_walls  = DirichletBC(self.W.sub(0), Constant((0.0, 0.0)), bndry, _WALLS)
        bc_v_circle = DirichletBC(self.W.sub(0), Constant((0.0, 0.0)), bndry, _FLUID_CYLINDER)
        bc_u_in     = DirichletBC(self.W.sub(1), Constant((0.0, 0.0)), bndry, _INFLOW)
        bc_u_walls  = DirichletBC(self.W.sub(1).sub(1), Constant(0.0), bndry, _WALLS)
        bc_u_circle = DirichletBC(self.W.sub(1), Constant((0.0, 0.0)), bndry, _FLUID_CYLINDER)
        bc_u_out    = DirichletBC(self.W.sub(1), Constant((0.0, 0.0)), bndry, _OUTFLOW)
        self.bcs = [bc_v_in, bc_v_walls, bc_v_circle, bc_u_in, bc_u_walls, bc_u_circle, bc_u_out]


        #info("Normal and Circumradius.")
        self.n = FacetNormal(self.mesh)
        I = Identity(self.W.mesh().geometry().dim())

        # Define functions
        self.w  = Function(self.W)      
        self.w0 = Function(self.W)

        (v_, u_, p_) = TestFunctions(self.W)
        (self.v, self.u, self.p) = split(self.w)
        (self.v0, self.u0, self.p0) = split(self.w0)

        # define coupling BCs
        self.u_D = Constant((0.0, 0.0))                     # Dirichlet BC (for Fluid)
        self.u_D_function = interpolate(self.u_D, self.U)   # Dirichlet BC as function
        self.f_N = Constant((0.0, 0.0))                     # Neumann BC (for Solid)
        self.f_N_function = interpolate(self.f_N, self.U)   # Neumann BC as function

        # start preCICE adapter
        info('Initialize Adapter.')
        self.precice = Adapter()
        # read forces(Neumann condition for solid) and write displacement(Dirichlet condition for fluid)
        info('Call precice.initialize(...).')
        self.precice_dt = self.precice.initialize(
                coupling_subdomain=self.coupling_boundary, mesh=self.mesh, 
                read_field=self.u_D_function, write_field=self.f_N_function, u_n=self.w,#u_n,
                coupling_marker=self.bndry)


        # TODO: need to set DirichletBC for displacement AND velocity
        # functions for displacement BC
        self.u_bc  = self.precice.create_coupling_dirichlet_boundary_condition(self.U)  
        self.u_bc0 = self.u_bc

        dt = self.dt.values()[0]
        self.v_bc = self.u_bc                                   # wrong condition, but code runs
        #self.v_bc = (1.0/self.dt)*(self.u_bc - self.u_bc0)     # I need to do this, but raises error

        bc_u_FSI = DirichletBC(self.W.sub(1), self.u_bc, coupling_boundary)
        bc_v_FSI = DirichletBC(self.W.sub(0), self.v_bc, coupling_boundary)

        self.bcs.append(bc_u_FSI)
        self.bcs.append(bc_v_FSI)

        # define deformation gradient, Jacobian
        self.FF  = I + grad(self.u)
        self.FF0 = I + grad(self.u0)
        self.JJ  = det(self.FF)
        self.JJ0 = det(self.FF0)

        # mesh-moving eqaution (pseudoelasticity)
        self.gamma = 9.0/8.0
        h = CellVolume(self.mesh)**(self.gamma)     # makes mesh stiffer when mesh finer 
                                                    # (our mesh is finer by the FSI -moving- interface)
        E = Constant(1.0)

        E_mesh = E/h                                                    # Young Modulus
        nu_mesh = Constant(-0.02)                                       # Poisson ratio
        mu_mesh = E_mesh/(2*(1.0+nu_mesh))                              # Lame 1
        lambda_mesh = (nu_mesh*E_mesh)/((1+nu_mesh)*(1-2*nu_mesh))      # Lame 2

        # variational formulation for mesh motion
        F_mesh = inner(mu_mesh*2*sym(grad(self.u)), grad(u_))*dx(0) \
                + lambda_mesh*inner(div(self.u), div(u_))*dx(0)


        # define referential Grad and Div shortcuts
        def Grad(f, F): return dot( grad(f), inv(F) )
        def Div(f, F): return tr( Grad(f, F) )

        # approximate time derivatives
        du = (1.0/self.dt)*(self.u - self.u0)
        dv = (1.0/self.dt)*(self.v - self.v0)

        # compute velocuty part of Cauchy stress tensor for fluid
        self.T_f  = -self.p*I + 2*self.mu_f*sym(Grad(self.v,  self.FF))
        self.T_f0 = -self.p*I + 2*self.mu_f*sym(Grad(self.v0, self.FF0))
        # compute 1st Piola-Kirchhoff tensor for fluid
        self.S_f  = self.JJ *( -self.p*I + self.T_f )*inv(self.FF).T
        self.S_f0 = -self.JJ*self.p*I*inv(self.FF).T \
                     + self.JJ0*self.T_f0*inv(self.FF0).T

        # write equations for fluid
        a_fluid  = inner(self.S_f , Grad(v_, self.FF))*self.JJ*dx \
               + inner(self.rho_f*Grad(self.v, self.FF )*(self.v  - du), v_)*self.JJ*dx
        a_fluid0 = inner(self.S_f0, Grad(v_, self.FF0))*self.JJ0*dx \
               + inner(self.rho_f*Grad(self.v0, self.FF0)*(self.v0 - du), v_)*self.JJ0*dx

        b_fluid  = inner(Div( self.v, self.FF ), p_)*self.JJ*dx
        b_fluid0 = inner(Div( self.v, self.FF ), p_)*self.JJ*dx

        # final variationl formulation
        self.F_fluid  = (self.theta*self.JJ+(1.0 - self.theta)*self.JJ0)*self.rho_f*inner(dv, v_)*dx\
                   + self.theta*(a_fluid + b_fluid) + (1.0 - self.theta)*(a_fluid0 + b_fluid0) \
                   + F_mesh

        # differentiate w.r.t. unknown
        dF_fluid = derivative(self.F_fluid, self.w)

        # define problem and its solver
        self.problem = NonlinearVariationalProblem(self.F_fluid, self.w, bcs=self.bcs, J=dF_fluid)
        self.solver  = NonlinearVariationalSolver(self.problem)

        # Variational problem for extracting forces
        (v, u, p) = TrialFunctions(self.W)
        self.traction = self.F_fluid - inner(v, v_)*ds(_FSI)
        self.hBC = DirichletBC(self.W.sub(0), Constant((0.0, 0.0)), self.complementary_boundary)


        # configure solver parameters
        self.solver.parameters['newton_solver']['relative_tolerance'] = 1e-6
        self.solver.parameters['newton_solver']['absolute_tolerance'] = 1e-10
        self.solver.parameters['newton_solver']['maximum_iterations'] = 50
        self.solver.parameters['newton_solver']['linear_solver']      = 'mumps'

        # create files for saving
        if my_rank == 0:
            if not os.path.exists(result):
                os.makedirs(result)
        self.vfile = XDMFFile("%s/velocity.xdmf" % result)
        self.ufile = XDMFFile("%s/displacement.xdmf" % result)
        self.pfile = XDMFFile("%s/pressure.xdmf" % result)
        self.sfile = XDMFFile("%s/stress.xdmf" % result)
        self.vfile.parameters["flush_output"] = True
        self.ufile.parameters["flush_output"] = True
        self.pfile.parameters["flush_output"] = True
        self.sfile.parameters["flush_output"] = True
        with open(result+'/data.csv', 'w') as data_file:
            writer = csv.writer(data_file, delimiter=';', lineterminator='\n')
            writer.writerow(['time',
                              'x-coordinate of end of beam', 'y-coordinate of end of beam',
                              'drag', 'lift'])

        #info("Fluid __init__ done")

    def solve(self, t, n):
        self.t = t
        self.v_in.t = t
        self.dt.assign(np.min([self.fenics_dt, self.precice_dt]))
        # solve fluid equations
        self.solver.solve()

        # force-extracting procedure
        ABdry = assemble(lhs(self.traction),keep_diagonal=True)
        bBdry = assemble(rhs(self.traction))
        self.hBC.apply(ABdry, bBdry)
        solve(ABdry, self.fun4forces.vector(), bBdry)
        self.fun4forces.set_allow_extrapolation(True)
        (self.forces, _, _) = split(self.fun4forces)
        self.forces = project(self.forces, self.U)

        forces_for_solid = interpolate(self.forces, self.W_boundary) 

        # precice coupling step
        t, n, precice_timestep_complete, self.precice_dt \
                = self.precice.advance(forces_for_solid, self.w, self.w0, t, self.dt.values()[0], n)

        return t, n, precice_timestep_complete

    def save(self, t):
        (v, u, p) = self.w.split()

        # save functions to files
        v.rename("v", "velocity")
        u.rename("u", "displacement")
        p.rename("p", "pressure")
        self.vfile.write(v, t)
        self.ufile.write(u, t)
        self.pfile.write(p, t)

        # Compute drag and lift
        w_ = Function(self.W)
        Fbc1 = DirichletBC(self.W.sub(0), Constant((1.0, 0.0)), self.bndry, _FLUID_CYLINDER)
        Fbc2 = DirichletBC(self.W.sub(0), Constant((1.0, 0.0)), self.bndry, _FSI)
        Fbc1.apply(w_.vector())
        Fbc2.apply(w_.vector())
        drag = -assemble(action(self.F_fluid,w_))
        w_ = Function(self.W)
        Fbc1 = DirichletBC(self.W.sub(0), Constant((0.0, 1.0)), self.bndry, _FLUID_CYLINDER)
        Fbc2 = DirichletBC(self.W.sub(0), Constant((0.0, 1.0)), self.bndry, _FSI)
        Fbc1.apply(w_.vector())
        Fbc2.apply(w_.vector())
        lift = -assemble(action(self.F_fluid,w_))

        # MPI trick to extract beam displacement
        self.w.set_allow_extrapolation(True)
        Ax_loc = self.u[0]((A.x(), A.y()))
        Ay_loc = self.u[1]((A.x(), A.y()))
        self.w.set_allow_extrapolation(False)
        
        pi = 0
        if self.bb.compute_first_collision(A) < 4294967295:
            pi = 1
        else:
            Ax_loc = 0.0
            Ay_loc = 0.0
        Ax = MPI.sum(comm, Ax_loc) / MPI.sum(comm, pi)
        Ay = MPI.sum(comm, Ay_loc) / MPI.sum(comm, pi)

        pi = 0
        if self.bb.compute_first_collision(B) < 4294967295:
            pi = 1
        else:
            pB_loc = 0.0
        pB = MPI.sum(comm, pB_loc) / MPI.sum(comm, pi)
        p_diff = pB - pA

        # write data to data file
        if my_rank == 0:
            with open(result+'/data.csv', 'a') as data_file:
                writer = csv.writer(data_file, delimiter=';', lineterminator='\n')
                writer.writerow([t, Ax, Ay, drag, lift])
Esempio n. 3
0
class Solid(object):
    """
    Solid is defined as a object for which the subroutines solve() and save() are called externally in the time loop.
    """
    def __init__(self, mesh, coupling_boundary, complementary_boundary, bndry,
                 dt, theta, lambda_s, mu_s, rho_s, result, *args, **kwargs):

        self.mesh = mesh
        self.coupling_boundary = coupling_boundary
        self.complementary_boundary = complementary_boundary

        self.fenics_dt = dt
        self.dt = Constant(dt)
        self.theta = theta

        self.lambda_s = lambda_s
        self.mu_s = mu_s
        self.rho_s = rho_s

        self.bndry = bndry

        # bounding box tree - in parallel computations takes care about proper parallelization
        self.bb = BoundingBoxTree()
        self.bb.build(self.mesh)

        # Define finite elements
        eV = VectorElement("CG", mesh.ufl_cell(), 2)  # velocity space
        eU = VectorElement("CG", mesh.ufl_cell(), 2)  # displacement space

        eW = MixedElement([eV, eU])  # function space
        W = FunctionSpace(self.mesh, eW)
        self.W = W
        self.U = FunctionSpace(self.mesh,
                               eU)  # function space for exchanging BCs

        # define Dirichlet boundary conditions
        bc_u = DirichletBC(self.W.sub(1), Constant((0.0, 0.0)),
                           self.complementary_boundary)
        bc_v = DirichletBC(self.W.sub(0), Constant((0.0, 0.0)),
                           self.complementary_boundary)
        self.bcs = [bc_v, bc_u]

        # define normal
        self.n = FacetNormal(self.mesh)
        # define Identity
        I = Identity(self.W.mesh().geometry().dim())

        # Define functions
        self.w = Function(self.W)  # solution in current time step
        self.w0 = Function(self.W)  # solution from previous time step

        (v_, u_) = TestFunctions(self.W)  # test functions
        # split mixed functions into velocity (v) and displacement part (u)
        (self.v, self.u) = split(self.w)
        (self.v0, self.u0) = split(self.w0)

        # define coupling BCs
        self.u_D = Constant((0.0, 0.0))  # Dirichlet BC (for Fluid)
        self.u_D_function = interpolate(self.u_D,
                                        self.U)  # Dirichlet BC as function
        self.f_N = Constant((0.0, 0.0))  # Neumann BC (for Solid)
        self.f_N_function = interpolate(self.f_N,
                                        self.U)  # Neumann BC as function

        # initial value of Dirichlet BC
        self.u_n = interpolate(self.u_D, self.U)

        # create self.displacement function for exchanging BCs
        self.displacement = Function(self.U)
        self.displacement.assign(project(self.u, self.U))
        self.displacement.rename("Displacement", "")

        # start preCICE adapter
        info('Initialize Adapter.')
        self.precice = Adapter()
        # read forces(Neumann condition for solid) and write displacement(Dirichlet condition for fluid)
        info('Call precice.initialize(...).')
        self.precice_dt = self.precice.initialize(
            coupling_subdomain=self.coupling_boundary,
            mesh=self.mesh,
            read_field=self.f_N_function,
            write_field=self.u_D_function,
            u_n=self.w,  #u_n,
            coupling_marker=self.bndry)

        # choose time step
        self.dt.assign(np.min([self.precice_dt, self.fenics_dt]))

        # define deformation gradient
        self.FF = I + grad(self.u)
        self.FF0 = I + grad(self.u0)

        # approximate time derivatives
        du = (1.0 / self.dt) * (self.u - self.u0)
        dv = (1.0 / self.dt) * (self.v - self.v0)

        # write Green-St. Venant strain tensor
        E_s = 0.5 * (self.FF.T * self.FF - I)
        E_s0 = 0.5 * (self.FF0.T * self.FF0 - I)

        # compute 1st Piola-Kirchhoff tensor for solid (St. Venant - Kirchhoff model)
        S_s = self.FF * (self.lambda_s * tr(E_s) * I + 2.0 * self.mu_s * (E_s))
        S_s0 = self.FF0 * (self.lambda_s * tr(E_s0) * I + 2.0 * self.mu_s *
                           (E_s0))

        #delta_W = 0.01
        alpha = Constant(1.0)  # Constant(1.0/delta_W) # Constant(1000)
        # get surface forces from precice
        self.f_surface = alpha * self.precice.create_coupling_neumann_boundary_condition(
            v_, 1, self.U)
        #self.f_surface = Function(self.U)

        # write equation for solid
        self.F_solid = rho_s*inner(dv, v_)*dx \
                   + self.theta*inner(S_s , grad(v_))*dx \
                   + (1.0 - self.theta)*inner(S_s0, grad(v_))*dx \
                   + inner(du - (self.theta*self.v + (1.0 - self.theta)*self.v0), u_)*dx \
                   - inner(self.f_surface, v_)*dss(1)

        # apply Neumann boundary condition on coupling interface
        #self.F_solid += self.precice.create_coupling_neumann_boundary_condition(v_, 1, self.U)

        # differentiate equation for solid for the Newton scheme
        self.dF_solid = derivative(self.F_solid, self.w)

        # define Nonlinear Variational problem and Newton solver
        self.problem = NonlinearVariationalProblem(self.F_solid,
                                                   self.w,
                                                   bcs=self.bcs,
                                                   J=self.dF_solid)
        self.solver = NonlinearVariationalSolver(self.problem)

        # configure solver parameters
        self.solver.parameters['newton_solver']['relative_tolerance'] = 1e-6
        self.solver.parameters['newton_solver']['absolute_tolerance'] = 1e-10
        self.solver.parameters['newton_solver']['maximum_iterations'] = 50
        self.solver.parameters['newton_solver']['linear_solver'] = 'mumps'

        # create files for saving
        if not os.path.exists(result):
            os.makedirs(result)
        self.vfile = XDMFFile("%s/velocity.xdmf" % result)
        self.ufile = XDMFFile("%s/displacement.xdmf" % result)
        self.sfile = XDMFFile("%s/stress.xdmf" % result)
        #self.ffile = XDMFFile("%s/forces.xdmf" % result)
        self.vfile.parameters["flush_output"] = True
        self.ufile.parameters["flush_output"] = True
        self.sfile.parameters["flush_output"] = True
        #self.ffile.parameters["flush_output"] = True
        with open(result + '/data.csv', 'w') as data_file:
            writer = csv.writer(data_file, delimiter=';', lineterminator='\n')
            writer.writerow(
                ['time', 'Ax_displacement', 'Ay_displacement', 'lift', 'drag'])
        info("Solid __init__ done")

    def solve(self, t, n):
        self.t = t
        self.dt.assign(np.min([self.fenics_dt, self.precice_dt]))

        # solve problem
        self.solver.solve()

        # extract velocity v and displacement u from mixed vector w
        (v, u) = self.w.split()

        # update displacement (Dirichlet BC for fluid)
        self.displacement.assign(project(u, self.U))
        #self.displacement0.assign(self.displacement)
        #self.displacement.assign(project(self.u, self.U))

        # precice coupling step
        t, n, precice_timestep_complete, self.precice_dt \
                = self.precice.advance(self.displacement, self.w, self.w0,#self.displacement, self.displacement0, 
                        t, self.dt.values()[0], n)

        return t, n, precice_timestep_complete

    def save(self, t):
        (v, u) = self.w.split()

        # save velocity and displacement
        v.rename("v", "velocity")
        u.rename("u", "displacement")
        self.vfile.write(v, t)
        self.ufile.write(u, t)
        #self.ffile.write(self.f_surface, t)

        # extract values of displacament in point A = (0.6, 0.2)
        self.w.set_allow_extrapolation(True)
        Ax_loc = self.u[0]((A.x(), A.y()))
        Ay_loc = self.u[1]((A.x(), A.y()))
        self.w.set_allow_extrapolation(False)

        # MPI trick to extract nodal values in case of more processes
        pi = 0
        if self.bb.compute_first_collision(A) < 4294967295:
            pi = 1
        else:
            Ax_loc = 0.0
            Ay_loc = 0.0
        Ax = MPI.sum(comm, Ax_loc) / MPI.sum(comm, pi)
        Ay = MPI.sum(comm, Ay_loc) / MPI.sum(comm, pi)

        # evaluate forces exerted by the fluid (lift and drag)
        drag = -assemble(self.f_surface[0] * dss(1))
        lift = -assemble(self.f_surface[1] * dss(1))

        # write displacement and forces to file
        with open(result + '/data.csv', 'a') as data_file:
            writer = csv.writer(data_file, delimiter=';', lineterminator='\n')
            writer.writerow([t, Ax, Ay, lift, drag])

        info(' Ax: {}\n Ay: {} \n lift: {} \n drag: {}'.format(
            Ax, Ay, lift, drag))
Esempio n. 4
0
u_D_function = interpolate(u_D, V)
# Define flux in x direction on coupling interface (grad(u_D) in normal direction)
f_N = Constant('0')
f_N_function = interpolate(f_N, V)

coupling_boundary = TopBoundary()
bottom_boundary = BottomBoundary()

# Define initial value
u_n = interpolate(u_D, V)
u_n.rename("T", "")

# Adapter definition and initialization
precice = Adapter(adapter_config_filename="precice-adapter-config.json")

precice_dt = precice.initialize(coupling_boundary, mesh, V)

# Create a FEniCS Expression to define and control the coupling boundary values
coupling_expression = precice.create_coupling_expression()

# Assigning appropriate dt
dt = Constant(0)
dt.assign(np.min([fenics_dt, precice_dt]))

# Define variational problem
u = TrialFunction(V)
v = TestFunction(V)
F = u * v / dt * dx + alpha * dot(grad(u), grad(v)) * dx - u_n * v / dt * dx

# apply constant Dirichlet boundary condition at bottom edge
# apply Dirichlet boundary condition on coupling interface
Esempio n. 5
0
# get the adapter ready

# read fenics-adapter json-config-file)
adapter_config_filename = "precice-adapter-config-fsi-s.json"

# create Adapter
precice = Adapter(adapter_config_filename)

# create subdomains used by the adapter
clamped_boundary_domain = AutoSubDomain(left_boundary)
force_boundary = AutoSubDomain(remaining_boundary)

precice_dt = precice.initialize(coupling_subdomain=coupling_boundary,
                                mesh=mesh,
                                read_field=f_N_function,
                                write_field=u_function,
                                u_n=u_n,
                                dimension=dim,
                                dirichlet_boundary=clamped_boundary_domain)

fenics_dt = precice_dt  # if fenics_dt == precice_dt, no subcycling is applied
#fenics_dt = 0.02  # if fenics_dt < precice_dt, subcycling is applied
dt = Constant(np.min([precice_dt, fenics_dt]))

# generalized alpha method (time stepping) parameters
alpha_m = Constant(0.2)
alpha_f = Constant(0.4)
gamma = Constant(0.5 + alpha_f - alpha_m)
beta = Constant((gamma + 0.5)**2 * 0.25)

# clamp (u == 0) the beam at the left
Esempio n. 6
0
# Function to calculate displacement Deltas
u_delta = Function(V)
u_ref = Function(V)

f_N_function = interpolate(Expression(("1", "0"), degree=1), V)
u_function = interpolate(Expression(("0", "0"), degree=1), V)

coupling_boundary = AutoSubDomain(neumann_boundary)
fixed_boundary = AutoSubDomain(clamped_boundary)

precice = Adapter(adapter_config_filename="precice-adapter-config-fsi-s.json")

# Initialize the coupling interface
precice_dt = precice.initialize(coupling_boundary,
                                read_function_space=V,
                                write_object=V,
                                fixed_boundary=fixed_boundary)

fenics_dt = precice_dt  # if fenics_dt == precice_dt, no subcycling is applied
# fenics_dt = 0.02  # if fenics_dt < precice_dt, subcycling is applied
dt = Constant(np.min([precice_dt, fenics_dt]))

# clamp the beam at the bottom
bc = DirichletBC(V, Constant((0, 0)), fixed_boundary)

# alpha method parameters
alpha_m = Constant(0.2)
alpha_f = Constant(0.4)
gamma = Constant(0.5 + alpha_f - alpha_m)
beta = Constant((gamma + 0.5)**2 / 4.)