Esempio n. 1
0
    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'
            ])