def update(self, x, flag, iteration): """Update domain and solution to state and adjoint equation.""" if self.Q.update_domain(x): try: # We use pyadjoint to calculate adjoint and shape derivatives, # in order to do this we need to "record a tape of the forward # solve", pyadjoint will then figure out all necessary # adjoints. import firedrake_adjoint as fda tape = fda.get_working_tape() tape.clear_tape() # ensure we are annotating from pyadjoint.tape import annotate_tape safety_counter = 0 while not annotate_tape(): safety_counter += 1 fda.continue_annotation() if safety_counter > 1e2: import sys sys.exit('Cannot annotate even after 100 attempts.') mesh_m = self.J.Q.mesh_m s = fd.Function(self.J.V_m) mesh_m.coordinates.assign(mesh_m.coordinates + s) self.s = s self.c = fda.Control(s) self.e.solve() Jpyadj = fd.assemble(self.J.value_form()) self.Jred = fda.ReducedFunctional(Jpyadj, self.c) fda.pause_annotation() except fd.ConvergenceError: if self.cb is not None: self.cb() raise if iteration >= 0 and self.cb is not None: self.cb()
def update(self, x, flag, iteration): """Update domain and solution to state and adjoint equation.""" if self.Q.update_domain(x): try: # We use pyadjoint to calculate adjoint and shape derivatives, # in order to do this we need to "record a tape of the forward # solve", pyadjoint will then figure out all necessary # adjoints. tape = fda.get_working_tape() tape.clear_tape() fda.continue_annotation() mesh_m = self.J.Q.mesh_m s = fda.Function(self.J.V_m) mesh_m.coordinates.assign(mesh_m.coordinates + s) self.s = s self.c = fda.Control(s) self.e.solve() Jpyadj = fda.assemble(self.J.value_form()) self.Jred = fda.ReducedFunctional(Jpyadj, self.c) fda.pause_annotation() except fd.ConvergenceError: if self.cb is not None: self.cb() raise if iteration >= 0 and self.cb is not None: self.cb()
def test_line_search(): mesh = fd.UnitSquareMesh(50, 50) # Shape derivative S = fd.VectorFunctionSpace(mesh, "CG", 1) s = fd.Function(S, name="deform") mesh.coordinates.assign(mesh.coordinates + s) # Level set PHI = fd.FunctionSpace(mesh, "CG", 1) x, y = fd.SpatialCoordinate(mesh) with fda.stop_annotating(): phi = fd.interpolate(-(x - 0.5), PHI) phi.rename("original") solver_parameters = { "ts_atol": 1e-4, "ts_rtol": 1e-4, "ts_dt": 1e-2, "ts_exact_final_time": "matchstep", "ts_monitor": None, } ## Search direction with fda.stop_annotating(): delta_x = fd.interpolate(fd.as_vector([-100 * x, 0.0]), S) delta_x.rename("velocity") # Cost function J = fd.assemble(hs(-phi) * dx) # Reduced Functional c = fda.Control(s) Jhat = LevelSetFunctional(J, c, phi) # InfDim Problem beta_param = 0.08 reg_solver = RegularizationSolver(S, mesh, beta=beta_param, gamma=1e5, dx=dx) solver_parameters = {"hj_solver": solver_parameters} problem = InfDimProblem(Jhat, reg_solver, solver_parameters=solver_parameters) new_phi = fd.Function(PHI, name="new_ls") orig_phi = fd.Function(PHI) with fda.stop_annotating(): orig_phi.assign(phi) problem.delta_x.assign(delta_x) AJ, AC = 1.0, 1.0 C = np.array([]) merit = merit_eval_new(AJ, J, AC, C) rtol = 1e-4 new_phi, newJ, newG, newH = line_search( problem, orig_phi, new_phi, merit_eval_new, merit, AJ, AC, dt=1.0, tol_merit=rtol, maxtrials=20, ) assert_allclose(newJ, J, rtol=rtol)
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
def gradient_test_acoustic(model, mesh, V, comm, vp_exact, vp_guess, mask=None): #{{{ import firedrake_adjoint as fire_adj with fire_adj.stop_annotating(): if comm.comm.rank == 0: print('######## Starting gradient test ########', flush=True) sources = spyro.Sources(model, mesh, V, comm) receivers = spyro.Receivers(model, mesh, V, comm) wavelet = spyro.full_ricker_wavelet( model["timeaxis"]["dt"], model["timeaxis"]["tf"], model["acquisition"]["frequency"], ) point_cloud = receivers.set_point_cloud(comm) # simulate the exact model if comm.comm.rank == 0: print('######## Running the exact model ########', flush=True) p_exact_recv = forward(model, mesh, comm, vp_exact, sources, wavelet, point_cloud) # simulate the guess model if comm.comm.rank == 0: print('######## Running the guess model ########', flush=True) p_guess_recv, Jm = forward(model, mesh, comm, vp_guess, sources, wavelet, point_cloud, fwi=True, true_rec=p_exact_recv) if comm.comm.rank == 0: print("\n Cost functional at fixed point : " + str(Jm) + " \n ", flush=True) # compute the gradient of the control (to be verified) if comm.comm.rank == 0: print( '######## Computing the gradient by automatic differentiation ########', flush=True) control = fire_adj.Control(vp_guess) dJ = fire_adj.compute_gradient(Jm, control) if mask: dJ *= mask # File("gradient.pvd").write(dJ) #steps = [1e-3, 1e-4, 1e-5, 1e-6, 1e-7] # step length #steps = [1e-4, 1e-5, 1e-6, 1e-7] # step length steps = [1e-5, 1e-6, 1e-7, 1e-8] # step length with fire_adj.stop_annotating(): delta_m = Function(V) # model direction (random) delta_m.assign(dJ) Jhat = fire_adj.ReducedFunctional(Jm, control) derivative = enlisting.Enlist(Jhat.derivative()) hs = enlisting.Enlist(delta_m) projnorm = sum(hi._ad_dot(di) for hi, di in zip(hs, derivative)) # this deepcopy is important otherwise pertubations accumulate vp_original = vp_guess.copy(deepcopy=True) if comm.comm.rank == 0: print( '######## Computing the gradient by finite diferences ########', flush=True) errors = [] for step in steps: # range(3): # steps.append(step) # perturb the model and calculate the functional (again) # J(m + delta_m*h) vp_guess = vp_original + step * delta_m p_guess_recv, Jp = forward(model, mesh, comm, vp_guess, sources, wavelet, point_cloud, fwi=True, true_rec=p_exact_recv) fd_grad = (Jp - Jm) / step if comm.comm.rank == 0: print("\n Cost functional for step " + str(step) + " : " + str(Jp) + ", fd approx.: " + str(fd_grad) + ", grad'*dir : " + str(projnorm) + " \n ", flush=True) errors.append(100 * ((fd_grad - projnorm) / projnorm)) fire_adj.get_working_tape().clear_tape() # all errors less than 1 % errors = np.array(errors) assert (np.abs(errors) < 5.0).all()
def compliance(): parser = argparse.ArgumentParser(description="Compliance problem with MMA") parser.add_argument( "--nref", action="store", dest="nref", type=int, help="Number of mesh refinements", default=2, ) parser.add_argument( "--uniform", action="store", dest="uniform", type=int, help="Use uniform mesh", default=0, ) parser.add_argument( "--inner_product", action="store", dest="inner_product", type=str, help="Inner product, euclidean or L2", default="L2", ) parser.add_argument( "--output_dir", action="store", dest="output_dir", type=str, help="Directory for all the output", default="./", ) args = parser.parse_args() nref = args.nref inner_product = args.inner_product output_dir = args.output_dir assert inner_product == "L2" or inner_product == "euclidean" mesh = fd.Mesh("./beam_uniform.msh") #mh = fd.MeshHierarchy(mesh, 2) #mesh = mh[-1] if nref > 0: mh = fd.MeshHierarchy(mesh, nref) mesh = mh[-1] elif nref < 0: raise RuntimeError("Non valid mesh argument") V = fd.VectorFunctionSpace(mesh, "CG", 1) u, v = fd.TrialFunction(V), fd.TestFunction(V) # Elasticity parameters E, nu = 1e0, 0.3 mu, lmbda = fd.Constant(E / (2 * (1 + nu))), fd.Constant( E * nu / ((1 + nu) * (1 - 2 * nu)) ) # Helmholtz solver RHO = fd.FunctionSpace(mesh, "CG", 1) rho = fd.interpolate(fd.Constant(0.1), RHO) af, b = fd.TrialFunction(RHO), fd.TestFunction(RHO) #filter_radius = fd.Constant(0.2) #x, y = fd.SpatialCoordinate(mesh) #x_ = fd.interpolate(x, RHO) #y_ = fd.interpolate(y, RHO) #aH = filter_radius * inner(grad(af), grad(b)) * dx + af * b * dx #LH = rho * b * dx rhof = fd.Function(RHO) solver_params = { "ksp_type": "preonly", "pc_type": "lu", "pc_factor_mat_solver_type": "mumps", "mat_mumps_icntl_14": 200, "mat_mumps_icntl_24": 1, } #fd.solve(aH == LH, rhof, solver_parameters=solver_params) rhof.assign(rho) rhofControl = fda.Control(rhof) eps = fd.Constant(1e-5) p = fd.Constant(3.0) def simp(rho): return eps + (fd.Constant(1.0) - eps) * rho ** p def epsilon(v): return sym(nabla_grad(v)) def sigma(v): return 2.0 * mu * epsilon(v) + lmbda * tr(epsilon(v)) * Identity(2) DIRICHLET = 3 NEUMANN = 4 a = inner(simp(rhof) * sigma(u), epsilon(v)) * dx load = fd.Constant((0.0, -1.0)) L = inner(load, v) * ds(NEUMANN) u_sol = fd.Function(V) bcs = fd.DirichletBC(V, fd.Constant((0.0, 0.0)), DIRICHLET) fd.solve(a == L, u_sol, bcs=bcs, solver_parameters=solver_params) c = fda.Control(rho) J = fd.assemble(fd.Constant(1e-4) * inner(u_sol, load) * ds(NEUMANN)) Vol = fd.assemble(rhof * dx) VolControl = fda.Control(Vol) with fda.stop_annotating(): Vlimit = fd.assemble(fd.Constant(1.0) * dx(domain=mesh)) * 0.5 rho_viz_f = fd.Function(RHO, name="rho") plot_file = f"{output_dir}/design_{inner_product}.pvd" controls_f = fd.File(plot_file) def deriv_cb(j, dj, rho): with fda.stop_annotating(): rho_viz_f.assign(rhofControl.tape_value()) controls_f.write(rho_viz_f) Jhat = fda.ReducedFunctional(J, c, derivative_cb_post=deriv_cb) Volhat = fda.ReducedFunctional(Vol, c) class VolumeConstraint(fda.InequalityConstraint): def __init__(self, Vhat, Vlimit, VolControl): self.Vhat = Vhat self.Vlimit = float(Vlimit) self.VolControl = VolControl def function(self, m): # Compute the integral of the control over the domain integral = self.VolControl.tape_value() with fda.stop_annotating(): value = -integral / self.Vlimit + 1.0 return [value] def jacobian(self, m): with fda.stop_annotating(): gradients = self.Vhat.derivative() with gradients.dat.vec as v: v.scale(-1.0 / self.Vlimit) return [gradients] def output_workspace(self): return [0.0] def length(self): """Return the number of components in the constraint vector (here, one).""" return 1 lb = 1e-5 ub = 1.0 problem = fda.MinimizationProblem( Jhat, bounds=(lb, ub), constraints=[VolumeConstraint(Volhat, Vlimit, VolControl)], ) parameters_mma = { "move": 0.2, "maximum_iterations": 200, "m": 1, "IP": 0, "tol": 1e-6, "accepted_tol": 1e-4, "norm": inner_product, #"norm": "euclidean", "gcmma": False, } solver = MMASolver(problem, parameters=parameters_mma) rho_opt = solver.solve() with open(f"{output_dir}/finished_{inner_product}.txt", "w") as f: f.write("Done")
def compliance_optimization(n_iters=200): output_dir = "cantilever/" path = os.path.abspath(__file__) dir_path = os.path.dirname(path) m = fd.Mesh(f"{dir_path}/mesh_cantilever.msh") mesh = fd.MeshHierarchy(m, 0)[-1] # 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) lx = 2.0 ly = 1.0 phi_expr = ( -cos(6.0 / lx * pi * x) * cos(4.0 * pi * y) - 0.6 + max_value(200.0 * (0.01 - x ** 2 - (y - ly / 2) ** 2), 0.0) + max_value(100.0 * (x + y - lx - ly + 0.1), 0.0) + max_value(100.0 * (x - y - lx + 0.1), 0.0) ) # 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. Elasticity rho_min = 1e-5 beta = fd.Constant(200.0) def hs(phi, beta): return fd.Constant(1.0) / ( fd.Constant(1.0) + exp(-beta * phi) ) + fd.Constant(rho_min) H1_elem = fd.VectorElement("CG", mesh.ufl_cell(), 1) W = fd.FunctionSpace(mesh, H1_elem) u = fd.TrialFunction(W) v = fd.TestFunction(W) # Elasticity parameters E, nu = 1.0, 0.3 mu, lmbda = fd.Constant(E / (2 * (1 + nu))), fd.Constant( E * nu / ((1 + nu) * (1 - 2 * nu)) ) def epsilon(u): return sym(nabla_grad(u)) def sigma(v): return 2.0 * mu * epsilon(v) + lmbda * tr(epsilon(v)) * Identity(2) a = inner(hs(-phi, beta) * sigma(u), nabla_grad(v)) * dx t = fd.Constant((0.0, -75.0)) L = inner(t, v) * ds(2) bc = fd.DirichletBC(W, fd.Constant((0.0, 0.0)), 1) parameters = { "ksp_type": "preonly", "pc_type": "lu", "mat_type": "aij", "ksp_converged_reason": None, "pc_factor_mat_solver_type": "mumps", } u_sol = fd.Function(W) F = fd.action(a, u_sol) - L problem = fd.NonlinearVariationalProblem(F, u_sol, bcs=bc) solver = fd.NonlinearVariationalSolver( problem, solver_parameters=parameters ) solver.solve() # fd.solve( # a == L, u_sol, bcs=[bc], solver_parameters=parameters # ) # , nullspace=nullspace) with fda.stop_annotating(): fd.File("u_sol.pvd").write(u_sol) # Cost function: Compliance J = fd.assemble( fd.Constant(1e-2) * inner(hs(-phi, beta) * sigma(u_sol), epsilon(u_sol)) * dx ) # Constraint: Volume with fda.stop_annotating(): total_volume = fd.assemble(fd.Constant(1.0) * dx(domain=mesh)) VolPen = fd.assemble(hs(-phi, beta) * dx) # Needed to track the value of the volume VolControl = fda.Control(VolPen) Vval = total_volume / 2.0 phi_pvd = fd.File("phi_evolution.pvd", target_continuity=fd.H1) def deriv_cb(phi): with fda.stop_annotating(): phi_pvd.write(phi[0]) c = fda.Control(s) Jhat = LevelSetFunctional(J, c, phi, derivative_cb_pre=deriv_cb) Vhat = LevelSetFunctional(VolPen, c, phi) beta_param = 0.1 # Boundary conditions for the shape derivatives. # They must be zero at the boundary conditions. bcs_vel = fd.DirichletBC(S, fd.Constant((0.0, 0.0)), (1, 2)) # Regularize the shape derivatives reg_solver = RegularizationSolver( S, mesh, beta=beta_param, gamma=1.0e5, dx=dx, bcs=bcs_vel, output_dir=None, ) # Hamilton-Jacobi equation to advect the level set dt = 0.05 tol = 1e-5 # Optimization problem vol_constraint = Constraint(Vhat, Vval, VolControl) problem = InfDimProblem(Jhat, reg_solver, ineqconstraints=vol_constraint) parameters = { "ksp_type": "preonly", "pc_type": "lu", "mat_type": "aij", "ksp_converged_reason": None, "pc_factor_mat_solver_type": "mumps", } params = { "alphaC": 3.0, "K": 0.1, "debug": 5, "alphaJ": 1.0, "dt": dt, "maxtrials": 10, "maxit": n_iters, "itnormalisation": 50, "tol": tol, } results = nlspace_solve(problem, params) return results
def main(): mesh = fd.Mesh("./mesh_stokes.msh") mh = fd.MeshHierarchy(mesh, 1) mesh = mh[-1] # mesh = fd.Mesh("./mesh_stokes_inlets.msh") S = fd.VectorFunctionSpace(mesh, "CG", 1) s = fd.Function(S, name="deform") mesh.coordinates.assign(mesh.coordinates + s) x, y = fd.SpatialCoordinate(mesh) PHI = fd.FunctionSpace(mesh, "DG", 1) lx = 1.0 # phi_expr = -0.5 * cos(3.0 / lx * pi * x + 1.0) * cos(3.0 * pi * y) - 0.3 lx = 2.0 phi_expr = -cos(6.0 / lx * pi * x + 1.0) * cos(4.0 * pi * y) - 0.6 with stop_annotating(): phi = fd.interpolate(phi_expr, PHI) phi.rename("LevelSet") nu = fd.Constant(1.0) V = fd.VectorFunctionSpace(mesh, "CG", 1) P = fd.FunctionSpace(mesh, "CG", 1) W = V * P w_sol = fd.Function(W) brinkmann_penalty = 1e6 F = NavierStokesBrinkmannForm(W, w_sol, phi, nu, brinkmann_penalty=brinkmann_penalty) x, y = fd.SpatialCoordinate(mesh) u_inflow = 1.0 y_inlet_1_1 = 0.2 y_inlet_1_2 = 0.4 inflow1 = fd.as_vector([ u_inflow * 100 * (y - y_inlet_1_1) * (y - y_inlet_1_2), 0.0, ]) y_inlet_2_1 = 0.6 y_inlet_2_2 = 0.8 inflow2 = fd.as_vector([ u_inflow * 100 * (y - y_inlet_2_1) * (y - y_inlet_2_2), 0.0, ]) noslip = fd.Constant((0.0, 0.0)) bc1 = fd.DirichletBC(W.sub(0), noslip, 5) bc2 = fd.DirichletBC(W.sub(0), inflow1, (1)) bc3 = fd.DirichletBC(W.sub(0), inflow2, (2)) bcs = [bc1, bc2, bc3] problem = fd.NonlinearVariationalProblem(F, w_sol, bcs=bcs) solver_parameters = { "ksp_type": "preonly", "pc_type": "lu", "mat_type": "aij", "ksp_converged_reason": None, "pc_factor_mat_solver_type": "mumps", } # solver_parameters = { # "ksp_type": "fgmres", # "pc_type": "hypre", # "pc_hypre_type": "euclid", # "pc_hypre_euclid_level": 5, # "mat_type": "aij", # "ksp_converged_reason": None, # "ksp_atol": 1e-3, # "ksp_rtol": 1e-3, # "snes_atol": 1e-3, # "snes_rtol": 1e-3, # } solver = NavierStokesBrinkmannSolver(problem, solver_parameters=solver_parameters) solver.solve() pvd_file = fd.File("ns_solution.pvd") u, p = w_sol.split() pvd_file.write(u, p) u, p = fd.split(w_sol) Vol = fd.assemble(hs(-phi) * fd.Constant(1.0) * dx(0, domain=mesh)) VControl = fda.Control(Vol) Vval = fd.assemble(fd.Constant(0.5) * dx(domain=mesh), annotate=False) with stop_annotating(): print("Initial constraint function value {}".format(Vol)) J = fd.assemble( fd.Constant(brinkmann_penalty) * hs(phi) * inner(u, u) * dx(0) + nu / fd.Constant(2.0) * inner(grad(u), grad(u)) * dx) c = fda.Control(s) phi_pvd = fd.File("phi_evolution_euclid.pvd", target_continuity=fd.H1) def deriv_cb(phi): with stop_annotating(): phi_pvd.write(phi[0]) Jhat = LevelSetFunctional(J, c, phi, derivative_cb_pre=deriv_cb) Vhat = LevelSetFunctional(Vol, c, phi) bcs_vel_1 = fd.DirichletBC(S, noslip, (1, 2, 3, 4)) bcs_vel = [bcs_vel_1] reg_solver = RegularizationSolver(S, mesh, beta=0.5, gamma=1e5, dx=dx, bcs=bcs_vel, design_domain=0) tol = 1e-5 dt = 0.0002 params = { "alphaC": 1.0, "debug": 5, "alphaJ": 1.0, "dt": dt, "K": 0.1, "maxit": 2000, "maxtrials": 5, "itnormalisation": 10, "tol_merit": 1e-4, # new merit can be within 5% of the previous merit # "normalize_tol" : -1, "tol": tol, } problem = InfDimProblem( Jhat, reg_solver, ineqconstraints=[Constraint(Vhat, Vval, VControl)], ) _ = nullspace_shape(problem, params)
def compliance_bridge(): parser = argparse.ArgumentParser(description="Heat exchanger") parser.add_argument( "--n_iters", dest="n_iters", type=int, action="store", default=1000, help="Number of optimization iterations", ) parser.add_argument( "--output_dir", dest="output_dir", type=str, action="store", default="./", help="Output directory", ) opts = parser.parse_args() output_dir = opts.output_dir # Elasticity parameters E, nu = 1.0, 0.3 rho_min = fd.Constant(1e-4) # Min Vol fraction eps = fd.Constant(100.0) # Heaviside parameter mu, lmbda = fd.Constant(E / (2 * (1 + nu))), fd.Constant( E * nu / ((1 + nu) * (1 - 2 * nu))) mesh = fd.RectangleMesh(20, 40, 0.5, 1, quadrilateral=True) mh = fd.MeshHierarchy(mesh, 1) m = fd.ExtrudedMeshHierarchy(mh, height=1, base_layer=40) mesh = m[-1] S = fd.VectorFunctionSpace(mesh, "CG", 1) s = fd.Function(S, name="deform") mesh.coordinates.assign(mesh.coordinates + s) x, y, z = fd.SpatialCoordinate(mesh) PHI = fd.FunctionSpace(mesh, "CG", 1) lx = 1.0 ly = 2.0 lz = ly phi_expr = (-cos(4.0 / lx * pi * x) * cos(4.0 * pi / ly * y) * cos(4.0 / lz * pi * z) - 0.6) with fda.stop_annotating(): phi = fd.interpolate(-phi_expr, PHI) phi.rename("LevelSet") H1 = fd.VectorElement("CG", mesh.ufl_cell(), 1) W = fd.FunctionSpace(mesh, H1) print(f"DOFS: {W.dim()}") modes = [fd.Function(W) for _ in range(6)] modes[0].interpolate(fd.Constant([1, 0, 0])) modes[1].interpolate(fd.Constant([0, 1, 0])) modes[2].interpolate(fd.Constant([0, 0, 1])) modes[3].interpolate(fd.as_vector([0, z, -y])) modes[4].interpolate(fd.as_vector([-z, 0, x])) modes[5].interpolate(fd.as_vector([y, -x, 0])) nullmodes = fd.VectorSpaceBasis(modes) # Make sure they're orthonormal. nullmodes.orthonormalize() u = fd.TrialFunction(W) v = fd.TestFunction(W) def epsilon(u): return sym(nabla_grad(u)) def sigma(v): return 2.0 * mu * epsilon(v) + lmbda * tr(epsilon(v)) * Identity(3) # Variational forms a = inner(hs(phi, eps, min_value=rho_min) * sigma(u), nabla_grad(v)) * dx(degree=2) t = fd.Constant((0.0, 0.0, -1.0e-1)) L = inner(t, v) * ds_t # Dirichlet BCs ylimits = (0.2, 1.8) xlimits = (0.4, 0.6) I_BC = create_function_marker(PHI, W, xlimits, ylimits) bc1 = MyBC(W, 0, I_BC) bc2 = fd.DirichletBC(W.sub(0), fd.Constant(0.0), 2) bc3 = fd.DirichletBC(W.sub(1), fd.Constant(0.0), 4) u_sol = fd.Function(W) fd.solve( a == L, u_sol, bcs=[bc1, bc2, bc3], solver_parameters=gamg_parameters, near_nullspace=nullmodes, ) # Cost function Jform = fd.assemble( inner(hs(phi, eps, min_value=rho_min) * sigma(u_sol), epsilon(u_sol)) * dx(degree=2)) # Constraint VolPen = fd.assemble(hs(phi, eps, min_value=rho_min) * dx(degree=2)) total_vol = fd.assemble(fd.Constant(1.0) * dx(domain=mesh), annotate=False) VolControl = fda.Control(VolPen) Vval = 0.15 * total_vol # Plotting global_counter1 = itertools.count() phi_pvd = fd.File(f"{output_dir}/level_set_evolution.pvd") def deriv_cb(phi): iter = next(global_counter1) if iter % 10 == 0: phi_pvd.write(phi[0]) c = fda.Control(s) Jhat = LevelSetFunctional(Jform, c, phi, derivative_cb_pre=deriv_cb) Vhat = LevelSetFunctional(VolPen, c, phi) # Regularization solver. Zero on the BCs boundaries beta_param = 0.005 bcs_vel_1 = MyBC(S, 0, I_BC) bcs_vel_2 = fd.DirichletBC(S, fd.Constant((0.0, 0.0, 0.0)), "top") bcs_vel = [bcs_vel_1, bcs_vel_2] reg_solver = RegularizationSolver( S, mesh, beta=beta_param, gamma=1.0e4, dx=dx, bcs=bcs_vel, output_dir=None, solver_parameters=gamg_parameters, ) dt = 0.05 tol = 1e-5 params = { "alphaC": 1.0, "K": 0.0001, "debug": 5, "maxit": opts.n_iters, "alphaJ": 2.0, "dt": dt, "maxtrials": 500, "tol_merit": 5e-2, # new merit can be within 0.5% of the previous merit "itnormalisation": 50, "tol": tol, } hj_solver_parameters["ts_dt"] = dt / 50.0 solver_parameters = { "hj_solver": hj_solver_parameters, "reinit_solver": reinit_solver_parameters, } vol_constraint = Constraint(Vhat, Vval, VolControl) problem = InfDimProblem( Jhat, reg_solver, ineqconstraints=vol_constraint, solver_parameters=solver_parameters, ) _ = nlspace_solve(problem, params)
u_interpolated = firedrake.Function(V, name=f'u_interpolated_{method}_{num_points}') u_interpolated.dat.data[:] = interpolator(X[:, 0], X[:, 1]) # Two terms in the functional - note difference in misfit term! misfit_expr = 0.5 * ((u_interpolated - u) / σ)**2 α = firedrake.Constant(0.5) regularisation_expr = 0.5 * α**2 * inner(grad(q), grad(q)) # Should be able to write firedrake.assemble(misfit + regularisation * dx) but can't yet # because of the meshes being different in the point-cloud case print('Assembling J') J = firedrake.assemble(misfit_expr * dx) + firedrake.assemble(regularisation_expr * dx) # Create reduced functional print('Creating q̂ and Ĵ') q̂ = firedrake_adjoint.Control(q) Ĵ = firedrake_adjoint.ReducedFunctional(J, q̂) # Minimise reduced functional print('Minimising Ĵ to get q_min') q_min = firedrake_adjoint.minimize( Ĵ, method='Newton-CG', options={'disp': True} ) q_min.rename(name=f'q_min_{method}_{num_points}') # Clear tape to avoid memory leak print('Clearing tape') tape.clear_tape() # Calculate error terms print('Calculating error')
def heat_exchanger_3D(): parser = argparse.ArgumentParser(description="Level set method parameters") parser.add_argument( "--nu", action="store", dest="nu", type=float, help="Kinematic Viscosity", default=1.0, ) parser.add_argument( "--brinkmann_penalty", action="store", dest="brinkmann_penalty", type=float, help="Brinkmann term", default=1e5, ) parser.add_argument( "--refinement", action="store", dest="refinement", type=int, help="Level of refinement", default=0, ) parser.add_argument( "--pressure_drop_constraint", action="store", dest="pressure_drop_constraint", type=float, help="Pressure drop constraint", default=10.0, ) parser.add_argument( "--n_iters", dest="n_iters", type=int, action="store", default=1000, help="Number of optimization iterations", ) parser.add_argument( "--output_dir", dest="output_dir", type=str, action="store", default="./", help="Output folder", ) parser.add_argument( "--type", dest="type_he", type=str, action="store", default="parallel", help="Type of heat exchanger: parallel or counter", ) opts = parser.parse_args() print(f"Parameters used: {opts}") beta_param = 0.4 mesh = fd.Mesh("./box_heat_exch.msh") from parameters_box import ( INLET2, INLET1, OUTLET1, OUTLET2, INMOUTH1, INMOUTH2, OUTMOUTH1, OUTMOUTH2, ) if opts.type_he == "counter": INLET1, OUTLET1 = OUTLET1, INLET1 elif opts.type_he == "u_flow": INLET2, OUTLET1 = OUTLET1, INLET2 OUTMOUTH1, INMOUTH2 = INMOUTH2, OUTMOUTH1 markers = { "WALLS": WALLS, "INLET1": INLET1, "INLET2": INLET2, "OUTLET1": OUTLET1, "OUTLET2": OUTLET2, } no_flow_domain_1 = [INMOUTH2, OUTMOUTH2] no_flow_domain_2 = [INMOUTH1, OUTMOUTH1] no_flow = no_flow_domain_1.copy() no_flow.extend(no_flow_domain_2) mesh = mark_no_flow_regions(mesh, no_flow, no_flow) mh = fd.MeshHierarchy(mesh, opts.refinement) mesh = mh[-1] pressure_drop_constraint = opts.pressure_drop_constraint pressure_drop_1 = pressure_drop_constraint pressure_drop_2 = pressure_drop_constraint S = fd.VectorFunctionSpace(mesh, "CG", 1) PHI = fd.FunctionSpace(mesh, "CG", 1) phi = fd.Function(PHI, name="LevelSet") x, y, z = fd.SpatialCoordinate(mesh) ω = 0.25 phi_expr = sin(y * pi / ω) * cos(x * pi / ω) * sin( z * pi / ω) - fd.Constant(0.2) checkpoints = is_checkpoint(opts.output_dir) if checkpoints: current_iter = read_checkpoint(checkpoints, phi) with open(f"{opts.output_dir}/brinkmann_penalty.txt", "r") as txt_brinkmann: brinkmann_penalty_initial = fd.Constant(txt_brinkmann.read()) print( f"Current brinkmann term: {brinkmann_penalty_initial.values()[0]}") else: with stop_annotating(): phi.interpolate(phi_expr) current_iter = 0 brinkmann_penalty_initial = fd.Constant(opts.brinkmann_penalty) P2 = fd.VectorElement("CG", mesh.ufl_cell(), 1) P1 = fd.FiniteElement("CG", mesh.ufl_cell(), 1) TH = P2 * P1 W = fd.FunctionSpace(mesh, TH) print(f"DOFS: {W.dim()}") T = fd.FunctionSpace(mesh, "CG", 1) global_counter = count(current_iter) def receive_signal(signum, stack): iter_current = next(copy(global_counter)) print(f"Received: {signum}, iter: {iter_current}") with fd.HDF5File( f"{opts.output_dir}/checkpoint_iter_{iter_current}.h5", "w") as checkpoint: checkpoint.write(phi, "/checkpoint") with open(f"{opts.output_dir}/brinkmann_penalty.txt", "w") as txt_brinkmann: txt_brinkmann.write(str(brinkmann_penalty.values()[0])) signal.signal(signal.SIGHUP, receive_signal) phi_pvd = fd.File( f"{opts.output_dir}/phi_evolution.pvd", target_degree=1, target_continuity=fd.H1, mode="a", ) ns1 = fd.File(f"{opts.output_dir}/navier_stokes_1.pvd", mode="a") ns2 = fd.File(f"{opts.output_dir}/navier_stokes_2.pvd", mode="a") temperature = fd.File(f"{opts.output_dir}/temperature.pvd", mode="a") temperature_pvd = fd.Function(T) def termination_event_1(): p1_constraint = P1control.tape_value() - 1 p2_constraint = P2control.tape_value() - 1 event_value = max(p1_constraint, p2_constraint) print(f"Value event: {event_value}") return event_value def termination_event_2(): iter_current = next(copy(global_counter)) print(f"Value event iter count: {iter_current}") return float(iter_current % 500) brinkmann_penalty_initial_value = brinkmann_penalty_initial.values()[0] if brinkmann_penalty_initial_value > opts.brinkmann_penalty: termination_events = [termination_event_2, termination_event_2] brinkmann_pen_terms = [ brinkmann_penalty_initial, fd.Constant(brinkmann_penalty_initial_value * 10), ] else: termination_events = [termination_event_1, None] brinkmann_pen_terms = [ brinkmann_penalty_initial, fd.Constant(brinkmann_penalty_initial_value * 5), ] for termination_event, brinkmann_penalty in zip(termination_events, brinkmann_pen_terms): s = fd.Function(S, name="deform") mesh.coordinates.assign(mesh.coordinates + s) # w_sol1, w_sol2, t = forward(brinkmann_penalty) w_sol1, w_sol2, t = forward( W, T, phi, opts, brinkmann_penalty, no_flow_domain_1=no_flow_domain_1, no_flow_domain_2=no_flow_domain_2, markers=markers, ) w_sol1_control = fda.Control(w_sol1) w_sol2_control = fda.Control(w_sol2) t_control = fda.Control(t) J = heat_flux(w_sol2, t, OUTLET2) J_hot = heat_flux(w_sol1, t, OUTLET1) Pdrop1 = pressure_drop(w_sol1, INLET1, OUTLET1, pressure_drop_1) Pdrop2 = pressure_drop(w_sol2, INLET2, OUTLET2, pressure_drop_2) print(f"Cold flux: {J}, hot flux: {J_hot}") c = fda.Control(s) J_hot_control = fda.Control(J_hot) def deriv_cb(phi): with stop_annotating(): iter = next(global_counter) print(f"Hot flux: {J_hot_control.tape_value()}") if iter % 15 == 0: u_sol1, p_sol1 = w_sol1_control.tape_value().split() u_sol2, p_sol2 = w_sol2_control.tape_value().split() u_sol1.rename("Velocity1") p_sol1.rename("Pressure1") u_sol2.rename("Velocity2") p_sol2.rename("Pressure2") ns1.write(u_sol1, p_sol1, time=iter) ns2.write(u_sol2, p_sol2, time=iter) phi_pvd.write(phi[0], time=iter) temperature_pvd.assign(t_control.tape_value()) temperature_pvd.rename("temperature") temperature.write(temperature_pvd, time=iter) # Reduced Functionals Jhat = LevelSetFunctional(J, c, phi, derivative_cb_pre=deriv_cb) P1hat = LevelSetFunctional(Pdrop1, c, phi) P1control = fda.Control(Pdrop1) P2hat = LevelSetFunctional(Pdrop2, c, phi) P2control = fda.Control(Pdrop2) print("Pressure drop 1 {:.5f}".format(Pdrop1)) print("Pressure drop 2 {:.5f}".format(Pdrop2)) reg_solver = RegularizationSolver( S, mesh, beta=beta_param, gamma=1e6, dx=dx, design_domain=DESIGN_DOMAIN, solver_parameters=regularization_solver_parameters, ) tol = 1e-7 dt = 0.02 params = { "alphaC": 0.1, "debug": 5, "alphaJ": 0.1, "dt": dt, "K": 1e-3, "maxit": opts.n_iters, "maxtrials": 10, "itnormalisation": 10, "tol_merit": 1e-2, # new merit can be within 1% of the previous merit # "normalize_tol" : -1, "tol": tol, } hj_solver_parameters["ts_dt"] = dt / 50.0 solver_parameters = { "hj_solver": hj_solver_parameters, "reinit_solver": reinit_solver_parameters, } problem = InfDimProblem( Jhat, reg_solver, ineqconstraints=[ Constraint(P1hat, 1.0, P1control), Constraint(P2hat, 1.0, P2control), ], reinit_distance=0.08, solver_parameters=solver_parameters, ) problem.set_termination_event(termination_event, termination_tolerance=1e-1) _ = nlspace_solve(problem, params) fda.get_working_tape().clear_tape()