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)
Exemple #2
0
    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
Exemple #3
0
 def epsilon(v):
     return sym(nabla_grad(v))
Exemple #4
0
 def epsilon(u):
     return 0.5 * (fd.nabla_grad(u) + fd.nabla_grad(u).T)
Exemple #5
0
 def epsilon(u):
     return sym(nabla_grad(u))
Exemple #6
0
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
Exemple #7
0
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)
Exemple #8
0
    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