def setup_solver(self, up_init=None): """ Setup the solvers """ self.up0 = Function(self.W) if up_init is not None: chk_in = checkpointing.HDF5File(up_init, file_mode='r') chk_in.read(self.up0, "/up") chk_in.close() self.u0, self.p0 = split(self.up0) self.up = Function(self.W) if up_init is not None: chk_in = checkpointing.HDF5File(up_init, file_mode='r') chk_in.read(self.up, "/up") chk_in.close() self.u1, self.p1 = split(self.up) self.up.sub(0).rename("velocity") self.up.sub(1).rename("pressure") v, q = TestFunctions(self.W) h = CellVolume(self.mesh) u_norm = sqrt(dot(self.u0, self.u0)) if self.has_nullspace: nullspace = MixedVectorSpaceBasis( self.W, [self.W.sub(0), VectorSpaceBasis(constant=True)]) else: nullspace = None tau = ((2.0 / self.dt)**2 + (2.0 * u_norm / h)**2 + (4.0 * self.nu / h**2)**2)**(-0.5) # temporal discretization F = (1.0 / self.dt) * inner(self.u1 - self.u0, v) * dx # weak form F += (+inner(dot(self.u0, nabla_grad(self.u1)), v) * dx + self.nu * inner(grad(self.u1), grad(v)) * dx - (1.0 / self.rho) * self.p1 * div(v) * dx + div(self.u1) * q * dx - inner(self.forcing, v) * dx) # residual form R = (+(1.0 / self.dt) * (self.u1 - self.u0) + dot(self.u0, nabla_grad(self.u1)) - self.nu * div(grad(self.u1)) + (1.0 / self.rho) * grad(self.p1) - self.forcing) # GLS F += tau * inner( +dot(self.u0, nabla_grad(v)) - self.nu * div(grad(v)) + (1.0 / self.rho) * grad(q), R) * dx self.problem = NonlinearVariationalProblem(F, self.up, self.bcs) self.solver = NonlinearVariationalSolver( self.problem, nullspace=nullspace, solver_parameters=self.solver_parameters)
def advection(self): n = FacetNormal(self.mesh) if len(self.wind) > 1: element = self.V._ufl_element degree = element.degree(0) space_type = repr(element)[0] element_type = element.family() mesh = self.V.mesh() V = VectorFunctionSpace(mesh, element_type, degree) else: V = self.V if self.opt['form'] == 'linear': w_ = Expression(self.wind, V) u_ = self.sol elif self.opt['form'] == 'bilinear': w_ = Expression(self.wind, V) u_ = self.u elif self.opt['form'] == 'nonlinear': w_ = self.sol u_ = self.sol return inner(dot(w_, nabla_grad(u_)), self.v) * dx
def epsilon(v): return sym(nabla_grad(v))
def epsilon(u): return 0.5 * (fd.nabla_grad(u) + fd.nabla_grad(u).T)
def epsilon(u): return sym(nabla_grad(u))
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 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)
def residual(self, test, trial, trial_lagged, fields, bcs): if 'background_viscosity' in fields: assert('grid_resolution' in fields) mu_background = fields['background_viscosity'] grid_dx = fields['grid_resolution'][0] grid_dz = fields['grid_resolution'][1] mu_h = 0.5*abs(trial[0]) * grid_dx + mu_background mu_v = 0.5*abs(trial[1]) * grid_dz + mu_background print("use redx viscosity") diff_tensor = as_tensor([[mu_h, 0], [0, mu_v]]) else: mu = fields['viscosity'] if len(mu.ufl_shape) == 2: diff_tensor = mu else: diff_tensor = mu * Identity(self.dim) phi = test n = self.n u = trial u_lagged = trial_lagged grad_test = nabla_grad(phi) stress = dot(diff_tensor, nabla_grad(u)) if self.symmetric_stress: stress += dot(diff_tensor, grad(u)) F = 0 F += inner(grad_test, stress)*self.dx # Interior Penalty method # # see https://www.researchgate.net/publication/260085826 for details # on the choice of sigma degree = self.trial_space.ufl_element().degree() if not isinstance(degree, int): degree = max(degree[0], degree[1]) # safety factor: 1.0 is theoretical minimum alpha = fields.get('interior_penalty', 2.0) if degree == 0: # probably only works for orthog. quads and hexes sigma = 1.0 else: nf = self.mesh.ufl_cell().num_facets() family = self.trial_space.ufl_element().family() if family in ['DQ', 'TensorProductElement', 'EnrichedElement']: degree_gradient = degree else: degree_gradient = degree - 1 sigma = alpha * cell_edge_integral_ratio(self.mesh, degree_gradient) * nf # we use (3.23) + (3.20) from https://www.researchgate.net/publication/260085826 # instead of maximum over two adjacent cells + and -, we just sum (which is 2*avg()) # and the for internal facets we have an extra 0.5: # WEIRDNESS: avg(1/CellVolume(mesh)) crashes TSFC - whereas it works in scalar diffusion! - instead just writing out explicitly sigma *= FacetArea(self.mesh)*(1/CellVolume(self.mesh)('-') + 1/CellVolume(self.mesh)('+'))/2 if not is_continuous(self.trial_space): u_tensor_jump = tensor_jump(n, u) if self.symmetric_stress: u_tensor_jump += transpose(u_tensor_jump) F += sigma*inner(tensor_jump(n, phi), dot(avg(diff_tensor), u_tensor_jump))*self.dS F += -inner(avg(dot(diff_tensor, nabla_grad(phi))), u_tensor_jump)*self.dS F += -inner(tensor_jump(n, phi), avg(stress))*self.dS for id, bc in bcs.items(): if 'u' in bc or 'un' in bc: if 'u' in bc: u_tensor_jump = outer(n, u-bc['u']) else: u_tensor_jump = outer(n, n)*(dot(n, u)-bc['un']) if self.symmetric_stress: u_tensor_jump += transpose(u_tensor_jump) # this corresponds to the same 3 terms as the dS integrals for DG above: F += 2*sigma*inner(outer(n, phi), dot(diff_tensor, u_tensor_jump))*self.ds(id) F += -inner(dot(diff_tensor, nabla_grad(phi)), u_tensor_jump)*self.ds(id) if 'u' in bc: F += -inner(outer(n, phi), stress) * self.ds(id) elif 'un' in bc: # we only keep, the normal part of stress, the tangential # part is assumed to be zero stress (i.e. free slip), or prescribed via 'stress' F += -dot(n, phi)*dot(n, dot(stress, n)) * self.ds(id) if 'stress' in bc: # a momentum flux, a.k.a. "force" # here we need only the third term, because we assume jump_u=0 (u_ext=u) # the provided stress = n.(mu.stress_tensor) F += dot(-phi, bc['stress']) * self.ds(id) if 'drag' in bc: # (bottom) drag of the form tau = -C_D u |u| C_D = bc['drag'] if 'coriolis_frequency' in fields and self.dim == 2: assert 'u_velocity' in fields u_vel_component = fields['u_velocity'] unorm = pow(dot(u_lagged, u_lagged) + pow(u_vel_component, 2) + 1e-6, 0.5) else: unorm = pow(dot(u_lagged, u_lagged) + 1e-6, 0.5) F += dot(-phi, -C_D*unorm*u) * self.ds(id) # NOTE 1: unspecified boundaries are equivalent to free stress (i.e. free in all directions) # NOTE 2: 'un' can be combined with 'stress' provided the stress force is tangential (e.g. no-normal flow with wind) if 'u' in bc and 'stress' in bc: raise ValueError("Cannot apply both 'u' and 'stress' bc on same boundary") if 'u' in bc and 'drag' in bc: raise ValueError("Cannot apply both 'u' and 'drag' bc on same boundary") if 'u' in bc and 'un' in bc: raise ValueError("Cannot apply both 'u' and 'un' bc on same boundary") return -F