Пример #1
0
    def __init__(self,problem,params = fsinewton_params):
        
        timings.startnext("Fsi Newton Solver init")
        info_blue("Initializing FSI Newton Solver")
        info("Using params \n" + str(params) )
        #Initialize base class
        ccom.CBCSolver.__init__(self)
        self.problem = problem
        self.params = params
        #Define the various helper objects of the fsinewton solver
        self.spaces = FSISpaces(problem,params)
        self.fsibc = FSIBC(problem,self.spaces)

        #Define mixed Functions
        self.U0 = self.__initial_state()
        self.U1 = Function(self.spaces.fsispace)

        #Define Subfunction references to the mixed functions
        (self.U1_F,self.P1_F,self.L1_U,self.D1_S,self.U1_S,self.D1_F,self.L1_D) = self.U1.split()
        (self.U0_F,self.P0_F,self.L0_U,self.D0_S,self.U0_S,self.D0_F,self.L0_D) = self.U0.split()

        #Define list tensor references to the mixed funtions
        self.U1list = self.spaces.unpack_function(self.U1)
        self.U0list = self.spaces.unpack_function(self.U0)

        #Define Mixed Trial and Test Functions
        self.IU = TrialFunctions(self.spaces.fsispace)
        self.V = TestFunctions(self.spaces.fsispace)

        #Define time relevant variables
        self.dt = self.problem.initial_step()
        self.kn = Constant(self.dt)
        self.t = 0.0
        
        #Define Time Descretized Functions
        self.Umid,self.Udot = self.time_discreteU(self.U1list,self.U0list,self.kn)
        self.IUmid,self.IUdot = self.time_discreteI(self.IU,self.kn)
        
        #Initialize any Body forces if present
        self.__init_forces()

        #Define Forms and buffered part of the jacobian matrix.
        self.r,self.j,self.j_buff = self.create_forms()
        self.runtimedata = FsiRunTimeData(self)
        timings.stop("Fsi Newton Solver init")
Пример #2
0
class FSINewtonSolver(ccom.CBCSolver):
    """A Monolithic Newton Solver for FSI problems"""
    def __init__(self,problem,params = fsinewton_params):
        
        timings.startnext("Fsi Newton Solver init")
        info_blue("Initializing FSI Newton Solver")
        info("Using params \n" + str(params) )
        #Initialize base class
        ccom.CBCSolver.__init__(self)
        self.problem = problem
        self.params = params
        #Define the various helper objects of the fsinewton solver
        self.spaces = FSISpaces(problem,params)
        self.fsibc = FSIBC(problem,self.spaces)

        #Define mixed Functions
        self.U0 = self.__initial_state()
        self.U1 = Function(self.spaces.fsispace)

        #Define Subfunction references to the mixed functions
        (self.U1_F,self.P1_F,self.L1_U,self.D1_S,self.U1_S,self.D1_F,self.L1_D) = self.U1.split()
        (self.U0_F,self.P0_F,self.L0_U,self.D0_S,self.U0_S,self.D0_F,self.L0_D) = self.U0.split()

        #Define list tensor references to the mixed funtions
        self.U1list = self.spaces.unpack_function(self.U1)
        self.U0list = self.spaces.unpack_function(self.U0)

        #Define Mixed Trial and Test Functions
        self.IU = TrialFunctions(self.spaces.fsispace)
        self.V = TestFunctions(self.spaces.fsispace)

        #Define time relevant variables
        self.dt = self.problem.initial_step()
        self.kn = Constant(self.dt)
        self.t = 0.0
        
        #Define Time Descretized Functions
        self.Umid,self.Udot = self.time_discreteU(self.U1list,self.U0list,self.kn)
        self.IUmid,self.IUdot = self.time_discreteI(self.IU,self.kn)
        
        #Initialize any Body forces if present
        self.__init_forces()

        #Define Forms and buffered part of the jacobian matrix.
        self.r,self.j,self.j_buff = self.create_forms()
        self.runtimedata = FsiRunTimeData(self)
        timings.stop("Fsi Newton Solver init")

    def prepare_solve(self):
        """Setup helper objects for a solve"""        
        
        #Init the plotter if necessary
        if self.params["plot"]:
            self.plotter = FSIPlotter(self.U1)
        #Init Storage
        if self.params["store"] != False:
            self.storage = FSIStorer(self.params["store"])
        
        #Define nonlinear problem for newton solver.
        self.nonlinearproblem = \
        MyNonlinearProblem(self.r, self.U1, self.fsibc.bcallI,
                           self.j,J_buff = self.j_buff,
                           cell_domains = self.problem.meshfunctions["cell"],
                           interior_facet_domains = self.problem.meshfunctions["interiorfacet"],
                           exterior_facet_domains= self.problem.meshfunctions["exteriorfacet"],
                           spaces = self.spaces)
        
        #Create a Newton Solver object
        self.newtonsolver = MyNewtonSolver(self.nonlinearproblem,
                                           itrmax = self.params["newtonitrmax"],
                                           reuse_jacobian = self.params["optimization"]["reuse_jacobian"],
                                           max_reuse_jacobian = self.params["optimization"]["max_reuse_jacobian"],
                                           runtimedata = self.params["runtimedata"]["newtonsolver"],
                                           tol = self.params["newtonsoltol"],
                                           reduce_quadrature =  self.params["optimization"]["reduce_quadrature"] )
         
        info_blue("Newton Solver Tolerance is %s"%self.newtonsolver.tol)
        self.prebuild_jacobians()
        
    def solve(self):
        """Solve the FSI problem over time"""
        self.prepare_solve()
        
        #Time Loop
        info(" ".join(["\n Solving FSI problem",self.problem.__str__() ,"with Newton's method \n"]))
        if self.params["solve"] != False:
            
            #Store the initial value if necessary
            if self.params["store"] != False:
                self.storage.store_solution(self.U0,self.t)
                
            while self.t < self.problem.end_time() - DOLFIN_EPS:
                ret = self.time_step()
                if self.params["store"] != False:
                    self.storage.store_solution(self.U1,self.t)
        self.post_processing()

    def prebuild_jacobians(self):
        """Buffer or assemble jacobians if neccessary"""
        #Build the buffered jacobian if necessary
        if self.params["jacobian"] == "buff":
            self.nonlinearproblem.J_buff = self.assemble_J_buff()
        
        #Prebuild step jacobian if necessary
        if self.params["optimization"]["reuse_jacobian"] == True:
            self.newtonsolver.build_jacobian()

    def assemble_J_buff(self):
        """Assembles the buffered jacobian"""
        info("Assembling Buffered Jacobian")
        timings.startnext("Buffered Jacobian assembly")
        J_buff = assemble(self.j_buff,
                          cell_domains = self.problem.cellfunc,
                          interior_facet_domains = self.problem.fsiboundfunc,
                          exterior_facet_domains = self.problem.extboundfunc)
        timings.stop("Buffered Jacobian assembly")
        return J_buff

    def time_step(self):
        """Newton solve for the values of the FSI system at the next time level"""
        
        #update the body forces
        self.__update_forces(self.t)

        #Update the time
        self.t += self.dt
        info("\n t = %f"%self.t)
        
        #Initial guess is previous time step value
        self.U1.vector()[:] = self.U0.vector()

        #Apply initial guess BC (not homogeneous)
        for bc in self.fsibc.bcallU1_ini:
            bc.apply(self.U1.vector())

        try:
            #Call newton solve and store the last iteration for testing
            self.last_itr = self.newtonsolver.solve(t = self.t)                    
            info("Newton Solver Converged in %i iterations"%(len(self.last_itr)))

            #store fsisolver runtimedata
            if self.params["runtimedata"]["fsisolver"] != False:
                #Save the number of iterations and lagrange multiplier
                #precision for later ploting
                
                self.runtimedata.times.append(self.t)
                self.runtimedata.newtonitr.append(len(self.last_itr))
                self.runtimedata.store_fluid_lm(self.U1_F,self.U1_S,self.spaces.fsimeshcoord)
                self.runtimedata.store_mesh_lm(self.D1_F,self.D1_S,self.spaces.fsimeshcoord)

            #store newtonsolverruntimedata
            if self.params["runtimedata"]["newtonsolver"]:
                self.runtimedata.newtonsolverdata.append(self.newtonsolver.runtimedata)
            
            #Assign the new time step value to U0
            self.U0.vector()[:] = self.U1.vector()

            #Plot if necessary
            if self.params["plot"]:
                self.plotter.plot()
            
        except NewtonConverganceError as NCE:
            #Print some analysis of why convergence failed
            NCE.report()
            raise 

        except NanError as NE:
            #Print analysis of why the Nan happened
            ne.zTOL = 1.0e-5
            print NE.mess
            NE.analysis()
            raise

    def __init_forces(self):
        """Create a list of all present forces"""
        self.G_S = self.problem.structure_boundary_traction_extra()
        self.F_F = self.problem.fluid_body_force()
        self.F_S = self.problem.structure_body_force()
        self.F_M = self.problem.mesh_right_hand_side()
        self.G_F = self.problem.fluid_velocity_neumann_values()
        self.G_F_FSI = self.problem.fluid_fsi_stress()
        self.forces = [f for f in [self.G_S,self.F_F,self.F_S,self.F_M, self.G_F,self.G_F_FSI]\
                       if f is not None and f != []]

    def __update_forces(self,t):
        for f in self.forces:
            f.t = t + self.dt
            
        #Update functions connected to the problem if possible.
        try:
            self.problem.update(t, t + self.dt , self.dt)
        except:
            raise Exception("Update of functions failed")


    def create_forms(self):
        """Generate FSI residual and jacobian"""
        
        #Material Paramter Dictionary
        matparams = {"mu_F": self.problem.fluid_viscosity(),
                     "rho_F": self.problem.fluid_density(),
                     "mu_S": self.problem.structure_mu(),
                     "lmbda_S": self.problem.structure_lmbda(),
                     "rho_S": self.problem.structure_density(),
                     "mu_M": self.problem.mesh_mu(),
                     "lmbda_M": self.problem.mesh_lmbda() }

        info("Using material parameters\n" + str(matparams))

        #Turn the numbers into dolfin constants
        for k in matparams:
            matparams[k] = Constant(matparams[k])
        

        #Normals Dictionary
        normals = {"N_F":FacetNormal(self.problem.fluidmesh), \
                   "N_S":FacetNormal(self.problem.strucmesh)}
        
        #Measures dictionary
        measures = self.problem.measures

        #Forces dictionary
        forces = {"F_F":self.F_F,
                  "F_S":self.F_S,
                  "F_M":self.F_M,
                  "G_S":self.G_S,
                  "G_F":self.G_F,
                  "G_F_FSI":self.G_F_FSI}

        #Define full FSI residual and store block residuals for testing
        r,self.blockresiduals = rf.fsi_residual(self.U1list,self.Umid,self.Udot, 
                                                self.V,matparams,measures,forces,
                                                normals,self.params)
        #Calculcate Jacobian forms
        if self.params["jacobian"] == "auto":
            info("Using Automatic Jacobian")
            j_buff = None
            j = derivative(r,self.U1)
        else:
        #Not Automatic so manual calculation
            j,j_buff =  jfor.fsi_jacobian(self.IU,self.IUdot,self.IUmid,self.U1list,
                        self.Umid,self.Udot,self.V,self.V,
                         matparams,measures,forces,normals,self.params)
            if self.params["jacobian"] == "buff":
                info("Using Buffered Jacobian")
            elif self.params["jacobian"] == "manual":
                info("Using Manual Jacobian")
                j += j_buff 
                j_buff = None
            else:
                raise Exception("only auto, buff, and manual are possible jacobian parameters")
        return r,j,j_buff

    def assemble_J_buff(self):
        """Assembles the buffered jacobian"""
        info("Assembling Buffered Jacobian")
        timings.startnext("Buffered Jacobian assembly")
        J_buff = assemble(self.j_buff,
                          cell_domains = self.problem.meshfunctions["cell"],
                          interior_facet_domains = self.problem.meshfunctions["interiorfacet"],
                          exterior_facet_domains = self.problem.meshfunctions["exteriorfacet"]  )
        timings.stop("Buffered Jacobian assembly")
        return J_buff

    def __initial_state(self):
        "Get the initial state of the fsi system by inserting values from subspace functions"
        info_blue("Creating initial conditions")
        #Generate a zerovector with same dim as mesh
        d = self.problem.singlemesh.topology().dim()
        zerovec = ["0.0" for i in range(d)]

        #Take the initial data in a dictionary in whatever form it may be
        ini_data    = {"U_F":self.problem.fluid_velocity_initial_condition,\
                       "P_F":self.problem.fluid_pressure_initial_condition,\
                       "D_S":self.problem.struc_displacement_initial_condition,\
                       "U_S":self.problem.struc_velocity_initial_condition,\
                       "D_F":self.problem.mesh_displacement_initial_condition}

        spaces = self.spaces.subloc.spaces

        #Try to get all initial data as a dolfin function.
        for funcname in ini_data.keys():
            success = "Initial data created for " + funcname
            fail = "Warning, could not create initial condition for "+funcname+ " default value is 0"
            try:
                ini_data[funcname] = ini_data[funcname]()
                #Try the CBCSolver initial function method
                ini_data[funcname] = self.create_initial_condition(ini_data[funcname],spaces[funcname])
                
                #Try to strings into expressions and interpolate them
                if isinstance(ini_data[funcname], basestring):
                    ini_data[funcname] = interpolate(Expression(ini_data[funcname]),spaces[funcname])
                    info(success)

                #If a list or tuple
                elif type(ini_data[funcname]) == type([]) or type(ini_data[funcname]) == type(()):
                    #First assume there are strings in the tuple already
                    try:
                        ini_data[funcname] = interpolate(Expression(ini_data[funcname]),spaces[funcname])
                        info(success)
                    except:
                        #If this doesn't work interpolate as constant
                        ini_data[funcname] = interpolate(Constant(ini_data[funcname]),spaces[funcname])
                        info(success)
    
                #If already an expression interpolate it.
                elif isinstance(ini_data[funcname],Expression):
                    ini_data[funcname] = interpolate(ini_data[funcname],spaces[funcname])
                    info(success)

                #If a function try to project it.
                elif isinstance(ini_data[funcname],Function):
                    ini_data[funcname] = project(ini_data[funcname],spaces[funcname])
                elif ini_data[funcname] is not None:
                    warning(fail)
            except:
                warning(fail)
                ini_data[funcname] = None
        
        U0 = Function(self.spaces.fsispace)

        #insert the data into U0
        for funcname in ini_data.keys():
            if ini_data[funcname] is not None:
                print funcname
                U0.vector()[self.spaces.subloc.spacebegins[funcname]: \
                            self.spaces.subloc.spaceends[funcname]] = \
                ini_data[funcname].vector()[:]
            
        fsi_dofs = self.spaces.fsidofs["fsispace"]
        cellfunc = self.problem.meshfunctions["cell"]
        strucdomains = self.problem.domainnums["structure"]
        fluiddomains = self.problem.domainnums["fluid"]
        
        #Zero out fluid variables outside of their domain.
        mf.assign_to_region(U0,zerovec,cellfunc,strucdomains,V = self.spaces.V_F,exclude = fsi_dofs)
        mf.assign_to_region(U0,"0.0",cellfunc,strucdomains,V = self.spaces.Q_F,exclude = fsi_dofs)
        mf.assign_to_region(U0,zerovec,cellfunc,strucdomains,V = self.spaces.C_F,exclude = fsi_dofs)

        #Zero out structure variables outside of their domain
        mf.assign_to_region(U0,zerovec,cellfunc,fluiddomains,V = self.spaces.C_S,exclude = fsi_dofs)
        mf.assign_to_region(U0,zerovec,cellfunc,fluiddomains,V = self.spaces.V_S,exclude = fsi_dofs)
        return U0
        
    def time_discreteU(self,U1,U0,kn):
        Umid = tuple([(x+y)*0.5 for x,y in zip(U1,U0)])
        Udot = tuple([(x-y)*(1/kn) for x,y in zip(U1,U0)])  
        return Umid,Udot                                            
        
    def time_discreteI(self,IU,kn):
        IUmid = tuple([x*0.5 for x in IU])
        IUdot = tuple([x/kn for x in IU])  
        return IUmid,IUdot

    def post_processing(self):
        #Write a report of the timings
        info(timings.report_str())
        
        if self.params["runtimedata"]["fsisolver"] != "False":
            if  self.params["bigblue"] == False:
                
                #Create plots with matplotlibs
                self.runtimedata.plot_newtonitr(self.params["runtimedata"]["fsisolver"])
                self.runtimedata.plot_lm(self.params["runtimedata"]["fsisolver"])
                info("Total number of newton iterations is %i"%sum(self.runtimedata.newtonitr))
                
            elif self.params["bigblue"] == True:
                #cPickle data for later plotting 
                self.runtimedata.pickle(self.params["runtimedata"]["fsisolver"])

        if self.params["runtimedata"]["newtonsolver"] != "False":
            if self.params["bigblue"] == False: mode = "plot"
            else:mode = "store"
            
            self.runtimedata.store_newtonsolverdata(path = self.params["runtimedata"]["newtonsolver"],
                                                    mode = mode)