def sw_solve(config, state, turbine_field=None, functional=None, annotate=True, u_source=None):
    '''Solve the shallow water equations with the parameters specified in params.
       Options for linear_solver and preconditioner are:
        linear_solver: lu, cholesky, cg, gmres, bicgstab, minres, tfqmr, richardson
        preconditioner: none, ilu, icc, jacobi, bjacobi, sor, amg, additive_schwarz, hypre_amg, hypre_euclid, hypre_parasails, ml_amg
    '''

    ############################### Setting up the equations ###########################

    # Define variables for all used parameters
    ds = config.domain.ds
    params = config.params

    # To begin with, check if the provided parameters are valid
    params.check()

    theta = params["theta"]
    dt = params["dt"]
    g = params["g"]
    depth = params["depth"]
    # Reset the time
    params["current_time"] = params["start_time"]
    t = params["current_time"]
    quadratic_friction = params["quadratic_friction"]
    include_advection = params["include_advection"]
    include_diffusion = params["include_diffusion"]
    include_time_term = params["include_time_term"]
    diffusion_coef = params["diffusion_coef"]
    newton_solver = params["newton_solver"]
    picard_relative_tolerance = params["picard_relative_tolerance"]
    picard_iterations = params["picard_iterations"]
    linear_solver = params["linear_solver"]
    preconditioner = params["preconditioner"]
    bctype = params["bctype"]
    strong_bc = params["strong_bc"]
    free_slip_on_sides = params["free_slip_on_sides"]
    steady_state = params["steady_state"]
    functional_final_time_only = params["functional_final_time_only"]
    functional_quadrature_degree = params["functional_quadrature_degree"]
    is_nonlinear = (include_advection or quadratic_friction)
    turbine_thrust_parametrisation = params["turbine_thrust_parametrisation"]
    implicit_turbine_thrust_parametrisation = params["implicit_turbine_thrust_parametrisation"]
    cache_forward_state = params["cache_forward_state"]

    if not 0 <= functional_quadrature_degree <= 1:
        raise ValueError("functional_quadrature_degree must be 0 or 1.")

    if implicit_turbine_thrust_parametrisation:
        function_space = config.function_space_2enriched
    elif turbine_thrust_parametrisation:
        function_space = config.function_space_enriched
    else:
        function_space = config.function_space

    # Take care of the steady state case
    if steady_state:
        dt = 1.
        params["finish_time"] = params["start_time"] + dt / 2
        theta = 1.

    # Define test functions
    if implicit_turbine_thrust_parametrisation:
        v, q, o, o_adv = TestFunctions(function_space)
    elif turbine_thrust_parametrisation:
        v, q, o = TestFunctions(function_space)
    else:
        v, q = TestFunctions(function_space)

    # Define functions
    state_new = Function(function_space, name="New_state")  # solution of the next timestep
    state_nl = Function(function_space, name="Best_guess_state")  # the last computed state of the next timestep, used for the picard iteration

    if not newton_solver and (turbine_thrust_parametrisation or implicit_turbine_thrust_parametrisation):
        raise NotImplementedError("Thrust turbine representation does currently only work with the newton solver.")

    # Split mixed functions
    if is_nonlinear and newton_solver:
        if implicit_turbine_thrust_parametrisation:
            u, h, up_u, up_u_adv = split(state_new)
        elif turbine_thrust_parametrisation:
            u, h, up_u = split(state_new)
        else:
            u, h = split(state_new)
    else:
        u, h = TrialFunctions(function_space)

    if implicit_turbine_thrust_parametrisation:
        u0, h0, up_u0, up_u_adv0 = split(state)
    elif turbine_thrust_parametrisation:
        u0, h0, up_u0 = split(state)
    else:
        u0, h0 = split(state)
        u_nl, h_nl = split(state_nl)

    # Create initial conditions and interpolate
    state_new.assign(state, annotate=annotate)

    # u_(n+theta) and h_(n+theta)
    u_mid = (1.0 - theta) * u0 + theta * u
    h_mid = (1.0 - theta) * h0 + theta * h

    # If a picard iteration is used we need an intermediate state
    if is_nonlinear and not newton_solver:
        u_nl, h_nl = split(state_nl)
        state_nl.assign(state, annotate=annotate)
        u_mid_nl = (1.0 - theta) * u0 + theta * u_nl

    # The normal direction
    n = FacetNormal(function_space.mesh())

    # Mass matrix

    M = inner(v, u) * dx
    M += inner(q, h) * dx
    M0 = inner(v, u0) * dx
    M0 += inner(q, h0) * dx

    # Divergence term.
    Ct_mid = -depth * inner(u_mid, grad(q)) * dx
    #+inner(avg(u_mid),jump(q,n))*dS # This term is only needed for dg element pairs

    if bctype == 'dirichlet':
        if steady_state:
            raise ValueError("Can not use a time dependent boundary condition for a steady state simulation")
        # The dirichlet boundary condition on the left hand side
        expr = config.params["weak_dirichlet_bc_expr"]
        bc_contr = - depth * dot(expr, n) * q * ds(1)

        # The dirichlet boundary condition on the right hand side
        bc_contr -= depth * dot(expr, n) * q * ds(2)

        # We enforce a no-normal flow on the sides by removing the surface integral.
        # bc_contr -= dot(u_mid, n) * q * ds(3)

    elif bctype == 'flather':
        if steady_state:
            raise ValueError("Can not use a time dependent boundary condition for a steady state simulation")
        # The Flather boundary condition on the left hand side
        expr = config.params["flather_bc_expr"]
        bc_contr = - depth * dot(expr, n) * q * ds(1)
        Ct_mid += sqrt(g * depth) * inner(h_mid, q) * ds(1)

        # The contributions of the Flather boundary condition on the right hand side
        Ct_mid += sqrt(g * depth) * inner(h_mid, q) * ds(2)

    elif bctype == 'strong_dirichlet':
        # Do not replace anything in the surface integrals as the strong Dirichlet Boundary condition will do that
        bc_contr = -depth * dot(u_mid, n) * q * ds(1)
        bc_contr -= depth * dot(u_mid, n) * q * ds(2)
        if not free_slip_on_sides:
            bc_contr -= depth * dot(u_mid, n) * q * ds(3)

    else:
        info_red("Unknown boundary condition type: %s" % bctype)
        sys.exit(1)

    # Pressure gradient operator
    C_mid = g * inner(v, grad(h_mid)) * dx
    #+inner(avg(v),jump(h_mid,n))*dS # This term is only needed for dg element pairs

    # Bottom friction
    friction = params["friction"]

    if turbine_field:
        if type(turbine_field) == list:
            tf = Function(turbine_field[0], name="turbine_friction", annotate=annotate)
        else:
            tf = Function(turbine_field, name="turbine_friction", annotate=annotate)

        if turbine_thrust_parametrisation or implicit_turbine_thrust_parametrisation:
            print0("Adding thrust force")
            # Compute the upstream velocities

            if implicit_turbine_thrust_parametrisation:
                up_u_eq = upstream_u_implicit_equation(config, tf, u, up_u, o, up_u_adv, o_adv)
            elif turbine_thrust_parametrisation:
                up_u_eq = upstream_u_equation(config, tf, u, up_u, o)

            def thrust_force(up_u, min=smooth_uflmin):
                ''' Returns the thrust force for a given upstream velcocity '''
                # Now apply a pointwise transformation based on the interpolation of a loopup table
                c_T_coeffs = [0.08344535, -1.42428216, 9.13153605, -26.19370168, 28.8752054]
                c_T_coeffs.reverse()
                c_T = min(0.88, sum([c_T_coeffs[i] * up_u ** i for i in range(len(c_T_coeffs))]))

                # The amount of forcing we want to apply
                turbine_radius = 15.
                A_c = pi * Constant(turbine_radius ** 2)  # Turbine cross section
                f = 0.5 * c_T * up_u ** 2 * A_c
                return f

            # Apply the force in the opposite direction of the flow
            f_dir = -thrust_force(up_u) * u / norm_approx(u, alpha=1e-6)
            # Distribute this force over the turbine area
            thrust = inner(f_dir * tf / (Constant(config.turbine_cache.turbine_integral()) * config.params["depth"]), v) * dx

    # Friction term
    # With Newton we can simply use a non-linear form
    if quadratic_friction and newton_solver:
        R_mid = friction / depth * dot(u_mid, u_mid) ** 0.5 * inner(u_mid, v) * dx

        if turbine_field and not (turbine_thrust_parametrisation or implicit_turbine_thrust_parametrisation):
            R_mid += tf / depth * dot(u_mid, u_mid) ** 0.5 * inner(u_mid, v) * config.site_dx(1)

    # With a picard iteration we need to linearise using the best guess
    elif quadratic_friction and not newton_solver:
        R_mid = friction / depth * dot(u_mid_nl, u_mid_nl) ** 0.5 * inner(u_mid, v) * dx

        if turbine_field and not (turbine_thrust_parametrisation or implicit_turbine_thrust_parametrisation):
            R_mid += tf / depth * dot(u_mid_nl, u_mid_nl) ** 0.5 * inner(u_mid, v) * config.site_dx(1)

    # Use a linear drag
    else:
        R_mid = friction / depth * inner(u_mid, v) * dx

        if turbine_field and not (turbine_thrust_parametrisation or implicit_turbine_thrust_parametrisation):
            R_mid += tf / depth * inner(u_mid, v) * config.site_dx(1)

    # Advection term
    # With a newton solver we can simply use a quadratic form
    if include_advection and newton_solver:
        Ad_mid = inner(dot(grad(u_mid), u_mid), v) * dx
    # With a picard iteration we need to linearise using the best guess
    if include_advection and not newton_solver:
        Ad_mid = inner(dot(grad(u_mid), u_mid_nl), v) * dx

    if include_diffusion:
        # Check that we are not using a DG velocity function space, as the facet integrals are not implemented.
        if "Discontinuous" in str(function_space.split()[0]):
            raise NotImplementedError("The diffusion term for discontinuous elements is not implemented yet.")
        D_mid = diffusion_coef * inner(grad(u_mid), grad(v)) * dx

    # Create the final form
    G_mid = C_mid + Ct_mid + R_mid
    # Add the advection term
    if include_advection:
        G_mid += Ad_mid
    # Add the diffusion term
    if include_diffusion:
        G_mid += D_mid
    # Add the source term
    if u_source:
        G_mid -= inner(u_source, v) * dx
    F = dt * G_mid - dt * bc_contr
    # Add the time term
    if include_time_term and not steady_state:
        F += M - M0

    if turbine_thrust_parametrisation or implicit_turbine_thrust_parametrisation:
        F += up_u_eq
        if turbine_field:
            F -= thrust

    # Preassemble the lhs if possible
    use_lu_solver = (linear_solver == "lu")
    if not is_nonlinear:
        lhs_preass = assemble(dolfin.lhs(F))
        # Precompute the LU factorisation
        if use_lu_solver:
            info("Computing the LU factorisation for later use ...")
            if bctype == 'strong_dirichlet':
                raise NotImplementedError("Strong boundary condition and reusing LU factorisation is currently not implemented")
            lu_solver = LUSolver(lhs_preass)
            lu_solver.parameters["reuse_factorization"] = True

    solver_parameters = {"linear_solver": linear_solver, "preconditioner": preconditioner}

    # Do some parameter checking:
    if "dynamic_turbine_friction" in params["controls"]:
        if len(config.params["turbine_friction"]) != (params["finish_time"] - t) / dt + 1:
            print0("You control the turbine friction dynamically, but your turbine friction parameter is not an array of length 'number of timesteps' (here: %i)." % ((params["finish_time"] - t) / dt + 1))
            import sys
            sys.exit(1)

    ############################### Perform the simulation ###########################

    if params["dump_period"] > 0:
        try:
            statewriter_cb = config.statewriter_callback
        except AttributeError:
            statewriter_cb = None 

        writer = StateWriter(config, optimisation_iteration=config.optimisation_iteration, callback=statewriter_cb)
        if not steady_state and include_time_term:
            print0("Writing state to disk...")
            writer.write(state)

    step = 0

    if functional is not None:
        if steady_state or functional_final_time_only:
            j = 0.
            if params["print_individual_turbine_power"]:
                j_individual = [0] * len(params["turbine_pos"])
                force_individual = [0] * len(params["turbine_pos"])

        else:
            if functional_quadrature_degree == 0:
                quad = 0.0
            else:
                quad = 0.5
            j = dt * quad * assemble(functional.Jt(state, tf))
            if params["print_individual_turbine_power"]:
                j_individual = []
                force_individual = []
                for i in range(len(params["turbine_pos"])):
                    j_individual.append(dt * quad * assemble(functional.Jt_individual(state, i)))
                    force_individual.append(dt * quad * assemble(functional.force_individual(state, i)))

    print0("Start of time loop")
    adjointer.time.start(t)
    timestep = 0
    while (t < params["finish_time"]):
        timestep += 1
        t += dt
        params["current_time"] = t

        # Update bc's
        if bctype == "strong_dirichlet":
            strong_bc.update_time(t)
        else:
            expr.t = t - (1.0 - theta) * dt
        # Update source term
        if u_source:
            u_source.t = t - (1.0 - theta) * dt
        step += 1

        # Solve non-linear system with a Newton sovler
        if is_nonlinear and newton_solver:
            # Use a Newton solver to solve the nonlinear problem.
            #solver_parameters["linear_solver"] = "gmres"
            #solver_parameters["linear_solver"] = "superlu_dist"
            #solver_parameters["preconditioner"] = "ilu" # does not work in parallel
            #solver_parameters["preconditioner"] = "amg"
            #solver_parameters["linear_solver"] = "mumps"
            #solver_parameters["linear_solver"] = "umfpack"
            solver_parameters["newton_solver"] = {}
            solver_parameters["newton_solver"]["error_on_nonconvergence"] = True
            solver_parameters["newton_solver"]["maximum_iterations"] = 20
            solver_parameters["newton_solver"]["convergence_criterion"] = "incremental"
            solver_parameters["newton_solver"]["relative_tolerance"] = 1e-16

            if cache_forward_state and state_cache.has_key(t):
                print0("Load initial guess from cache for time %f." % t)
                # Load initial guess for solver from cache
                state_new.assign(state_cache[t], annotate=False)
            elif not include_time_term:
                print0("Set the initial guess for the nonlinear solver to the initial condition.")
                # Reset the initial guess after each timestep
                ic = config.params['initial_condition']
                state_new.assign(ic, annotate=False)

            info_blue("Solve shallow water equations at time %s (Newton iteration) ..." % params["current_time"])
            if bctype == 'strong_dirichlet':
                solve(F == 0, state_new, bcs=strong_bc.bcs, solver_parameters=solver_parameters, annotate=annotate)
            else:
                solve(F == 0, state_new, solver_parameters=solver_parameters, annotate=annotate)

            if turbine_thrust_parametrisation or implicit_turbine_thrust_parametrisation:
                print0("Inflow velocity: ", u[0]((10, 160)))
                print0("Estimated upstream velocity: ", up_u((640. / 3, 160)))
                print0("Expected thrust force: ", thrust_force(u[0]((10, 160)), min=min)((0)))
                print0("Total amount of thurst force applied: ", assemble(inner(Constant(1), thrust_force(up_u) * tf / config.turbine_cache.turbine_integral()) * dx))

                us.append(u[0]((10, 160)))
                thrusts.append(thrust_force(u[0]((10, 160)))((0)))
                thrusts_est.append(assemble(inner(Constant(1), thrust_force(up_u) * tf / config.turbine_cache.turbine_integral()) * dx))

                import matplotlib.pyplot as plt
                plt.clf()
                plt.plot(us, thrusts, label="Analytical")
                plt.plot(us, thrusts_est, label="Approximated")
                plt.legend(loc=2)
                plt.savefig("thrust_plot.pdf", format='pdf')

        # Solve non-linear system with a Picard iteration
        elif is_nonlinear:
            # Solve the problem using a picard iteration
            iter_counter = 0
            while True:
                info_blue("Solving shallow water equations at time %s (Picard iteration %d) ..." % (params["current_time"], iter_counter))
                if bctype == 'strong_dirichlet':
                    solve(dolfin.lhs(F) == dolfin.rhs(F), state_new, bcs=strong_bc.bcs, solver_parameters=solver_parameters)
                else:
                    solve(dolfin.lhs(F) == dolfin.rhs(F), state_new, solver_parameters=solver_parameters, annotate=annotate)
                iter_counter += 1
                if iter_counter > 0:
                    relative_diff = abs(assemble(inner(state_new - state_nl, state_new - state_nl) * dx)) / assemble(inner(state_new, state_new) * dx)
                    info_blue("Picard iteration " + str(iter_counter) + " relative difference: " + str(relative_diff))

                    if relative_diff < picard_relative_tolerance:
                        info("Picard iteration converged after " + str(iter_counter) + " iterations.")
                        break
                    elif iter_counter >= picard_iterations:
                        info_red("Picard iteration reached maximum number of iterations (" + str(picard_iterations) + ") with a relative difference of " + str(relative_diff) + ".")
                        break

            state_nl.assign(state_new)

        # Solve linear system with preassembled matrices
        else:
            # dolfin can't assemble empty forms which can sometimes happen here.
            # A simple workaround is to add a dummy term:
            dummy_term = Constant(0) * q * dx
            rhs_preass = assemble(dolfin.rhs(F + dummy_term))
            # Apply dirichlet boundary conditions
            info_blue("Solving shallow water equations at time %s (preassembled matrices) ..." % (params["current_time"]))
            if bctype == 'strong_dirichlet':
                [bc.apply(lhs_preass, rhs_preass) for bc in strong_bc.bcs]
            if use_lu_solver:
                info("Using a LU solver to solve the linear system.")
                lu_solver.solve(state.vector(), rhs_preass, annotate=annotate)
            else:
                solve(lhs_preass, state_new.vector(), rhs_preass, solver_parameters["linear_solver"], solver_parameters["preconditioner"], annotate=annotate)

        # After the timestep solve, update state
        state.assign(state_new)
        if cache_forward_state:
            # Save state for initial guess cache
            print0("Cache initial guess for time %f." % t)
            if not state_cache.has_key(t):
                state_cache[t] = Function(state_new.function_space())
            state_cache[t].assign(state_new, annotate=False)

        # Set the control function for the upcoming timestep.
        if turbine_field:
            if type(turbine_field) == list:
                tf.assign(turbine_field[timestep])
            else:
                tf.assign(turbine_field)

        if params["dump_period"] > 0 and step % params["dump_period"] == 0:
            print0("Write state to disk...")
            writer.write(state)

        if functional is not None:
            if not (functional_final_time_only and t < params["finish_time"]):
                if steady_state or functional_final_time_only or functional_quadrature_degree == 0:
                    quad = 1.0
                elif t >= params["finish_time"]:
                    quad = 0.5 * dt
                else:
                    quad = 1.0 * dt

                j += quad * assemble(functional.Jt(state, tf))
                if params["print_individual_turbine_power"]:
                    info_green("Computing individual turbine power extraction contribution...")
                    individual_contribution_list = ['x_pos', 'y_pos', 'turbine_power', 'total_force_on_turbine', 'turbine_friction']
                    fr_individual = range(len(params["turbine_pos"]))
                    for i in range(len(params["turbine_pos"])):
                        j_individual[i] += dt * quad * assemble(functional.Jt_individual(state, i))
                        force_individual[i] += dt * quad * assemble(functional.force_individual(state, i))

                        if len(params["turbine_friction"]) > 0:
                            fr_individual[i] = params["turbine_friction"][i]
                        else:
                            fr_individual = [params["turbine_friction"]] * len(params["turbine_pos"])

                        individual_contribution_list.append((params["turbine_pos"][i])[0])
                        individual_contribution_list.append((params["turbine_pos"][i])[1])
                        individual_contribution_list.append(j_individual[i])
                        individual_contribution_list.append(force_individual[i])
                        individual_contribution_list.append(fr_individual[i])

                        print0("Contribution of turbine number %d at co-ordinates:" % (i + 1), params["turbine_pos"][i], ' is: ', j_individual[i] * 0.001, 'kW', 'with friction of', fr_individual[i])

        # Increase the adjoint timestep
        adj_inc_timestep(time=t, finished=(not t < params["finish_time"]))
    print0("End of time loop.")

    # Write the turbine positions, power extraction and friction to a .csv file named turbine_info.csv
    if params['print_individual_turbine_power']:
        f = config.params['base_path'] + os.path.sep + "iter_" + str(config.optimisation_iteration) + '/'
        # Save the very first result in a different file
        if config.optimisation_iteration == 0 and not os.path.isfile(f):
            f += 'initial_turbine_info.csv'
        else:
            f += 'turbine_info.csv'

        output_turbines = open(f, 'w')
        for i in range(0, len(individual_contribution_list), 5):
            print >> output_turbines, '%s, %s, %s, %s, %s' % (individual_contribution_list[i], individual_contribution_list[i + 1], individual_contribution_list[i + 2], individual_contribution_list[i + 3], individual_contribution_list[i + 4])
        print 'Total of individual turbines is', sum(j_individual)

    if functional is not None:
        return j
Пример #2
0
    def solve(self):

        # Fetch the function spaces
        V, Q = self.problem.parameters.discretisation
        # Set up the velocity functions 
        u = TrialFunction(V)
        v = TestFunction(V)
        u0 = Function(V)
        u1 = Function(V) 
	# Set up the pressure functions
        p = TrialFunction(Q)
        q = TestFunction(Q)
        p1 = Function(Q)

        # Define coefficients
	k = Constant(self.problem.parameters.dt)
	f = Constant((0,0))
	nu = Constant(self.problem.parameters.viscosity)

	# Tentative velocity step
	F1 = (1/k) * inner(u-u0, v)*dx          \
	     + inner(grad(u0)*u0, v)*dx         \
	     + nu * inner(grad(u), grad(v))*dx  \
	     - inner(f, v)*dx
	a1 = lhs(F1)
	L1 = rhs(F1)
	A1 = assemble(a1)

	# Pressure update
	a2 = inner(grad(p), grad(q))*dx
	L2 = (-1./k) * div(u1) * q * dx
	A2 = assemble(a2)

	# Velocity update
	a3 = inner(u, v)*dx
	L3 = inner(u1, v)*dx - k * inner(grad(p1), v)*dx
	A3 = assemble(a3)

        # Iterate over timesteps, start at dt
        self.t = self.problem.parameters.dt
        while self.t < self.problem.parameters.finish_time:
            print 'Starting timestep at time = ', self.t

            # Update the boundary conditions for the current timestep
            self.bcs.update_time(t=self.t)

            # Tentative velocity step
            print 'Step 1: Computing tentative velocity'
            b1 = assemble(L1)
            [bc.apply(A1, b1) for bc in self.bcs.bc_u]
            solve(A1, u1.vector(), b1, "gmres", "default")
            end()

            # Pressure update
            print 'Step 2: Updating pressure'
            b2 = assemble(L2)
            [bc.apply(A2, b2) for bc in self.bcs.bc_p]
            solve(A2, p1.vector(), b2, "gmres", self.parameters.prec)
            end()

	    # Velocity update
            print 'Step 3: Updating velocity'
            b3 = assemble(L3)
            [bc.apply(A3, b3) for bc in self.bcs.bc_u]
            solve(A3, u1.vector(), b3, "gmres", "default")
            end()

            # Timestep update
            print 'Step 4: Dump and update timestep'
            if (self.t/self.problem.parameters.dt)%self.parameters.dump_period == 0:
                writer = StateWriter(solver=self)
                writer.write(u1, p1)
            u0.assign(u1)
            self.t += self.problem.parameters.dt
            
            if self.parameters.live_plotting:
                plot(p1, title="Pressure", rescale=True)
                plot(u1, title="Velocity", rescale=True)

        if self.parameters.live_plotting:
            interactive()
Пример #3
0
    def solve(self):

        # Fetch the function spaces
        V, Q = self.problem.parameters.discretisation
        # Set up the velocity functions
        u = TrialFunction(V)
        v = TestFunction(V)
        u0 = Function(V)
        u1 = Function(V)
        # Set up the pressure functions
        p = TrialFunction(Q)
        q = TestFunction(Q)
        p1 = Function(Q)

        # Define coefficients
        k = Constant(self.problem.parameters.dt)
        f = Constant((0, 0))
        nu = Constant(self.problem.parameters.viscosity)

        # Tentative velocity step
        F1 = (1/k) * inner(u-u0, v)*dx          \
             + inner(grad(u0)*u0, v)*dx         \
             + nu * inner(grad(u), grad(v))*dx  \
             - inner(f, v)*dx
        a1 = lhs(F1)
        L1 = rhs(F1)
        A1 = assemble(a1)

        # Pressure update
        a2 = inner(grad(p), grad(q)) * dx
        L2 = (-1. / k) * div(u1) * q * dx
        A2 = assemble(a2)

        # Velocity update
        a3 = inner(u, v) * dx
        L3 = inner(u1, v) * dx - k * inner(grad(p1), v) * dx
        A3 = assemble(a3)

        # Iterate over timesteps, start at dt
        self.t = self.problem.parameters.dt
        while self.t < self.problem.parameters.finish_time:
            print 'Starting timestep at time = ', self.t

            # Update the boundary conditions for the current timestep
            self.bcs.update_time(t=self.t)

            # Tentative velocity step
            print 'Step 1: Computing tentative velocity'
            b1 = assemble(L1)
            [bc.apply(A1, b1) for bc in self.bcs.bc_u]
            solve(A1, u1.vector(), b1, "gmres", "default")
            end()

            # Pressure update
            print 'Step 2: Updating pressure'
            b2 = assemble(L2)
            [bc.apply(A2, b2) for bc in self.bcs.bc_p]
            solve(A2, p1.vector(), b2, "gmres", self.parameters.prec)
            end()

            # Velocity update
            print 'Step 3: Updating velocity'
            b3 = assemble(L3)
            [bc.apply(A3, b3) for bc in self.bcs.bc_u]
            solve(A3, u1.vector(), b3, "gmres", "default")
            end()

            # Timestep update
            print 'Step 4: Dump and update timestep'
            if (self.t / self.problem.parameters.dt
                ) % self.parameters.dump_period == 0:
                writer = StateWriter(solver=self)
                writer.write(u1, p1)
            u0.assign(u1)
            self.t += self.problem.parameters.dt

            if self.parameters.live_plotting:
                plot(p1, title="Pressure", rescale=True)
                plot(u1, title="Velocity", rescale=True)

        if self.parameters.live_plotting:
            interactive()