def mms_source(sim, strong_residual, manufactured_solution): V = sim.solution.function_space() _r = strong_residual(sim, solution=manufactured_solution(sim)) if type(sim.solution.function_space().ufl_element()) is fe.FiniteElement: r = (_r, ) else: r = _r psi = fe.TestFunctions(V) s = fe.inner(psi[0], r[0]) if len(r) > 1: for psi_i, r_i in zip(psi[1:], r[1:]): s += fe.inner(psi_i, r_i) return s
def equation(z): Z = z.function_space() φ, v = firedrake.TestFunctions(Z) h, q = firedrake.split(z) F_h, F_q = _fluxes(h, q, g) mesh = Z.mesh() n = firedrake.FacetNormal(mesh) c = abs(inner(q / h, n)) + sqrt(g * h) sources = -inner(g * h * grad(b), v) * dx fluxes = (forms.cell_flux(F_h, φ) + forms.central_facet_flux(F_h, φ) + forms.lax_friedrichs_facet_flux(h, c, φ) + forms.cell_flux(F_q, v) + forms.central_facet_flux(F_q, v) + forms.lax_friedrichs_facet_flux(q, c, v)) boundary_ids = set(mesh.exterior_facets.unique_markers) wall_ids = tuple(boundary_ids - set(outflow_ids) - set(inflow_ids)) q_wall = q - 2 * inner(q, n) * n boundary_fluxes = ( _boundary_flux(z, h, q, g, outflow_ids) + _boundary_flux(z, h_in, q_in, g, inflow_ids) + _boundary_flux(z, h, q_wall, g, wall_ids) + forms.lax_friedrichs_boundary_flux(h, h, c, φ, outflow_ids) + forms.lax_friedrichs_boundary_flux(q, q, c, v, outflow_ids) + forms.lax_friedrichs_boundary_flux(h, h_in, c, φ, inflow_ids) + forms.lax_friedrichs_boundary_flux(q, q_in, c, v, inflow_ids) + forms.lax_friedrichs_boundary_flux(h, h, c, φ, wall_ids) + forms.lax_friedrichs_boundary_flux(q, q_wall, c, v, wall_ids)) return sources - fluxes - boundary_fluxes
def variational_form_residual(sim, solution): Gr = sim.grashof_number Pr = sim.prandtl_number ihat, jhat = sapphire.simulation.unit_vectors(sim.mesh) sim.gravity_direction = fe.Constant(-jhat) ghat = sim.gravity_direction p, u, T = fe.split(solution) psi_p, psi_u, psi_T = fe.TestFunctions(solution.function_space()) mass = psi_p * div(u) momentum = dot(psi_u, grad(u)*u + Gr*T*ghat) \ - div(psi_u)*p + 2.*inner(sym(grad(psi_u)), sym(grad(u))) energy = psi_T * dot(u, grad(T)) + dot(grad(psi_T), 1. / Pr * grad(T)) dx = fe.dx(degree=sim.quadrature_degree) return (mass + momentum + energy) * dx
def energy(self): Re = self.reynolds_number Pr = self.prandtl_number Ste = self.stefan_number _, u, T = fe.split(self.solution) phil = self.liquid_volume_fraction(temperature = T) rho_sl = self.density_solid_to_liquid_ratio c_sl = self.heat_capacity_solid_to_liquid_ratio C = phase_dependent_material_property(rho_sl*c_sl)(phil) k_sl = self.thermal_conductivity_solid_to_liquid_ratio k = phase_dependent_material_property(k_sl)(phil) CT_t, phil_t = self.extra_time_discrete_terms() _, _, psi_T = fe.TestFunctions(self.solution_space) dx = fe.dx(degree = self.quadrature_degree) return (psi_T*(CT_t + 1./Ste*phil_t + dot(u, grad(C*T))) \ + 1./(Re*Pr)*dot(grad(psi_T), k*grad(T)))*dx
def compute_inf_sup_constant(spaces, b, inner_products): V, Q = spaces m, n = inner_products Z = V * Q u, p = firedrake.TrialFunctions(Z) v, q = firedrake.TestFunctions(Z) A = assemble(-(m(u, v) + b(v, p) + b(u, q)), mat_type='aij').M.handle M = assemble(n(p, q), mat_type='aij').M.handle opts = PETSc.Options() opts.setValue('eps_gen_hermitian', None) opts.setValue('eps_target_real', None) opts.setValue('eps_smallest_magnitude', None) opts.setValue('st_type', 'sinvert') opts.setValue('st_ksp_type', 'preonly') opts.setValue('st_pc_type', 'lu') opts.setValue('st_pc_factor_mat_solver_type', 'mumps') opts.setValue('eps_tol', 1e-8) num_values = 1 eigensolver = SLEPc.EPS().create(comm=firedrake.COMM_WORLD) eigensolver.setDimensions(num_values) eigensolver.setOperators(A, M) eigensolver.setFromOptions() eigensolver.solve() Vr, Vi = A.getVecs() λ = eigensolver.getEigenpair(0, Vr, Vi) return λ
def heat_driven_cavity_variational_form_residual(sim, solution): mass = sapphire.simulations.convection_coupled_phasechange.\ mass(sim, solution) stabilization = sapphire.simulations.convection_coupled_phasechange.\ stabilization(sim, solution) p, u, T = fe.split(solution) b = sapphire_ice.simulation.water_buoyancy(sim=sim, temperature=T) Pr = sim.prandtl_number _, psi_u, psi_T = fe.TestFunctions(sim.function_space) inner, dot, grad, div, sym = \ fe.inner, fe.dot, fe.grad, fe.div, fe.sym momentum = dot(psi_u, grad(u)*u + b) \ - div(psi_u)*p + 2.*inner(sym(grad(psi_u)), sym(grad(u))) energy = psi_T * dot(u, grad(T)) + dot(grad(psi_T), 1. / Pr * grad(T)) return mass + momentum + energy + stabilization
def energy(sim, solution): Pr = sim.prandtl_number Ste = sim.stefan_number _, u, T = fe.split(solution) phil = liquid_volume_fraction(sim=sim, temperature=T) rho_sl = sim.density_solid_to_liquid_ratio c_sl = sim.heat_capacity_solid_to_liquid_ratio C = phase_dependent_material_property(rho_sl * c_sl)(phil) k_sl = sim.thermal_conductivity_solid_to_liquid_ratio k = phase_dependent_material_property(k_sl)(phil) _, CT_t, phil_t = time_discrete_terms(sim=sim) _, _, psi_T = fe.TestFunctions(solution.function_space()) return psi_T*(CT_t + 1./Ste*phil_t + dot(u, grad(C*T))) \ + 1./Pr*dot(grad(psi_T), k*grad(T))
def __init__(self, *args, meshsize, **kwargs): self.hot_wall_temperature = fe.Constant(1.) self.cold_wall_temperature = fe.Constant(-0.01) self.topwall_heatflux = fe.Constant(0.) super().__init__( *args, mesh=fe.UnitSquareMesh(meshsize, meshsize), initial_values=initial_values, dirichlet_boundary_conditions=dirichlet_boundary_conditions, **kwargs) q = self.topwall_heatflux _, _, psi_T = fe.TestFunctions(self.function_space) ds = fe.ds(domain=self.mesh, subdomain_id=4) self.variational_form_residual += psi_T * q * ds Ra = 3.27e5 Pr = 56.2 self.grashof_number = self.grashof_number.assign(Ra / Pr) self.prandtl_number = self.prandtl_number.assign(Pr) self.stefan_number = self.stefan_number.assign(0.045) self.liquidus_temperature = self.liquidus_temperature.assign(0.)
def get_weak_form(self): (v, q) = fd.TestFunctions(self.V) (u, p) = fd.split(self.solution) F = self.nu * fd.inner(fd.grad(u), fd.grad(v)) * fd.dx \ - p * fd.div(v) * fd.dx \ + fd.div(u) * q * fd.dx \ + fd.inner(fda.Constant((0., 0.)), v) * fd.dx return F
def mass(self): u, _ = fe.split(self.solution) _, psi_p = fe.TestFunctions(self.solution_space) dx = fe.dx(degree = self.quadrature_degree) return psi_p*div(u)*dx
def stabilization(sim, solution): p, _, _ = fe.split(solution) psi_p, _, _ = fe.TestFunctions(solution.function_space()) gamma = sim.pressure_penalty_factor return gamma * psi_p * p
def energy(self): _, T_t = self.time_discrete_terms() _, _, psi_T = fe.TestFunctions(self.solution_space) dx = fe.dx(degree=self.quadrature_degree) return super().energy() + psi_T * T_t * dx
def momentum(self): u_t = self.time_discrete_terms() psi_u, _ = fe.TestFunctions(self.solution_space) dx = fe.dx(degree=self.quadrature_degree) return super().momentum() + dot(psi_u, u_t) * dx
def momentum(self): _, u, T = fe.split(self.solution) d = self.solid_velocity_relaxation(temperature = T) _, psi_u, _ = fe.TestFunctions(self.solution_space) dx = fe.dx(degree = self.quadrature_degree) return super().momentum() + dot(psi_u, d*u)*dx
def mass(sim, solution): _, u, _ = fe.split(solution) psi_p, _, _ = fe.TestFunctions(solution.function_space()) div = fe.div mass = psi_p * div(u) return mass
def _boundary_flux(z, h_ext, q_ext, g, boundary_ids): Z = z.function_space() n = firedrake.FacetNormal(Z.mesh()) φ, v = firedrake.TestFunctions(Z) F_hx, F_qx = _fluxes(h_ext, q_ext, g) h, q = firedrake.split(z) F_h, F_q = _fluxes(h, q, g) return 0.5 * (inner(F_hx, φ * n) + inner(F_qx, outer(v, n)) + inner( F_h, φ * n) + inner(F_q, outer(v, n))) * ds(boundary_ids)
def momentum(self): u, p = fe.split(self.solution) Re = self.reynolds_number psi_u, _ = fe.TestFunctions(self.solution_space) dx = fe.dx(degree = self.quadrature_degree) return (dot(psi_u, grad(u)*u) - div(psi_u)*p + \ 2./Re*inner(sym(grad(psi_u)), sym(grad(u))))*dx
def dgls_form(self, problem, mesh, bcs_p): rho = problem.rho mu = problem.mu k = problem.k f = problem.f q, p = fire.TrialFunctions(self._W) w, v = fire.TestFunctions(self._W) n = fire.FacetNormal(mesh) h = fire.CellDiameter(mesh) # Stabilizing parameters has_mesh_characteristic_length = True delta_0 = fire.Constant(1) delta_1 = fire.Constant(-1 / 2) delta_2 = fire.Constant(1 / 2) delta_3 = fire.Constant(1 / 2) eta_p = fire.Constant(100) eta_q = fire.Constant(100) h_avg = (h("+") + h("-")) / 2.0 if has_mesh_characteristic_length: delta_2 = delta_2 * h * h delta_3 = delta_3 * h * h kappa = rho * k / mu inv_kappa = 1.0 / kappa # Classical mixed terms a = (dot(inv_kappa * q, w) - div(w) * p - delta_0 * v * div(q)) * dx L = -delta_0 * f * v * dx # DG terms a += jump(w, n) * avg(p) * dS - avg(v) * jump(q, n) * dS # Edge stabilizing terms a += (eta_q * h_avg) * avg(inv_kappa) * ( jump(q, n) * jump(w, n)) * dS + (eta_p / h_avg) * avg(kappa) * dot( jump(v, n), jump(p, n)) * dS # Add the contributions of the pressure boundary conditions to L for pboundary, iboundary in bcs_p: L -= pboundary * dot(w, n) * ds(iboundary) # Stabilizing terms a += (delta_1 * inner(kappa * (inv_kappa * q + grad(p)), delta_0 * inv_kappa * w + grad(v)) * dx) a += delta_2 * inv_kappa * div(q) * div(w) * dx a += delta_3 * inner(kappa * curl(inv_kappa * q), curl( inv_kappa * w)) * dx L += delta_2 * inv_kappa * f * div(w) * dx return a, L
def momentum(self): p, u, T = fe.split(self.solution) _, psi_u, _ = fe.TestFunctions(self.solution_space) b = self.buoyancy(temperature=T) Re = self.reynolds_number dx = fe.dx(degree=self.quadrature_degree) return (dot(psi_u, grad(u)*u + b) - div(psi_u)*p + \ 2./Re*inner(sym(grad(psi_u)), sym(grad(u))))*dx
def variational_form_residual(sim, solution): u, p = fe.split(solution) psi_u, psi_p = fe.TestFunctions(solution.function_space()) mass = psi_p * div(u) momentum = dot(psi_u, grad(u)*u) - div(psi_u)*p + \ 2.*inner(sym(grad(psi_u)), sym(grad(u))) dx = fe.dx(degree=sim.quadrature_degree) return (mass + momentum) * dx
def momentum(sim, solution, buoyancy=linear_boussinesq_buoyancy): p, u, T = fe.split(solution) u_t, _, _ = time_discrete_terms(sim=sim) b = buoyancy(sim=sim, temperature=T) d = solid_velocity_relaxation(sim=sim, temperature=T) _, psi_u, _ = fe.TestFunctions(solution.function_space()) return dot(psi_u, u_t + grad(u)*u + b + d*u) \ - div(psi_u)*p + 2.*inner(sym(grad(psi_u)), sym(grad(u)))
def energy(self): Re = self.reynolds_number Pr = self.prandtl_number _, u, T = fe.split(self.solution) _, _, psi_T = fe.TestFunctions(self.solution_space) dx = fe.dx(degree=self.quadrature_degree) return (psi_T * dot(u, grad(T)) + dot(grad(psi_T), 1. / (Re * Pr) * grad(T))) * dx
def _set_para_form(self): """ Constructs the bilinear form for the all at once system. Specific to the theta-centred Crank-Nicholson method """ M = self.M w_all_cpts = fd.split(self.w_all) test_fns = fd.TestFunctions(self.W_all) dt = fd.Constant(self.dt) theta = fd.Constant(self.theta) alpha = fd.Constant(self.alpha) wMs = w_all_cpts[self.ncpts * (M - 1):] if self.circ == "picard": if self.ncpts == 1: wMkm1s = [self.w_prev] else: wMkm1s = fd.split(self.w_prev) for n in range(M): # previous time level if n == 0: w0ss = fd.split(self.w0) if self.circ == "picard": w0s = [ w0ss[i] + alpha * (wMs[i] - wMkm1s[i]) for i in range(self.ncpts) ] else: w0s = w0ss else: w0s = w_all_cpts[self.ncpts * (n - 1):self.ncpts * n] # current time level w1s = w_all_cpts[self.ncpts * n:self.ncpts * (n + 1)] dws = test_fns[self.ncpts * n:self.ncpts * (n + 1)] # time derivative if n == 0: p_form = (1.0 / dt) * self.form_mass(*w1s, *dws) else: p_form += (1.0 / dt) * self.form_mass(*w1s, *dws) p_form -= (1.0 / dt) * self.form_mass(*w0s, *dws) # vector field p_form += theta * self.form_function(*w1s, *dws) p_form += (1 - theta) * self.form_function(*w0s, *dws) self.para_form = p_form
def variational_form_residual(sim, solution): u, p = fe.split(sim.solution) u_t, _ = sapphire.simulation.time_discrete_terms( solutions=sim.solutions, timestep_size=sim.timestep_size) psi_u, psi_p = fe.TestFunctions(sim.solution.function_space()) mass = psi_p * div(u) momentum = dot(psi_u, u_t + grad(u)*u) - div(psi_u)*p + \ 2.*inner(sym(grad(psi_u)), sym(grad(u))) dx = fe.dx(degree=sim.quadrature_degree) return (mass + momentum) * dx
def test_solver_no_flow_region(): mesh = fd.Mesh("./2D_mesh.msh") no_flow = [2] no_flow_markers = [1] mesh = mark_no_flow_regions(mesh, no_flow, no_flow_markers) P2 = fd.VectorElement("CG", mesh.ufl_cell(), 1) P1 = fd.FiniteElement("CG", mesh.ufl_cell(), 1) TH = P2 * P1 W = fd.FunctionSpace(mesh, TH) (v, q) = fd.TestFunctions(W) # Stokes 1 w_sol1 = fd.Function(W) nu = fd.Constant(0.05) F = NavierStokesBrinkmannForm(W, w_sol1, nu, beta_gls=2.0) x, y = fd.SpatialCoordinate(mesh) u_mms = fd.as_vector( [sin(2.0 * pi * x) * sin(pi * y), sin(pi * x) * sin(2.0 * pi * y)]) p_mms = -0.5 * (u_mms[0]**2 + u_mms[1]**2) f_mms_u = (grad(u_mms) * u_mms + grad(p_mms) - 2.0 * nu * div(sym(grad(u_mms)))) f_mms_p = div(u_mms) F += -inner(f_mms_u, v) * dx - f_mms_p * q * dx bc1 = fd.DirichletBC(W.sub(0), u_mms, "on_boundary") bc2 = fd.DirichletBC(W.sub(1), p_mms, "on_boundary") bc_no_flow = InteriorBC(W.sub(0), fd.Constant((0.0, 0.0)), no_flow_markers) solver_parameters = {"ksp_max_it": 500, "ksp_monitor": None} problem1 = fd.NonlinearVariationalProblem(F, w_sol1, bcs=[bc1, bc2, bc_no_flow]) solver1 = NavierStokesBrinkmannSolver( problem1, options_prefix="navier_stokes", solver_parameters=solver_parameters, ) solver1.solve() u_sol, _ = w_sol1.split() u_mms_func = fd.interpolate(u_mms, W.sub(0)) error = fd.errornorm(u_sol, u_mms_func) assert error < 0.07
def run_solver(r): mesh = fd.UnitSquareMesh(2**r, 2**r) P2 = fd.VectorElement("CG", mesh.ufl_cell(), 1) P1 = fd.FiniteElement("CG", mesh.ufl_cell(), 1) TH = P2 * P1 W = fd.FunctionSpace(mesh, TH) (v, q) = fd.TestFunctions(W) # Stokes 1 w_sol1 = fd.Function(W) nu = fd.Constant(0.05) F = NavierStokesBrinkmannForm(W, w_sol1, nu, beta_gls=2.0) from firedrake import sin, grad, pi, sym, div, inner x, y = fd.SpatialCoordinate(mesh) u_mms = fd.as_vector( [sin(2.0 * pi * x) * sin(pi * y), sin(pi * x) * sin(2.0 * pi * y)]) p_mms = -0.5 * (u_mms[0]**2 + u_mms[1]**2) f_mms_u = (grad(u_mms) * u_mms + grad(p_mms) - 2.0 * nu * div(sym(grad(u_mms)))) f_mms_p = div(u_mms) F += -inner(f_mms_u, v) * dx - f_mms_p * q * dx bc1 = fd.DirichletBC(W.sub(0), u_mms, "on_boundary") bc2 = fd.DirichletBC(W.sub(1), p_mms, "on_boundary") solver_parameters = {"ksp_max_it": 200} problem1 = fd.NonlinearVariationalProblem(F, w_sol1, bcs=[bc1, bc2]) solver1 = NavierStokesBrinkmannSolver( problem1, options_prefix="navier_stokes", solver_parameters=solver_parameters, ) solver1.solve() u_sol, _ = w_sol1.split() fd.File("test_u_sol.pvd").write(u_sol) u_mms_func = fd.interpolate(u_mms, W.sub(0)) error = fd.errornorm(u_sol, u_mms_func) print(f"Error: {error}") return error
def __init__(self, equations, solution, fields, coupling, dt, bcs=None, solver_parameters={}, theta=1.0, predictor_solver_parameters={}, picard_iterations=1, pressure_nullspace=None): super().__init__(equations, solution, fields, coupling, dt, bcs=bcs, solver_parameters=solver_parameters) self.theta = firedrake.Constant(theta) self.theta_p = 1 # should not be used for now - maybe revisit with free surface terms self.predictor_solver_parameters = predictor_solver_parameters self.picard_iterations = picard_iterations self.pressure_nullspace = pressure_nullspace self.solution_old = firedrake.Function(self.solution) self.solution_lag = firedrake.Function(self.solution) self.u_test, self.p_test = firedrake.TestFunctions(self.solution.function_space()) # the predictor space is the same as the first sub-space of the solution space, but indexed independently mesh = self.solution.function_space().mesh() self.u_space = firedrake.FunctionSpace(mesh, self.solution.split()[0].ufl_element()) self.u_star_test = firedrake.TestFunction(self.u_space) self.u_star = firedrake.Function(self.u_space) self._initialized = False
def __init__(self, equations, solution, fields, coupling, dt, bcs=None, solver_parameters={}): """ :arg equations: the equations to solve :type equations: list of :class:`BaseEquation` objects :arg solution: :class:`Function` of MixedFunctionSpace where solution will be stored :arg fields: Dictionary of fields that are passed to the equation (any functions that are not part of the solution) :type fields: dict of :class:`Function` or :class:`Constant` objects :arg coupling: for each equation a map (dict) from field name to number of the (different) equation whose trial function should be used for that field name (see example below) :type coulings: list of dicts :arg float dt: time step in seconds :kwarg dict solver_parameters: PETSc solver options Example for coupling: Suppose we couple three equations: 0) a scalar adv. diff. equation for a density 1) a momentum equation to solve for velocity 2) a contintuity equation with associated trial function of pressure Then if equation 0) uses velocity of 1), equation 1) uses pressure of 2) and density of 0), and equation 2) is a continuity constraint on velocity of 1), we get: coupling = [{'velocity': 1}, {'pressure': 2, 'density': 0}, {'velocity': 1}] """ super(CoupledTimeIntegrator, self).__init__() self.equations = equations self.solution = solution self.test = firedrake.TestFunctions(solution.function_space()) self.fields = fields self.coupling = coupling self.dt = dt self.dt_const = firedrake.Constant(dt) self.bcs = bcs # unique identifier used in solver self.name = '-'.join([self.__class__.__name__] + [eq.__class__.__name__ for eq in self.equations]) self.solver_parameters = {} self.solver_parameters.update(solver_parameters)
V = fd.VectorFunctionSpace(mesh, "DQ", order - 1) T = fd.TensorFunctionSpace(mesh, "DQ", order - 1) else: RT = fd.FunctionSpace(mesh, "RT", order) DG = fd.FunctionSpace(mesh, "DG", order - 1) # Others function space V = fd.VectorFunctionSpace(mesh, "DG", order - 1) T = fd.TensorFunctionSpace(mesh, "DG", order - 1) W = RT * DG # test and trial functions on the subspaces of the mixed function spaces as # follows: :: u, p = fd.TrialFunctions(W) v, q = fd.TestFunctions(W) # ----- # 3.2) material property # perm lognormal Kinv = fd.Function(T, name="Kinv") k = np.random.randn(nx * n, ny * n) + par_a * np.random.randn(1) kf = ndimage.gaussian_filter(k, sigma) kl = np.exp(par_b + par_c * kf) * 1e-13 # m² if verbose: fig, axes = plt.subplots(ncols=3) img0 = axes[0].imshow(k, interpolation='nearest', cmap='viridis')
vDG = fd.VectorFunctionSpace(mesh, "DQ", 1) else: DG1 = fd.FunctionSpace(mesh, "DG", order) vDG1 = fd.VectorFunctionSpace(mesh, "DG", order) Mh = fd.FunctionSpace(mesh, "HDiv Trace", order) DG0 = fd.FunctionSpace(mesh, "DG", 0) vDG = fd.VectorFunctionSpace(mesh, "DG", 1) W = DG1 * vDG1 * Mh # 3.1) Define trial and test functions w = fd.Function(W) w.assign(0.0) ch, qh, lmbd_h = fd.split(w) wh, vh, mu_h = fd.TestFunctions(W) # 3.2) Set initial conditions # ---- previous solution # concentrations c0 = fd.Function(DG1, name="c0") c0.assign(0.0) # ---------------------- # 3.4) Variational Form # ---------------------- # coefficients dt = np.sqrt(tol) dtc = fd.Constant(dt) n = fd.FacetNormal(mesh) h = fd.sqrt(2) * fd.CellVolume(mesh) / fd.CellDiameter(mesh)