def AdvectionDiffusionGLS( V: fd.FunctionSpace, theta: fd.Function, phi: fd.Function, PeInv: float = 1e-4, phi_t: fd.Function = None, ): PeInv_ct = fd.Constant(PeInv) rho = fd.TestFunction(V) F = (inner(theta, grad(phi)) * rho + PeInv_ct * inner(grad(phi), grad(rho))) * dx if phi_t: F += phi_t * rho * dx h = fd.CellDiameter(V.ufl_domain()) R_U = dot(theta, grad(phi)) - PeInv_ct * div(grad(phi)) if phi_t: R_U += phi_t beta_gls = 0.9 tau_gls = beta_gls * ((4.0 * dot(theta, theta) / h**2) + 9.0 * (4.0 * PeInv_ct / h**2)**2)**(-0.5) theta_U = dot(theta, grad(rho)) - PeInv_ct * div(grad(rho)) F += tau_gls * inner(R_U, theta_U) * dx() return F
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 min_mesh_size(mesh): """Calculate minimum cell diameter in mesh Args: mesh ([type]): [description] Returns: float: [description] """ DG0 = fd.FunctionSpace(mesh, "DG", 0) h_sizes = fd.assemble( fd.CellDiameter(mesh) / fd.CellVolume(mesh) * fd.TestFunction(DG0) * fd.dx).dat.data_ro local_min_size = np.max(h_sizes) min_size = mesh.comm.allreduce(local_min_size, op=MPI.MAX) return min_size
def penalty(u): r"""Return the penalty of the shallow ice action functional The penalty of the shallow ice action functional is .. math:: E(u) = \frac{1}{2}\int_\Omega l^2\nabla u\cdot \nabla u\; dx Parameters ---------- u : firedrake.Function ice velocity Returns ------- firedrake.Form """ l = 2 * firedrake.CellDiameter(u.ufl_domain()) return .5 * l**2 * inner(grad(u), grad(u))
def penalty(**kwargs): r"""Return the penalty of the shallow ice action functional The penalty for the shallow ice action functional is .. math:: E(u) = \frac{1}{2}\int_\Omega l^2\nabla u\cdot \nabla u\; dx Parameters ---------- velocity : firedrake.Function thickness : firedrake.Function Returns ------- firedrake.Form """ u, h = get_kwargs_alt(kwargs, ('velocity', 'thickness'), ('u', 'h')) l = 2 * firedrake.max_value(firedrake.CellDiameter(u.ufl_domain()), 5 * h) return .5 * l**2 * inner(grad(u), grad(u))
def heat_exchanger_optimization(mu=0.03, n_iters=1000): output_dir = "2D/" path = os.path.abspath(__file__) dir_path = os.path.dirname(path) mesh = fd.Mesh(f"{dir_path}/2D_mesh.msh") # Perturb the mesh coordinates. Necessary to calculate shape derivatives S = fd.VectorFunctionSpace(mesh, "CG", 1) s = fd.Function(S, name="deform") mesh.coordinates.assign(mesh.coordinates + s) # Initial level set function x, y = fd.SpatialCoordinate(mesh) PHI = fd.FunctionSpace(mesh, "CG", 1) phi_expr = sin(y * pi / 0.2) * cos(x * pi / 0.2) - fd.Constant(0.8) # Avoid recording the operation interpolate into the tape. # Otherwise, the shape derivatives will not be correct with fda.stop_annotating(): phi = fd.interpolate(phi_expr, PHI) phi.rename("LevelSet") fd.File(output_dir + "phi_initial.pvd").write(phi) # Physics mu = fd.Constant(mu) # viscosity alphamin = 1e-12 alphamax = 2.5 / (2e-4) parameters = { "mat_type": "aij", "ksp_type": "preonly", "ksp_converged_reason": None, "pc_type": "lu", "pc_factor_mat_solver_type": "mumps", } stokes_parameters = parameters temperature_parameters = parameters u_inflow = 2e-3 tin1 = fd.Constant(10.0) tin2 = fd.Constant(100.0) P2 = fd.VectorElement("CG", mesh.ufl_cell(), 2) P1 = fd.FiniteElement("CG", mesh.ufl_cell(), 1) TH = P2 * P1 W = fd.FunctionSpace(mesh, TH) U = fd.TrialFunction(W) u, p = fd.split(U) V = fd.TestFunction(W) v, q = fd.split(V) epsilon = fd.Constant(10000.0) def hs(phi, epsilon): return fd.Constant(alphamax) * fd.Constant(1.0) / ( fd.Constant(1.0) + exp(-epsilon * phi)) + fd.Constant(alphamin) def stokes(phi, BLOCK_INLET_MOUTH, BLOCK_OUTLET_MOUTH): a_fluid = mu * inner(grad(u), grad(v)) - div(v) * p - q * div(u) darcy_term = inner(u, v) return (a_fluid * dx + hs(phi, epsilon) * darcy_term * dx(0) + alphamax * darcy_term * (dx(BLOCK_INLET_MOUTH) + dx(BLOCK_OUTLET_MOUTH))) # Dirichlet boundary conditions inflow1 = fd.as_vector([ u_inflow * sin( ((y - (line_sep - (dist_center + inlet_width))) * pi) / inlet_width), 0.0, ]) inflow2 = fd.as_vector([ u_inflow * sin(((y - (line_sep + dist_center)) * pi) / inlet_width), 0.0, ]) noslip = fd.Constant((0.0, 0.0)) # Stokes 1 bcs1_1 = fd.DirichletBC(W.sub(0), noslip, WALLS) bcs1_2 = fd.DirichletBC(W.sub(0), inflow1, INLET1) bcs1_3 = fd.DirichletBC(W.sub(1), fd.Constant(0.0), OUTLET1) bcs1_4 = fd.DirichletBC(W.sub(0), noslip, INLET2) bcs1_5 = fd.DirichletBC(W.sub(0), noslip, OUTLET2) bcs1 = [bcs1_1, bcs1_2, bcs1_3, bcs1_4, bcs1_5] # Stokes 2 bcs2_1 = fd.DirichletBC(W.sub(0), noslip, WALLS) bcs2_2 = fd.DirichletBC(W.sub(0), inflow2, INLET2) bcs2_3 = fd.DirichletBC(W.sub(1), fd.Constant(0.0), OUTLET2) bcs2_4 = fd.DirichletBC(W.sub(0), noslip, INLET1) bcs2_5 = fd.DirichletBC(W.sub(0), noslip, OUTLET1) bcs2 = [bcs2_1, bcs2_2, bcs2_3, bcs2_4, bcs2_5] # Forward problems U1, U2 = fd.Function(W), fd.Function(W) L = inner(fd.Constant((0.0, 0.0, 0.0)), V) * dx problem = fd.LinearVariationalProblem(stokes(-phi, INMOUTH2, OUTMOUTH2), L, U1, bcs=bcs1) solver_stokes1 = fd.LinearVariationalSolver( problem, solver_parameters=stokes_parameters, options_prefix="stokes_1") solver_stokes1.solve() problem = fd.LinearVariationalProblem(stokes(phi, INMOUTH1, OUTMOUTH1), L, U2, bcs=bcs2) solver_stokes2 = fd.LinearVariationalSolver( problem, solver_parameters=stokes_parameters, options_prefix="stokes_2") solver_stokes2.solve() # Convection difussion equation ks = fd.Constant(1e0) cp_value = 5.0e5 cp = fd.Constant(cp_value) T = fd.FunctionSpace(mesh, "DG", 1) t = fd.Function(T, name="Temperature") w = fd.TestFunction(T) # Mesh-related functions n = fd.FacetNormal(mesh) h = fd.CellDiameter(mesh) u1, p1 = fd.split(U1) u2, p2 = fd.split(U2) def upwind(u): return (dot(u, n) + abs(dot(u, n))) / 2.0 u1n = upwind(u1) u2n = upwind(u2) # Penalty term alpha = fd.Constant(500.0) # Bilinear form a_int = dot(grad(w), ks * grad(t) - cp * (u1 + u2) * t) * dx a_fac = (fd.Constant(-1.0) * ks * dot(avg(grad(w)), jump(t, n)) * dS + fd.Constant(-1.0) * ks * dot(jump(w, n), avg(grad(t))) * dS + ks("+") * (alpha("+") / avg(h)) * dot(jump(w, n), jump(t, n)) * dS) a_vel = (dot( jump(w), cp * (u1n("+") + u2n("+")) * t("+") - cp * (u1n("-") + u2n("-")) * t("-"), ) * dS + dot(w, cp * (u1n + u2n) * t) * ds) a_bnd = (dot(w, cp * dot(u1 + u2, n) * t) * (ds(INLET1) + ds(INLET2)) + w * t * (ds(INLET1) + ds(INLET2)) - w * tin1 * ds(INLET1) - w * tin2 * ds(INLET2) + alpha / h * ks * w * t * (ds(INLET1) + ds(INLET2)) - ks * dot(grad(w), t * n) * (ds(INLET1) + ds(INLET2)) - ks * dot(grad(t), w * n) * (ds(INLET1) + ds(INLET2))) aT = a_int + a_fac + a_vel + a_bnd LT_bnd = (alpha / h * ks * tin1 * w * ds(INLET1) + alpha / h * ks * tin2 * w * ds(INLET2) - tin1 * ks * dot(grad(w), n) * ds(INLET1) - tin2 * ks * dot(grad(w), n) * ds(INLET2)) problem = fd.LinearVariationalProblem(derivative(aT, t), LT_bnd, t) solver_temp = fd.LinearVariationalSolver( problem, solver_parameters=temperature_parameters, options_prefix="temperature", ) solver_temp.solve() # fd.solve(eT == 0, t, solver_parameters=temperature_parameters) # Cost function: Flux at the cold outlet scale_factor = 4e-4 Jform = fd.assemble( fd.Constant(-scale_factor * cp_value) * inner(t * u1, n) * ds(OUTLET1)) # Constraints: Pressure drop on each fluid power_drop = 1e-2 Power1 = fd.assemble(p1 / power_drop * ds(INLET1)) Power2 = fd.assemble(p2 / power_drop * ds(INLET2)) phi_pvd = fd.File("phi_evolution.pvd") def deriv_cb(phi): with stop_annotating(): phi_pvd.write(phi[0]) c = fda.Control(s) # Reduced Functionals Jhat = LevelSetFunctional(Jform, c, phi, derivative_cb_pre=deriv_cb) P1hat = LevelSetFunctional(Power1, c, phi) P1control = fda.Control(Power1) P2hat = LevelSetFunctional(Power2, c, phi) P2control = fda.Control(Power2) Jhat_v = Jhat(phi) print("Initial cost function value {:.5f}".format(Jhat_v), flush=True) print("Power drop 1 {:.5f}".format(Power1), flush=True) print("Power drop 2 {:.5f}".format(Power2), flush=True) beta_param = 0.08 # Regularize the shape derivatives only in the domain marked with 0 reg_solver = RegularizationSolver(S, mesh, beta=beta_param, gamma=1e5, dx=dx, design_domain=0) tol = 1e-5 dt = 0.05 params = { "alphaC": 1.0, "debug": 5, "alphaJ": 1.0, "dt": dt, "K": 1e-3, "maxit": n_iters, "maxtrials": 5, "itnormalisation": 10, "tol_merit": 5e-3, # new merit can be within 0.5% of the previous merit # "normalize_tol" : -1, "tol": tol, } solver_parameters = { "reinit_solver": { "h_factor": 2.0, } } # Optimization problem problem = InfDimProblem( Jhat, reg_solver, ineqconstraints=[ Constraint(P1hat, 1.0, P1control), Constraint(P2hat, 1.0, P2control), ], solver_parameters=solver_parameters, ) results = nlspace_solve(problem, params) return results
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) # advective velocity velocity = fd.Function(vDG1) velocity.interpolate(v_inlet) vnorm = fd.sqrt(fd.dot(velocity, velocity)) # upwind term vn = 0.5 * (fd.dot(velocity, n) + abs(fd.dot(velocity, n))) cupw = fd.conditional(fd.dot(velocity, n) > 0, ch, lmbd_h) # Diffusion tensor Diff = fd.Identity(mesh.geometric_dimension()) * Dm # Diff = fd.Identity(mesh.geometric_dimension())*(Dm + d_t*vnorm) + \ # fd.Constant(d_l-d_t)*fd.outer(velocity, velocity)/vnorm
def penalty_low(**kwargs): u = kwargs["velocity"] h = kwargs["thickness"] l = 2 * max_value(firedrake.CellDiameter(u.ufl_domain()), h) return 0.5 * l**2 * inner(grad(u), grad(u))
def sdhm_form(self, problem, mesh, bcs_p, bcs_u): rho = problem.rho mu = problem.mu k = problem.k f = problem.f q, p, lambda_h = fire.split(self.solution) w, v, mu_h = fire.TestFunctions(self._W) n = fire.FacetNormal(mesh) h = fire.CellDiameter(mesh) # Stabilizing parameters has_mesh_characteristic_length = True beta_0 = fire.Constant(1e-15) delta_0 = fire.Constant(1) delta_1 = fire.Constant(-1 / 2) delta_2 = fire.Constant(1 / 2) delta_3 = fire.Constant(1 / 2) # h_avg = (h('+') + h('-')) / 2. beta = beta_0 / h beta_avg = beta_0 / h("+") 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 # Hybridization terms a += lambda_h("+") * dot(w, n)("+") * dS + mu_h("+") * dot(q, n)("+") * dS a += beta_avg * kappa("+") * (lambda_h("+") - p("+")) * (mu_h("+") - v("+")) * dS # Add the contributions of the pressure boundary conditions to L primal_bc_markers = list(mesh.exterior_facets.unique_markers) for pboundary, iboundary in bcs_p: primal_bc_markers.remove(iboundary) a += (pboundary * dot(w, n) + mu_h * dot(q, n)) * ds(iboundary) a += beta * kappa * (lambda_h - pboundary) * mu_h * ds(iboundary) unprescribed_primal_bc = primal_bc_markers for bc_marker in unprescribed_primal_bc: a += (lambda_h * dot(w, n) + mu_h * dot(q, n)) * ds(bc_marker) a += beta * kappa * lambda_h * mu_h * ds(bc_marker) # Add the (weak) contributions of the velocity boundary conditions to L for uboundary, iboundary, component in bcs_u: if component is not None: dim = mesh.geometric_dimension() bc_array = [] for _ in range(dim): bc_array.append(0.0) bc_array[component] = uboundary bc_as_vector = fire.Constant(bc_array) L += mu_h * dot(bc_as_vector, n) * ds(iboundary) else: L += mu_h * dot(uboundary, 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 cgls_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) # Stabilizing parameters h = fire.CellDiameter(mesh) 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) # Some good stabilizing methods that I use: # 1) CLGS (Correa and Loula method, it's a Galerkin Least-Squares residual formulation): # * delta_0 = 1 # * delta_1 = -1/2 # * delta_2 = 1/2 # * delta_3 = 1/2 # 2) CLGS (Div): # * delta_0 = 1 # * delta_1 = -1/2 # * delta_2 = 1/2 # * delta_3 = 0 # 3) Original Hughes's adjoint (variational multiscale) method (HVM): # * delta_0 = -1 # * delta_1 = 1/2 # * delta_2 = 0 # * delta_3 = 0 # 4) HVM (Div): # * delta_0 = -1 # * delta_1 = 1/2 # * delta_2 = 1/2 # * delta_3 = 0 # 5) Enhanced HVM (eHVM, this one is proposed by me. It was never published before): # * delta_0 = -1 # * delta_1 = 1/2 # * delta_2 = 1/2 # * delta_3 = 1/2 # I'm currently investigating these modifications in my thesis. They work good for DG methods. 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 # 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