def tail(self, t, it, logger): cc = self.cc pv = self.pv # Get div(v) locally div_v = self.div_v if div_v is not None: div_v.assign( df.project(df.div(pv["v"]), div_v.function_space())) # Compute required functionals keys = ["t", "E_kin", "Psi", "mean_p"] vals = {} vals[keys[0]] = t vals[keys[1]] = df.assemble(0.5 * cc["rho"] * df.inner(pv["v"], pv["v"]) * df.dx) vals[keys[2]] = df.assemble(( 0.25 * cc["a"] * cc["eps"] *\ df.inner(df.dot(cc["LA"], df.grad(pv["phi"])), df.grad(pv["phi"])) + (cc["b"] / cc["eps"]) * cc["F"] ) * df.dx) vals[keys[3]] = df.assemble(pv["p"] * df.dx) if it % self.mod == 0: for key in keys: self.functionals[key].append(vals[key]) # Logging and reporting df.info("") df.begin("Reported functionals:") for key in keys[1:]: desc = "{:6s} = %g".format(key) logger.info(desc, (vals[key], ), (key, ), t) df.end() df.info("")
def _create_coefficients(r_dens, r_visc): c = OrderedDict() c[r"r_dens"] = r_dens c[r"r_visc"] = r_visc # Problem parameters c[r"\rho_1"] = 1.0 c[r"\rho_2"] = r_dens * c[r"\rho_1"] c[r"\nu_1"] = 1.0 c[r"\nu_2"] = r_visc * c[r"\nu_1"] c[r"\eps"] = 0.1 c[r"g_a"] = 1.0 # Characteristic quantities c[r"L_0"] = 1.0 c[r"V_0"] = 1.0 c[r"\rho_0"] = c[r"\rho_1"] df.begin("\nDimensionless numbers") At = (c[r"\rho_1"] - c[r"\rho_2"]) / (c[r"\rho_1"] + c[r"\rho_2"]) Re_1 = c[r"\rho_1"] / c[r"\nu_1"] Re_2 = c[r"\rho_2"] / c[r"\nu_2"] df.info("r_dens = {}".format(r_dens)) df.info("r_visc = {}".format(r_visc)) df.info("Re_1 = {}".format(Re_1)) df.info("Re_2 = {}".format(Re_2)) df.info("At = {}".format(At)) df.end() return c
def _load_from_file(cls, h5name, h5group, comm): df.begin(LogLevel.PROGRESS, "Load mesh from h5 file") geo = load_geometry_from_h5(h5name, h5group, include_sheets=False, comm=comm) df.end() f0 = get_attribute(geo, "f0", "fiber", None) s0 = get_attribute(geo, "s0", "sheet", None) n0 = get_attribute(geo, "n0", "sheet_normal", None) c0 = get_attribute(geo, "c0", "circumferential", None) r0 = get_attribute(geo, "r0", "radial", None) l0 = get_attribute(geo, "l0", "longitudinal", None) vfun = get_attribute(geo, "vfun", None) ffun = get_attribute(geo, "ffun", None) cfun = get_attribute(geo, "cfun", "sfun", None) kwargs = { "mesh": geo.mesh, "markers": geo.markers, "markerfunctions": MarkerFunctions2D(vfun=vfun, ffun=ffun, cfun=cfun), "microstructure": Microstructure(f0=f0, s0=s0, n0=n0), "crl_basis": CRLBasis(c0=c0, r0=r0, l0=l0), } return kwargs
def time_step(self, rhs): dfn.begin("Computing velocity correction") if params['all_steps_adaptive']: self.adaptive_step(rhs) else: [bc.apply(self.A, rhs) for bc in self.bcs] dfn.solve(self.A, self.cur_vel.vector(), rhs, "cg", "amg") dfn.end()
def log(level, msg): """Write a message to the logger. Parameters ---------- level : int Log level of the message. msg : str Log message. """ df.begin(level, msg) df.end()
def copy(self, deepcopy=False): """Returns a copy of self. Parameters ---------- deepcopy : bool True if copy should be deep, default False. """ msg = "Copying geometry" if deepcopy: msg += " with deepcopy." df.begin(LogLevel.DEBUG, msg) cp = self.__class__(**self._copy(deepcopy)) df.end() return cp
def solve(self, flag): """ :param flag : "p" to plot """ nSteps = 20 for i in range(nSteps): try: self.solver.solve() except RuntimeError as message: print message d.end() response = raw_input('Press ENTER to continue ("q" to quit) ') if response == 'q': sys.exit() self.plot(i) self.update_mesh()
def save(self, h5name, h5group="", other_functions=None, other_attributes=None, overwrite_file=False, overwrite_group=True): """Saves a geometry to a h5 file. Parameters ---------- h5name : str The location and name of the output file without file extension. h5group : str The h5 group of the geometry. other_functions : Extra mesh functions. other_attributes : Extra mesh attributes. overwrite_file : bool True if existing file should be overwritten. overwrite_group : bool True if existing group should be overwritten. """ df.begin(LogLevel.PROGRESS, "Saving geometry to {}...".format(h5name)) save_geometry_to_h5( self.mesh, h5name, h5group=h5group, markers=self.markers, markerfunctions=namedtuple_as_dict(self.markerfunctions), microstructure=namedtuple_as_dict(self.microstructure), local_basis=namedtuple_as_dict(self.crl_basis), overwrite_file=overwrite_file, overwrite_group=overwrite_group) df.end()
def check_h5group(h5name, h5group, delete=False, comm=mpi_comm_world): if not isinstance(h5group, str): msg = "Error! h5group has to be of type string. Your h5group is of type {}".format( type(h5group)) raise TypeError(msg) h5group_in_h5file = False if not os.path.isfile(h5name): return False filemode = "a" if delete else "r" if not os.access(h5name, os.W_OK): filemode = "r" if delete: df.begin( df.LogLevel.WARNING, "You do not have write access to file " "{}".format(h5name)) delete = False df.end() with open_h5py(h5name, filemode, comm) as h5file: if h5group in h5file: h5group_in_h5file = True if delete: if parallel_h5py: df.begin( df.LogLevel.DEBUG, "Deleting existing group: " "'{}'".format(h5group)) del h5file[h5group] df.end() else: if df.MPI.rank(comm) == 0: df.begin( df.LogLevel.DEBUG, "Deleting existing group: " "'{}'".format(h5group)) del h5file[h5group] df.end() return h5group_in_h5file
def solve(self): """ Perform one solution step (in time). """ solver = self.data["solver"] begin("Advance-phase") for i, A in enumerate(self.data["A"]["chi"]): b = assemble(self.data["rhs"]["chi"][i]) solver["chi"].solve(A, self.data["sol"]["chi"][i].vector(), b) for i, A in enumerate(self.data["A"]["phi"]): b = assemble(self.data["rhs"]["phi"][i]) solver["phi"].solve(A, self.data["sol"]["phi"][i].vector(), b) end() begin("Pressure step") b = assemble(self.data["rhs"]["p"]) for bc in self.data["bcs"].get("p", []): bc.apply(b) if self._flags["fix_p"]: # Orthogonalize RHS vector b with respect to the null space self.data["null_space"].orthogonalize(b) solver["p"].solve(self.data["A"]["p"], self.data["sol"]["p"].vector(), b) if self._flags["fix_p"]: self._calibrate_pressure(self.data["sol"]["p"], self.data["null_fcn"]) end() begin("Velocity step") for i, A in enumerate(self.data["A"]["v"]): b = assemble(self.data["rhs"]["v"][i]) # FIXME: How to apply bcs in a symmetric fashion? for bc in self.data["bcs"].get("v", []): if bc[i] is not None: bc[i].apply(b) solver["v"].solve(A, self.data["sol"]["v"][i].vector(), b) end()
def test_karman(num_steps=2, lcar=0.1, show=False): mesh = create_mesh(lcar) W_element = VectorElement('Lagrange', mesh.ufl_cell(), 2) P_element = FiniteElement('Lagrange', mesh.ufl_cell(), 1) WP = FunctionSpace(mesh, W_element * P_element) W = WP.sub(0) # P = WP.sub(1) mesh_eps = 1.0e-12 # Define mesh and boundaries. # pylint: disable=no-self-use class LeftBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[0] < x0 + mesh_eps left_boundary = LeftBoundary() class RightBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[0] > x1 - mesh_eps right_boundary = RightBoundary() class LowerBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[1] < y0 + mesh_eps lower_boundary = LowerBoundary() class UpperBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[1] > y1 - mesh_eps upper_boundary = UpperBoundary() class ObstacleBoundary(SubDomain): def inside(self, x, on_boundary): return (on_boundary and x0 + mesh_eps < x[0] < x1 - mesh_eps and y0 + mesh_eps < x[1] < y1 - mesh_eps) obstacle_boundary = ObstacleBoundary() # Boundary conditions for the velocity. # Proper inflow and outflow conditions are a matter of voodoo. See for # example Gresho/Sani, or # # Boundary conditions for open boundaries for the incompressible # Navier-Stokes equation; # B.C.V. Johansson; # J. Comp. Phys. 105, 233-251 (1993). # # The latter in particularly suggest for the inflow: # # u = u0, # d^r v / dx^r = v_r, # div(u) = 0, # # where u and v are the velocities in normal and tangential directions, # respectively, and r\in{0,1,2}. The setting r=0 essentially means to set # (u,v) statically at the left boundary, r=1 means to set u and control # dv/dn, which is what we do here (namely implicitly by dv/dn=0). # At the outflow, # # d^j u / dx^j = 0, # d^q v / dx^q = 0, # p = p0, # # is suggested with j=q+1. Choosing q=0, j=1 means setting the tangential # component of the outflow to 0, and letting the normal component du/dn=0 # (again, this is achieved implicitly by the weak formulation). # inflow = Expression('%e * (%e - x[1]) * (x[1] - %e) / %e' % (entrance_velocity, y1, y0, (0.5 * (y1 - y0))**2), degree=2) outflow = Expression('%e * (%e - x[1]) * (x[1] - %e) / %e' % (entrance_velocity, y1, y0, (0.5 * (y1 - y0))**2), degree=2) u_bcs = [ DirichletBC(W, (0.0, 0.0), upper_boundary), DirichletBC(W, (0.0, 0.0), lower_boundary), DirichletBC(W, (0.0, 0.0), obstacle_boundary), DirichletBC(W.sub(0), inflow, left_boundary), # DirichletBC(W.sub(0), outflow, right_boundary), ] # dudt_bcs = [ # DirichletBC(W, (0.0, 0.0), upper_boundary), # DirichletBC(W, (0.0, 0.0), lower_boundary), # DirichletBC(W, (0.0, 0.0), obstacle_boundary), # DirichletBC(W.sub(0), 0.0, left_boundary), # # DirichletBC(W.sub(1), 0.0, right_boundary), # ] # If there is a penetration boundary (i.e., n.u!=0), then the pressure must # be set somewhere to make sure that the Navier-Stokes problem remains # consistent. # When solving Stokes with no Dirichlet conditions whatsoever, the pressure # tends to 0 at the outlet. This is natural since there, the liquid can # flow out at the rate it needs to be under no pressure at all. # Hence, at outlets, set the pressure to 0. p_bcs = [ # DirichletBC(P, 0.0, right_boundary) ] # Getting vortices is not easy. If we take the actual viscosity of water, # they don't appear. mu = 0.002 # mu = materials.water.dynamic_viscosity(T=293.0) # For starting off, solve the Stokes equation. u0, p0 = flow.stokes.solve(WP, u_bcs + p_bcs, mu, f=Constant((0.0, 0.0)), verbose=False, tol=1.0e-13, max_iter=10000) u0.rename('velocity', 'velocity') p0.rename('pressure', 'pressure') rho = materials.water.density(T=293.0) # stepper = flow.navier_stokes.Chorin() # stepper = flow.navier_stokes.IPCS() stepper = flow.navier_stokes.Rotational() W2 = u0.function_space() P2 = p0.function_space() u_bcs = [ DirichletBC(W2, (0.0, 0.0), upper_boundary), DirichletBC(W2, (0.0, 0.0), lower_boundary), DirichletBC(W2, (0.0, 0.0), obstacle_boundary), DirichletBC(W2.sub(0), inflow, left_boundary), # DirichletBC(W2.sub(0), outflow, right_boundary), ] # TODO settting the outflow _and_ the pressure at the outlet is actually # not necessary. Even without the pressure Dirichlet conditions, the # pressure correction system should be consistent. p_bcs = [DirichletBC(P2, 0.0, right_boundary)] # Report Reynolds number. # https://en.wikipedia.org/wiki/Reynolds_number#Sphere_in_a_fluid reynolds = entrance_velocity * obstacle_diameter * rho / mu print('Reynolds number: %e' % reynolds) dt = 1.0e-5 dt_max = 1.0 t = 0.0 with XDMFFile(mpi_comm_world(), 'karman.xdmf') as xdmf_file: xdmf_file.parameters['flush_output'] = True xdmf_file.parameters['rewrite_function_mesh'] = False k = 0 while k < num_steps: k += 1 print() print('t = %f' % t) if show: plot(u0) plot(p0) xdmf_file.write(u0, t) xdmf_file.write(p0, t) u1, p1 = stepper.step(Constant(dt), {0: u0}, p0, u_bcs, p_bcs, Constant(rho), Constant(mu), f={ 0: Constant((0.0, 0.0)), 1: Constant((0.0, 0.0)) }, verbose=False, tol=1.0e-10) u0.assign(u1) p0.assign(p1) # Adaptive stepsize control based solely on the velocity field. # CFL-like condition for time step. This should be some sort of # average of the temperature in the current step and the target # step. # # More on step-size control for Navier--Stokes: # # Adaptive time step control for the incompressible # Navier-Stokes equations; # Volker John, Joachim Rang; # Comput. Methods Appl. Mech. Engrg. 199 (2010) 514-524; # <http://www.wias-berlin.de/people/john/ELECTRONIC_PAPERS/JR10.CMAME.pdf>. # # Section 3.3 in that paper notes that time-adaptivity for theta- # schemes is too costly. They rather reside to DIRK- and # Rosenbrock-methods. # begin('Step size adaptation...') ux, uy = u0.split() unorm = project(sqrt(ux**2 + uy**2), FunctionSpace(mesh, 'Lagrange', 2), form_compiler_parameters={'quadrature_degree': 4}) unorm = norm(unorm.vector(), 'linf') # print('||u||_inf = %e' % unorm) # Some smooth step-size adaption. target_dt = 1.0 * mesh.hmax() / unorm print('current dt: %e' % dt) print('target dt: %e' % target_dt) # alpha is the aggressiveness factor. The distance between the # current step size and the target step size is reduced by # |1-alpha|. Hence, if alpha==1 then dt_next==target_dt. Otherwise # target_dt is approached more slowly. alpha = 0.5 dt = min( dt_max, # At most double the step size from step to step. dt * min(2.0, 1.0 + alpha * (target_dt - dt) / dt)) print('next dt: %e' % dt) t += dt end() return
def solve(self): """ Perform one solution step (in time). """ begin("Cahn-Hilliard step") self.iters["CH"][-1] = self.data["solver"]["CH"]["nln"].solve( self.data["problem_ch"], self.data["sol_ch"].vector())[0] self.iters["CH"][0] += self.iters["CH"][-1] end() begin("Navier-Stokes step") # Update stabilization terms if self.data["model"].parameters["semi"]["sdstab"]: self.data["model"]._update_sd_stab_parameter() pcd_assembler = self.data.get("pcd_assembler", None) if pcd_assembler: # Symmetric assembly of the linear system A, b = PETScMatrix(self.comm()), PETScVector(self.comm()) pcd_assembler.system_matrix(A) pcd_assembler.rhs_vector(b) else: # Standard assembly of the linear system A = assemble(self.data["forms"]["lin"]["lhs"]) b = assemble(self.data["forms"]["lin"]["rhs"]) for bc in self.data["bcs_ns"]: bc.apply(A, b) if self._flags["fix_p"]: # Attach null space to PETSc matrix as_backend_type(A).set_nullspace(self.data["null_space"]) # Orthogonalize RHS vector b with respect to the null space self.data["null_space"].orthogonalize(b) if pcd_assembler: # Symmetric assembly of the preconditioner P = PETScMatrix(self.comm()) pcd_assembler.pc_matrix(P) # FIXME: Should we attach the null space also to preconditioner? # Probably not as 'set_nullspace' is related to KSP (not PC). # if P.empty(): # P = A # else: # as_backend_type(P).set_nullspace(self.data["null_space"]) P = A if P.empty() else P # Standard assembly of the preconditioner matrix # if self.data["forms"]["pcd"]["a_pc"] is not None: # P = assemble(self.data["forms"]["pcd"]["a_pc"]) # for bc in self.data["bcs_ns"]: # bc.apply(P) # else: # P = A self.data["solver"]["NS"].set_operators(A, P) if not self._flags["init_pcd_called"]: # only one call is allowed self.data["solver"]["NS"].init_pcd(pcd_assembler) self._flags["init_pcd_called"] = True else: # FIXME: Here we assume that LU solver is used assert self.data["solver"]["NS"].parameter_type() == 'lu_solver' self.data["solver"]["NS"].set_operator(A) # NOTE: Preconditioner matrix can't be set for LUSolver. self.iters["NS"][-1] = \ self.data["solver"]["NS"].solve(self.data["sol_ns"].vector(), b) info("Navier-Stokes solver finished in {} iterations".format( self.iters["NS"][-1])) self.iters["NS"][0] += self.iters["NS"][-1] if self._flags["fix_p"]: self._calibrate_pressure(self.data["sol_ns"], self.data["null_fcn"]) end()
def save_geometry_to_h5(mesh, h5name, h5group="", markers=None, markerfunctions={}, microstructure={}, local_basis={}, comm=mpi_comm_world, other_functions={}, other_attributes={}, overwrite_file=False, overwrite_group=True): """ Save geometry and other geometrical functions to a HDF file. Parameters ---------- mesh : :class:`dolfin.mesh` The mesh h5name : str Path to the file h5group : str Folder within the file. Default is "" which means in the top folder. markers : dict A dictionary with markers. See `get_markers`. fields : list A list of functions for the microstructure local_basis : list A list of functions for the crl basis meshfunctions : dict A dictionary with keys being the dimensions the the values beeing the meshfunctions. comm : :class:`dolfin.MPI` MPI communicator other_functions : dict Dictionary with other functions you want to save other_attributes: dict Dictionary with other attributes you want to save overwrite_file : bool If true, and the file exists, the file will be overwritten (default: False) overwrite_group : bool If true and h5group exist, the group will be overwritten. """ h5name = os.path.splitext(h5name)[0] + ".h5" assert isinstance(mesh, Mesh) file_mode = "a" if os.path.isfile(h5name) and not overwrite_file else "w" # IF we should append the file but overwrite the group we need to # check that the group does not exist. If so we need to open it in # h5py and delete it. if file_mode == "a" and overwrite_group and h5group != "": check_h5group(h5name, h5group, delete=True, comm=comm) with HDF5File(comm, h5name, file_mode) as h5file: # Save mesh ggroup = "{}/geometry".format(h5group) mgroup = "{}/mesh".format(ggroup) h5file.write(mesh, mgroup) # Save markerfunctions df.begin(LogLevel.PROGRESS, "Saving marker functions.") for dim, key in enumerate(markerfunctions.keys()): mf = markerfunctions[key] if mf is not None: dgroup = "{}/mesh/meshfunction_{}".format(ggroup, dim) h5file.write(mf, dgroup) df.end() # Save markers df.begin(LogLevel.PROGRESS, "Saving markers.") for name, (marker, dim) in markers.items(): for key_str in ["domain", "meshfunction"]: dgroup = "{}/mesh/{}_{}".format(ggroup, key_str, dim) if h5file.has_dataset(dgroup): aname = "marker_name_{}".format(name) h5file.attributes(dgroup)[aname] = marker df.end() # Save microstructure df.begin(LogLevel.PROGRESS, "Saving microstructure.") for key in microstructure.keys(): ms = microstructure[key] if ms is not None: fgroup = "{}/microstructure".format(h5group) fsubgroup = "{}/{}".format(fgroup, key) h5file.write(microstructure[key], fsubgroup) h5file.attributes(fsubgroup)["name"] = key elm = ms.function_space().ufl_element() try: family, degree = elm.family(), elm.degree() fspace = "{}_{}".format(family, degree) h5file.attributes(fgroup)["space"] = fspace h5file.attributes(fgroup)["names"] = ":".join( microstructure.keys()) except: pass df.end() # Save local basis df.begin(LogLevel.PROGRESS, "Saving local basis.") for key in local_basis.keys(): ml = local_basis[key] if ml is not None: lgroup = "{}/local basis functions".format(h5group) h5file.write(ml, lgroup + "/{}".format(key)) elm = ml.function_space().ufl_element() try: family, degree = elm.family(), elm.degree() lspace = "{}_{}".format(family, degree) h5file.attributes(lgroup)["space"] = lspace h5file.attributes(lgroup)["names"] = ":".join(local_basis.keys()) except: pass df.end() # Save other functions df.begin(LogLevel.PROGRESS, "Saving other functions") for key in other_functions.keys(): mo = other_functions[key] if mo is not None: fungroup = "/".join([h5group, key]) h5file.write(mo, fungroup) elm = mo.function_space().ufl_element() try: family, degree, vsize = elm.family(), elm.degree(), elm.value_size( ) fspace = "{}_{}".format(family, degree) h5file.attributes(fungroup)["space"] = fspace h5file.attributes(fungroup)["value_size"] = vsize except: pass df.end() # Save other attributes df.begin(LogLevel.PROGRESS, "Saving other attributes") for key in other_attributes: if isinstance(other_attributes[key], str) and isinstance(key, str): h5file.attributes(h5group)[key] = other_attributes[key] else: begin( df.LogLevel.WARNING, "Invalid attribute {} = {}".format(key, other_attributes[key])) end() df.end() df.begin(df.LogLevel.INFO, "Geometry saved to {}".format(h5name)) df.end()
def __exit__(self, tpe, value, traceback): end() return
def compute_boussinesq(target_time, lcar, supg=False): mesh, hot_boundary, cool_boundary = create_mesh(lcar) room_temp = 293.0 # Density depends on temperature. rho = materials.water.density # Take dynamic viscosity at room temperature. mu = materials.water.dynamic_viscosity(room_temp) cp = materials.water.specific_heat_capacity kappa = materials.water.thermal_conductivity # Start time, end time, time step. dt_max = 1.0 dt0 = 1.0e-2 # This should be # umax = 1.0e-1 # dt0 = 0.2 * mesh.hmin() / umax t = 0.0 max_heater_temp = 320.0 # Gravity accelleration. accelleration_constant = -9.81 g = Constant((0.0, accelleration_constant)) W_element = VectorElement('Lagrange', mesh.ufl_cell(), 2) P_element = FiniteElement('Lagrange', mesh.ufl_cell(), 1) WP = FunctionSpace(mesh, W_element * P_element) Q = FunctionSpace(mesh, 'Lagrange', 2) # Everything at room temperature for starters theta0 = project(Constant(room_temp), Q) theta0.rename('temperature', 'temperature') u_bcs = [DirichletBC(WP.sub(0), (0.0, 0.0), 'on_boundary')] p_bcs = [] # Solve Stokes for initial state. # Make sure that the right-hand side is the same as in the first step of # Navier-Stokes below. This avoids problems with the initial pressure being # a bit off, which leads to errors. # u0, p0 = flow.stokes.solve( # WP, # u_bcs + p_bcs, # mu, # f=rho(theta0) * g, # verbose=False, # tol=1.0e-15, # max_iter=1000 # ) y = SpatialCoordinate(mesh)[1] u0 = project(Constant([0, 0]), FunctionSpace(mesh, W_element)) u0.rename('velocity', 'velocity') p0 = project( rho(theta0) * accelleration_constant * y, FunctionSpace(mesh, P_element)) p0.rename('pressure', 'pressure') # div_u = Function(Q) dt = dt0 with XDMFFile(mpi_comm_world(), 'boussinesq.xdmf') as xdmf_file: xdmf_file.parameters['flush_output'] = True xdmf_file.parameters['rewrite_function_mesh'] = False while t < target_time + DOLFIN_EPS: begin('Time step %e -> %e...' % (t, t + dt)) # Crank up the heater from room_temp to max_heater_temp in t1 secs. t1 = 30.0 heater_temp = (+room_temp + min(1.0, t / t1) * (max_heater_temp - room_temp)) # heater_temp = room_temp # Velocity and heat and connected by # # theta1 = F_theta(u1, theta0) # u1, p1 = F_u(u0, p0, theta1) # # One can either replace u1, theta1 on the right-hand side by u0, # theta0, respectively, or wrap the whole thing in a Banach # iteration 'a la # # theta = F_theta(u_prev, theta0) # (u, p) = F_u(u0, p0, theta_prev) # # and do that until the residuals are close to 0. u_prev = Function(u0.function_space()) u_prev.assign(u0) theta_prev = Function(theta0.function_space()) theta_prev.assign(theta0) is_banach_converged = False banach_tol = 1.0e-1 max_banach_steps = 10 target_banach_steps = 5 banach_step = 0 while not is_banach_converged: banach_step += 1 if banach_step > max_banach_steps: info('\nBanach solver failed to converge. ' 'Decrease time step from %e to %e and try again.\n' % (dt, 0.25 * dt)) dt *= 0.25 end() # time step break begin('Banach step %d:' % banach_step) # Do one heat time step. begin('Computing heat...') heat_bcs = [ DirichletBC(Q, heater_temp, hot_boundary), DirichletBC(Q, room_temp, cool_boundary), ] # Use all quantities at room temperature to avoid nonlinearity stepper = parabolic.ImplicitEuler( heat.Heat(Q, u_prev, kappa(room_temp), rho(room_temp), cp(room_temp), heat_bcs, Constant(0.0), supg_stabilization=supg)) theta1 = stepper.step(theta0, t, dt) end() # Do one Navier-Stokes time step. begin('Computing flux and pressure...') # stepper = flow.navier_stokes.Chorin() # stepper = flow.navier_stokes.IPCS() stepper = flow.navier_stokes.Rotational() W = u0.function_space() u_bcs = [DirichletBC(W, (0.0, 0.0), 'on_boundary')] p_bcs = [] try: u1, p1 = stepper.step( Constant(dt), {0: u0}, p0, u_bcs, p_bcs, # TODO use rho(theta) rho(room_temp), Constant(mu), f={ 0: rho(theta_prev) * g, 1: rho(theta_prev) * g }, verbose=False, tol=1.0e-10) except RuntimeError: info('Navier--Stokes solver failed to converge. ' 'Decrease time step from %e to %e and try again.' % (dt, 0.5 * dt)) dt *= 0.5 end() # navier-stokes end() # banach step end() # time step # http://stackoverflow.com/a/1859099/353337 break end() # navier-stokes u1x, u1y = u1.split() uprevx, uprevy = u_prev.split() unorm = project( abs(u1x - uprevx) + abs(u1y - uprevy), Q, form_compiler_parameters={'quadrature_degree': 4}) u_diff_norm = norm(unorm.vector(), 'linf') theta_diff = Function(theta1.function_space()) theta_diff.vector()[:] = theta1.vector() - theta_prev.vector() theta_diff_norm = norm(theta_diff.vector(), 'linf') info('Banach residuals:') info(' ||u - u_prev|| = %e' % u_diff_norm) info(' ||theta - theta_prev|| = %e' % theta_diff_norm) is_banach_converged = \ u_diff_norm < banach_tol and theta_diff_norm < banach_tol u_prev.assign(u1) theta_prev.assign(theta1) end() # banach step else: # from dolfin import plot, interactive # plot(u0) # plot(p0) # u1.rename('u1', 'u1') # plot(u1) # p1.rename('p1', 'p1') # plot(p1) # interactive() # Assigning and plotting. We do that here so all methods have # access to `x` and `x_1`. theta0.assign(theta1) u0.assign(u1) p0.assign(p1) # write to file xdmf_file.write(theta0, t) xdmf_file.write(u0, t) xdmf_file.write(p0, t) # from dolfin import plot, interactive # plot(theta0, title='theta') # plot(u0, title='u') # # plot(div(u), title='div(u)', rescale=True) # plot(p0, title='p') # interactive() end() # time step begin('\nStep size adaptation...') # Step-size control can be done purely based on the velocity # field; see # # Adaptive time step control for the incompressible # Navier-Stokes equations; # Volker John, Joachim Rang; # Comput. Methods Appl. Mech. Engrg. 199 (2010) 514-524; # <http://www.wias-berlin.de/people/john/ELECTRONIC_PAPERS/JR10.CMAME.pdf>. # # Section 3.3 in that paper notes that time-adaptivity for # theta- schemes is too costly. They rather reside to DIRK- and # Rosenbrock- methods. # # Implementation: # ux, uy = u0.split() # unorm = project( # abs(ux) + abs(uy), # Q, # form_compiler_parameters={'quadrature_degree': 4} # ) # unorm = norm(unorm.vector(), 'linf') # # print('||u||_inf = %e' % unorm) # # Some smooth step-size adaption. # target_dt = 0.2 * mesh.hmax() / unorm # In our case, step failures are almost always because Banach # didn't converge. Hence, design a step size adaptation based # on the Banach steps. target_dt = dt * target_banach_steps / banach_step info('current dt: %e' % dt) info('target dt: %e' % target_dt) # alpha is the aggressiveness factor. The distance between the # current step size and the target step size is reduced by # |1-alpha|. Hence, if alpha==1 then dt_next==target_dt. # Otherwise target_dt is approached more slowly. alpha = 0.5 dt = min( dt_max, # At most double the step size from step to step. dt * min(2.0, 1.0 + alpha * (target_dt - dt) / dt)) info('next dt: %e\n' % dt) t += dt end() return u1, p1, theta1
def test(problem, max_num_steps=2, show=False): # # Density depends on temperature. # material = 'water' # rho = params[material]['density'](293.0) # mu = params[material]['dynamic viscosity'](293.0) rho = 1.0 mu = 1.0 # Start time, end time, time step. t = 0.0 T = 8.0 dt = 1.0e-5 dt_max = 1.0e-1 num_subspaces = problem.W.num_sub_spaces() if num_subspaces == 2: # g = Constant((0.0, 0.0)) g = Constant((0.0, -9.81)) elif num_subspaces == 3: # g = Constant((0.0, 0.0, 0.0)) g = Constant((0.0, -9.81, 0.0)) else: raise RuntimeError("Illegal number of subspaces ({}).".format(num_subspaces)) initial_stokes = False if initial_stokes: u0, p0 = cyl_stokes.solve( problem.W, problem.P, mu, rho, problem.u_bcs, problem.p_bcs, f=rho * g, tol=1.0e-10, maxiter=2000, ) else: # Initial states. u0 = Function(problem.W, name="velocity") u0.vector().zero() p0 = Function(problem.P, name="pressure") p0.vector().zero() filename = "navier_stokes.xdmf" with XDMFFile(mpi_comm_world(), filename) as xdmf_file: xdmf_file.parameters["flush_output"] = True xdmf_file.parameters["rewrite_function_mesh"] = False if show: xdmf_file.write(u0, t) xdmf_file.write(p0, t) stepper = cyl_ns.IPCS(time_step_method="backward euler") steps = 0 while t < T + DOLFIN_EPS and steps < max_num_steps: steps += 1 begin("Time step {:e} -> {:e}...".format(t, t + dt)) try: u1, p1 = stepper.step( Constant(dt), {0: u0}, p0, problem.W, problem.P, problem.u_bcs, problem.p_bcs, Constant(rho), Constant(mu), f={0: rho * g, 1: rho * g}, tol=1.0e-10, ) except RuntimeError: print( "Navier--Stokes solver failed to converge. " "Decrease time step from {} to {} and try again.".format( dt, 0.5 * dt ) ) dt *= 0.5 end() end() end() continue u0.assign(u1) p0.assign(p1) if show: # Save to files. xdmf_file.write(u0, t + dt) xdmf_file.write(p0, t + dt) # Plotting for some reason takes up a lot of memory. plot(u0, title="velocity", rescale=True) plot(p0, title="pressure", rescale=True) # interactive() begin("Step size adaptation...") # unorm = project(abs(u[0]) + abs(u[1]) + abs(u[2]), # P, # form_compiler_parameters={'quadrature_degree': 4} # ) unorm = project( norm(u1), problem.P, form_compiler_parameters={"quadrature_degree": 4} ) unorm = norm(unorm.vector(), "linf") # print('||u||_inf = {:e}'.format(unorm)) # Some smooth step-size adaption. target_dt = 0.2 * problem.mesh.hmax() / unorm print("current dt: {:e}".format(dt)) print("target dt: {:e}".format(target_dt)) # alpha is the aggressiveness factor. The distance between the # current step size and the target step size is reduced by # |1-alpha|. Hence, if alpha==1 then dt_next==target_dt. Otherwise # target_dt is approached slowlier. alpha = 0.5 dt = min( dt_max, # At most double the step size from step to step. dt * min(2.0, 1.0 + alpha * (target_dt - dt) / dt), ) print("next dt: {:e}".format(dt)) t += dt end() end() return