def __init__(self, mesh, bndry, interface, v_max, lambda_s, mu_s, rho_s, mu_f, rho_f, t_end, time_discretization, dt_atol, dt_rtol, result, *args, **kwargs): """ Write boundary conditions, equations and create the files for solution. """ #info("Flow initialization.") self.mesh = mesh self.t = 0.0 self.v_max = v_max self.mu_f = mu_f self.rho_f = rho_f self.lambda_s = lambda_s self.mu_s = mu_s self.rho_s = rho_s self.bndry = bndry self.interface = interface # bounding box tree self.bb = BoundingBoxTree() self.bb.build(self.mesh) # Define finite elements eV = VectorElement("CG", mesh.ufl_cell(), 2) # velocity element eB = VectorElement("Bubble", mesh.ufl_cell(), mesh.geometry().dim() + 1) # Bubble element eU = VectorElement("CG", mesh.ufl_cell(), 2) # displacement element eP = FiniteElement("DG", mesh.ufl_cell(), 1) # pressure element eW = MixedElement([eV, eB, eU, eB, eP]) # final mixed element W = FunctionSpace(self.mesh, eW) # mixed function space self.W = W self.V = FunctionSpace(self.mesh, eV) # 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, _CIRCLE) bc_u_in = DirichletBC(self.W.sub(2), Constant((0.0, 0.0)), bndry, _INFLOW) bc_u_circle = DirichletBC(self.W.sub(2), Constant((0.0, 0.0)), bndry, _CIRCLE) #bc_u_walls = DirichletBC(self.W.sub(2), Constant((0.0, 0.0)), bndry, _WALLS) bc_u_walls = DirichletBC( self.W.sub(2).sub(1), Constant(0.0), bndry, _WALLS) bc_u_out = DirichletBC(self.W.sub(2), 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("Mesh BC.") bc_mesh = DirichletBC(self.W.sub(2), Constant((0.0, 0.0)), interface, _FSI) self.bcs_mesh = [bc_mesh] #info("Normal and Circumradius.") self.n = FacetNormal(self.mesh) self.h = Circumradius(self.mesh) I = Identity(self.W.mesh().geometry().dim()) # Define functions self.w = Function(self.W) self.wdot = Function(self.W) (v__, bv_, u__, bu_, p_) = TestFunctions(self.W) v_ = v__ + bv_ u_ = u__ + bu_ (v, bv, u, bu, self.p) = split(self.w) self.v = v + bv self.u = u + bu (vdot, bvdot, udot, budot, self.pdot) = split(self.wdot) self.vdot = vdot + bvdot self.udot = udot + budot # define deformation gradient, Jacobian self.FF = I + grad(self.u) self.JJ = det(self.FF) # write ALE mesh movement self.gamma = 9.0 / 8.0 h = CellVolume(self.mesh)**(self.gamma) E = Constant(1.0) E_mesh = E / h nu_mesh = Constant(-0.02) mu_mesh = E_mesh / (2 * (1.0 + nu_mesh)) lambda_mesh = (nu_mesh * E_mesh) / ((1 + nu_mesh) * (1 - 2 * nu_mesh)) 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): return dot(grad(f), inv(self.FF)) def Div(f): return tr(Grad(f)) # all dx integrals in the reference should have self.JJ*dx # compute Cauchy stress tensor for fluid self.T_f = -self.p * I + 2 * self.mu_f * sym(Grad(self.v)) # compute 1st Piola-Kirchhoff tensor for fluid self.S_f = self.JJ * (self.T_f) * inv(self.FF).T # write equations for fluid a_fluid = inner(self.T_f , Grad(v_))*self.JJ*dx(0) \ + inner(self.rho_f*Grad(self.v )*(self.v - self.udot), v_)*self.JJ*dx(0) b_fluid = inner(Div(self.v), p_) * self.JJ * dx(0) self.F_fluid = self.rho_f*inner(self.vdot, v_)*self.JJ*dx(0) \ + (a_fluid + b_fluid) \ + F_mesh # compute 1st Piola-Kirchhoff tensor for solid (St. Vennant - Kirchhoff model) B_s = self.FF.T * self.FF self.S_s = self.FF * (0.5 * self.lambda_s * tr(B_s - I) * I + self.mu_s * (B_s - I)) # write equation for solid alpha = Constant(1.0) # Constant(1e10) # self.F_solid = rho_s*inner(self.vdot, v_)*dx(1) \ + inner(self.S_s , grad(v_))*dx(1) \ + alpha*inner(self.udot - self.v, u_)*dx(1) self.dF_fluid_u = derivative(self.F_fluid, self.w) self.dF_fluid_udot = derivative(self.F_fluid, self.wdot) self.dF_solid_u = derivative(self.F_solid, self.w) self.dF_solid_udot = derivative(self.F_solid, self.wdot) self.problem = TS.Problem(self.F_fluid, self.F_solid, self.w, self.wdot, self.bcs_mesh, self.bcs, self.dF_fluid_u, self.dF_fluid_udot, self.dF_solid_u, self.dF_solid_udot, update=self.update, report=self.report, form_compiler_parameters=ffc_opts) self.solver = TS.Solver(self.problem, tag) self.solver.ts.setProblemType(self.solver.ts.ProblemType.NONLINEAR) self.solver.ts.setEquationType( self.solver.ts.EquationType.DAE_IMPLICIT_INDEX2) if time_discretization == 'BDF': self.solver.ts.setType( self.solver.ts.Type.BDF ) #BDF #ALPHA #THETA #CN #ARKIMEX #ROSW #BEULER elif time_discretization == 'CN': self.solver.ts.setType( self.solver.ts.Type.CN ) #BDF #ALPHA #THETA #CN #ARKIMEX #ROSW #BEULER elif time_discretization == 'BEULER': self.solver.ts.setType( self.solver.ts.Type.BEULER ) #BDF #ALPHA #THETA #CN #ARKIMEX #ROSW #BEULER else: raise ValueError('{} is an invalid name for time discretization scheme.'\ .format(time_discretiazation)) #min_step = 1e-02 min_step = 5e-03 # BEULER makes long steps when the bounds for time steps are too loose self.solver.ts.setTime(0.0) self.solver.ts.setMaxTime(t_end) self.solver.ts.setTimeStep(min_step) self.solver.ts.setMaxSteps(80000) self.solver.ts.setMaxStepRejections(-1) self.solver.ts.setMaxSNESFailures( -1 ) # allow an unlimited number of failures (step will be rejected and retried) self.solver.ts.setExactFinalTime(True) self.solver.ksp.setType('preonly') self.solver.pc.setType('lu') self.solver.pc.setFactorSolverPackage( 'mumps') # fenics 2018 (petsc4py 3.8) #self.solver.pc.setFactorSolverType('mumps') # fenics 2019 (petsc4py 3.10) # use adaptive timestep (no petsc4py interface for this) opts['ts_adapt_type'] = 'basic' # 'none' # opts['ts_adapt_dt_min'] = 1e-6 # 0.002 # opts['ts_adapt_dt_max'] = min_step # set the adaptivity control only on velocity and displacement # get the pressure dofs pdofs = W.sub(4).dofmap().dofs() atol = self.problem.A_petsc.createVecRight() rtol = self.problem.A_petsc.createVecRight() atol.set(dt_atol) rtol.set(dt_rtol) atol.setValues(pdofs, [0.0] * len(pdofs)) rtol.setValues(pdofs, [0.0] * len(pdofs)) self.solver.ts.setTolerances(atol, rtol) tag.log("system size {0:d}".format(self.W.dim())) self.ok = False self.its = 0 # create files for saving if my_rank == 0: if not os.path.exists(result): os.makedirs(result) MPI.barrier(comm) 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', 'mean pressure on outflow', 'pressure_jump', 'x-coordinate of end of beam', 'y-coordinate of end of beam', 'pressure difference', 'drag_circle', 'drag_fluid', 'drag_solid', 'drag_fullfluid', 'lift_circle', 'lift_fluid', 'lift_solid', 'lift_fullfluid' ])