Пример #1
0
def test_taylor_hood_cube():
    pytest.xfail("Problem with Mixed Function Spaces")
    meshc = UnitCubeMesh(MPI.comm_world, 2, 2, 2)
    meshf = UnitCubeMesh(MPI.comm_world, 3, 4, 5)

    Ve = VectorElement("CG", meshc.ufl_cell(), 2)
    Qe = FiniteElement("CG", meshc.ufl_cell(), 1)
    Ze = MixedElement([Ve, Qe])

    Zc = FunctionSpace(meshc, Ze)
    Zf = FunctionSpace(meshf, Ze)

    z = Expression(
        ("x[0]*x[1]", "x[1]*x[2]", "x[2]*x[0]", "x[0] + 3*x[1] + x[2]"),
        degree=2)
    zc = interpolate(z, Zc)
    zf = interpolate(z, Zf)

    mat = PETScDMCollection.create_transfer_matrix(Zc, Zf)
    Zuc = Function(Zf)
    mat.mult(zc.vector(), Zuc.vector())
    Zuc.vector().update_ghost_values()

    diff = Function(Zf)
    diff.assign(Zuc - zf)
    assert diff.vector().norm("l2") < 1.0e-12
Пример #2
0
 def assemble_solution(self, t):  # returns
     """
     :param t: time
     :return: Womersley flow (analytic solution) at time t
     analytic solution at any time is a steady parabolic flow + linear combination of 8 modes
     modes were precomputed as 8 functions on given mesh and stored in hdf5 file
     """
     if self.tc is not None:
         self.tc.start('assembleSol')
     sol = Function(self.solutionSpace)
     # analytic solution has zero x and y components
     dofs2 = self.solutionSpace.sub(2).dofmap().dofs(
     )  # gives field of indices corresponding to z axis
     sol.assign(Constant(("0.0", "0.0", "0.0")))  # QQ not needed
     sol.vector()[dofs2] += self.factor * self.bessel_parabolic.vector(
     ).array()  # parabolic part of sol
     for idx in range(8):  # add modes of Womersley sol
         sol.vector()[dofs2] += self.factor * cos(
             self.coefs_exp[idx] * pi *
             t) * self.bessel_real[idx].vector().array()
         sol.vector()[dofs2] += self.factor * -sin(
             self.coefs_exp[idx] * pi *
             t) * self.bessel_complex[idx].vector().array()
     if self.tc is not None:
         self.tc.end('assembleSol')
     return sol
Пример #3
0
def test_failsafe_sweep():
    interpolate_expression = Expression('x[0]', degree=1)
    mesh = UnitSquareMesh(5, 5)
    V = FunctionSpace(mesh, "DG", 1)

    v = Function(V)
    v.assign(interpolate_expression)

    np_min, np_max = 1, 2
    np_failsafe = 4

    # Initialize particles
    x = RandomRectangle(Point(0.0, 0.0), Point(1., 1.)).generate([100, 100])
    s = assign_particle_values(x, interpolate_expression)
    # Broadcast to other procs
    x = comm.bcast(x, root=0)
    s = comm.bcast(s, root=0)

    property_idx = 1
    p = particles(x, [s], mesh)
    AD = AddDelete(p, np_min, np_max, [v])
    AD.do_sweep_failsafe(np_failsafe)

    # Must recover linear
    lstsq_rho = l2projection(p, V, property_idx)
    lstsq_rho.project(v.cpp_object())

    error = sqrt(
        assemble(
            (v - interpolate_expression) * (v - interpolate_expression) * dx))

    assert len(p.positions() == mesh.num_cells() * np_failsafe)
    assert error < 1e-12
Пример #4
0
def test_closed_boundary(advection_scheme):
    # FIXME: rk3 scheme does not bounces off the wall properly
    xmin, xmax = 0., 1.
    ymin, ymax = 0., 1.

    mesh = RectangleMesh(Point(xmin, ymin), Point(xmax, ymax), 10, 10)

    # Particle
    x = np.array([[0.975, 0.475]])

    # Given velocity field:
    vexpr = Constant((1., 0.))
    # Given time do_step:
    dt = 0.05
    # Then bounced position is
    x_bounced = np.array([[0.975, 0.475]])

    p = particles(x, [x, x], mesh)

    V = VectorFunctionSpace(mesh, "CG", 1)
    v = Function(V)
    v.assign(vexpr)

    # Different boundary parts
    bound_left = UnitSquareLeft()
    bound_right = UnitSquareRight()
    bound_top = UnitSquareTop()
    bound_bottom = UnitSquareBottom()

    # Mark all facets
    facet_marker = MeshFunction('size_t', mesh, mesh.topology().dim() - 1)
    facet_marker.set_all(0)

    # Mark as closed
    bound_right.mark(facet_marker, 1)

    # Mark other boundaries as open
    bound_left.mark(facet_marker, 2)
    bound_top.mark(facet_marker, 2)
    bound_bottom.mark(facet_marker, 2)

    if advection_scheme == 'euler':
        ap = advect_particles(p, V, v, facet_marker)
    elif advection_scheme == 'rk2':
        ap = advect_rk2(p, V, v, facet_marker)
    elif advection_scheme == 'rk3':
        ap = advect_rk3(p, V, v, facet_marker)
    else:
        assert False

    # Do one timestep, particle must bounce from wall of
    ap.do_step(dt)
    xpE = p.positions()

    # Check if particle correctly bounced off from closed wall
    xpE_root = comm.gather(xpE, root=0)
    if comm.rank == 0:
        xpE_root = np.float64(np.vstack(xpE_root))
        error = np.linalg.norm(x_bounced - xpE_root)
        assert(error < 1e-10)
Пример #5
0
def test_taylor_hood_cube():
    pytest.xfail("Problem with Mixed Function Spaces")
    meshc = UnitCubeMesh(MPI.comm_world, 2, 2, 2)
    meshf = UnitCubeMesh(MPI.comm_world, 3, 4, 5)

    Ve = VectorElement("CG", meshc.ufl_cell(), 2)
    Qe = FiniteElement("CG", meshc.ufl_cell(), 1)
    Ze = MixedElement([Ve, Qe])

    Zc = FunctionSpace(meshc, Ze)
    Zf = FunctionSpace(meshf, Ze)

    def z(values, x):
        values[:, 0] = x[:, 0] * x[:, 1]
        values[:, 1] = x[:, 1] * x[:, 2]
        values[:, 2] = x[:, 2] * x[:, 0]
        values[:, 3] = x[:, 0] + 3.0 * x[:, 1] + x[:, 2]

    zc = interpolate(z, Zc)
    zf = interpolate(z, Zf)

    mat = PETScDMCollection.create_transfer_matrix(Zc, Zf)
    Zuc = Function(Zf)
    mat.mult(zc.vector, Zuc.vector)
    Zuc.vector.update_ghost_values()

    diff = Function(Zf)
    diff.assign(Zuc - zf)
    assert diff.vector.norm("l2") < 1.0e-12
Пример #6
0
def mk_scheme(N, Vname, Vorder, cpp_expr, expr_args, convection_inp, dim=2, comm=None):
    if comm is None:
        comm = MPI.comm_world

    parameters['ghost_mode'] = 'shared_vertex'
    if dim == 2:
        mesh = UnitSquareMesh(comm, N, N)
    else:
        mesh = UnitCubeMesh(comm, N, N, N)

    V = FunctionSpace(mesh, Vname, Vorder)
    C = Function(V)
    e = Expression(cpp_expr, element=V.ufl_element(), **expr_args)
    C.interpolate(e)

    D = Function(V)
    D.assign(C)

    sim = Simulation()
    sim.set_mesh(mesh)
    sim.data['constrained_domain'] = None
    sim.data['C'] = C
    for key, value in convection_inp.items():
        sim.input.set_value('convection/C/%s' % key, value)

    scheme_name = convection_inp['convection_scheme']
    return get_convection_scheme(scheme_name)(sim, 'C')
Пример #7
0
 def __init__(self, V):
     u = Function(V)
     u.assign(Constant('1.0'))
     self.one = u.vector()
     self.Mdiag = self.one.copy()
     self.Mdiag2 = self.one.copy()
     self.invMdiag = self.one.copy()
Пример #8
0
def test_advect_periodic(advection_scheme):
    # FIXME: this unit test is sensitive to the ordering of the particle
    # array, i.e. xp0_root and xpE_root may contain exactly the same entries
    # but only in a different order. This will return an error right now

    xmin, xmax = 0., 1.
    ymin, ymax = 0., 1.
    pres = 3

    mesh = RectangleMesh(Point(xmin, ymin), Point(xmax, ymax), 10, 10)

    lims = np.array([[xmin, xmin, ymin, ymax], [xmax, xmax, ymin, ymax],
                     [xmin, xmax, ymin, ymin], [xmin, xmax, ymax, ymax]])

    vexpr = Constant((1., 1.))
    V = VectorFunctionSpace(mesh, "CG", 1)

    x = RandomRectangle(Point(0.05, 0.05), Point(0.15, 0.15)).generate([pres, pres])
    x = comm.bcast(x, root=0)
    dt = 0.05

    v = Function(V)
    v.assign(vexpr)

    p = particles(x, [x*0, x**2], mesh)

    if advection_scheme == 'euler':
        ap = advect_particles(p, V, v, 'periodic', lims.flatten())
    elif advection_scheme == 'rk2':
        ap = advect_rk2(p, V, v, 'periodic', lims.flatten())
    elif advection_scheme == 'rk3':
        ap = advect_rk3(p, V, v, 'periodic', lims.flatten())
    else:
        assert False

    xp0 = p.positions()
    t = 0.
    while t < 1.-1e-12:
        ap.do_step(dt)
        t += dt
    xpE = p.positions()

    # Check if position correct
    xp0_root = comm.gather(xp0, root=0)
    xpE_root = comm.gather(xpE, root=0)

    num_particles = p.number_of_particles()

    if comm.Get_rank() == 0:
        xp0_root = np.float32(np.vstack(xp0_root))
        xpE_root = np.float32(np.vstack(xpE_root))

        # Sort on x positions
        xp0_root = xp0_root[xp0_root[:, 0].argsort(), :]
        xpE_root = xpE_root[xpE_root[:, 0].argsort(), :]

        error = np.linalg.norm(xp0_root - xpE_root)
        assert error < 1e-10
        assert num_particles - pres**2 == 0
def test_advect_periodic(advection_scheme):
    xmin, ymin, zmin = 0., 0., 0.
    xmax, ymax, zmax = 1., 1., 1.
    pres = 10

    mesh = UnitCubeMesh(10, 10, 10)

    lims = np.array([[xmin, xmin, ymin, ymax, zmin, zmax],
                     [xmax, xmax, ymin, ymax, zmin, zmax],
                     [xmin, xmax, ymin, ymin, zmin, zmax],
                     [xmin, xmax, ymax, ymax, zmin, zmax],
                     [xmin, xmax, ymin, ymax, zmin, zmin],
                     [xmin, xmax, ymin, ymax, zmax, zmax]])

    vexpr = Constant((1., 1., 1.))
    V = VectorFunctionSpace(mesh, "CG", 1)
    v = Function(V)
    v.assign(vexpr)

    x = RandomBox(Point(0., 0., 0.), Point(1., 1.,
                                           1.)).generate([pres, pres, pres])
    x = comm.bcast(x, root=0)
    dt = 0.05

    p = particles(x, [x * 0, x**2], mesh)

    if advection_scheme == 'euler':
        ap = advect_particles(p, V, v, 'periodic', lims.flatten())
    elif advection_scheme == 'rk2':
        ap = advect_rk2(p, V, v, 'periodic', lims.flatten())
    elif advection_scheme == 'rk3':
        ap = advect_rk3(p, V, v, 'periodic', lims.flatten())
    else:
        assert False

    xp0 = p.positions()
    t = 0.
    while t < 1. - 1e-12:
        ap.do_step(dt)
        t += dt
    xpE = p.positions()

    xp0_root = comm.gather(xp0, root=0)
    xpE_root = comm.gather(xpE, root=0)

    assert len(xp0) == len(xpE)
    num_particles = p.number_of_particles()

    if comm.Get_rank() == 0:
        xp0_root = np.float32(np.vstack(xp0_root))
        xpE_root = np.float32(np.vstack(xpE_root))

        # Sort on x positions
        xp0_root = xp0_root[xp0_root[:, 0].argsort(), :]
        xpE_root = xpE_root[xpE_root[:, 0].argsort(), :]

        error = np.linalg.norm(xp0_root - xpE_root)
        assert error < 1e-10
        assert num_particles - pres**3 == 0
Пример #10
0
def test_open_boundary(advection_scheme):
    xmin, xmax = 0.0, 1.0
    ymin, ymax = 0.0, 1.0
    pres = 3

    mesh = RectangleMesh(Point(xmin, ymin), Point(xmax, ymax), 10, 10)

    # Particle
    x = RandomRectangle(Point(0.955, 0.45), Point(1.0,
                                                  0.55)).generate([pres, pres])
    x = comm.bcast(x, root=0)

    # Given velocity field:
    vexpr = Constant((1.0, 1.0))
    # Given time do_step:
    dt = 0.05

    p = particles(x, [x, x], mesh)

    V = VectorFunctionSpace(mesh, "CG", 1)
    v = Function(V)
    v.assign(vexpr)

    # Different boundary parts
    bound_left = UnitSquareLeft()
    bound_right = UnitSquareRight()
    bound_top = UnitSquareTop()
    bound_bottom = UnitSquareBottom()

    # Mark all facets
    facet_marker = MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
    facet_marker.set_all(0)

    # Mark as open
    bound_right.mark(facet_marker, 2)

    # Mark other boundaries as closed
    bound_left.mark(facet_marker, 1)
    bound_top.mark(facet_marker, 1)
    bound_bottom.mark(facet_marker, 1)

    if advection_scheme == "euler":
        ap = advect_particles(p, V, v, facet_marker)
    elif advection_scheme == "rk2":
        ap = advect_rk2(p, V, v, facet_marker)
    elif advection_scheme == "rk3":
        ap = advect_rk3(p, V, v, facet_marker)
    else:
        assert False

    # Do one timestep, particle must bounce from wall of
    ap.do_step(dt)
    num_particles = p.number_of_particles()

    # Check if all particles left domain
    if comm.rank == 0:
        assert (num_particles == 0)
Пример #11
0
def test_advect_periodic_facet_marker(advection_scheme):
    xmin, xmax = 0.0, 1.0
    ymin, ymax = 0.0, 1.0

    mesh = RectangleMesh(Point(xmin, ymin), Point(xmax, ymax), 10, 10)
    facet_marker = MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
    facet_marker.set_all(0)
    boundaries = Boundaries()
    boundaries.mark(facet_marker, 3)

    lims = np.array([
        [xmin, xmin, ymin, ymax],
        [xmax, xmax, ymin, ymax],
        [xmin, xmax, ymin, ymin],
        [xmin, xmax, ymax, ymax],
    ])

    vexpr = Constant((1.0, 1.0))
    V = VectorFunctionSpace(mesh, "CG", 1)

    x = RandomRectangle(Point(0.05, 0.05), Point(0.15, 0.15)).generate([3, 3])
    x = comm.bcast(x, root=0)
    dt = 0.05

    v = Function(V)
    v.assign(vexpr)

    p = particles(x, [x * 0, x**2], mesh)

    if advection_scheme == "euler":
        ap = advect_particles(p, V, v, facet_marker, lims.flatten())
    elif advection_scheme == "rk2":
        ap = advect_rk2(p, V, v, facet_marker, lims.flatten())
    elif advection_scheme == "rk3":
        ap = advect_rk3(p, V, v, facet_marker, lims.flatten())
    else:
        assert False

    xp0 = p.positions()
    t = 0.0
    while t < 1.0 - 1e-12:
        ap.do_step(dt)
        t += dt
    xpE = p.positions()

    # Check if position correct
    xp0_root = comm.gather(xp0, root=0)
    xpE_root = comm.gather(xpE, root=0)

    if comm.Get_rank() == 0:
        xp0_root = np.float32(np.vstack(xp0_root))
        xpE_root = np.float32(np.vstack(xpE_root))
        error = np.linalg.norm(xp0_root - xpE_root)
        assert error < 1e-10
Пример #12
0
def gaussian_distribution(mb,
                          mu,
                          sigma,
                          function=None,
                          lumping=True,
                          nsteps=100):
    "Gaussian distribution via heat equation"

    tend = 0.5 * sigma**2
    dt = Constant(tend / nsteps, name="smooth")

    # prepare the problem
    P1e = FiniteElement("CG", mb.ufl_cell(), 1)
    Ve = FunctionSpace(mb, P1e)

    u, v = TrialFunction(Ve), TestFunction(Ve)
    uold = Function(Ve)

    if lumping:
        # diffusion
        K = assemble(dt * inner(grad(u), grad(v)) * dx)
        # we use mass lumping to avoid negative values
        Md = assemble(action(u * v * dx, Constant(1.0)))
        # full matrix (divide my mass)
        M = Matrix(K)
        M.zero()
        M.set_diagonal(Md)
        A = M + K
    else:
        a = u * v * dx + dt * inner(grad(u), grad(v)) * dx
        L = uold * v * dx
        A = assemble(a)

    # initial conditions
    dist = function or Function(Ve)

    dist.vector().zero()
    PointSource(Ve, mu, 1.0).apply(dist.vector())

    # iterations
    for t in range(nsteps):
        uold.assign(dist)
        if lumping:
            solve(A, dist.vector(), M * uold.vector())
        else:
            b = assemble(L)
            solve(A, dist.vector(), b)

    # normalize
    area = assemble(dist * dx)
    dist.vector()[:] /= area

    if function is None:
        return dist
Пример #13
0
def test_bounded_domain_boundary(xlims, ylims, advection_scheme):
    xmin, xmax = xlims
    ymin, ymax = ylims
    pres = 1

    mesh = RectangleMesh(Point(xmin, ymin), Point(xmax, ymax), 10, 10)

    ymin += 0.0025
    lims = np.array([xmin, xmax, ymin, ymax])

    v_arr = np.array([-1.0, -1.0])
    vexpr = Constant(v_arr)
    V = VectorFunctionSpace(mesh, "CG", 1)

    x = RandomRectangle(Point(0.05, 0.05), Point(0.15,
                                                 0.15)).generate([pres, pres])
    dt = 0.005

    v = Function(V)
    v.assign(vexpr)

    p = particles(x, [x], mesh)

    if advection_scheme == 'euler':
        ap = advect_particles(p, V, v, 'bounded', lims.flatten())
    elif advection_scheme == 'rk2':
        ap = advect_rk2(p, V, v, 'bounded', lims.flatten())
    elif advection_scheme == 'rk3':
        ap = advect_rk3(p, V, v, 'bounded', lims.flatten())
    else:
        assert False

    original_num_particles = p.number_of_particles()
    t = 0.
    while t < 3.0 - 1e-12:
        ap.do_step(dt)
        t += dt

        assert p.number_of_particles() == original_num_particles

        xpn = np.array(p.get_property(0)).reshape((-1, 2))
        x0 = np.array(p.get_property(1)).reshape((-1, 2))

        analytical_position = x0 + t * v_arr

        analytical_position[:, 0] = np.maximum(
            np.minimum(xmax, analytical_position[:, 0]), xmin)
        analytical_position[:, 1] = np.maximum(
            np.minimum(ymax, analytical_position[:, 1]), ymin)

        error = np.abs(xpn - analytical_position)

        assert np.all(np.abs(error) < 1e-12)
Пример #14
0
def _compute_time_errors(problem, method, mesh_sizes, Dt, plot_error=False):
    mesh_generator, solution, ProblemClass, cell_type = problem()
    # Translate data into FEniCS expressions.
    fenics_sol = Expression(smp.printing.ccode(solution['value']),
                            degree=solution['degree'],
                            t=0.0,
                            cell=cell_type
                            )
    # Compute the problem
    errors = {'theta': numpy.empty((len(mesh_sizes), len(Dt)))}
    # Create initial state.
    # Deepcopy the expression into theta0. Specify the cell to allow for
    # more involved operations with it (e.g., grad()).
    theta0 = Expression(fenics_sol.cppcode,
                        degree=solution['degree'],
                        t=0.0,
                        cell=cell_type
                        )
    for k, mesh_size in enumerate(mesh_sizes):
        mesh = mesh_generator(mesh_size)
        V = FunctionSpace(mesh, 'CG', 1)
        theta_approx = Function(V)
        theta0p = project(theta0, V)
        stepper = method(ProblemClass(V))
        if plot_error:
            error = Function(V)
        for j, dt in enumerate(Dt):
            # TODO We are facing a little bit of a problem here, being the
            # fact that the time stepper only accept elements from V as u0.
            # In principle, though, this isn't necessary or required. We
            # could allow for arbitrary expressions here, but then the API
            # would need changing for problem.lhs(t, u).
            # Think about this.
            stepper.step(theta_approx, theta0p,
                         0.0, dt,
                         tol=1.0e-12,
                         verbose=False
                         )
            fenics_sol.t = dt
            #
            # NOTE
            # When using errornorm(), it is quite likely to see a good part
            # of the error being due to the spatial discretization.  Some
            # analyses "get rid" of this effect by (sometimes implicitly)
            # projecting the exact solution onto the discrete function
            # space.
            errors['theta'][k][j] = errornorm(fenics_sol, theta_approx)
            if plot_error:
                error.assign(project(fenics_sol - theta_approx, V))
                plot(error, title='error (dt=%e)' % dt)
                interactive()
    return errors, stepper.name, stepper.order
Пример #15
0
 def assemble_solution(self, t):  # returns Womersley sol for time t
     if self.tc is not None:
         self.tc.start('assembleSol')
     sol = Function(self.solutionSpace)
     dofs2 = self.solutionSpace.sub(2).dofmap().dofs()  # gives field of indices corresponding to z axis
     sol.assign(Constant(("0.0", "0.0", "0.0")))  # QQ not needed
     sol.vector()[dofs2] += self.factor * self.bessel_parabolic.vector().array()  # parabolic part of sol
     for idx in range(8):  # add modes of Womersley sol
         sol.vector()[dofs2] += self.factor * cos(self.coefs_exp[idx] * pi * t) * self.bessel_real[idx].vector().array()
         sol.vector()[dofs2] += self.factor * -sin(self.coefs_exp[idx] * pi * t) * self.bessel_complex[idx].vector().array()
     if self.tc is not None:
         self.tc.end('assembleSol')
     return sol
Пример #16
0
def test_advect_open(advection_scheme):
    pres = 3

    mesh = UnitCubeMesh(10, 10, 10)

    # Particle
    x = RandomBox(Point(0.955, 0.45, 0.5),
                  Point(0.99, 0.55, 0.6)).generate([pres, pres, pres])
    x = comm.bcast(x, root=0)

    # Given velocity field:
    vexpr = Constant((1.0, 1.0, 1.0))
    # Given time do_step:
    dt = 0.05

    p = particles(x, [x, x], mesh)

    V = VectorFunctionSpace(mesh, "CG", 1)
    v = Function(V)
    v.assign(vexpr)

    # Different boundary parts
    bounds = Boundaries()
    bound_right = UnitCubeRight()

    # Mark all facets
    facet_marker = MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
    facet_marker.set_all(0)
    bounds.mark(facet_marker, 1)
    bound_right.mark(facet_marker, 2)

    # Mark as open
    bound_right.mark(facet_marker, 2)

    if advection_scheme == "euler":
        ap = advect_particles(p, V, v, facet_marker)
    elif advection_scheme == "rk2":
        ap = advect_rk2(p, V, v, facet_marker)
    elif advection_scheme == "rk3":
        ap = advect_rk3(p, V, v, facet_marker)
    else:
        assert False

    # Do one timestep, particle must bounce from wall of
    ap.do_step(dt)
    num_particles = p.number_of_particles()

    # Check if all particles left domain
    if comm.rank == 0:
        assert num_particles == 0
Пример #17
0
def test_advect_particle(advection_scheme):
    if comm.rank == 0:
        print('Run advect_particle')

    # Rotate one particle, and compute the error
    mesh = UnitSquareMesh(10, 10)

    # Particle
    x = np.array([[0.25, 0.25]])
    dt_list = [0.08, 0.04, 0.02, 0.01, 0.005]

    # Velocity field
    vexpr = Expression(('-pi*(x[1] - 0.5)', 'pi*(x[0]-0.5)'), degree=3)
    V = VectorFunctionSpace(mesh, "CG", 1)
    v = Function(V)
    v.assign(vexpr)

    error_list = []

    for dt in dt_list:
        p = particles(x, [x, x], mesh)
        if advection_scheme == 'euler':
            ap = advect_particles(p, V, v, 'closed')
        elif advection_scheme == 'rk2':
            ap = advect_rk2(p, V, v, 'closed')
        elif advection_scheme == 'rk3':
            ap = advect_rk3(p, V, v, 'closed')
        else:
            assert False

        xp_0 = p.positions()
        t = 0.
        while t < 2.-1e-12:
            ap.do_step(dt)
            t += dt

        xp_end = p.positions()
        error_list.append(np.linalg.norm(xp_0 - xp_end))

    if not all(eps == 0 for eps in error_list):
        rate = compute_convergence(dt_list, error_list)
        if advection_scheme == 'euler':
            # First order for euler
            assert any(i > 0.9 for i in rate)
        elif advection_scheme == 'rk2':
            # Second order for rk2
            assert any(i > 1.95 for i in rate)
        elif advection_scheme == 'rk3':
            # Third order for rk3
            assert any(i > 2.9 for i in rate)
Пример #18
0
def compute_velocity_correction(
    ui, p0, p1, u_bcs, rho, mu, dt, rotational_form, my_dx, tol, verbose
):
    """Compute the velocity correction according to

    .. math::

        U = u_0 - \\frac{dt}{\\rho} \\nabla (p_1-p_0).
    """
    W = ui.function_space()
    P = p1.function_space()

    u = TrialFunction(W)
    v = TestFunction(W)
    a3 = dot(u, v) * my_dx
    phi = Function(P)
    phi.assign(p1)
    if p0:
        phi -= p0
    if rotational_form:
        r = SpatialCoordinate(W.mesh())[0]
        div_ui = 1 / r * (r * ui[0]).dx(0) + ui[1].dx(1)
        phi += mu * div_ui
    L3 = dot(ui, v) * my_dx - dt / rho * (phi.dx(0) * v[0] + phi.dx(1) * v[1]) * my_dx
    u1 = Function(W)
    solve(
        a3 == L3,
        u1,
        bcs=u_bcs,
        solver_parameters={
            "linear_solver": "iterative",
            "symmetric": True,
            "preconditioner": "hypre_amg",
            "krylov_solver": {
                "relative_tolerance": tol,
                "absolute_tolerance": 0.0,
                "maximum_iterations": 100,
                "monitor_convergence": verbose,
            },
        },
    )
    # u = project(ui - k/rho * grad(phi), V)
    # div_u = 1/r * div(r*u)
    r = SpatialCoordinate(W.mesh())[0]
    div_u1 = 1.0 / r * (r * u1[0]).dx(0) + u1[1].dx(1)
    info("||u||_div = {:e}".format(sqrt(assemble(div_u1 * div_u1 * my_dx))))
    return u1
Пример #19
0
def _compute_time_errors(problem, method, mesh_sizes, Dt, plot_error=False):
    mesh_generator, solution, ProblemClass, _ = problem()
    # Translate data into FEniCS expressions.
    fenics_sol = Expression(sympy.printing.ccode(solution),
                            degree=MAX_DEGREE,
                            t=0.0)
    # Compute the problem
    errors = numpy.empty((len(mesh_sizes), len(Dt)))
    # Create initial state.
    # Deepcopy the expression into theta0. Specify the cell to allow for
    # more involved operations with it (e.g., grad()).
    theta0 = Expression(fenics_sol.cppcode, degree=MAX_DEGREE, t=0.0)
    for k, mesh_size in enumerate(mesh_sizes):
        mesh = mesh_generator(mesh_size)

        # Choose the function space such that the exact solution can be
        # represented as well as possible.
        V = FunctionSpace(mesh, 'CG', 4)

        theta_approx = Function(V)
        theta0p = project(theta0, V)

        stepper = method(ProblemClass(V))
        if plot_error:
            error = Function(V)
        for j, dt in enumerate(Dt):
            # TODO We are facing a little bit of a problem here, being the fact
            # that the time stepper only accept elements from V as u0.  In
            # principle, though, this isn't necessary or required. We could
            # allow for arbitrary expressions here, but then the API would need
            # changing for problem.lhs(t, u).  Think about this.
            theta_approx.assign(stepper.step(theta0p, 0.0, dt))
            fenics_sol.t = dt

            # NOTE
            # When using errornorm(), it is quite likely to see a good part of
            # the error being due to the spatial discretization.  Some analyses
            # "get rid" of this effect by (sometimes implicitly) projecting the
            # exact solution onto the discrete function space.

            errors[k][j] = errornorm(fenics_sol, theta_approx)
            if plot_error:
                error.assign(project(fenics_sol - theta_approx, V))
                plot(error, title='error (dt={:e})'.format(dt))
                plt.show()
    return errors
Пример #20
0
 def assemble_solution(self, t):  # returns
     """
     :param t: time
     :return: Womersley flow (analytic solution) at time t
     analytic solution at any time is a steady parabolic flow + linear combination of 8 modes
     modes were precomputed as 8 functions on given mesh and stored in hdf5 file
     """
     if self.tc is not None:
         self.tc.start('assembleSol')
     sol = Function(self.solutionSpace)
     # analytic solution has zero x and y components
     dofs2 = self.solutionSpace.sub(2).dofmap().dofs()  # gives field of indices corresponding to z axis
     sol.assign(Constant(("0.0", "0.0", "0.0")))  # QQ not needed
     sol.vector()[dofs2] += self.factor * self.bessel_parabolic.vector().array()  # parabolic part of sol
     for idx in range(8):  # add modes of Womersley sol
         sol.vector()[dofs2] += self.factor * cos(self.coefs_exp[idx] * pi * t) * self.bessel_real[idx].vector().array()
         sol.vector()[dofs2] += self.factor * -sin(self.coefs_exp[idx] * pi * t) * self.bessel_complex[idx].vector().array()
     if self.tc is not None:
         self.tc.end('assembleSol')
     return sol
Пример #21
0
def test_l2_projection_3D(polynomial_order, in_expression):
    xmin, ymin, zmin = 0.0, 0.0, 0.0
    xmax, ymax, zmax = 1.0, 1.0, 1.0
    nx = 25

    property_idx = 1
    mesh = BoxMesh(Point(xmin, ymin, zmin), Point(xmax, ymax, zmax), nx, nx,
                   nx)

    interpolate_expression = Expression(in_expression, degree=3)

    if len(interpolate_expression.ufl_shape) == 0:
        V = FunctionSpace(mesh, "DG", polynomial_order)
    elif len(interpolate_expression.ufl_shape) == 1:
        V = VectorFunctionSpace(mesh, "DG", polynomial_order)

    v_exact = Function(V)
    v_exact.assign(interpolate_expression)

    x = RandomBox(Point(0.0, 0.0, 0.0), Point(1.0, 1.0,
                                              1.0)).generate([4, 4, 4])
    s = assign_particle_values(x, interpolate_expression)

    # Just make a complicated particle, possibly with scalars and vectors mixed
    p = particles(x, [s], mesh)

    # Do AddDelete sweep
    AD = AddDelete(p, 13, 15, [v_exact])
    AD.do_sweep()

    vh = Function(V)
    lstsq_vh = l2projection(p, V, property_idx)
    lstsq_vh.project(vh.cpp_object())

    error_sq = abs(assemble(dot(v_exact - vh, v_exact - vh) * dx))
    if comm.Get_rank() == 0:
        assert error_sq < 1e-13
Пример #22
0
    def solve(self, problem):
        self.problem = problem
        doSave = problem.doSave
        save_this_step = False
        onlyVel = problem.saveOnlyVel
        dt = self.metadata['dt']

        nu = Constant(self.problem.nu)
        # TODO check proper use of watches
        self.tc.init_watch('init', 'Initialization', True, count_to_percent=False)
        self.tc.init_watch('rhs', 'Assembled right hand side', True, count_to_percent=True)
        self.tc.init_watch('updateBC', 'Updated velocity BC', True, count_to_percent=True)
        self.tc.init_watch('applybc1', 'Applied velocity BC 1st step', True, count_to_percent=True)
        self.tc.init_watch('applybc3', 'Applied velocity BC 3rd step', True, count_to_percent=True)
        self.tc.init_watch('applybcP', 'Applied pressure BC or othogonalized rhs', True, count_to_percent=True)
        self.tc.init_watch('assembleMatrices', 'Initial matrix assembly', False, count_to_percent=True)
        self.tc.init_watch('solve 1', 'Running solver on 1st step', True, count_to_percent=True)
        self.tc.init_watch('solve 2', 'Running solver on 2nd step', True, count_to_percent=True)
        self.tc.init_watch('solve 3', 'Running solver on 3rd step', True, count_to_percent=True)
        self.tc.init_watch('solve 4', 'Running solver on 4th step', True, count_to_percent=True)
        self.tc.init_watch('assembleA1', 'Assembled A1 matrix (without stabiliz.)', True, count_to_percent=True)
        self.tc.init_watch('assembleA1stab', 'Assembled A1 stabilization', True, count_to_percent=True)
        self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True)
        self.tc.init_watch('saveVel', 'Saved velocity', True)

        self.tc.start('init')

        # Define function spaces (P2-P1)
        mesh = self.problem.mesh
        self.V = VectorFunctionSpace(mesh, "Lagrange", 2)  # velocity
        self.Q = FunctionSpace(mesh, "Lagrange", 1)  # pressure
        self.PS = FunctionSpace(mesh, "Lagrange", 2)  # partial solution (must be same order as V)
        self.D = FunctionSpace(mesh, "Lagrange", 1)   # velocity divergence space
        if self.bc == 'lagrange':
            L = FunctionSpace(mesh, "R", 0)
            QL = self.Q*L

        problem.initialize(self.V, self.Q, self.PS, self.D)

        # Define trial and test functions
        u = TrialFunction(self.V)
        v = TestFunction(self.V)
        if self.bc == 'lagrange':
            (pQL, rQL) = TrialFunction(QL)
            (qQL, lQL) = TestFunction(QL)
        else:
            p = TrialFunction(self.Q)
            q = TestFunction(self.Q)

        n = FacetNormal(mesh)
        I = Identity(u.geometric_dimension())

        # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure
        [u1, u0, p0] = self.problem.get_initial_conditions([{'type': 'v', 'time': -dt},
                                                          {'type': 'v', 'time': 0.0},
                                                          {'type': 'p', 'time': 0.0}])

        if doSave:
            problem.save_vel(False, u0, 0.0)
            problem.save_vel(True, u0, 0.0)

        u_ = Function(self.V)         # current tentative velocity
        u_cor = Function(self.V)         # current corrected velocity
        if self.bc == 'lagrange':
            p_QL = Function(QL)    # current pressure or pressure help function from rotation scheme
            pQ = Function(self.Q)     # auxiliary function for conversion between QL.sub(0) and Q
        else:
            p_ = Function(self.Q)         # current pressure or pressure help function from rotation scheme
        p_mod = Function(self.Q)      # current modified pressure from rotation scheme

        # Define coefficients
        k = Constant(self.metadata['dt'])
        f = Constant((0, 0, 0))

        # Define forms
        # step 1: Tentative velocity, solve to u_
        u_ext = 1.5*u0 - 0.5*u1  # extrapolation for convection term

        # Stabilisation
        h = CellSize(mesh)
        # CBC delta:
        if self.cbcDelta:
            delta = Constant(self.stabCoef)*h/(sqrt(inner(u_ext, u_ext))+h)
        else:
            delta = Constant(self.stabCoef)*h**2/(2*nu*k + k*h*inner(u_ext, u_ext)+h**2)

        if self.use_full_SUPG:
            v1 = v + delta*0.5*k*dot(grad(v), u_ext)
            parameters['form_compiler']['quadrature_degree'] = 6
        else:
            v1 = v

        def nonlinearity(function):
            if self.use_ema:
               return 2*inner(dot(sym(grad(function)), u_ext), v1) * dx + inner(div(function)*u_ext, v1) * dx
                # return 2*inner(dot(sym(grad(function)), u_ext), v) * dx + inner(div(u_ext)*function, v) * dx
                # QQ implement this way?
            else:
                return inner(dot(grad(function), u_ext), v1) * dx

        def diffusion(fce):
            if self.useLaplace:
                return nu*inner(grad(fce), grad(v1)) * dx
            else:
                form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx
                if self.bcv == 'CDN':
                    # IMP will work only if p=0 on output, or we must add term
                    # inner(p0*n, v)*problem.get_outflow_measure_form() to avoid boundary layer
                    return form
                if self.bcv == 'LAP':
                    return form - inner(nu*dot(grad(fce).T, n), v1)  * problem.get_outflow_measure_form()
                if self.bcv == 'DDN':
                    # IMP will work only if p=0 on output, or we must add term
                    # inner(p0*n, v)*problem.get_outflow_measure_form() to avoid boundary layer
                    return form  # additional term must be added to non-constant part

        def pressure_rhs():
            if self.useLaplace or self.bcv == 'LAP':
                return inner(p0, div(v1)) * dx - inner(p0*n, v1) * problem.get_outflow_measure_form()
                # NT term inner(inner(p, n), v) is 0 when p=0 on outflow
            else:
                return inner(p0, div(v1)) * dx

        a1_const = (1./k)*inner(u, v1)*dx + diffusion(0.5*u)
        a1_change = nonlinearity(0.5*u)
        if self.bcv == 'DDN':
            # IMP Problem: Does not penalize influx for current step, only for the next one
            # IMP this can lead to oscilation: DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx...
            # u and u_ext cannot be switched, min_value is nonlinear function
            a1_change += -0.5*min_value(Constant(0.), inner(u_ext, n))*inner(u, v1)*problem.get_outflow_measure_form()
            # IMP works only with uflacs compiler

        L1 = (1./k)*inner(u0, v1)*dx - nonlinearity(0.5*u0) - diffusion(0.5*u0) + pressure_rhs()
        if self.bcv == 'DDN':
            L1 += 0.5*min_value(0., inner(u_ext, n))*inner(u0, v1)*problem.get_outflow_measure_form()

        # Non-consistent SUPG stabilisation
        if self.stabilize and not self.use_full_SUPG:
            # a1_stab = delta*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx
            a1_stab = 0.5*delta*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6})
            # NT optional: use Crank Nicolson in stabilisation term: change RHS
            # L1 += -0.5*delta*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6})

        outflow_area = Constant(problem.outflow_area)
        need_outflow = Constant(0.0)
        if self.useRotationScheme:
            # Rotation scheme
            if self.bc == 'lagrange':
                F2 = inner(grad(pQL), grad(qQL))*dx + (1./k)*qQL*div(u_)*dx + pQL*lQL*dx + qQL*rQL*dx
            else:
                F2 = inner(grad(p), grad(q))*dx + (1./k)*q*div(u_)*dx
        else:
            # Projection, solve to p_
            if self.bc == 'lagrange':
                F2 = inner(grad(pQL - p0), grad(qQL))*dx + (1./k)*qQL*div(u_)*dx + pQL*lQL*dx + qQL*rQL*dx
            else:
                if self.forceOutflow and problem.can_force_outflow:
                    info('Forcing outflow.')
                    F2 = inner(grad(p - p0), grad(q))*dx + (1./k)*q*div(u_)*dx
                    for m in problem.get_outflow_measures():
                        F2 += (1./k)*(1./outflow_area)*need_outflow*q*m
                else:
                    F2 = inner(grad(p - p0), grad(q))*dx + (1./k)*q*div(u_)*dx
        a2, L2 = system(F2)

        # step 3: Finalize, solve to u_
        if self.useRotationScheme:
            # Rotation scheme
            if self.bc == 'lagrange':
                F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_QL.sub(0)), v)*dx
            else:
                F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_), v)*dx
        else:
            if self.bc == 'lagrange':
                F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_QL.sub(0) - p0), v)*dx
            else:
                F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_ - p0), v)*dx
        a3, L3 = system(F3)

        if self.useRotationScheme:
            # Rotation scheme: modify pressure
            if self.bc == 'lagrange':
                pr = TrialFunction(self.Q)
                qr = TestFunction(self.Q)
                F4 = (pr - p0 - p_QL.sub(0) + nu*div(u_))*qr*dx
            else:
                F4 = (p - p0 - p_ + nu*div(u_))*q*dx
            # TODO zkusit, jestli to nebude rychlejsi? nepocitat soustavu, ale p.assign(...), nutno project(div(u),Q) coz je pocitani podobne soustavy
            # TODO zkusit v project zadat solver_type='lu' >> primy resic by mel byt efektivnejsi
            a4, L4 = system(F4)

        # Assemble matrices
        self.tc.start('assembleMatrices')
        A1_const = assemble(a1_const)  # need to be here, so A1 stays one Python object during repeated assembly
        A1_change = A1_const.copy()  # copy to get matrix with same sparse structure (data will be overwriten)
        if self.stabilize and not self.use_full_SUPG:
            A1_stab = A1_const.copy()  # copy to get matrix with same sparse structure (data will be overwriten)
        A2 = assemble(a2)
        A3 = assemble(a3)
        if self.useRotationScheme:
            A4 = assemble(a4)
        self.tc.end('assembleMatrices')

        if self.solvers == 'direct':
            self.solver_vel_tent = LUSolver('mumps')
            self.solver_vel_cor = LUSolver('mumps')
            self.solver_p = LUSolver('umfpack')
            if self.useRotationScheme:
                self.solver_rot = LUSolver('umfpack')
        else:
            # NT not needed, chosen not to use hypre_parasails
            # if self.prec_v == 'hypre_parasails':  # in FEniCS 1.6.0 inaccessible using KrylovSolver class
            #     self.solver_vel_tent = PETScKrylovSolver('gmres')   # PETSc4py object
            #     self.solver_vel_tent.ksp().getPC().setType('hypre')
            #     PETScOptions.set('pc_hypre_type', 'parasails')
            #     # this is global setting, but preconditioners for pressure solvers are set by their constructors
            # else:
            self.solver_vel_tent = KrylovSolver('gmres', self.prec_v)   # nonsymetric > gmres
            # IMP cannot use 'ilu' in parallel (choose different default option)
            self.solver_vel_cor = KrylovSolver('cg', 'hypre_amg')   # nonsymetric > gmres
            self.solver_p = KrylovSolver('cg', self.prec_p)          # symmetric > CG
            if self.useRotationScheme:
                self.solver_rot = KrylovSolver('cg', self.prec_p)

        solver_options = {'monitor_convergence': True, 'maximum_iterations': 1000, 'nonzero_initial_guess': True}
        # 'nonzero_initial_guess': True   with  solver.solbe(A, u, b) means that
        # Solver will use anything stored in u as an initial guess

        # Get the nullspace if there are no pressure boundary conditions
        foo = Function(self.Q)     # auxiliary vector for setting pressure nullspace
        if self.bc in ['nullspace', 'nullspace_s']:
            null_vec = Vector(foo.vector())
            self.Q.dofmap().set(null_vec, 1.0)
            null_vec *= 1.0/null_vec.norm('l2')
            self.null_space = VectorSpaceBasis([null_vec])
            if self.bc == 'nullspace':
                as_backend_type(A2).set_nullspace(self.null_space)

        # apply global options for Krylov solvers
        self.solver_vel_tent.parameters['relative_tolerance'] = 10 ** (-self.precision_rel_v_tent)
        self.solver_vel_tent.parameters['absolute_tolerance'] = 10 ** (-self.precision_abs_v_tent)
        self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12
        self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4
        self.solver_p.parameters['relative_tolerance'] = 10**(-self.precision_p)
        self.solver_p.parameters['absolute_tolerance'] = 10E-10
        if self.useRotationScheme:
            self.solver_rot.parameters['relative_tolerance'] = 10**(-self.precision_p)
            self.solver_rot.parameters['absolute_tolerance'] = 10E-10

        if self.solvers == 'krylov':
            for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_p, self.solver_rot] if \
                    self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]:
                for key, value in solver_options.items():
                    try:
                        solver.parameters[key] = value
                    except KeyError:
                        info('Invalid option %s for KrylovSolver' % key)
                        return 1
                solver.parameters['preconditioner']['structure'] = 'same'
                # matrices A2-A4 do not change, so we can reuse preconditioners

        self.solver_vel_tent.parameters['preconditioner']['structure'] = 'same_nonzero_pattern'
        # matrix A1 changes every time step, so change of preconditioner must be allowed

        if self.bc == 'lagrange':
            fa = FunctionAssigner(self.Q, QL.sub(0))

        # boundary conditions
        bcu, bcp = problem.get_boundary_conditions(self.bc == 'outflow', self.V, self.Q)
        self.tc.end('init')
        # Time-stepping
        info("Running of Incremental pressure correction scheme n. 1")
        ttime = self.metadata['time']
        t = dt
        step = 1
        while t < (ttime + dt/2.0):
            info("t = %f" % t)
            self.problem.update_time(t, step)
            if self.MPI_rank == 0:
                problem.write_status_file(t)

            if doSave:
                save_this_step = problem.save_this_step

            # DDN debug
            # u_ext_in = assemble(inner(u_ext, n)*problem.get_outflow_measure_form())
            # DDN_triggered = assemble(min_value(Constant(0.), inner(u_ext, n))*problem.get_outflow_measure_form())
            # print('DDN: u_ext*n dSout = ', u_ext_in)
            # print('DDN: negative part of u_ext*n dSout = ', DDN_triggered)

            # assemble matrix (it depends on solution)
            self.tc.start('assembleA1')
            assemble(a1_change, tensor=A1_change)  # assembling into existing matrix is faster than assembling new one
            A1 = A1_const.copy()  # we dont want to change A1_const
            A1.axpy(1, A1_change, True)
            self.tc.end('assembleA1')
            self.tc.start('assembleA1stab')
            if self.stabilize and not self.use_full_SUPG:
                assemble(a1_stab, tensor=A1_stab)  # assembling into existing matrix is faster than assembling new one
                A1.axpy(1, A1_stab, True)
            self.tc.end('assembleA1stab')

            # Compute tentative velocity step
            begin("Computing tentative velocity")
            self.tc.start('rhs')
            b = assemble(L1)
            self.tc.end('rhs')
            self.tc.start('applybc1')
            [bc.apply(A1, b) for bc in bcu]
            self.tc.end('applybc1')
            try:
                self.tc.start('solve 1')
                self.solver_vel_tent.solve(A1, u_.vector(), b)
                self.tc.end('solve 1')
                if save_this_step:
                    self.tc.start('saveVel')
                    problem.save_vel(True, u_, t)
                    self.tc.end('saveVel')
                if save_this_step and not onlyVel:
                    problem.save_div(True, u_)
                problem.compute_err(True, u_, t)
                problem.compute_div(True, u_)
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            end()

            # DDN debug
            # u_ext_in = assemble(inner(u_, n)*problem.get_outflow_measure_form())
            # DDN_triggered = assemble(min_value(Constant(0.), inner(u_, n))*problem.get_outflow_measure_form())
            # print('DDN: u_tent*n dSout = ', u_ext_in)
            # print('DDN: negative part of u_tent*n dSout = ', DDN_triggered)

            if self.useRotationScheme:
                begin("Computing tentative pressure")
            else:
                begin("Computing pressure")
            if self.forceOutflow and problem.can_force_outflow:
                out = problem.compute_outflow(u_)
                info('Tentative outflow: %f' % out)
                n_o = -problem.last_inflow-out
                info('Needed outflow: %f' % n_o)
                need_outflow.assign(n_o)
            self.tc.start('rhs')
            b = assemble(L2)
            self.tc.end('rhs')
            self.tc.start('applybcP')
            [bc.apply(A2, b) for bc in bcp]
            if self.bc in ['nullspace', 'nullspace_s']:
                self.null_space.orthogonalize(b)
            self.tc.end('applybcP')
            try:
                self.tc.start('solve 2')
                if self.bc == 'lagrange':
                    self.solver_p.solve(A2, p_QL.vector(), b)
                else:
                    self.solver_p.solve(A2, p_.vector(), b)
                self.tc.end('solve 2')
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            if self.useRotationScheme:
                foo = Function(self.Q)
                if self.bc == 'lagrange':
                    fa.assign(pQ, p_QL.sub(0))
                    foo.assign(pQ + p0)
                else:
                    foo.assign(p_+p0)
                problem.averaging_pressure(foo)
                if save_this_step and not onlyVel:
                    problem.save_pressure(True, foo)
            else:
                if self.bc == 'lagrange':
                    fa.assign(pQ, p_QL.sub(0))
                    problem.averaging_pressure(pQ)
                    if save_this_step and not onlyVel:
                        problem.save_pressure(False, pQ)
                else:
                    # we do not want to change p=0 on outflow, it conflicts with do-nothing conditions
                    foo = Function(self.Q)
                    foo.assign(p_)
                    problem.averaging_pressure(foo)
                    if save_this_step and not onlyVel:
                        problem.save_pressure(False, foo)
            end()

            begin("Computing corrected velocity")
            self.tc.start('rhs')
            b = assemble(L3)
            self.tc.end('rhs')
            if not self.B:
                self.tc.start('applybc3')
                [bc.apply(A3, b) for bc in bcu]
                self.tc.end('applybc3')
            try:
                self.tc.start('solve 3')
                self.solver_vel_cor.solve(A3, u_cor.vector(), b)
                self.tc.end('solve 3')
                problem.compute_err(False, u_cor, t)
                problem.compute_div(False, u_cor)
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            if save_this_step:
                self.tc.start('saveVel')
                problem.save_vel(False, u_cor, t)
                self.tc.end('saveVel')
            if save_this_step and not onlyVel:
                problem.save_div(False, u_cor)
            end()

            # DDN debug
            # u_ext_in = assemble(inner(u_cor, n)*problem.get_outflow_measure_form())
            # DDN_triggered = assemble(min_value(Constant(0.), inner(u_cor, n))*problem.get_outflow_measure_form())
            # print('DDN: u_cor*n dSout = ', u_ext_in)
            # print('DDN: negative part of u_cor*n dSout = ', DDN_triggered)

            if self.useRotationScheme:
                begin("Rotation scheme pressure correction")
                self.tc.start('rhs')
                b = assemble(L4)
                self.tc.end('rhs')
                try:
                    self.tc.start('solve 4')
                    self.solver_rot.solve(A4, p_mod.vector(), b)
                    self.tc.end('solve 4')
                except RuntimeError as inst:
                    problem.report_fail(t)
                    return 1
                problem.averaging_pressure(p_mod)
                if save_this_step and not onlyVel:
                    problem.save_pressure(False, p_mod)
                end()

            # compute functionals (e. g. forces)
            problem.compute_functionals(u_cor,
                                        p_mod if self.useRotationScheme else (pQ if self.bc == 'lagrange' else p_), t)

            # Move to next time step
            self.tc.start('next')
            u1.assign(u0)
            u0.assign(u_cor)
            u_.assign(u_cor)  # use corretced velocity as initial guess in first step

            if self.useRotationScheme:
                p0.assign(p_mod)
            else:
                if self.bc == 'lagrange':
                    p0.assign(pQ)
                else:
                    p0.assign(p_)

            t = round(t + dt, 6)  # round time step to 0.000001
            step += 1
            self.tc.end('next')

        info("Finished: Incremental pressure correction scheme n. 1")
        problem.report()
        return 0
Пример #23
0
def solve_fixed_point(
        mesh,
        W_element, P_element, Q_element,
        u0, p0, theta0,
        kappa, rho, mu, cp,
        g, extra_force,
        heat_source,
        u_bcs, p_bcs,
        theta_dirichlet_bcs,
        theta_neumann_bcs,
        my_dx, my_ds,
        max_iter,
        tol
        ):
    # Solve the coupled heat-Stokes equation approximately. Do this
    # iteratively by solving the heat equation, then solving Stokes with the
    # updated heat, the heat equation with the updated velocity and so forth
    # until the change is 'small'.
    WP = FunctionSpace(mesh, MixedElement([W_element, P_element]))
    Q = FunctionSpace(mesh, Q_element)
    # Initialize functions.
    up0 = Function(WP)
    u0, p0 = up0.split()

    theta1 = Function(Q)
    for _ in range(max_iter):
        heat_problem = heat.Heat(
            Q,
            kappa=kappa,
            rho=rho(theta0),
            cp=cp,
            convection=u0,
            source=heat_source,
            dirichlet_bcs=theta_dirichlet_bcs,
            neumann_bcs=theta_neumann_bcs,
            my_dx=my_dx,
            my_ds=my_ds
            )

        theta1.assign(heat_problem.solve_stationary())

        # Solve problem for velocity, pressure.
        f = rho(theta0) * g  # coupling
        if extra_force:
            f += as_vector((extra_force[0], extra_force[1], 0.0))
        # up1 = up0.copy()
        stokes.stokes_solve(
            up0,
            mu,
            u_bcs, p_bcs,
            f,
            my_dx=my_dx,
            tol=1.0e-10,
            verbose=False,
            maxiter=1000
            )

        # from dolfin import plot
        # plot(u0)
        # plot(theta0)

        theta_diff = errornorm(theta0, theta1)
        info('||theta - theta0|| = {:e}'.format(theta_diff))
        # info('||u - u0||         = {:e}'.format(u_diff))
        # info('||p - p0||         = {:e}'.format(p_diff))
        # diff = theta_diff + u_diff + p_diff
        diff = theta_diff
        info('sum = {:e}'.format(diff))

        # # Show the iterates.
        # plot(theta0, title='theta0')
        # plot(u0, title='u0')
        # interactive()
        # #exit()
        if diff < tol:
            break

        theta0.assign(theta1)

    # Create a *deep* copy of u0, p0, to be able to deal with them as
    # actually separate entities.
    u0, p0 = up0.split(deepcopy=True)
    return u0, p0, theta0
Пример #24
0
            print("Starting computation with grid resolution " + str(nx))

        output_field = File(outdir + "psi_h" + "_nx" + str(nx) + ".pvd")

        # Compute num steps till completion
        num_steps = np.rint(Tend / float(dt))

        # Generate mesh
        mesh = RectangleMesh.create(
            [Point(xmin, ymin), Point(xmax, ymax)], [nx, nx], CellType.Type.triangle
        )

        # Velocity and initial condition
        V = VectorFunctionSpace(mesh, "CG", 1)
        uh = Function(V)
        uh.assign(Expression((ux, vy), degree=1))

        psi0_expression = SineHump(center=[0.5, 0.5], U=[float(ux), float(vy)], time=0.0, degree=6)

        # Generate particles
        x = RegularRectangle(Point(xmin, ymin), Point(xmax, ymax)).generate([pres, pres])
        s = np.zeros((len(x), 1), dtype=np.float_)

        # Initialize particles with position x and scalar property s at the mesh
        p = particles(x, [s], mesh)
        property_idx = 1  # Scalar quantity is stored at slot 1

        # Initialize advection class, simple forward Euler suffices
        ap = advect_particles(p, V, uh, "periodic", lims.flatten())

        # Define the variational (projection problem)
Пример #25
0
class OSI(Field):

    @classmethod
    def default_params(cls):
        params = Field.default_params()
        params.update(
            finalize=True,
            )
        return params

    def add_fields(self):
        params = self.params.copy_recursive()
        params["save"] = False
        params["plot"] = False
        #params["callback"] = False
        #params.pop("finalize")

        fields = []
        #fields.append(WSS(params=params))
        #return fields

        f = TimeIntegral("WSS", params=params, label="OSI")
        fields.append(f)
        fields.append(Magnitude(f, params=params))

        f = Magnitude("WSS", params=params)
        fields.append(f)
        fields.append(TimeIntegral(f, params=params, label="OSI"))

        #f = TimeIntegral("WSS", label="OSI")
        #fields.append(f)
        #fields.append(Magnitude(f))

        #f = Magnitude("WSS")
        #fields.append(f)
        #fields.append(TimeIntegral(f, label="OSI"))

        return fields

    def before_first_compute(self, get):
        tau = get("WSS")
        self.osi = Function(tau.sub(0).function_space().collapse())

    def compute(self, get):
        # Requires the fields Magnitude(TimeIntegral("WSS", label="OSI")) and
        # TimeIntegral(Magnitude("WSS"), label="OSI")
        #self.mag_ta_wss = get("Magnitude_TimeIntegral_WSS_OSI")
        #self.ta_mag_wss = get("TimeIntegral_Magnitude_WSS_OSI")
        self.mag_ta_wss = get("Magnitude_TimeIntegral_WSS-OSI")
        self.ta_mag_wss = get("TimeIntegral_Magnitude_WSS-OSI")

        if self.params.finalize:
            return None
        elif self.mag_ta_wss == None or self.ta_mag_wss == None:
            return None
        else:
            expr = conditional(self.ta_mag_wss < 1e-15,
                               0.0,
                               0.5 * (1.0 - self.mag_ta_wss / self.ta_mag_wss))
            self.osi.assign(project(expr, self.osi.function_space()))
            return self.osi

    def after_last_compute(self, get):
        self.mag_ta_wss = get("Magnitude_TimeIntegral_WSS-OSI")
        self.ta_mag_wss = get("TimeIntegral_Magnitude_WSS-OSI")
        #print self.name, " Calling after_last_compute"

        expr = conditional(self.ta_mag_wss < 1e-15,
                           0.0,
                           0.5 * (1.0 - self.mag_ta_wss / self.ta_mag_wss))
        self.osi.assign(project(expr, self.osi.function_space()))

        return self.osi
Пример #26
0
class GeneralProblem(object):
    def __init__(self, args, tc, metadata):
        self.MPI_rank = MPI.rank(mpi_comm_world())

        self.metadata = metadata

        # need to be specified in subclass init before calling this init
        self.problem_code = self.problem_code
        self.metadata['pcode'] = self.problem_code
        self.has_analytic_solution = self.has_analytic_solution
        self.metadata['hasAnalyticSolution'] = self.has_analytic_solution

        self.args = args
        self.tc = tc
        self.tc.init_watch('saveP', 'Saved pressure', True)
        self.tc.init_watch('saveVel', 'Saved velocity', True)
        self.tc.init_watch('averageP', 'Averaged pressure', True)
        self.tc.init_watch('updateBC', 'Updated velocity BC', True)
        self.tc.init_watch('div', 'Computed and saved divergence', True)
        self.tc.init_watch('divNorm', 'Computed norm of divergence', True)

        # If it is sensible (and implemented) to force pressure gradient on outflow boundary
        # 1. set self.outflow_area in initialize
        # 2. implement self.compute_outflow and get_outflow_measures
        self.can_force_outflow = False
        self.outflow_area = None
        self.normal = None
        self.mesh = None
        self.facet_function = None
        self.mesh_volume = None
        self.outflow_measures = []

        # stopping criteria (for relative H1 velocity error norm) (if known)
        self.divergence_treshold = 10

        # used for writing status files .run to monitor progress during computation:
        self.last_status_functional = 0.0
        self.status_functional_str = 'to be defined in Problem class'

        self.stepsInSecond = None
        self.volume = None
        self.vSpace = None
        self.vFunction = None
        self.divSpace = None
        self.divFunction = None
        self.pSpace = None
        self.pFunction = None
        self.solutionSpace = None
        self.solution = None

        self.actual_time = 0.0
        self.step_number = 0
        self.save_this_step = False
        self.isWholeSecond = None
        self.N1 = None
        self.N0 = None

        self.vel_normalization_factor = []
        self.pg_normalization_factor = []
        self.p_normalization_factor = []
        self.scale_factor = []

        self.analytic_v_norm_L2 = None
        self.analytic_v_norm_H1 = None
        self.analytic_v_norm_H1w = None

        # dictionaries for output files
        self.fileDict = {'u': {'name': 'velocity'},
                         'p': {'name': 'pressure'},
                         # 'pg': {'name': 'pressure_grad'},
                         'd': {'name': 'divergence'}}
        self.fileDictTent = {'u2': {'name': 'velocity_tent'},
                             'd2': {'name': 'divergence_tent'}}
        self.fileDictDiff = {'uD': {'name': 'velocity_diff'},
                             'pD': {'name': 'pressure_diff'},
                             'pgD': {'name': 'pressure_grad_diff'}}
        self.fileDictTentDiff = {'u2D': {'name': 'velocity_tent_diff'}}
        self.fileDictTentP = {'p2': {'name': 'pressure_tent'}}
        #                      'pg2': {'name': 'pressure_grad_tent'}}
        self.fileDictTentPDiff = {'p2D': {'name': 'pressure_tent_diff'}}
        #                          'pg2D': {'name': 'pressure_grad_tent_diff'}}
        self.fileDictLDSG = {'ldsg': {'name': 'ldsg'},
                             'ldsg2': {'name': 'ldsg_tent'}}
        self.fileDictWSS = {'wss': {'name': 'wss'}, }

        # lists of functionals and other scalar output data
        self.time_list = []  # list of times, when error is  measured (used in report)
        self.second_list = []
        self.listDict = {}  # list of fuctionals
        # dictionary of data lists {list, name, abbreviation, add scaled row to report}
        # normalisation coefficients (time-independent) are added to some lists to be used in normalized data series
        #   coefficients are lists (updated during initialisation, so we cannot use float type)
        #   coefs are equal to average of respective value of analytic solution
        # norm lists (time-dependent normalisation coefficients) are added to some lists to be used in relative data
        #  series (to remove natural pulsation of error due to change in volume flow rate)
        # slist - lists for cycle-averaged values
        # L2(0) means L2 difference of pressures taken with zero average
        self.listDict = {
            'd': {'list': [], 'name': 'corrected velocity L2 divergence', 'abrev': 'DC', 'scale': self.scale_factor, 'slist': []},
            'd2': {'list': [], 'name': 'tentative velocity L2 divergence', 'abrev': 'DT', 'scale': self.scale_factor, 'slist': []},
            'pg': {'list': [], 'name': 'computed pressure gradient', 'abrev': 'PG', 'scale': self.scale_factor,
                   'norm': self.pg_normalization_factor},
            'pg2': {'list': [], 'name': 'computed pressure tent gradient', 'abrev': 'PTG', 'scale': self.scale_factor,
                    'norm': self.pg_normalization_factor},
            # 'dsg-l': {'list': [], 'name': 'div(2sym(grad(u))-laplace(u))', 'abrev': 'DSG-L'},  # div(2sym(grad(u))-laplace(u))
            # 'dgt': {'list': [], 'name': 'div(transpose(grad(u)))', 'abrev': 'DGT'},  # div(transpose(grad(u)))
        }
        if self.has_analytic_solution:
            self.listDict.update({
                'u_L2': {'list': [], 'name': 'corrected velocity L2 error', 'abrev': 'CE_L2', 'scale': self.scale_factor,
                         'relative': 'av_norm_L2', 'slist': [], 'norm': self.vel_normalization_factor},
                'u2L2': {'list': [], 'name': 'tentative velocity L2 error', 'abrev': 'TE_L2', 'scale': self.scale_factor,
                         'relative': 'av_norm_L2', 'slist': [], 'norm': self.vel_normalization_factor},
                'u_L2test': {'list': [], 'name': 'test corrected L2 velocity error', 'abrev': 'TestCE_L2', 'scale': self.scale_factor},
                'u2L2test': {'list': [], 'name': 'test tentative L2 velocity error', 'abrev': 'TestTE_L2', 'scale': self.scale_factor},
                'u_H1': {'list': [], 'name': 'corrected velocity H1 error', 'abrev': 'CE_H1', 'scale': self.scale_factor,
                         'relative': 'av_norm_H1', 'slist': []},
                'u2H1': {'list': [], 'name': 'tentative velocity H1 error', 'abrev': 'TE_H1', 'scale': self.scale_factor,
                         'relative': 'av_norm_H1', 'slist': []},
                'u_H1test': {'list': [], 'name': 'test corrected H1 velocity error', 'abrev': 'TestCE_H1', 'scale': self.scale_factor},
                'u2H1test': {'list': [], 'name': 'test tentative H1 velocity error', 'abrev': 'TestTE_H1', 'scale': self.scale_factor},
                'apg': {'list': [], 'name': 'analytic pressure gradient', 'abrev': 'APG', 'scale': self.scale_factor,
                        'norm': self.pg_normalization_factor},
                'av_norm_L2': {'list': [], 'name': 'analytic velocity L2 norm', 'abrev': 'AVN_L2'},
                'av_norm_H1': {'list': [], 'name': 'analytic velocity H1 norm', 'abrev': 'AVN_H1'},
                'ap_norm': {'list': [], 'name': 'analytic pressure norm', 'abrev': 'APN'},
                'p': {'list': [], 'name': 'pressure L2(0) error', 'abrev': 'PE', 'scale': self.scale_factor, 'slist': [],
                      'norm': self.p_normalization_factor},
                'pgE': {'list': [], 'name': 'computed pressure gradient error', 'abrev': 'PGE', 'scale': self.scale_factor,
                        'norm': self.pg_normalization_factor, 'slist': []},
                'pgEA': {'list': [], 'name': 'computed absolute pressure gradient error', 'abrev': 'PGEA',
                         'scale': self.scale_factor, 'norm': self.pg_normalization_factor},
                'p2': {'list': [], 'name': 'pressure tent L2(0) error', 'abrev': 'PTE', 'scale': self.scale_factor,
                       'slist': [], 'norm': self.p_normalization_factor},
                'pgE2': {'list': [], 'name': 'computed tent pressure tent gradient error', 'abrev': 'PTGE',
                         'scale': self.scale_factor, 'norm': self.pg_normalization_factor, 'slist': []},
                'pgEA2': {'list': [], 'name': 'computed absolute pressure tent gradient error',
                          'abrev': 'PTGEA', 'scale': self.scale_factor, 'norm': self.pg_normalization_factor}
            })

        # parse arguments
        self.nu_factor = args.nu
        self.onset = args.onset
        self.onset_factor = 0

        self.doSave = False
        self.saveOnlyVel = False
        self.doSaveDiff = False
        self.save_nth = args.savespace
        option = args.save
        if option == 'doSave' or option == 'diff' or option == 'only_vel':
            self.doSave = True
            if option == 'diff':
                self.doSaveDiff = True
                info('Saving velocity differences.')
            if option == 'only_vel':
                self.saveOnlyVel = True
                info('Saving only velocity profiles.')
            info('Saving solution ON.')
        elif option == 'noSave':
            self.doSave = False
            info('Saving solution OFF.')

        self.doErrControl = None
        self.testErrControl = False
        if args.error == "noEC":
            self.doErrControl = False
            info("Error control omitted")
        else:
            self.doErrControl = True
            if args.error == "test":
                self.testErrControl = True
                info("Error control in testing mode")
            else:
                info("Error control on")

        self.str_dir_name = "%s_%s_results" % (self.problem_code, metadata['name'])
        self.metadata['dir'] = self.str_dir_name
        # create directory, needed because of using "with open(..." construction later
        if not os.path.exists(self.str_dir_name) and self.MPI_rank == 0:
            os.mkdir(self.str_dir_name)

    @staticmethod
    def setup_parser_options(parser):
        parser.add_argument('-e', '--error', help='Error control mode', choices=['doEC', 'noEC', 'test'], default='doEC')
        parser.add_argument('-S', '--save', help='Save solution mode', choices=['doSave', 'noSave', 'diff', 'only_vel'],
                            default='noSave')
        parser.add_argument('--savespace', help='save only n-th step in first cycle', type=int, default=1)
        #   doSave: create .xdmf files with velocity, pressure, divergence
        #   diff: save also difference vel-sol
        #   noSave: do not create .xdmf files with velocity, pressure, divergence
        parser.add_argument('--nu', help='kinematic viscosity factor', type=float, default=1.0)
        parser.add_argument('--onset', help='boundary condition onset length', type=float, default=0.0)
        parser.add_argument('--ldsg', help='save laplace(u) - div(2sym(grad(u))) difference', action='store_true')
        parser.add_argument('--wss', help='compute wall shrear stress', action='store_true')

    @staticmethod
    def loadMesh(mesh):
        f = HDF5File(mpi_comm_world(), 'meshes/'+mesh+'.hdf5', 'r')
        mesh = Mesh()
        f.read(mesh, 'mesh', False)
        facet_function = MeshFunction("size_t", mesh)
        f.read(facet_function, 'facet_function')
        return mesh, facet_function

    def initialize(self, V, Q, PS, D):
        self.vSpace = V
        self.divSpace = D
        self.pSpace = Q
        self.solutionSpace = V
        self.vFunction = Function(V)
        self.divFunction = Function(D)
        self.pFunction = Function(Q)
        self.volume = assemble(interpolate(Expression("1.0"), Q) * dx)

        if self.doSave:
            # self.pgSpace = VectorFunctionSpace(mesh, "DG", 0)
            # self.pgFunction = Function(self.pgSpace)
            self.initialize_xdmf_files()
        self.stepsInSecond = int(round(1.0 / self.metadata['dt']))
        info('stepsInSecond = %d' % self.stepsInSecond)

    def initialize_xdmf_files(self):
        info('  Initializing output files.')
        # for creating paraview scripts
        self.metadata['filename_base'] = self.problem_code + '_' + self.metadata['name']

        # assemble file dictionary
        if self.doSaveDiff:
            self.fileDict.update(self.fileDictDiff)
        if self.metadata['hasTentativeV']:
            self.fileDict.update(self.fileDictTent)
            if self.doSaveDiff:
                self.fileDict.update(self.fileDictTentDiff)
        if self.metadata['hasTentativeP']:
            self.fileDict.update(self.fileDictTentP)
            if self.doSaveDiff:
                self.fileDict.update(self.fileDictTentPDiff)
        if self.args.ldsg:
            self.fileDict.update(self.fileDictLDSG)
        if self.args.wss:
            self.fileDict.update(self.fileDictWSS)
        # create files
        for key, value in self.fileDict.iteritems():
            value['file'] = XDMFFile(mpi_comm_world(), self.str_dir_name + "/" + self.problem_code + '_' +
                                     self.metadata['name'] + value['name'] + ".xdmf")
            value['file'].parameters['rewrite_function_mesh'] = False  # saves lots of space (for use with static mesh)

    # method for saving divergence (ensuring, that it will be one time line in ParaView)
    def save_div(self, is_tent, field):
        self.tc.start('div')
        self.divFunction.assign(project(div(field), self.divSpace))
        self.fileDict['d2' if is_tent else 'd']['file'] << self.divFunction
        self.tc.end('div')

    def compute_div(self, is_tent, velocity):
        self.tc.start('divNorm')
        div_list = self.listDict['d2' if is_tent else 'd']['list']
        div_list.append(norm(velocity, 'Hdiv0'))
        if self.isWholeSecond:
            self.listDict['d2' if is_tent else 'd']['slist'].append(
                sum([i*i for i in div_list[self.N0:self.N1]])/self.stepsInSecond)
        self.tc.end('divNorm')

    # method for saving velocity (ensuring, that it will be one time line in ParaView)
    def save_vel(self, is_tent, field, t):
        self.vFunction.assign(field)
        self.fileDict['u2' if is_tent else 'u']['file'] << self.vFunction
        if self.doSaveDiff:
            self.vFunction.assign((1.0 / self.vel_normalization_factor[0]) * (field - self.solution))
            self.fileDict['u2D' if is_tent else 'uD']['file'] << self.vFunction
        if self.args.ldsg:
            # info(div(2.*sym(grad(field))-grad(field)).ufl_shape)
            form = div(2.*sym(grad(field))-grad(field))
            self.pFunction.assign(project(sqrt_ufl(inner(form, form)), self.pSpace))
            self.fileDict['ldsg2' if is_tent else 'ldsg']['file'] << self.pFunction
            # self.vFunction.assign(project(div(2.*sym(grad(field))-grad(field)), self.vSpace))
            # self.fileDict['ldsg2' if is_tent else 'ldsg']['file'] << self.vFunction

    def compute_err(self, is_tent, velocity, t):
        if self.doErrControl and self.has_analytic_solution:
            er_list_L2 = self.listDict['u2L2' if is_tent else 'u_L2']['list']
            er_list_H1 = self.listDict['u2H1' if is_tent else 'u_H1']['list']
            self.tc.start('errorV')
            errorL2_sq = assemble(inner(velocity - self.solution, velocity - self.solution) * dx)  # faster than errornorm
            errorH1seminorm_sq = assemble(inner(grad(velocity - self.solution), grad(velocity - self.solution)) * dx)  # faster than errornorm
            info('  H1 seminorm error: %f' % sqrt(errorH1seminorm_sq))
            errorL2 = sqrt(errorL2_sq)
            errorH1 = sqrt(errorL2_sq + errorH1seminorm_sq)
            info("  Relative L2 error in velocity = %f" % (errorL2 / self.analytic_v_norm_L2))
            self.last_error = errorH1 / self.analytic_v_norm_H1
            self.last_status_functional = self.last_error
            info("  Relative H1 error in velocity = %f" % self.last_error)
            er_list_L2.append(errorL2)
            er_list_H1.append(errorH1)
            self.tc.end('errorV')
            if self.testErrControl:
                er_list_test_H1 = self.listDict['u2H1test' if is_tent else 'u_H1test']['list']
                er_list_test_L2 = self.listDict['u2L2test' if is_tent else 'u_L2test']['list']
                self.tc.start('errorVtest')
                er_list_test_L2.append(errornorm(velocity, self.solution, norm_type='L2', degree_rise=0))
                er_list_test_H1.append(errornorm(velocity, self.solution, norm_type='H1', degree_rise=0))
                self.tc.end('errorVtest')
            if self.isWholeSecond:
                self.listDict['u2L2' if is_tent else 'u_L2']['slist'].append(
                    sqrt(sum([i*i for i in er_list_L2[self.N0:self.N1]])/self.stepsInSecond))
                self.listDict['u2H1' if is_tent else 'u_H1']['slist'].append(
                    sqrt(sum([i*i for i in er_list_H1[self.N0:self.N1]])/self.stepsInSecond))
            # stopping criteria
            if self.last_error > self.divergence_treshold:
                raise RuntimeError('STOPPED: Failed divergence test!')

    def averaging_pressure(self, pressure):
        self.tc.start('averageP')
        # averaging pressure (substract average)
        p_average = assemble((1.0/self.volume) * pressure * dx)
        info('Average pressure: %f' % p_average)
        p_average_function = interpolate(Expression("p", p=p_average), self.pSpace)
        # info(p_average_function, pressure, pressure_Q)
        pressure.assign(pressure - p_average_function)
        self.tc.end('averageP')

    def save_pressure(self, is_tent, pressure):
        self.tc.start('saveP')
        self.fileDict['p2' if is_tent else 'p']['file'] << pressure
        # pg = project((1.0 / self.pg_normalization_factor[0]) * grad(pressure), self.pgSpace)  # NT normalisation factor defined only in Womersley
        # self.pgFunction.assign(pg)
        # self.fileDict['pg2' if is_tent else 'pg'][0] << self.pgFunction
        self.tc.end('saveP')

    def get_boundary_conditions(self, use_pressure_BC, v_space, p_space):
        pass

    def get_initial_conditions(self, function_list):
        """
        :param function_list: [{'type': 'v'/'p', 'time':-0.1},...]
        :return: velocities and pressures in selected times
        """
        pass

    def get_v_solution(self, t):
        pass

    def get_p_solution(self, t):
        pass

    def update_time(self, actual_time, step_number):
        self.actual_time = actual_time
        self.step_number = step_number
        self.time_list.append(self.actual_time)
        if self.onset < 0.001 or self.actual_time > self.onset:
            self.onset_factor = 1.
        else:
            self.onset_factor = (1. - cos(pi * actual_time / self.onset))*0.5
        info('Onset factor: %f' % self.onset_factor)

        # save only n-th step in first second
        if self.doSave:
            if self.save_nth == 1 or actual_time > (1. - self.metadata['dt']/2.) or self.step_number % self.save_nth == 0:
                self.save_this_step = True
            else:
                self.save_this_step = False

    def compute_functionals(self, velocity, pressure, t):
        if self.args.wss:
            info('Computing stress tensor')
            I = Identity(velocity.geometric_dimension())
            T = TensorFunctionSpace(self.mesh, 'Lagrange', 1)
            stress = project(-pressure*I + 2*sym(grad(velocity)), T)
            info('Generating boundary mesh')
            wall_mesh = BoundaryMesh(self.mesh, 'exterior')
            # wall_mesh = SubMesh(self.mesh, self.facet_function, 1)   # QQ why does not work?
            # plot(wall_mesh, interactive=True)
            info('  Boundary mesh geometric dim: %d' % wall_mesh.geometry().dim())
            info('  Boundary mesh topologic dim: %d' % wall_mesh.topology().dim())
            info('Projecting stress to boundary mesh')
            Tb = TensorFunctionSpace(wall_mesh, 'Lagrange', 1)
            stress_b = interpolate(stress, Tb)
            self.fileDict['wss']['file'] << stress_b


            if False:  # does not work
                info('Computing WSS')
                n = FacetNormal(wall_mesh)
                info(stress_b, True)
                # wss = stress_b*n - inner(stress_b*n, n)*n
                wss = dot(stress_b, n) - inner(dot(stress_b, n), n)*n   # equivalent
                Vb = VectorFunctionSpace(wall_mesh, 'Lagrange', 1)
                Sb = FunctionSpace(wall_mesh, 'Lagrange', 1)
                # wss_func = project(wss, Vb)
                wss_norm = project(sqrt(inner(wss, wss)), Sb)
                plot(wss_norm, interactive=True)

        # following was used to test laplace and stress formulation differences
        # dsgml = sqrt(assemble((1./self.mesh_volume)*inner(div(2*sym(grad(velocity))-grad(velocity)), div(2*sym(grad(velocity))-grad(velocity)))*dx))
        # dgt = sqrt(assemble((1./self.mesh_volume)*inner(div(transpose(grad(velocity))), div(transpose(grad(velocity))))*dx))
        # self.listDict['dsg-l']['list'].append(dsgml)
        # self.listDict['dgt']['list'].append(dgt)

    def compute_outflow(self, velocity):
        out = assemble(inner(velocity, self.normal)*self.get_outflow_measure_form())
        return out

    def get_outflow_measures(self):
        pass

    def get_outflow_measure_form(self):
        pass

    def get_metadata_to_save(self):
        return str(cPickle.dumps(self.metadata)).replace('\n', '$')

    def report(self):
        total = toc()
        md = self.metadata

        # compare errors measured by assemble and errornorm
        # TODO implement generally (if listDict[fcional]['testable'])
        # if self.testErrControl:
        #     for e in [[self.listDict['u_L2']['list'], self.listDict['u_L2test']['list'], 'L2'],
        #               [self.listDict['u_H1']['list'], self.listDict['u_H1test']['list'], 'H1']]:
        #         print('test ', e[2], sum([abs(e[0][i]-e[1][i]) for i in range(len(self.time_list))]))

        # report error norm, norm of div, and pressure gradients for individual time steps
        with open(self.str_dir_name + "/report_time_lines.csv", 'w') as reportFile:
            report_writer = csv.writer(reportFile, delimiter=';', escapechar='\\', quoting=csv.QUOTE_NONE)
            # report_writer.writerow(self.problem.get_metadata_to_save())
            report_writer.writerow(["name", "what", "time"] + self.time_list)
            for key in self.listDict:
                l = self.listDict[key]
                if l['list']:
                    abrev = l['abrev']
                    report_writer.writerow([md['name'], l['name'], abrev] + l['list'])
                    if 'scale' in l:
                        temp_list = [i/l['scale'][0] for i in l['list']]
                        report_writer.writerow([md['name'], "scaled " + l['name'], abrev+"s"] + temp_list +
                                               ['scale factor:' + str(l['scale'])])
                    if 'norm' in l:
                        if l['norm']:
                            temp_list = [i/l['norm'][0] for i in l['list']]
                            report_writer.writerow([md['name'], "normalized " + l['name'], abrev+"n"] + temp_list)
                        else:
                            info('Norm missing:' + str(l))
                            l['normalized_list_sec'] = []
                    if 'relative' in l:
                        norm_list = self.listDict[l['relative']]['list']
                        temp_list = [l['list'][i]/norm_list[i] for i in range(0, len(l['list']))]
                        self.listDict[key]['relative_list'] = temp_list
                        report_writer.writerow([md['name'], "relative " + l['name'], abrev+"r"] + temp_list)

        # report error norm, norm of div, and pressure gradients averaged over seconds
        with open(self.str_dir_name + "/report_seconds.csv", 'w') as reportFile:
            report_writer = csv.writer(reportFile, delimiter=';', escapechar='|', quoting=csv.QUOTE_NONE)
            # report_writer.writerow(self.problem.get_metadata_to_save())
            report_writer.writerow(["name", "what", "time"] + self.second_list)
            for key in self.listDict.iterkeys():
                l = self.listDict[key]
                if 'slist' in l:
                    abrev = l['abrev']
                    value = l['slist']
                    report_writer.writerow([md['name'], l['name'], abrev] + value)
                    if 'scale' in l:
                        temp_list = [i/l['scale'][0] for i in value]
                        report_writer.writerow([md['name'], "scaled " + l['name'], abrev+"s"] + temp_list)
                    if 'norm' in l:
                        if l['norm']:
                            temp_list = [i/l['norm'][0] for i in value]
                            l['normalized_list_sec'] = temp_list
                            report_writer.writerow([md['name'], "normalized " + l['name'], abrev+"n"] + temp_list)
                        else:
                            info('Norm missing:' + str(l))
                            l['normalized_list_sec'] = []
                    if 'relative_list' in l:
                        temp_list = []
                        # info('relative second list of'+ str(l['abrev']))
                        for sec in self.second_list:
                            N0 = (sec-1)*self.stepsInSecond
                            N1 = sec*self.stepsInSecond
                            # info([sec,  N0, N1])
                            temp_list.append(sqrt(sum([i*i for i in l['relative_list'][N0:N1]])/float(self.stepsInSecond)))
                        l['relative_list_sec'] = temp_list
                        report_writer.writerow([md['name'], "relative " + l['name'], abrev+"r"] + temp_list)

        header_row = ["name", 'metadata', "totalTimeHours"]
        data_row = [md['name'], self.get_metadata_to_save(), total / 3600.0]
        for key in ['u_L2', 'u_H1', 'u_H1w', 'p', 'u2L2', 'u2H1', 'u2H1w', 'p2', 'pgE', 'pgE2', 'd', 'd2', 'force_wall']:
            if key in self.listDict:
                l = self.listDict[key]
                header_row += ['last_cycle_'+l['abrev']]
                data_row += [l['slist'][-1]] if l['slist'] else [0]
                if 'relative_list_sec' in l and l['relative_list_sec']:
                    header_row += ['last_cycle_'+l['abrev']+'r']
                    data_row += [l['relative_list_sec'][-1]]
                elif key in ['p', 'p2']:
                    header_row += ['last_cycle_'+l['abrev']+'n']
                    data_row += [l['normalized_list_sec'][-1]] if l['normalized_list_sec'] else [0]

        # report without header
        with open(self.str_dir_name + "/report.csv", 'w') as reportFile:
            report_writer = csv.writer(reportFile, delimiter=';', escapechar='|', quoting=csv.QUOTE_NONE)
            report_writer.writerow(data_row)

        # report with header
        with open(self.str_dir_name + "/report_h.csv", 'w') as reportFile:
            report_writer = csv.writer(reportFile, delimiter=';', escapechar='|', quoting=csv.QUOTE_NONE)
            report_writer.writerow(header_row)
            report_writer.writerow(data_row)

        # report time cotrol
        with open(self.str_dir_name + "/report_timecontrol.csv", 'w') as reportFile:
            self.tc.report(reportFile, self.metadata['name'])

        self.remove_status_file()

        # create file showing all was done well
        f = open(md['name'] + "_OK.report", "w")
        f.close()

    def report_fail(self, t):
        print("Runtime error:", sys.exc_info()[1])
        print("Traceback:")
        traceback.print_tb(sys.exc_info()[2])
        f = open(self.metadata['name'] + "_failed_at_%5.3f.report" % t, "w")
        f.write(traceback.format_exc())
        f.close()
        self.remove_status_file()

    def write_status_file(self, t):
        self.tc.start('status')
        f = open(self.metadata['name'] + ".run", "w")
        progress = t/self.metadata['time']
        f.write('t = %5.3f (dt=%3dms)\nprogress = %3.0f %%\n%s = %5.3f\n' %
                (t, self.metadata['dt_ms'], 100*progress, self.status_functional_str, self.last_status_functional))
        f.close()
        self.tc.end('status')

    def remove_status_file(self):
        if self.MPI_rank == 0:
            try:
                os.remove(self.metadata['name'] + ".run")
            except OSError:
                info('.run file probably not created')
Пример #27
0
def compute_time_errors(problem, MethodClass, mesh_sizes, Dt):

    mesh_generator, solution, f, mu, rho, cell_type = problem()

    # Compute the problem
    errors = {
        "u": numpy.empty((len(mesh_sizes), len(Dt))),
        "p": numpy.empty((len(mesh_sizes), len(Dt))),
    }
    for k, mesh_size in enumerate(mesh_sizes):
        info("")
        info("")
        with Message("Computing for mesh size {}...".format(mesh_size)):
            mesh = mesh_generator(mesh_size)

            # Define all expression with `domain`, see
            # <https://bitbucket.org/fenics-project/ufl/issues/96>.
            #
            # Translate data into FEniCS expressions.
            sol_u = Expression(
                (ccode(solution["u"]["value"][0]), ccode(solution["u"]["value"][1])),
                degree=_truncate_degree(solution["u"]["degree"]),
                t=0.0,
                domain=mesh,
            )
            sol_p = Expression(
                ccode(solution["p"]["value"]),
                degree=_truncate_degree(solution["p"]["degree"]),
                t=0.0,
                domain=mesh,
            )

            fenics_rhs0 = Expression(
                (ccode(f["value"][0]), ccode(f["value"][1])),
                degree=_truncate_degree(f["degree"]),
                t=0.0,
                mu=mu,
                rho=rho,
                domain=mesh,
            )
            # Deep-copy expression to be able to provide f0, f1 for the
            # Dirichlet boundary conditions later on.
            fenics_rhs1 = Expression(
                fenics_rhs0.cppcode,
                degree=_truncate_degree(f["degree"]),
                t=0.0,
                mu=mu,
                rho=rho,
                domain=mesh,
            )
            # Create initial states.
            W = VectorFunctionSpace(mesh, "CG", 2)
            P = FunctionSpace(mesh, "CG", 1)
            p0 = Expression(
                sol_p.cppcode,
                degree=_truncate_degree(solution["p"]["degree"]),
                t=0.0,
                domain=mesh,
            )

            mesh_area = assemble(1.0 * dx(mesh))
            method = MethodClass(
                time_step_method="backward euler",
                # time_step_method='crank-nicolson',
                # stabilization=None
                # stabilization='SUPG'
            )
            u1 = Function(W)
            p1 = Function(P)
            err_p = Function(P)
            divu1 = Function(P)
            for j, dt in enumerate(Dt):
                # Prepare previous states for multistepping.
                u = {
                    0: Expression(
                        sol_u.cppcode,
                        degree=_truncate_degree(solution["u"]["degree"]),
                        t=0.0,
                        cell=cell_type,
                    )
                }
                sol_u.t = dt
                u_bcs = [DirichletBC(W, sol_u, "on_boundary")]
                sol_p.t = dt
                # p_bcs = [DirichletBC(P, sol_p, 'on_boundary')]
                p_bcs = []
                fenics_rhs0.t = 0.0
                fenics_rhs1.t = dt
                u1, p1 = method.step(
                    Constant(dt),
                    u,
                    p0,
                    W,
                    P,
                    u_bcs,
                    p_bcs,
                    Constant(rho),
                    Constant(mu),
                    f={0: fenics_rhs0, 1: fenics_rhs1},
                    verbose=False,
                    tol=1.0e-10,
                )

                sol_u.t = dt
                sol_p.t = dt
                errors["u"][k][j] = errornorm(sol_u, u1)
                # The pressure is only determined up to a constant which makes
                # it a bit harder to define what the error is. For our
                # purposes, choose an alpha_0\in\R such that
                #
                #    alpha0 = argmin ||e - alpha||^2
                #
                # with  e := sol_p - p.
                # This alpha0 is unique and explicitly given by
                #
                #     alpha0 = 1/(2|Omega|) \int (e + e*)
                #            = 1/|Omega| \int Re(e),
                #
                # i.e., the mean error in \Omega.
                alpha = +assemble(sol_p * dx(mesh)) - assemble(p1 * dx(mesh))
                alpha /= mesh_area
                # We would like to perform
                #     p1 += alpha.
                # To avoid creating a temporary function every time, assume
                # that p1 lives in a function space where the coefficients
                # represent actual function values. This is true for CG
                # elements, for example. In that case, we can just add any
                # number to the vector of p1.
                p1.vector()[:] += alpha
                errors["p"][k][j] = errornorm(sol_p, p1)

                show_plots = False
                if show_plots:
                    plot(p1, title="p1", mesh=mesh)
                    plot(sol_p, title="sol_p", mesh=mesh)
                    err_p.vector()[:] = p1.vector()
                    sol_interp = interpolate(sol_p, P)
                    err_p.vector()[:] -= sol_interp.vector()
                    # plot(sol_p - p1, title='p1 - sol_p', mesh=mesh)
                    plot(err_p, title="p1 - sol_p", mesh=mesh)
                    # r = SpatialCoordinate(mesh)[0]
                    # divu1 = 1 / r * (r * u1[0]).dx(0) + u1[1].dx(1)
                    divu1.assign(project(u1[0].dx(0) + u1[1].dx(1), P))
                    plot(divu1, title="div(u1)")
    return errors
Пример #28
0
        num_steps = np.rint(Tend / float(dt))

        # Generate mesh
        mesh = Mesh("./../../meshes/circle_0.xml")
        n = nx
        while n > 1:
            mesh = refine(mesh)
            n /= 2

        output_field = XDMFFile(mesh.mpi_comm(),
                                outdir + "psi_h_nx" + str(nx) + ".xdmf")

        # Velocity and initial condition
        V = VectorFunctionSpace(mesh, "DG", 3)
        uh = Function(V)
        uh.assign(Expression(("-Uh*x[1]", "Uh*x[0]"), Uh=Uh, degree=3))

        psi0_expression = GaussianPulse(center=(xc, yc),
                                        sigma=float(sigma),
                                        U=[Uh, Uh],
                                        time=0.0,
                                        height=1.0,
                                        degree=3)

        # Generate particles
        x = RandomCircle(Point(x0, y0), r).generate([pres, pres])
        s = np.zeros((len(x), 1), dtype=np.float_)

        # Initialize particles with position x and scalar property s at the mesh
        p = particles(x, [s], mesh)
        property_idx = 1  # Scalar quantity is stored at slot 1
# Advective velocity
# Swirling deformation advection (see LeVeque)
ux = 'pow(sin(pi*x[0]), 2) * sin(2*pi*x[1])'
vy = '-pow(sin(pi*x[1]), 2) * sin(2*pi*x[0])'
gt_plus = '0.5 * cos(pi*t)'
gt_min = '-0.5 * cos(pi*t)'

u_expr = Expression((ux+'*'+gt_min, vy+'*'+gt_min), degree=2, t=0.)
u_expre_neg = Expression((ux+'*'+gt_plus, vy+'*'+gt_plus), degree=2, t=0.)

# Mesh velocity
umesh = Function(Vcg)

# Advective velocity
uh = Function(V)
uh.assign(u_expr)

# Total velocity
uadvect = uh - umesh

# Now throw in the particles
x = RandomRectangle(Point(xmin, ymin), Point(xmax, ymax)).generate([pres, pres])
s = assign_particle_values(x, CosineHill(radius=0.25, center=[0.25, 0.5],
                                         amplitude=1.0, degree=1))
p = particles(x, [s], mesh)

# Define projections problem
FuncSpace_adv = {'FuncSpace_local': Q_Rho, 'FuncSpace_lambda': T_1, 'FuncSpace_bar': Qbar}
FormsPDE = FormsPDEMap(mesh, FuncSpace_adv, beta_map=Constant(1e-8))
forms_pde = FormsPDE.forms_theta_linear(phih0, uadvect, dt, Constant(1.0), zeta=Constant(0.),
                                        h=Constant(0.))
Пример #30
0
def solve_fixed_point(mesh, W_element, P_element, Q_element, u0, p0, theta0,
                      kappa, rho, mu, cp, g, extra_force, heat_source, u_bcs,
                      p_bcs, theta_dirichlet_bcs, theta_neumann_bcs, my_dx,
                      my_ds, max_iter, tol):
    # Solve the coupled heat-Stokes equation approximately. Do this
    # iteratively by solving the heat equation, then solving Stokes with the
    # updated heat, the heat equation with the updated velocity and so forth
    # until the change is 'small'.
    WP = FunctionSpace(mesh, MixedElement([W_element, P_element]))
    Q = FunctionSpace(mesh, Q_element)
    # Initialize functions.
    up0 = Function(WP)
    u0, p0 = up0.split()

    theta1 = Function(Q)
    for _ in range(max_iter):
        heat_problem = heat.Heat(Q,
                                 kappa=kappa,
                                 rho=rho(theta0),
                                 cp=cp,
                                 convection=u0,
                                 source=heat_source,
                                 dirichlet_bcs=theta_dirichlet_bcs,
                                 neumann_bcs=theta_neumann_bcs,
                                 my_dx=my_dx,
                                 my_ds=my_ds)

        theta1.assign(heat_problem.solve_stationary())

        # Solve problem for velocity, pressure.
        f = rho(theta0) * g  # coupling
        if extra_force:
            f += as_vector((extra_force[0], extra_force[1], 0.0))
        # up1 = up0.copy()
        stokes.stokes_solve(up0,
                            mu,
                            u_bcs,
                            p_bcs,
                            f,
                            my_dx=my_dx,
                            tol=1.0e-10,
                            verbose=False,
                            maxiter=1000)

        # from dolfin import plot
        # plot(u0)
        # plot(theta0)

        theta_diff = errornorm(theta0, theta1)
        info('||theta - theta0|| = {:e}'.format(theta_diff))
        # info('||u - u0||         = {:e}'.format(u_diff))
        # info('||p - p0||         = {:e}'.format(p_diff))
        # diff = theta_diff + u_diff + p_diff
        diff = theta_diff
        info('sum = {:e}'.format(diff))

        # # Show the iterates.
        # plot(theta0, title='theta0')
        # plot(u0, title='u0')
        # interactive()
        # #exit()
        if diff < tol:
            break

        theta0.assign(theta1)

    # Create a *deep* copy of u0, p0, to be able to deal with them as
    # actually separate entities.
    u0, p0 = up0.split(deepcopy=True)
    return u0, p0, theta0
Пример #31
0
    def solve(self, problem):
        self.problem = problem
        doSave = problem.doSave
        save_this_step = False
        onlyVel = problem.saveOnlyVel
        dt = self.metadata['dt']

        nu = Constant(self.problem.nu)
        self.tc.init_watch('init',
                           'Initialization',
                           True,
                           count_to_percent=False)
        self.tc.init_watch('rhs',
                           'Assembled right hand side',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('applybc1',
                           'Applied velocity BC 1st step',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('applybc3',
                           'Applied velocity BC 3rd step',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('applybcP',
                           'Applied pressure BC or othogonalized rhs',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('assembleMatrices',
                           'Initial matrix assembly',
                           False,
                           count_to_percent=True)
        self.tc.init_watch('solve 1',
                           'Running solver on 1st step',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('solve 2',
                           'Running solver on 2nd step',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('solve 3',
                           'Running solver on 3rd step',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('solve 4',
                           'Running solver on 4th step',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('assembleA1',
                           'Assembled A1 matrix (without stabiliz.)',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('assembleA1stab',
                           'Assembled A1 stabilization',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('next',
                           'Next step assignments',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('saveVel', 'Saved velocity', True)

        self.tc.start('init')

        # Define function spaces (P2-P1)
        mesh = self.problem.mesh
        self.V = VectorFunctionSpace(mesh, "Lagrange", 2)  # velocity
        self.Q = FunctionSpace(mesh, "Lagrange", 1)  # pressure
        self.PS = FunctionSpace(
            mesh, "Lagrange", 2)  # partial solution (must be same order as V)
        self.D = FunctionSpace(mesh, "Lagrange",
                               1)  # velocity divergence space

        problem.initialize(self.V, self.Q, self.PS, self.D)

        # Define trial and test functions
        u = TrialFunction(self.V)
        v = TestFunction(self.V)
        p = TrialFunction(self.Q)
        q = TestFunction(self.Q)

        n = FacetNormal(mesh)
        I = Identity(find_geometric_dimension(u))

        # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure
        [u1, u0, p0] = self.problem.get_initial_conditions([{
            'type': 'v',
            'time': -dt
        }, {
            'type': 'v',
            'time': 0.0
        }, {
            'type': 'p',
            'time': 0.0
        }])

        u_ = Function(self.V)  # current tentative velocity
        u_cor = Function(self.V)  # current corrected velocity
        p_ = Function(
            self.Q
        )  # current pressure or pressure help function from rotation scheme
        p_mod = Function(
            self.Q)  # current modified pressure from rotation scheme

        # Define coefficients
        k = Constant(self.metadata['dt'])
        f = Constant((0, 0, 0))

        # Define forms
        # step 1: Tentative velocity, solve to u_
        u_ext = 1.5 * u0 - 0.5 * u1  # extrapolation for convection term

        # Stabilisation
        h = CellSize(mesh)
        if self.args.cbc_tau:
            # used in Simula cbcflow project
            tau = Constant(self.stabCoef) * h / (sqrt(inner(u_ext, u_ext)) + h)
        else:
            # proposed in R. Codina: On stabilized finite element methods for linear systems of
            # convection-diffusion-reaction equations.
            tau = Constant(self.stabCoef) * k * h**2 / (
                2 * nu * k + k * h * sqrt(DOLFIN_EPS + inner(u_ext, u_ext)) +
                h**2)
            # DOLFIN_EPS is added because of FEniCS bug that inner(u_ext, u_ext) can be negative when u_ext = 0

        if self.use_full_SUPG:
            v1 = v + tau * 0.5 * dot(grad(v), u_ext)
            parameters['form_compiler']['quadrature_degree'] = 6
        else:
            v1 = v

        def nonlinearity(function):
            if self.args.ema:
                return 2 * inner(dot(sym(grad(function)), u_ext), v1
                                 ) * dx + inner(div(function) * u_ext, v1) * dx
            else:
                return inner(dot(grad(function), u_ext), v1) * dx

        def diffusion(fce):
            if self.useLaplace:
                return nu * inner(grad(fce), grad(v1)) * dx
            else:
                form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx
                if self.bcv == 'CDN':
                    return form
                if self.bcv == 'LAP':
                    return form - inner(nu * dot(grad(fce).T, n), v1
                                        ) * problem.get_outflow_measure_form()
                if self.bcv == 'DDN':
                    return form  # additional term must be added to non-constant part

        def pressure_rhs():
            if self.args.bc == 'outflow':
                return inner(p0, div(v1)) * dx
            else:
                return inner(p0, div(v1)) * dx - inner(
                    p0 * n, v1) * problem.get_outflow_measure_form()

        a1_const = (1. / k) * inner(u, v1) * dx + diffusion(0.5 * u)
        a1_change = nonlinearity(0.5 * u)
        if self.bcv == 'DDN':
            # does not penalize influx for current step, only for the next one
            # this can lead to oscilation:
            # DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx...
            # u and u_ext cannot be switched, min_value is nonlinear function
            a1_change += -0.5 * min_value(Constant(0.), inner(
                u_ext, n)) * inner(u, v1) * problem.get_outflow_measure_form()
            # NT works only with uflacs compiler

        L1 = (1. / k) * inner(u0, v1) * dx - nonlinearity(
            0.5 * u0) - diffusion(0.5 * u0) + pressure_rhs()
        if self.bcv == 'DDN':
            L1 += 0.5 * min_value(0., inner(u_ext, n)) * inner(
                u0, v1) * problem.get_outflow_measure_form()

        # Non-consistent SUPG stabilisation
        if self.stabilize and not self.use_full_SUPG:
            # a1_stab = tau*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx
            a1_stab = 0.5 * tau * inner(dot(grad(u), u_ext), dot(
                grad(v), u_ext)) * dx(None, {'quadrature_degree': 6})
            # optional: to use Crank Nicolson in stabilisation term following change of RHS is needed:
            # L1 += -0.5*tau*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6})

        outflow_area = Constant(problem.outflow_area)
        need_outflow = Constant(0.0)
        if self.useRotationScheme:
            # Rotation scheme
            F2 = inner(grad(p), grad(q)) * dx + (1. / k) * q * div(u_) * dx
        else:
            # Projection, solve to p_
            if self.forceOutflow and problem.can_force_outflow:
                info('Forcing outflow.')
                F2 = inner(grad(p - p0),
                           grad(q)) * dx + (1. / k) * q * div(u_) * dx
                for m in problem.get_outflow_measures():
                    F2 += (1. / k) * (1. / outflow_area) * need_outflow * q * m
            else:
                F2 = inner(grad(p - p0),
                           grad(q)) * dx + (1. / k) * q * div(u_) * dx
        a2, L2 = system(F2)

        # step 3: Finalize, solve to u_
        if self.useRotationScheme:
            # Rotation scheme
            F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_), v) * dx
        else:
            F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_ - p0),
                                                          v) * dx
        a3, L3 = system(F3)

        if self.useRotationScheme:
            # Rotation scheme: modify pressure
            F4 = (p - p0 - p_ + nu * div(u_)) * q * dx
            a4, L4 = system(F4)

        # Assemble matrices
        self.tc.start('assembleMatrices')
        A1_const = assemble(
            a1_const
        )  # must be here, so A1 stays one Python object during repeated assembly
        A1_change = A1_const.copy(
        )  # copy to get matrix with same sparse structure (data will be overwritten)
        if self.stabilize and not self.use_full_SUPG:
            A1_stab = A1_const.copy(
            )  # copy to get matrix with same sparse structure (data will be overwritten)
        A2 = assemble(a2)
        A3 = assemble(a3)
        if self.useRotationScheme:
            A4 = assemble(a4)
        self.tc.end('assembleMatrices')

        if self.solvers == 'direct':
            self.solver_vel_tent = LUSolver('mumps')
            self.solver_vel_cor = LUSolver('mumps')
            self.solver_p = LUSolver('mumps')
            if self.useRotationScheme:
                self.solver_rot = LUSolver('mumps')
        else:
            # NT 2016-1  KrylovSolver >> PETScKrylovSolver

            # not needed, chosen not to use hypre_parasails:
            # if self.prec_v == 'hypre_parasails':  # in FEniCS 1.6.0 inaccessible using KrylovSolver class
            #     self.solver_vel_tent = PETScKrylovSolver('gmres')   # PETSc4py object
            #     self.solver_vel_tent.ksp().getPC().setType('hypre')
            #     PETScOptions.set('pc_hypre_type', 'parasails')
            #     # this is global setting, but preconditioners for pressure solvers are set by their constructors
            # else:
            self.solver_vel_tent = PETScKrylovSolver(
                'gmres', self.args.precV)  # nonsymetric > gmres
            # cannot use 'ilu' in parallel
            self.solver_vel_cor = PETScKrylovSolver('cg', self.args.precVC)
            self.solver_p = PETScKrylovSolver(
                self.args.solP,
                self.args.precP)  # almost (up to BC) symmetric > CG
            if self.useRotationScheme:
                self.solver_rot = PETScKrylovSolver('cg', 'hypre_amg')

        # setup Krylov solvers
        if self.solvers == 'krylov':
            # Get the nullspace if there are no pressure boundary conditions
            foo = Function(
                self.Q)  # auxiliary vector for setting pressure nullspace
            if self.args.bc == 'nullspace':
                null_vec = Vector(foo.vector())
                self.Q.dofmap().set(null_vec, 1.0)
                null_vec *= 1.0 / null_vec.norm('l2')
                self.null_space = VectorSpaceBasis([null_vec])
                as_backend_type(A2).set_nullspace(self.null_space)

            # apply global options for Krylov solvers
            solver_options = {
                'monitor_convergence': True,
                'maximum_iterations': 10000,
                'nonzero_initial_guess': True
            }
            # 'nonzero_initial_guess': True   with  solver.solve(A, u, b) means that
            # Solver will use anything stored in u as an initial guess
            for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_rot, self.solver_p] if \
                    self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]:
                for key, value in solver_options.items():
                    try:
                        solver.parameters[key] = value
                    except KeyError:
                        info('Invalid option %s for KrylovSolver' % key)
                        return 1

            if self.args.solP == 'richardson':
                self.solver_p.parameters['monitor_convergence'] = False

            self.solver_vel_tent.parameters['relative_tolerance'] = 10**(
                -self.args.prv1)
            self.solver_vel_tent.parameters['absolute_tolerance'] = 10**(
                -self.args.pav1)
            self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12
            self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4
            self.solver_p.parameters['relative_tolerance'] = 10**(
                -self.args.prp)
            self.solver_p.parameters['absolute_tolerance'] = 10**(
                -self.args.pap)
            if self.useRotationScheme:
                self.solver_rot.parameters['relative_tolerance'] = 10E-10
                self.solver_rot.parameters['absolute_tolerance'] = 10E-10

            if self.args.Vrestart > 0:
                self.solver_vel_tent.parameters['gmres'][
                    'restart'] = self.args.Vrestart

            if self.args.solP == 'gmres' and self.args.Prestart > 0:
                self.solver_p.parameters['gmres'][
                    'restart'] = self.args.Prestart

        # boundary conditions
        bcu, bcp = problem.get_boundary_conditions(self.args.bc == 'outflow',
                                                   self.V, self.Q)
        self.tc.end('init')
        # Time-stepping
        info("Running of Incremental pressure correction scheme n. 1")
        ttime = self.metadata['time']
        t = dt
        step = 1

        # debug function
        if problem.args.debug_rot:
            plot_cor_v = Function(self.V)

        while t < (ttime + dt / 2.0):
            self.problem.update_time(t, step)
            if self.MPI_rank == 0:
                problem.write_status_file(t)

            if doSave:
                save_this_step = problem.save_this_step

            # assemble matrix (it depends on solution)
            self.tc.start('assembleA1')
            assemble(
                a1_change, tensor=A1_change
            )  # assembling into existing matrix is faster than assembling new one
            A1 = A1_const.copy()  # we dont want to change A1_const
            A1.axpy(1, A1_change, True)
            self.tc.end('assembleA1')
            self.tc.start('assembleA1stab')
            if self.stabilize and not self.use_full_SUPG:
                assemble(
                    a1_stab, tensor=A1_stab
                )  # assembling into existing matrix is faster than assembling new one
                A1.axpy(1, A1_stab, True)
            self.tc.end('assembleA1stab')

            # Compute tentative velocity step
            begin("Computing tentative velocity")
            self.tc.start('rhs')
            b = assemble(L1)
            self.tc.end('rhs')
            self.tc.start('applybc1')
            [bc.apply(A1, b) for bc in bcu]
            self.tc.end('applybc1')
            try:
                self.tc.start('solve 1')
                self.solver_vel_tent.solve(A1, u_.vector(), b)
                self.tc.end('solve 1')
                if save_this_step:
                    self.tc.start('saveVel')
                    problem.save_vel(True, u_)
                    self.tc.end('saveVel')
                if save_this_step and not onlyVel:
                    problem.save_div(True, u_)
                problem.compute_err(True, u_, t)
                problem.compute_div(True, u_)
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            end()

            if self.useRotationScheme:
                begin("Computing tentative pressure")
            else:
                begin("Computing pressure")
            if self.forceOutflow and problem.can_force_outflow:
                out = problem.compute_outflow(u_)
                info('Tentative outflow: %f' % out)
                n_o = -problem.last_inflow - out
                info('Needed outflow: %f' % n_o)
                need_outflow.assign(n_o)
            self.tc.start('rhs')
            b = assemble(L2)
            self.tc.end('rhs')
            self.tc.start('applybcP')
            [bc.apply(A2, b) for bc in bcp]
            if self.args.bc == 'nullspace':
                self.null_space.orthogonalize(b)
            self.tc.end('applybcP')
            try:
                self.tc.start('solve 2')
                self.solver_p.solve(A2, p_.vector(), b)
                self.tc.end('solve 2')
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            if self.useRotationScheme:
                foo = Function(self.Q)
                foo.assign(p_ + p0)
                if save_this_step and not onlyVel:
                    problem.averaging_pressure(foo)
                    problem.save_pressure(True, foo)
            else:
                foo = Function(self.Q)
                foo.assign(p_)  # we do not want to change p_ by averaging
                if save_this_step and not onlyVel:
                    problem.averaging_pressure(foo)
                    problem.save_pressure(False, foo)
            end()

            begin("Computing corrected velocity")
            self.tc.start('rhs')
            b = assemble(L3)
            self.tc.end('rhs')
            if not self.args.B:
                self.tc.start('applybc3')
                [bc.apply(A3, b) for bc in bcu]
                self.tc.end('applybc3')
            try:
                self.tc.start('solve 3')
                self.solver_vel_cor.solve(A3, u_cor.vector(), b)
                self.tc.end('solve 3')
                problem.compute_err(False, u_cor, t)
                problem.compute_div(False, u_cor)
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            if save_this_step:
                self.tc.start('saveVel')
                problem.save_vel(False, u_cor)
                self.tc.end('saveVel')
            if save_this_step and not onlyVel:
                problem.save_div(False, u_cor)
            end()

            if self.useRotationScheme:
                begin("Rotation scheme pressure correction")
                self.tc.start('rhs')
                b = assemble(L4)
                self.tc.end('rhs')
                try:
                    self.tc.start('solve 4')
                    self.solver_rot.solve(A4, p_mod.vector(), b)
                    self.tc.end('solve 4')
                except RuntimeError as inst:
                    problem.report_fail(t)
                    return 1
                if save_this_step and not onlyVel:
                    problem.averaging_pressure(p_mod)
                    problem.save_pressure(False, p_mod)
                end()

                if problem.args.debug_rot:
                    # save applied pressure correction (expressed as a term added to RHS of next tentative vel. step)
                    # see comment next to argument definition
                    plot_cor_v.assign(project(k * grad(nu * div(u_)), self.V))
                    problem.fileDict['grad_cor']['file'].write(plot_cor_v, t)

            # compute functionals (e. g. forces)
            problem.compute_functionals(
                u_cor, p_mod if self.useRotationScheme else p_, t, step)

            # Move to next time step
            self.tc.start('next')
            u1.assign(u0)
            u0.assign(u_cor)
            u_.assign(
                u_cor)  # use corrected velocity as initial guess in first step

            if self.useRotationScheme:
                p0.assign(p_mod)
            else:
                p0.assign(p_)

            t = round(t + dt, 6)  # round time step to 0.000001
            step += 1
            self.tc.end('next')

        info("Finished: Incremental pressure correction scheme n. 1")
        problem.report()
        return 0
Пример #32
0
def compute_time_errors(problem, method, mesh_sizes, Dt):

    mesh_generator, solution, f, mu, rho, cell_type = problem()

    # Translate data into FEniCS expressions.
    u = solution['u']
    sol_u = Expression((ccode(u['value'][0]), ccode(u['value'][1])),
                       degree=_truncate_degree(u['degree']),
                       t=0.0)

    p = solution['p']
    sol_p = Expression(ccode(p['value']),
                       degree=_truncate_degree(p['degree']),
                       t=0.0)

    # Deep-copy expression to be able to provide f0, f1 for the Dirichlet-
    # boundary conditions later on.
    fenics_rhs0 = Expression((ccode(f['value'][0]), ccode(f['value'][1])),
                             degree=_truncate_degree(f['degree']),
                             t=0.0,
                             mu=mu,
                             rho=rho)
    fenics_rhs1 = Expression(fenics_rhs0.cppcode,
                             element=fenics_rhs0.ufl_element(),
                             **fenics_rhs0.user_parameters)
    # Create initial states.
    p = Expression(sol_p.cppcode,
                   degree=_truncate_degree(solution['p']['degree']),
                   t=0.0,
                   cell=cell_type)

    # Compute the problem
    errors = {
        'u': numpy.empty((len(mesh_sizes), len(Dt))),
        'p': numpy.empty((len(mesh_sizes), len(Dt)))
    }
    for k, mesh_size in enumerate(mesh_sizes):
        print()
        print()
        print('Computing for mesh size %r...' % mesh_size)
        mesh = mesh_generator(mesh_size)
        mesh_area = assemble(1.0 * dx(mesh))
        W = VectorFunctionSpace(mesh, 'CG', 2)
        P = FunctionSpace(mesh, 'CG', 1)
        u1 = Function(W)
        p1 = Function(P)
        err_p = Function(P)
        divu1 = Function(P)
        for j, dt in enumerate(Dt):
            # Prepare previous states for multistepping.
            u_1 = Expression(sol_u.cppcode,
                             degree=_truncate_degree(solution['u']['degree']),
                             t=-dt,
                             cell=cell_type)
            u_1 = project(u_1, W)
            u0 = Expression(
                sol_u.cppcode,
                degree=_truncate_degree(solution['u']['degree']),
                t=0.0,
                # t=0.5*dt,
                cell=cell_type)
            u0 = project(u0, W)
            sol_u.t = dt
            u_bcs = [DirichletBC(W, sol_u, 'on_boundary')]
            sol_p.t = dt
            p0 = project(p, P)
            p_bcs = []
            fenics_rhs0.t = 0.0
            fenics_rhs1.t = dt
            u1, p1 = method.step(Constant(dt), {
                -1: u_1,
                0: u0
            },
                                 p0,
                                 u_bcs=u_bcs,
                                 p_bcs=p_bcs,
                                 rho=Constant(rho),
                                 mu=Constant(mu),
                                 f={
                                     0: fenics_rhs0,
                                     1: fenics_rhs1
                                 },
                                 verbose=False,
                                 tol=1.0e-10)

            # plot(sol_u, mesh=mesh, title='u_sol')
            # plot(sol_p, mesh=mesh, title='p_sol')
            # plot(u1, title='u')
            # plot(p1, title='p')
            # from dolfin import div
            # plot(div(u1), title='div(u)')
            # plot(p1 - sol_p, title='p_h - p')
            # interactive()

            sol_u.t = dt
            sol_p.t = dt
            errors['u'][k][j] = errornorm(sol_u, u1)
            # The pressure is only determined up to a constant which makes
            # it a bit harder to define what the error is. For our
            # purposes, choose an alpha_0\in\R such that
            #
            #    alpha0 = argmin ||e - alpha||^2
            #
            # with  e := sol_p - p.
            # This alpha0 is unique and explicitly given by
            #
            #     alpha0 = 1/(2|Omega|) \int (e + e*)
            #            = 1/|Omega| \int Re(e),
            #
            # i.e., the mean error in \Omega.
            alpha = (+assemble(sol_p * dx(mesh)) - assemble(p1 * dx(mesh)))
            alpha /= mesh_area
            # We would like to perform
            #     p1 += alpha.
            # To avoid creating a temporary function every time, assume
            # that p1 lives in a function space where the coefficients
            # represent actual function values. This is true for CG
            # elements, for example. In that case, we can just add any
            # number to the vector of p1.
            p1.vector()[:] += alpha
            errors['p'][k][j] = errornorm(sol_p, p1)

            show_plots = False
            if show_plots:
                plot(p1, title='p1', mesh=mesh)
                plot(sol_p, title='sol_p', mesh=mesh)
                err_p.vector()[:] = p1.vector()
                sol_interp = interpolate(sol_p, P)
                err_p.vector()[:] -= sol_interp.vector()
                # plot(sol_p - p1, title='p1 - sol_p', mesh=mesh)
                plot(err_p, title='p1 - sol_p', mesh=mesh)
                # r = Expression('x[0]', degree=1, cell=triangle)
                # divu1 = 1 / r * (r * u1[0]).dx(0) + u1[1].dx(1)
                divu1.assign(project(u1[0].dx(0) + u1[1].dx(1), P))
                plot(divu1, title='div(u1)')
                interactive()
    return errors
Пример #33
0
def compute_time_errors(problem, MethodClass, mesh_sizes, Dt):

    mesh_generator, solution, f, mu, rho, cell_type = problem()
    # Translate data into FEniCS expressions.
    sol_u = Expression((smp.printing.ccode(solution['u']['value'][0]),
                        smp.printing.ccode(solution['u']['value'][1])
                        ),
                       degree=_truncate_degree(solution['u']['degree']),
                       t=0.0,
                       cell=cell_type
                       )
    sol_p = Expression(smp.printing.ccode(solution['p']['value']),
                       degree=_truncate_degree(solution['p']['degree']),
                       t=0.0,
                       cell=cell_type
                       )

    fenics_rhs0 = Expression((smp.printing.ccode(f['value'][0]),
                              smp.printing.ccode(f['value'][1])
                              ),
                             degree=_truncate_degree(f['degree']),
                             t=0.0,
                             mu=mu, rho=rho,
                             cell=cell_type
                             )
    # Deep-copy expression to be able to provide f0, f1 for the Dirichlet-
    # boundary conditions later on.
    fenics_rhs1 = Expression(fenics_rhs0.cppcode,
                             degree=_truncate_degree(f['degree']),
                             t=0.0,
                             mu=mu, rho=rho,
                             cell=cell_type
                             )
    # Create initial states.
    p0 = Expression(
        sol_p.cppcode,
        degree=_truncate_degree(solution['p']['degree']),
        t=0.0,
        cell=cell_type
        )

    # Compute the problem
    errors = {'u': numpy.empty((len(mesh_sizes), len(Dt))),
              'p': numpy.empty((len(mesh_sizes), len(Dt)))
              }
    for k, mesh_size in enumerate(mesh_sizes):
        info('')
        info('')
        with Message('Computing for mesh size %r...' % mesh_size):
            mesh = mesh_generator(mesh_size)
            mesh_area = assemble(1.0 * dx(mesh))
            W = VectorFunctionSpace(mesh, 'CG', 2)
            P = FunctionSpace(mesh, 'CG', 1)
            method = MethodClass(W, P,
                                 rho, mu,
                                 theta=1.0,
                                 #theta=0.5,
                                 stabilization=None
                                 #stabilization='SUPG'
                                 )
            u1 = Function(W)
            p1 = Function(P)
            err_p = Function(P)
            divu1 = Function(P)
            for j, dt in enumerate(Dt):
                # Prepare previous states for multistepping.
                u = [Expression(
                    sol_u.cppcode,
                    degree=_truncate_degree(solution['u']['degree']),
                    t=0.0,
                    cell=cell_type
                    ),
                    # Expression(
                    #sol_u.cppcode,
                    #degree=_truncate_degree(solution['u']['degree']),
                    #t=0.5*dt,
                    #cell=cell_type
                    #)
                    ]
                sol_u.t = dt
                u_bcs = [DirichletBC(W, sol_u, 'on_boundary')]
                sol_p.t = dt
                #p_bcs = [DirichletBC(P, sol_p, 'on_boundary')]
                p_bcs = []
                fenics_rhs0.t = 0.0
                fenics_rhs1.t = dt
                method.step(dt,
                            u1, p1,
                            u, p0,
                            u_bcs=u_bcs, p_bcs=p_bcs,
                            f0=fenics_rhs0, f1=fenics_rhs1,
                            verbose=False,
                            tol=1.0e-10
                            )
                sol_u.t = dt
                sol_p.t = dt
                errors['u'][k][j] = errornorm(sol_u, u1)
                # The pressure is only determined up to a constant which makes
                # it a bit harder to define what the error is. For our
                # purposes, choose an alpha_0\in\R such that
                #
                #    alpha0 = argmin ||e - alpha||^2
                #
                # with  e := sol_p - p.
                # This alpha0 is unique and explicitly given by
                #
                #     alpha0 = 1/(2|Omega|) \int (e + e*)
                #            = 1/|Omega| \int Re(e),
                #
                # i.e., the mean error in \Omega.
                alpha = assemble(sol_p * dx(mesh)) \
                    - assemble(p1 * dx(mesh))
                alpha /= mesh_area
                # We would like to perform
                #     p1 += alpha.
                # To avoid creating a temporary function every time, assume
                # that p1 lives in a function space where the coefficients
                # represent actual function values. This is true for CG
                # elements, for example. In that case, we can just add any
                # number to the vector of p1.
                p1.vector()[:] += alpha
                errors['p'][k][j] = errornorm(sol_p, p1)

                show_plots = False
                if show_plots:
                    plot(p1, title='p1', mesh=mesh)
                    plot(sol_p, title='sol_p', mesh=mesh)
                    err_p.vector()[:] = p1.vector()
                    sol_interp = interpolate(sol_p, P)
                    err_p.vector()[:] -= sol_interp.vector()
                    #plot(sol_p - p1, title='p1 - sol_p', mesh=mesh)
                    plot(err_p, title='p1 - sol_p', mesh=mesh)
                    #r = Expression('x[0]', degree=1, cell=triangle)
                    #divu1 = 1 / r * (r * u1[0]).dx(0) + u1[1].dx(1)
                    divu1.assign(project(u1[0].dx(0) + u1[1].dx(1), P))
                    plot(divu1, title='div(u1)')
                    interactive()
    return errors
Пример #34
0
    def run(self):
        outdir = self.parameters['outdir']
        savelag = self.parameters['savelag']
        solver = self.solver
        stability = self.stability
        alpha = solver.alpha
        u = solver.u


        load_steps = self.load_steps 
        alpha_old = Function(alpha.function_space())
        self.time_data_i = []
        stable = None; negev = -1; mineig = np.inf; iteration = 0
        diff = alpha.copy(deepcopy=True)
        for it, load in enumerate(load_steps):
            self.load_param.t = load
            alpha_old.assign(alpha)
            print('')
            ColorPrint.print_warn('Solving load = {:.2f}'.format(load))
            self.time_data_i, am_iter = solver.solve()

            diff.vector()[:] = alpha.vector() - alpha_old.vector()
            try:
                assert all(alpha.vector()[:]>=alpha_old.vector()[:])
            except AssertionError:
                print('check alpha.vector()[:]>=alpha_old.vector()')

            try:
                assert all(solver.problem_alpha.lb.vector()[:]==alpha_old.vector()[:])
            except AssertionError:
                print('check all(solver.problem_alpha.lb.vector()[:]==alpha_old.vector()[:])')

            if bool(strtobool(str(stability.parameters['checkstability']))):
                (stable, negev) = stability.solve(solver.problem_alpha.lb)
                ColorPrint.print_pass('Current state is{}stable'.format(' ' if stable else ' not '))
                if hasattr(stability, 'eigs') and len(stability.eigs)>0 and min(stability.eigs)<0:
                    pp.plot_eigenmodes(stability.eigendata, alpha, load, outdir)
                    self.user_postprocess_stability(load)
            else:
                solver.update()
            alpha.copy(deepcopy = True)

            if stable:
                solver.update()
            elif not stable and not bool(strtobool(str(stability.parameters['continuation']))):
                solver.update()

            elif not stable and bool(strtobool(str(stability.parameters['continuation']))):
                while stable == False:
                    adm_pert = np.where(np.array([e['en_diff'] for e in stability.eigendata]) < 0)[0]
                    if len(adm_pert)==0:
                        ColorPrint.print_warn('No admissible perturbations found')
                        ColorPrint.print_pass('Continuing load program')
                        break
                    else:
                        continuation_data = []
                        steepest = np.argmin([e['en_diff']  for e in stability.eigendata])
                        if self.parameters['perturbation_choice']=='first':
                            mode = 0
                        elif self.parameters['perturbation_choice'] == 'steepest':
                            mode = steepest
                        elif isinstance(self.parameters['perturbation_choice'], int):
                            mode = self.parameters['perturbation_choice']

                        perturbation_v = stability.eigendata[mode]['v_n']
                        perturbation_beta = stability.eigendata[mode]['beta_n']
                        hstar = stability.eigendata[mode]['hstar']
                        perturbation_v.rename('step displacement perturbation', 'step displacement perturbation')
                        perturbation_beta.rename('step damage perturbation', 'step damage perturbation')
                        ColorPrint.print_pass('Perturbation choice {}'.format(self.parameters['perturbation_choice']))
                        ColorPrint.print_pass('Perturbing current state with mode {} Delta E={:.5%} (estimated)'.format(mode, stability.eigendata[mode]['en_diff']))
                        ColorPrint.print_pass('...... chosen mode {} vs. steepest {} Delta E={:.5%} (estimated)'.format(mode, steepest, stability.eigendata[mode]['en_diff']/stability.eigendata[steepest]['en_diff']))
                        ColorPrint.print_pass('...... steepest descent mode {} Delta E={:.5%} (estimated)'.format(steepest,stability.eigendata[steepest]['en_diff']))

                        # perturb current state
                        self.compile_continuation_data(load, iteration, perturbed=False)
                        solver.alpha.copy(deepcopy=True)

                        ColorPrint.print_pass('Perturbing current state, looking for stability, iteration {}'.format(iteration))
                        uval = u.vector()[:]     + hstar * perturbation_v.vector()[:]
                        aval = alpha.vector()[:] + hstar * perturbation_beta.vector()[:]

                        alpha.vector().vec().ghostUpdate()
                        u.vector().vec().ghostUpdate()

                        self.time_data_i, am_iter = solver.solve()

                        if self.file_con is not None:
                            with self.file_con as f:
                                f.write(alpha, iteration)
                                f.write(u, iteration)

                        self.compile_continuation_data(load, iteration, perturbed=True)

                        ColorPrint.print_pass('Energy diff {}, rel thhreshold {}'
                                        .format(self.continuation_data_i["total_energy_diff"]/self.continuation_data_i["total_energy"], 
                                                self.stability.parameters['cont_rtol']))
                        continuation_data.append(self.continuation_data_i)

                        if np.mod(it, self.parameters['savelag']) == 0:
                            continuation_data_pd=pd.DataFrame(continuation_data)
                            continuation_data_pd.to_json(os.path.join(outdir + "/continuation_data.json"))

                        if self.continuation_data_i["total_energy_diff"]/self.continuation_data_i["total_energy"] < - self.stability.parameters['cont_rtol']:
                            ColorPrint.print_pass('Updating irreversibility')
                            solver.update()
                        else:
                            ColorPrint.print_pass('Threshold not met, continuing load program')
                            break

                        (stable, negev) = stability.solve(alpha_old)

                        if self.file_eig is not None:
                            with self.file_eig as f:
                                f.write(perturbation_beta, iteration)
                                f.write(perturbation_v, iteration)

                        iteration += 1

            time_data_pd = self.compile_time_data(load)

            if np.mod(it, self.parameters['savelag']) == 0:
                time_data_pd.to_json(os.path.join(outdir + "/time_data.json"))
                ColorPrint.print_pass('written data to file {}'.format(str(os.path.join(outdir + "/time_data.json"))))

                if self.file_out is not None:
                    with self.file_out as f:
                        f.write(alpha, load)
                        f.write(u, load)

            pp.plot_global_data(time_data_pd,load,outdir)
            self.user_postprocess_timestep(load)
        return time_data_pd
    def step(self,
             dt,
             u1, p1,
             u, p0,
             u_bcs, p_bcs,
             f0=None, f1=None,
             verbose=True,
             tol=1.0e-10
             ):
        # Some initial sanity checkups.
        assert dt > 0.0
        # Define trial and test functions
        ui = Function(self.W)
        v = TestFunction(self.W)
        # Create functions
        # Define coefficients
        k = Constant(dt)

        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        # Compute tentative velocity step:
        #
        #     F(u) = 0,
        #     F(u) := rho (U0 + (u.\nabla)u) - mu \div(\nabla u) - f = 0.
        #
        with Message('Computing tentative velocity'):
            # TODO higher-order scheme for time integration
            #
            # For higher-order schemes, see
            #
            #     A comparison of time-discretization/linearization approaches
            #     for the incompressible Navier-Stokes equations;
            #     Volker John, Gunar Matthies, Joachim Rang;
            #     Comput. Methods Appl. Mech. Engrg. 195 (2006) 5995-6010;
            #     <http://www.wias-berlin.de/people/john/ELECTRONIC_PAPERS/JMR06.CMAME.pdf>.
            #
            F1 = self.rho * inner((ui - u[0])/k, v) * dx

            if abs(self.theta) > DOLFIN_EPS:
                # Implicit terms.
                if f1 is None:
                    raise RuntimeError('Implicit schemes need right-hand side '
                                       'at target step (f1).')
                F1 -= self.theta * _rhs_weak(ui, v, f1, self.rho, self.mu)
            if abs(1.0 - self.theta) > DOLFIN_EPS:
                # Explicit terms.
                if f0 is None:
                    raise RuntimeError('Explicit schemes need right-hand side '
                                       'at current step (f0).')
                F1 -= (1.0 - self.theta) \
                    * _rhs_weak(u[0], v, f0, self.rho, self.mu)

            if p0:
                F1 += dot(grad(p0), v) * dx

            #if stabilization:
            #    tau = stab.supg2(V.mesh(),
            #                     u_1,
            #                     mu/rho,
            #                     V.ufl_element().degree()
            #                     )
            #    R = rho*(ui - u_1)/k
            #    if abs(theta) > DOLFIN_EPS:
            #        R -= theta * _rhs_strong(ui, f1, rho, mu)
            #    if abs(1.0-theta) > DOLFIN_EPS:
            #        R -= (1.0-theta) * _rhs_strong(u_1, f0, rho, mu)
            #    if p_1:
            #        R += grad(p_1)
            #    # TODO use u_1 or ui here?
            #    F1 += tau * dot(R, grad(v)*u_1) * dx

            # Get linearization and solve nonlinear system.
            # If the scheme is fully explicit (theta=0.0), then the system is
            # actually linear and only one Newton iteration is performed.
            J = derivative(F1, ui)

            # What is a good initial guess for the Newton solve?
            # Three choices come to mind:
            #
            #    (1) the previous solution u_1,
            #    (2) the intermediate solution from the previous step ui_1,
            #    (3) the solution of the semilinear system
            #        (u.\nabla(u) -> u_1.\nabla(u)).
            #
            # Numerical experiments with the Karman vortex street show that the
            # order of accuracy is (1), (3), (2). Typical norms would look like
            #
            #     ||u - u_1 || = 1.726432e-02
            #     ||u - ui_1|| = 2.720805e+00
            #     ||u - u_e || = 5.921522e-02
            #
            # Hence, use u_1 as initial guess.
            ui.assign(u[0])

            # Wrap the solution in a try-catch block to make sure we call end()
            # if necessary.
            #problem = NonlinearVariationalProblem(F1, ui, u_bcs, J)
            #solver  = NonlinearVariationalSolver(problem)
            solve(
                F1 == 0, ui,
                bcs=u_bcs,
                J=J,
                solver_parameters={
                    #'nonlinear_solver': 'snes',
                    'nonlinear_solver': 'newton',
                    'newton_solver': {
                        'maximum_iterations': 5,
                        'report': True,
                        'absolute_tolerance': tol,
                        'relative_tolerance': 0.0,
                        'linear_solver': 'iterative',
                        ## The nonlinear term makes the problem generally
                        ## nonsymmetric.
                        #'symmetric': False,
                        # If the nonsymmetry is too strong, e.g., if u_1 is
                        # large, then AMG preconditioning might not work very
                        # well.
                        'preconditioner': 'ilu',
                        #'preconditioner': 'hypre_amg',
                        'krylov_solver': {'relative_tolerance': tol,
                                          'absolute_tolerance': 0.0,
                                          'maximum_iterations': 1000,
                                          'monitor_convergence': verbose}
                        }})
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        with Message('Computing pressure correction'):
            #
            # The following is based on the update formula
            #
            #     rho/dt (u_{n+1}-u*) + \nabla phi = 0
            #
            # with
            #
            #     phi = (p_{n+1} - p*) + chi*mu*div(u*)
            #
            # and div(u_{n+1})=0. One derives
            #
            #   - \nabla^2 phi = rho/dt div(u_{n+1} - u*),
            #   - n.\nabla phi = rho/dt  n.(u_{n+1} - u*),
            #
            # In its weak form, this is
            #
            #     \int \grad(phi).\grad(q)
            #   = - rho/dt \int div(u*) q - rho/dt \int_Gamma n.(u_{n+1}-u*) q.
            #
            # If Dirichlet boundary conditions are applied to both u* and
            # u_{n+1} (the latter in the final step), the boundary integral
            # vanishes.
            #
            # Assume that on the boundary
            #   L2 -= inner(n, rho/k (u_bcs - ui)) * q * ds
            # is zero. This requires the boundary conditions to be set for
            # ui as well as u_final.
            # This creates some problems if the boundary conditions are
            # supposed to remain 'free' for the velocity, i.e., no Dirichlet
            # conditions in normal direction. In that case, one needs to
            # specify Dirichlet pressure conditions.
            #
            rotational_form = False
            self._pressure_poisson(p1, p0,
                                   self.mu, ui,
                                   divu=Constant(self.rho/dt)*div(ui),
                                   p_bcs=p_bcs,
                                   rotational_form=rotational_form,
                                   tol=tol,
                                   verbose=verbose
                                   )
            #plot(p - phi, title='p-phi')
            #plot(ui, title='u intermediate')
            #plot(f, title='f')
            ##plot(ui[1], title='u intermediate[1]')
            #plot(div(ui), title='div(u intermediate)')
            #plot(phi, title='phi')
            #interactive()
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        # Velocity correction.
        #   U = U0 - dt/rho \nabla p.
        with Message('Computing velocity correction'):
            u2 = TrialFunction(self.W)
            a3 = inner(u2, v) * dx
            if p0:
                phi = Function(self.P)
                phi.assign(p1)
                phi -= p0
            else:
                phi = p1
            if rotational_form:
                phi += self.mu * div(ui)
            L3 = inner(ui,  v) * dx \
                - k/self.rho * inner(grad(phi), v) * dx
            solve(a3 == L3, u1,
                  bcs=u_bcs,
                  solver_parameters={
                      'linear_solver': 'iterative',
                      'symmetric': True,
                      'preconditioner': 'jacobi',
                      'krylov_solver': {'relative_tolerance': tol,
                                        'absolute_tolerance': 0.0,
                                        'maximum_iterations': 100,
                                        'monitor_convergence': verbose}
                      })
        #u = project(ui - k/rho * grad(phi), V)
        #print '||u||_div = %e' % norm(u, 'Hdiv0')
        #uu = TrialFunction(Q)
        #vv = TestFunction(Q)
        #div_u = Function(Q)
        #solve(uu*vv*dx == div(u)*vv*dx, div_u,
        #      #bcs=DirichletBC(Q, 0.0, 'on_boundary')
        #      )
        #plot(div_u, title='div(u)')
        #interactive()
        return
# Set slotted disk
psi0_expr = SlottedDisk(radius=rdisk,
                        center=[xc, yc],
                        width=rwidth,
                        depth=0.,
                        degree=3,
                        lb=lb,
                        ub=ub)

# Function space and velocity field
W = FunctionSpace(mesh, 'DG', k)
psi_h = Function(W)

V = VectorFunctionSpace(mesh, 'DG', 3)
uh = Function(V)
uh.assign(Expression(('-Uh*x[1]', 'Uh*x[0]'), Uh=Uh, degree=3))

# Generate particles
x = RandomCircle(Point(x0, y0), r).generate([pres, pres])
s = assign_particle_values(x, psi0_expr)

p = particles(x, [s], mesh)
# Initialize advection class, use RK3 scheme
ap = advect_rk3(p, V, uh, 'closed')
# Init projection
lstsq_psi = l2projection(p, W, 1)

# Do projection to get initial field
lstsq_psi.project(psi_h.cpp_object(), lb, ub)
AD = AddDelete(p, 10, 20, [psi_h], [1], [lb, ub])
Пример #37
0
class GeneralProblem(object):
    def __init__(self, args, tc, metadata):
        self.MPI_rank = MPI.rank(mpi_comm_world())

        self.metadata = metadata

        # need to be specified in subclass init before calling this init
        self.problem_code = self.problem_code
        self.metadata['pcode'] = self.problem_code
        self.has_analytic_solution = self.has_analytic_solution
        self.metadata['hasAnalyticSolution'] = self.has_analytic_solution

        self.args = args
        # Time control
        self.tc = tc
        self.tc.init_watch('mesh', 'Imported mesh', True)
        self.tc.init_watch('saveP', 'Saved pressure', True)
        self.tc.init_watch('saveVel', 'Saved velocity', True)
        self.tc.init_watch('averageP', 'Averaged pressure', True)
        self.tc.init_watch('updateBC', 'Updated velocity BC', True, count_to_percent=True)
        self.tc.init_watch('div', 'Computed and saved divergence', True)
        self.tc.init_watch('divNorm', 'Computed norm of divergence', True)
        self.tc.init_watch('WSSinit', 'Initialized mesh for WSS', False)
        self.tc.init_watch('WSS', 'Computed and saved WSS', True)
        self.tc.init_watch('errorV', 'Computed velocity error', True)
        self.tc.init_watch('errorVtest', 'Computed velocity error test', True)

        # If it is sensible (and implemented) to force pressure gradient on outflow boundary
        # 1. set self.outflow_area in initialize
        # 2. implement self.compute_outflow and get_outflow_measures
        self.can_force_outflow = False
        self.outflow_area = None
        self.outflow_measures = []

        # stopping criteria (for relative H1 velocity error norm) (if known)
        self.divergence_treshold = 10

        # used for writing status files .run to monitor progress during computation:
        self.last_status_functional = 0.0
        self.status_functional_str = 'to be defined in Problem class'

        # mesh and function objects
        self.normal = None
        self.mesh = None
        self.facet_function = None
        self.mesh_volume = 0   # must be specified in subclass (needed to compute pressure average)
        self.stepsInCycle = None
        self.vSpace = None
        self.vFunction = None
        self.divSpace = None
        self.divFunction = None
        self.pSpace = None
        # self.pgSpace = None    # NT computing pressure gradient function not used (commented throughout code)
        self.pFunction = None
        # self.pgFunction = None
        self.solutionSpace = None
        self.solution = None

        # time related variables
        self.actual_time = 0.0
        self.cycle_length = 1.0
        self.step_number = 0
        self.chosen_steps = [0.1, 0.125, 0.15, 0.16, 0.165, 0.166, 0.167, 0.17, 0.188, 0.189, 0.2]  # must be specified in subclass
        # 0.166... is peak for real problem, 0.188 is peak for womersley profile
        # TODO chosen_steps should depend on problem (and inflow profile)
        self.distance_from_chosen_steps = 0.0
        self.save_this_step = False
        self.isWholeSecond = None
        self.N1 = None
        self.N0 = None

        # lists used for storing normalization and scaling coefficients
        # (time independent, mainly used in womersley_cylinder)
        self.vel_normalization_factor = []
        self.pg_normalization_factor = []
        self.p_normalization_factor = []
        self.scale_factor = []

        self.analytic_v_norm_L2 = None
        self.analytic_v_norm_H1 = None
        self.analytic_v_norm_H1w = None
        self.last_error = None

        # for WSS generation
        metadata['hasWSS'] = (args.wss != 'none')
        metadata['WSSmethod'] = self.args.wss_method
        self.T = None
        self.Tb = None
        self.wall_mesh = None
        self.wall_mesh_oriented = None
        self.Vb = None
        self.Sb = None
        self.VDG = None
        self.R = None
        self.SDG = None
        self.nb = None

        # dictionaries for output XDMF files
        self.fileDict = {'u': {'name': 'velocity'},
                         'p': {'name': 'pressure'},
                         # 'pg': {'name': 'pressure_grad'},
                         'd': {'name': 'divergence'}}
        self.fileDictTent = {'u2': {'name': 'velocity_tent'},
                             'd2': {'name': 'divergence_tent'}}
        self.fileDictDiff = {'uD': {'name': 'velocity_diff'},
                             'pD': {'name': 'pressure_diff'},
                             'pgD': {'name': 'pressure_grad_diff'}}
        self.fileDictTentDiff = {'u2D': {'name': 'velocity_tent_diff'}}
        self.fileDictTentP = {'p2': {'name': 'pressure_tent'}}
        #                      'pg2': {'name': 'pressure_grad_tent'}}
        self.fileDictTentPDiff = {'p2D': {'name': 'pressure_tent_diff'}}
        #                          'pg2D': {'name': 'pressure_grad_tent_diff'}}
        self.fileDictWSS = {'wss': {'name': 'wss'}, }
        self.fileDictWSSnorm = {'wss_norm': {'name': 'wss_norm'}, }
        self.fileDictDebugRot = {'grad_cor': {'name': 'grad_cor'}, }

        # lists of functionals and other scalar output data
        self.time_list = []  # list of times, when error is  measured (used in report)
        self.second_list = []
        self.listDict = {}  # list of fuctionals
        # dictionary of data lists {list, name, abbreviation, add scaled row to report}
        # normalisation coefficients (time-independent) are added to some lists to be used in normalized data series
        #   coefficients are lists (updated during initialisation, so we cannot use float type)
        #   coefs are equal to average of respective value of analytic solution
        # norm lists (time-dependent normalisation coefficients) are added to some lists to be used in relative data
        #  series (to remove natural pulsation of error due to change in volume flow rate)
        # slist - lists for cycle-averaged values
        # L2(0) means L2 difference of pressures taken with zero average
        self.listDict = {
            'd': {'list': [], 'name': 'corrected velocity L2 divergence', 'abrev': 'DC',
                  'scale': self.scale_factor, 'slist': []},
            'd2': {'list': [], 'name': 'tentative velocity L2 divergence', 'abrev': 'DT',
                   'scale': self.scale_factor, 'slist': []},
            'pg': {'list': [], 'name': 'computed pressure gradient', 'abrev': 'PG', 'scale': self.scale_factor,
                   'norm': self.pg_normalization_factor},
            'pg2': {'list': [], 'name': 'computed pressure tent gradient', 'abrev': 'PTG', 'scale': self.scale_factor,
                    'norm': self.pg_normalization_factor},
        }
        if self.has_analytic_solution:
            self.listDict.update({
                'u_L2': {'list': [], 'name': 'corrected velocity L2 error', 'abrev': 'CE_L2',
                         'scale': self.scale_factor, 'relative': 'av_norm_L2', 'slist': [],
                         'norm': self.vel_normalization_factor},
                'u2L2': {'list': [], 'name': 'tentative velocity L2 error', 'abrev': 'TE_L2',
                         'scale': self.scale_factor, 'relative': 'av_norm_L2', 'slist': [],
                         'norm': self.vel_normalization_factor},
                'u_L2test': {'list': [], 'name': 'test corrected L2 velocity error', 'abrev': 'TestCE_L2',
                             'scale': self.scale_factor},
                'u2L2test': {'list': [], 'name': 'test tentative L2 velocity error', 'abrev': 'TestTE_L2',
                             'scale': self.scale_factor},
                'u_H1': {'list': [], 'name': 'corrected velocity H1 error', 'abrev': 'CE_H1',
                         'scale': self.scale_factor, 'relative': 'av_norm_H1', 'slist': []},
                'u2H1': {'list': [], 'name': 'tentative velocity H1 error', 'abrev': 'TE_H1',
                         'scale': self.scale_factor, 'relative': 'av_norm_H1', 'slist': []},
                'u_H1test': {'list': [], 'name': 'test corrected H1 velocity error', 'abrev': 'TestCE_H1',
                             'scale': self.scale_factor},
                'u2H1test': {'list': [], 'name': 'test tentative H1 velocity error', 'abrev': 'TestTE_H1',
                             'scale': self.scale_factor},
                'apg': {'list': [], 'name': 'analytic pressure gradient', 'abrev': 'APG', 'scale': self.scale_factor,
                        'norm': self.pg_normalization_factor},
                'av_norm_L2': {'list': [], 'name': 'analytic velocity L2 norm', 'abrev': 'AVN_L2'},
                'av_norm_H1': {'list': [], 'name': 'analytic velocity H1 norm', 'abrev': 'AVN_H1'},
                'ap_norm': {'list': [], 'name': 'analytic pressure norm', 'abrev': 'APN'},
                'p': {'list': [], 'name': 'pressure L2(0) error', 'abrev': 'PE', 'scale': self.scale_factor,
                      'slist': [], 'norm': self.p_normalization_factor},
                'pgE': {'list': [], 'name': 'computed pressure gradient error', 'abrev': 'PGE',
                        'scale': self.scale_factor, 'norm': self.pg_normalization_factor, 'slist': []},
                'pgEA': {'list': [], 'name': 'computed absolute pressure gradient error', 'abrev': 'PGEA',
                         'scale': self.scale_factor, 'norm': self.pg_normalization_factor},
                'p2': {'list': [], 'name': 'pressure tent L2(0) error', 'abrev': 'PTE', 'scale': self.scale_factor,
                       'slist': [], 'norm': self.p_normalization_factor},
                'pgE2': {'list': [], 'name': 'computed tent pressure tent gradient error', 'abrev': 'PTGE',
                         'scale': self.scale_factor, 'norm': self.pg_normalization_factor, 'slist': []},
                'pgEA2': {'list': [], 'name': 'computed absolute pressure tent gradient error',
                          'abrev': 'PTGEA', 'scale': self.scale_factor, 'norm': self.pg_normalization_factor}
            })

        # parse arguments
        self.nu = 0.0  # nu should be specified in subclass (fixed or by argument)
        self.onset = args.onset
        self.onset_factor = 0
        # saving options:
        self.doSave = False
        self.saveOnlyVel = False
        self.doSaveDiff = False
        self.save_nth = args.saventh
        option = args.save
        if option == 'doSave' or option == 'diff' or option == 'only_vel':
            self.doSave = True
            if option == 'diff':
                self.doSaveDiff = True
                info('Saving velocity differences.')
            if option == 'only_vel':
                self.saveOnlyVel = True
                info('Saving only velocity profiles.')
            info('Saving solution ON.')
        elif option == 'noSave':
            self.doSave = False
            info('Saving solution OFF.')
        # error control options:
        self.doErrControl = None
        self.testErrControl = False
        if not self.has_analytic_solution:
            self.doErrControl = False
        elif args.error == "noEC":
            self.doErrControl = False
            info("Error control OFF")
        else:
            self.doErrControl = True
            if args.error == "test":
                self.testErrControl = True
                info("Error control in testing mode")
            else:
                info("Error control ON")

        # set directory for results and reports
        self.str_dir_name = "%s_%s_results" % (self.problem_code, metadata['name'])
        self.metadata['dir'] = self.str_dir_name
        # create directory, needed because of using "with open(..." construction later
        if not os.path.exists(self.str_dir_name) and self.MPI_rank == 0:
            os.mkdir(self.str_dir_name)

    @staticmethod
    def setup_parser_options(parser):
        parser.add_argument('-S', '--save', help='Save solution mode', choices=['doSave', 'noSave', 'diff', 'only_vel'],
                            default='noSave')
        #   doSave: create .xdmf files with velocity, pressure, divergence
        #   diff: save also difference vel-sol
        #   noSave: do not create .xdmf files with velocity, pressure, divergence
        parser.add_argument('--saventh', help='save only n-th step in first cycle', type=int, default=1)
        parser.add_argument('--ST', help='save modes',
                            choices=['min', 'peak', 'no_restriction'], default='no_restriction')
        # stronger than --saventh, to be used instead of it
        parser.add_argument('--onset', help='boundary condition onset length', type=float, default=0.5)
        parser.add_argument('--wss', help='compute wall shear stress', choices=['none', 'all', 'peak'], default='none')
        # --wss is independent of -S options
        parser.add_argument('--wss_method', help='compute wall shear stress', choices=['expression', 'integral'],
                            default='integral')
        # 'expression' works with BoundaryMesh object. It restricts stress to boundary mesh and then computes CG,1
        # vector WSS and its magnitude from values on boundary
        # expression does not work for too many processors (24 procs for 'HYK' is OK, 48 is too much), because the case
        # when some parallel process have no boundary mesh is not implemented in FEniCS
        # 'integral' works always. It computes facet average wss norm using assemble() to integrate over boundary
        #  facets. Results for each cell are stored in DG,0 scalar function (interior cells will have value 0, because
        # only exterior facet integrals are computed)
        # Note: it does not make sense to project the result into any other space, because zeros inside domain would
        # interfere with values on boundary
        parser.add_argument('--debug_rot', help='save more information about rotation correction', action='store_true')
        # idea was to save rotational correction contribution to velocity RHS
        # NT results are not reliable. One would need to project nu*div(v) into Q space before computing gradient.
        # NT not documented in readme
        parser.add_argument('-e', '--error', help='Error control mode', choices=['doEC', 'noEC', 'test'],
                            default='doEC')
        # compute error using analytic solution (if available)
        # 'test' mode computes errors using both assemble() and errornorm() to check if the results are equal

    @staticmethod
    def loadMesh(mesh):
        """
        :param mesh: name of mesh file (without extension)
        :return: tuple mesh, facet function read from .hdf5 file
        """
        f = HDF5File(mpi_comm_world(), 'meshes/'+mesh+'.hdf5', 'r')
        mesh = Mesh()
        f.read(mesh, 'mesh', False)
        facet_function = MeshFunction("size_t", mesh)
        f.read(facet_function, 'facet_function')
        return mesh, facet_function

    def initialize(self, V, Q, PS, D):
        """
        :param V: velocity space
        :param Q: pressure space
        :param PS: scalar space of same order as V, used for analytic solution generation
        :param D: divergence of velocity space
        """
        self.vSpace = V
        self.divSpace = D
        self.pSpace = Q
        self.solutionSpace = V
        self.vFunction = Function(V)
        self.divFunction = Function(D)
        self.pFunction = Function(Q)

        # self.pgSpace = VectorFunctionSpace(mesh, "DG", 0)    # used to save pressure gradient as vectors
        # self.pgFunction = Function(self.pgSpace)
        self.initialize_xdmf_files()
        self.stepsInCycle = self.cycle_length / self.metadata['dt']
        info('stepsInCycle = %f' % self.stepsInCycle)

        if self.args.wss != 'none':
            self.tc.start('WSSinit')
            if self.args.wss_method == 'expression':
                self.T = TensorFunctionSpace(self.mesh, 'Lagrange', 1)
                info('Generating boundary mesh')
                self.wall_mesh = BoundaryMesh(self.mesh, 'exterior')
                self.wall_mesh_oriented = BoundaryMesh(self.mesh, 'exterior', order=False)
                info('  Boundary mesh geometric dim: %d' % self.wall_mesh.geometry().dim())
                info('  Boundary mesh topologic dim: %d' % self.wall_mesh.topology().dim())
                self.Tb = TensorFunctionSpace(self.wall_mesh, 'Lagrange', 1)
                self.Vb = VectorFunctionSpace(self.wall_mesh, 'Lagrange', 1)
                info('Generating normal to boundary')
                normal_expr = self.NormalExpression(self.wall_mesh_oriented)
                Vn = VectorFunctionSpace(self.wall_mesh, 'DG', 0)
                self.nb = project(normal_expr, Vn)
                self.Sb = FunctionSpace(self.wall_mesh, 'DG', 0)

            if self.args.wss_method == 'integral':
                self.SDG = FunctionSpace(self.mesh, 'DG', 0)

            self.tc.end('WSSinit')

    def initialize_xdmf_files(self):
        info('  Initializing output files.')
        # for creating paraview scripts
        self.metadata['filename_base'] = self.problem_code + '_' + self.metadata['name']

        # assemble file dictionary
        if self.doSave:
            if self.doSaveDiff:
                self.fileDict.update(self.fileDictDiff)
            if self.metadata['hasTentativeV']:
                self.fileDict.update(self.fileDictTent)
                if self.doSaveDiff:
                    self.fileDict.update(self.fileDictTentDiff)
            if self.metadata['hasTentativeP']:
                self.fileDict.update(self.fileDictTentP)
                if self.doSaveDiff:
                    self.fileDict.update(self.fileDictTentPDiff)
        else:
            self.fileDict = {}
        if self.args.wss != 'none':
            self.fileDict.update(self.fileDictWSSnorm)
            if self.args.wss_method == 'expression':
                self.fileDict.update(self.fileDictWSS)
        if self.args.debug_rot:
            self.fileDict.update(self.fileDictDebugRot)
        # create and setup files
        for key, value in self.fileDict.iteritems():
            value['file'] = XDMFFile(mpi_comm_world(), self.str_dir_name + "/" + self.problem_code + '_' +
                                     self.metadata['name'] + value['name'] + ".xdmf")
            value['file'].parameters['rewrite_function_mesh'] = False  # save mesh only once per file

    # method for saving divergence (ensuring, that it will be one time line in ParaView)
    def save_div(self, is_tent, field):
        self.tc.start('div')
        self.divFunction.assign(project(div(field), self.divSpace))
        self.fileDict['d2' if is_tent else 'd']['file'].write(self.divFunction, self.actual_time)
        self.tc.end('div')

    def compute_div(self, is_tent, velocity):
        self.tc.start('divNorm')
        div_list = self.listDict['d2' if is_tent else 'd']['list']
        div_list.append(norm(velocity, 'Hdiv0'))
        self.tc.end('divNorm')

    # method for saving velocity (ensuring, that it will be one time line in ParaView)
    def save_vel(self, is_tent, field):
        self.vFunction.assign(field)
        self.fileDict['u2' if is_tent else 'u']['file'].write(self.vFunction, self.actual_time)
        if self.doSaveDiff:
            self.vFunction.assign((1.0 / self.vel_normalization_factor[0]) * (field - self.solution))
            self.fileDict['u2D' if is_tent else 'uD']['file'].write(self.vFunction, self.actual_time)

    def compute_err(self, is_tent, velocity, t):
        if self.doErrControl:
            er_list_L2 = self.listDict['u2L2' if is_tent else 'u_L2']['list']
            er_list_H1 = self.listDict['u2H1' if is_tent else 'u_H1']['list']
            self.tc.start('errorV')
            # assemble is faster than errornorm
            errorL2_sq = assemble(inner(velocity - self.solution, velocity - self.solution) * dx)
            errorH1seminorm_sq = assemble(inner(grad(velocity - self.solution), grad(velocity - self.solution)) * dx)
            info('  H1 seminorm error: %f' % sqrt(errorH1seminorm_sq))
            errorL2 = sqrt(errorL2_sq)
            errorH1 = sqrt(errorL2_sq + errorH1seminorm_sq)
            info("  Relative L2 error in velocity = %f" % (errorL2 / self.analytic_v_norm_L2))
            self.last_error = errorH1 / self.analytic_v_norm_H1
            self.last_status_functional = self.last_error
            info("  Relative H1 error in velocity = %f" % self.last_error)
            er_list_L2.append(errorL2)
            er_list_H1.append(errorH1)
            self.tc.end('errorV')
            if self.testErrControl:
                er_list_test_H1 = self.listDict['u2H1test' if is_tent else 'u_H1test']['list']
                er_list_test_L2 = self.listDict['u2L2test' if is_tent else 'u_L2test']['list']
                self.tc.start('errorVtest')
                er_list_test_L2.append(errornorm(velocity, self.solution, norm_type='L2', degree_rise=0))
                er_list_test_H1.append(errornorm(velocity, self.solution, norm_type='H1', degree_rise=0))
                self.tc.end('errorVtest')
            # stopping criteria for detecting diverging solution
            if self.last_error > self.divergence_treshold:
                raise RuntimeError('STOPPED: Failed divergence test!')

    def averaging_pressure(self, pressure):
        """
        :param pressure: average is subtracted from it
        """
        self.tc.start('averageP')
        # averaging pressure (subtract average)
        p_average = assemble((1.0 / self.mesh_volume) * pressure * dx)
        info('Average pressure: %f' % p_average)
        p_average_function = interpolate(Expression("p", p=p_average, degree=1), self.pSpace)
        pressure.assign(pressure - p_average_function)
        self.tc.end('averageP')

    def save_pressure(self, is_tent, pressure):
        self.tc.start('saveP')
        self.fileDict['p2' if is_tent else 'p']['file'].write(pressure, self.actual_time)
        # NT normalisation factor defined only in Womersley
        # pg = project((1.0 / self.pg_normalization_factor[0]) * grad(pressure), self.pgSpace)
        # self.pgFunction.assign(pg)
        # self.fileDict['pg2' if is_tent else 'pg'][0].write(self.pgFunction, self.actual_time
        self.tc.end('saveP')

    def get_boundary_conditions(self, use_pressure_BC, v_space, p_space):
        info('get_boundary_conditions() for this problem was not properly implemented.')

    def get_initial_conditions(self, function_list):
        """
        :param function_list: [{'type': 'v'/'p', 'time':-0.1},...]
        :return: velocities and pressures in selected times
        """
        info('get_initial_conditions for this problem was not properly implemented.')

    def get_v_solution(self, t):
        pass

    def get_p_solution(self, t):
        pass

    def update_time(self, actual_time, step_number):
        info('GS_UPDATE_TIME t = %f, step %d' % (actual_time, step_number))
        self.actual_time = actual_time
        self.step_number = step_number
        self.time_list.append(self.actual_time)
        if self.onset < 0.001 or self.actual_time > self.onset:  # TODO onset time relative to cycle_length
            self.onset_factor = 1.
        else:
            self.onset_factor = (1. - cos(pi * actual_time / self.onset))*0.5
        info('GS_UPDATE_TIME Onset factor: %f' % self.onset_factor)

        # Manage saving choices for this step
        # save only n-th step in first second

        dec, i = modf(actual_time / self.cycle_length)
        i = int(i)
        info('GS_UPDATE_TIME Cycles done: %d' % i)
        info('GS_UPDATE_TIME Relative part of cycle: %f' % dec)
        dec *= self.cycle_length
        info('GS_UPDATE_TIME Absolute time in  cycle: %f' % dec)
        self.distance_from_chosen_steps = min([abs(dec - d) for d in self.chosen_steps])
        # TODO use self.close_to_chosen_steps
        info('GS_UPDATE_TIME Distance from chosen steps: %f' % self.distance_from_chosen_steps)
        info('GS_UPDATE_TIME Distance threshold: %f' % (0.5 * self.metadata['dt'] + DOLFIN_EPS))

        if self.doSave:
            if self.args.ST == 'min':
                self.save_this_step = (i >= 1 and (self.distance_from_chosen_steps < 0.5 * self.metadata['dt'] + DOLFIN_EPS))
                # QQ might skip step? change 0.5 to 0.51?
            elif self.args.ST == 'peak':
                self.save_this_step = (i >= 1 and 0.1 < dec < 0.20001)  # TODO relative to cycle_length
            elif self.save_nth == 1 or i >= 1 or self.step_number % self.save_nth == 0:
                self.save_this_step = True
            else:
                self.save_this_step = False
            if self.save_this_step:
                info('GS_UPDATE_TIME Chose to save this step: (%f, %d)' % (actual_time, step_number))

    # this generates vector expression of normal on boundary mesh
    # for right orientation use BoundaryMesh(..., order=False)
    # IMP this does not work if some parallel process have no boundary mesh, because that is not implemented in FEniCS
    class NormalExpression(Expression):
        """ This generates vector Expression of normal on boundary mesh.
        For right outward orientation use BoundaryMesh(mesh, 'exterior', order=False)  """
        def __init__(self, mesh):
            self.mesh = mesh
            self.gdim = mesh.geometry().dim()

        def eval_cell(self, values, x, ufc_cell):
            cell = Cell(self.mesh, ufc_cell.index)
            v=cell.get_vertex_coordinates().reshape((-1, self.gdim))
            vec1 = v[1] - v[0]   # create vectors by subtracting coordinates of vertices
            vec2 = v[2] - v[0]
            n = np.cross(vec1, vec2)
            n /= np.linalg.norm(n)
            values[0] = n[0]
            values[1] = n[1]
            values[2] = n[2]

        def value_shape(self):
            return 3,

    def compute_functionals(self, velocity, pressure, t, step):
        if self.args.wss == 'all' or \
                (step >= self.stepsInCycle and self.args.wss == 'peak' and
                 (self.distance_from_chosen_steps < 0.5 * self.metadata['dt'])):
            # TODO check if choosing time steps works properly
            # QQ might skip step? change 0.5 to 0.51?
            self.tc.start('WSS')
            begin('WSS (%dth step)' % step)
            if self.args.wss_method == 'expression':
                stress = project(self.nu*2*sym(grad(velocity)), self.T)
                # pressure is not used as it contributes only to the normal component
                stress.set_allow_extrapolation(True)   # need because of some inaccuracies in BoundaryMesh coordinates
                stress_b = interpolate(stress, self.Tb)    # restrict stress to boundary mesh
                # self.fileDict['stress']['file'].write(stress_b, self.actual_time)
                # info('Saved stress tensor')
                info('Computing WSS')
                wss = dot(stress_b, self.nb) - inner(dot(stress_b, self.nb), self.nb)*self.nb
                wss_func = project(wss, self.Vb)
                wss_norm = project(sqrt_ufl(inner(wss, wss)), self.Sb)
                info('Saving WSS')
                self.fileDict['wss']['file'].write(wss_func, self.actual_time)
                self.fileDict['wss_norm']['file'].write(wss_norm, self.actual_time)
            if self.args.wss_method == 'integral':
                wss_norm = Function(self.SDG)
                mS = TestFunction(self.SDG)
                scaling = 1/FacetArea(self.mesh)
                stress = self.nu*2*sym(grad(velocity))
                wss = dot(stress, self.normal) - inner(dot(stress, self.normal), self.normal)*self.normal
                wss_norm_form = scaling*mS*sqrt_ufl(inner(wss, wss))*ds   # ds is integral over exterior facets only
                assemble(wss_norm_form, tensor=wss_norm.vector())
                self.fileDict['wss_norm']['file'].write(wss_norm, self.actual_time)

                # to get vector WSS values:
                # NT this works, but in ParaView for (DG,1)-vector space glyphs are displayed in cell centers
                # wss_vector = []
                # for i in range(3):
                #     wss_component = Function(self.SDG)
                #     wss_vector_form = scaling*wss[i]*mS*ds
                #     assemble(wss_vector_form, tensor=wss_component.vector())
                #     wss_vector.append(wss_component)
                # wss_func = project(as_vector(wss_vector), self.VDG)
                # self.fileDict['wss']['file'].write(wss_func, self.actual_time)
            self.tc.end('WSS')
            end()

    def compute_outflow(self, velocity):
        out = assemble(inner(velocity, self.normal)*self.get_outflow_measure_form())
        return out

    def get_outflow_measures(self):
        """
        return list of Measure objects for outflow boundary parts
        must be overridden in Problem before use (if needed)
        """
        info('Integration over outflow for this problem was not properly implemented.')

    def get_outflow_measure_form(self):
        """
        returns term (dSout1 + ... + dSoutN) that can be used in form to integrate over all outflow boundary parts
        works if get_outflow_measures is implemented
        """
        if not self.outflow_measures:
            self.outflow_measures = self.get_outflow_measures()
        if len(self.outflow_measures) == 1:
            return self.outflow_measures[0]
        else:
            out = self.outflow_measures[0]
            for m in self.outflow_measures[1:]:
                out += m
            return out

    def get_metadata_to_save(self):
        return str(cPickle.dumps(self.metadata))#.replace('\n', '$')

    # noinspection PyTypeChecker
    def report(self):
        total = toc()
        md = self.metadata

        # compare errors measured by assemble and errornorm
        # TODO implement generally (if listDict[fcional]['testable'])
        # if self.testErrControl:
        #     for e in [[self.listDict['u_L2']['list'], self.listDict['u_L2test']['list'], 'L2'],
        #               [self.listDict['u_H1']['list'], self.listDict['u_H1test']['list'], 'H1']]:
        #         print('test ', e[2], sum([abs(e[0][i]-e[1][i]) for i in range(len(self.time_list))]))

        # report error norms, norms of divergence etc. for individual time steps
        with open(self.str_dir_name + "/report_time_lines.csv", 'w') as reportFile:
            report_writer = csv.writer(reportFile, delimiter=';', escapechar='\\', quoting=csv.QUOTE_NONE)
            # report_writer.writerow(self.problem.get_metadata_to_save())
            report_writer.writerow(["name", "what", "time"] + self.time_list)
            keys = sorted(self.listDict.keys())
            for key in keys:
                l = self.listDict[key]
                if l['list']:
                    abrev = l['abrev']
                    report_writer.writerow([md['name'], l['name'], abrev] + l['list'])
                    if 'scale' in l:
                        temp_list = [i/l['scale'][0] for i in l['list']]
                        report_writer.writerow([md['name'], "scaled " + l['name'], abrev+"s"] + temp_list +
                                               ['scale factor:' + str(l['scale'])])
                    if 'norm' in l:
                        if l['norm']:
                            temp_list = [i/l['norm'][0] for i in l['list']]
                            report_writer.writerow([md['name'], "normalized " + l['name'], abrev+"n"] + temp_list)
                        else:
                            info('Norm missing:' + str(l))
                            l['normalized_list_sec'] = []
                    if 'relative' in l:
                        norm_list = self.listDict[l['relative']]['list']
                        temp_list = [l['list'][i]/norm_list[i] for i in range(0, len(l['list']))]
                        self.listDict[key]['relative_list'] = temp_list
                        report_writer.writerow([md['name'], "relative " + l['name'], abrev+"r"] + temp_list)

        # report error norms, norms of divergence etc. averaged over cycles
        # IFNEED rewrite to averaging over cycles (must track number of steps in cycle, it can be different for each cycle)
        # NT for cases when cycle_length % dt != 0 will be inacurate (less with smaller time steps)
        # note: self.stepsInCycle is float

        # if self.second_list:
        #     with open(self.str_dir_name + "/report_seconds.csv", 'w') as reportFile:
        #         report_writer = csv.writer(reportFile, delimiter=';', escapechar='|', quoting=csv.QUOTE_NONE)
        #         report_writer.writerow(["name", "what", "time"] + self.second_list)
        #         keys = sorted(self.listDict.keys())
        #         for key in keys:
        #             l = self.listDict[key]
        #             if 'slist' in l and l['list']:
        #                 abrev = l['abrev']
        #                 values = l['slist']
        #                 # generate averages over seconds from list
        #                 for sec in self.second_list:
        #                     N0 = (sec-1)*self.stepsInCycle
        #                     N1 = sec*self.stepsInCycle
        #                     values.append(sqrt(sum([i*i for i in l['list'][N0:N1]]) / float(self.stepsInCycle)))
        #
        #                 report_writer.writerow([md['name'], l['name'], abrev] + values)
        #                 if 'scale' in l:
        #                     temp_list = [i/l['scale'][0] for i in values]
        #                     report_writer.writerow([md['name'], "scaled " + l['name'], abrev+"s"] + temp_list)
        #                 if 'norm' in l:
        #                     if l['norm']:
        #                         temp_list = [i/l['norm'][0] for i in values]
        #                         l['normalized_list_sec'] = temp_list
        #                         report_writer.writerow([md['name'], "normalized " + l['name'], abrev+"n"] + temp_list)
        #                     else:
        #                         info('Norm missing:' + str(l))
        #                         l['normalized_list_sec'] = []
        #                 if 'relative_list' in l:
        #                     temp_list = []
        #                     for sec in self.second_list:
        #                         N0 = (sec-1)*self.stepsInCycle
        #                         N1 = sec*self.stepsInCycle
        #                         temp_list.append(sqrt(sum([i*i for i in l['relative_list'][N0:N1]]) / float(self.stepsInCycle)))
        #                     l['relative_list_sec'] = temp_list
        #                     report_writer.writerow([md['name'], "relative " + l['name'], abrev+"r"] + temp_list)

        header_row = ["name", "totalTimeHours"]
        data_row = [md['name'], total / 3600.0]
        for key in ['u_L2', 'u_H1', 'u_H1w', 'p', 'u2L2', 'u2H1', 'u2H1w', 'p2', 'pgE', 'pgE2', 'd', 'd2', 'force_wall']:
            if key in self.listDict:
                l = self.listDict[key]
                header_row += ['last_cycle_'+l['abrev']]
                data_row += [l['slist'][-1]] if l['slist'] else [0]
                if 'relative_list_sec' in l and l['relative_list_sec']:
                    header_row += ['last_cycle_'+l['abrev']+'r']
                    data_row += [l['relative_list_sec'][-1]]
                elif key in ['p', 'p2']:
                    header_row += ['last_cycle_'+l['abrev']+'n']
                    data_row += [l['normalized_list_sec'][-1]] if 'normalized_list_sec' in l \
                                                                  and l['normalized_list_sec'] else [0]

        # save metadata. Can be loaded and used in postprocessing
        with open(self.str_dir_name + "/metadata", 'w') as reportFile:
            reportFile.write(self.get_metadata_to_save())

        # report without header
        with open(self.str_dir_name + "/report.csv", 'w') as reportFile:
            report_writer = csv.writer(reportFile, delimiter=';', escapechar='|', quoting=csv.QUOTE_NONE)
            report_writer.writerow(data_row)

        # report with header
        with open(self.str_dir_name + "/report_h.csv", 'w') as reportFile:
            report_writer = csv.writer(reportFile, delimiter=';', escapechar='|', quoting=csv.QUOTE_NONE)
            report_writer.writerow(header_row)
            report_writer.writerow(data_row)

        # report time cotrol
        with open(self.str_dir_name + "/report_timecontrol.csv", 'w') as reportFile:
            self.tc.report(reportFile, self.metadata['name'])

        self.remove_status_file()

        # create file showing all was done well
        f = open(md['name'] + "_OK.report", "w")
        f.close()

    def report_fail(self, t):
        print("Runtime error:", sys.exc_info()[1])
        print("Traceback:")
        traceback.print_tb(sys.exc_info()[2])
        f = open(self.metadata['name'] + "_failed_at_%5.3f.report" % t, "w")
        f.write(traceback.format_exc())
        f.close()
        self.remove_status_file()

    def write_status_file(self, t):
        self.tc.start('status')
        f = open(self.metadata['name'] + ".run", "w")
        progress = t/self.metadata['time']
        f.write('t = %5.3f (dt=%f)\nprogress = %3.0f %%\n%s = %5.3f\n' %
                (t, self.metadata['dt'], 100*progress, self.status_functional_str, self.last_status_functional))
        f.close()
        self.tc.end('status')

    def remove_status_file(self):
        if self.MPI_rank == 0:
            try:
                os.remove(self.metadata['name'] + ".run")
            except OSError:
                info('.run file probably not created')
Пример #38
0
class Problem(gp.GeneralProblem):
    def __init__(self, args, tc, metadata):
        self.has_analytic_solution = True
        self.problem_code = 'WCYL'
        super(Problem, self).__init__(args, tc, metadata)

        self.tc.init_watch('assembleSol', 'Assembled analytic solution', True)
        self.tc.init_watch('analyticP', 'Analytic pressure', True)
        self.tc.init_watch('analyticVnorms',
                           'Computed analytic velocity norms', True)
        self.tc.init_watch('errorP', 'Computed pressure error', True)
        self.tc.init_watch('errorForce', 'Computed force error', True)
        self.tc.init_watch('computePG', 'Computed pressure gradient', True)

        self.name = 'womersley_cylinder'
        self.status_functional_str = 'last H1 velocity error'

        # input parameters
        self.ic = args.ic
        self.factor = args.factor
        self.metadata['factor'] = self.factor
        self.scale_factor.append(self.factor)

        # fixed parameters (used in analytic solution and in BC)
        self.nu = 3.71 * self.args.nufactor  # kinematic viscosity
        self.R = 5.0  # cylinder radius

        self.mesh_volume = pi * 25. * 20.

        # Import gmsh mesh
        self.tc.start('mesh')
        self.mesh, self.facet_function = super(Problem,
                                               self).loadMesh(args.mesh)
        self.dsIn = Measure("ds",
                            subdomain_id=2,
                            subdomain_data=self.facet_function)
        self.dsOut = Measure("ds",
                             subdomain_id=3,
                             subdomain_data=self.facet_function)
        self.dsWall = Measure("ds",
                              subdomain_id=1,
                              subdomain_data=self.facet_function)
        self.normal = FacetNormal(self.mesh)
        print("Mesh name: ", args.mesh, "    ", self.mesh)
        print("Mesh norm max: ", self.mesh.hmax())
        print("Mesh norm min: ", self.mesh.hmin())
        self.tc.end('mesh')

        self.sol_p = None
        self.last_analytic_pressure_norm = None
        self.v_in = None
        self.area = None

        choose_note = {1.0: '', 0.1: 'nuL10', 0.01: 'nuL100', 10.0: 'nuH10'}
        self.precomputed_filename = args.mesh + choose_note[self.args.nufactor]
        print('chosen filename for precomputed solution',
              self.precomputed_filename)

        # partial Bessel functions and coefficients
        self.bessel_parabolic = None
        self.bessel_real = []
        self.bessel_complex = []
        self.coefs_exp = [-8, -6, -4, -2, 2, 4, 6, 8]

        self.listDict.update({
            'u_H1w': {
                'list': [],
                'name': 'corrected velocity H1 error on wall',
                'abrev': 'CE_H1w',
                'scale': self.scale_factor,
                'relative': 'av_norm_H1w',
                'slist': []
            },
            'u2H1w': {
                'list': [],
                'name': 'tentative velocity H1 error on wall',
                'abrev': 'TE_H1w',
                'scale': self.scale_factor,
                'relative': 'av_norm_H1w',
                'slist': []
            },
            'av_norm_H1w': {
                'list': [],
                'name': 'analytic velocity H1 norm on wall',
                'abrev': 'AVN_H1w'
            },
            'a_force_wall': {
                'list': [],
                'name': 'analytic force on wall',
                'abrev': 'AF'
            },
            'a_force_wall_normal': {
                'list': [],
                'name': 'analytic force on wall',
                'abrev': 'AFN'
            },
            'a_force_wall_shear': {
                'list': [],
                'name': 'analytic force on wall',
                'abrev': 'AFS'
            },
            'force_wall': {
                'list': [],
                'name': 'force error on wall',
                'abrev': 'FE',
                'relative': 'a_force_wall',
                'slist': []
            },
            'force_wall_normal': {
                'list': [],
                'name': 'normal force error on wall',
                'abrev': 'FNE',
                'relative': 'a_force_wall',
                'slist': []
            },
            'force_wall_shear': {
                'list': [],
                'name': 'shear force error on wall',
                'abrev': 'FSE',
                'relative': 'a_force_wall',
                'slist': []
            },
        })

    def __str__(self):
        return 'womersley flow in cylinder'

    @staticmethod
    def setup_parser_options(parser):
        super(Problem, Problem).setup_parser_options(parser)
        parser.add_argument('--ic',
                            help='Initial condition',
                            choices=['zero', 'correct'],
                            default='zero')
        parser.add_argument('-F',
                            '--factor',
                            help='Velocity scale factor',
                            type=float,
                            default=1.0)
        parser.add_argument('--nufactor',
                            help='kinematic viscosity factor',
                            type=float,
                            default=1.0)

    def initialize(self, V, Q, PS, D):
        super(Problem, self).initialize(V, Q, PS, D)

        print("IC type: " + self.ic)
        print("Velocity scale factor = %4.2f" % self.factor)
        reynolds = 728.761 * self.factor / self.args.nufactor
        print("Computing with Re = %f" % reynolds)

        self.v_in = Function(V)
        print('Initializing error control')
        self.load_precomputed_bessel_functions(PS)

        self.solution = self.assemble_solution(0.0)

        # set constants for
        self.area = assemble(
            interpolate(Expression("1.0", degree=1), Q) *
            self.dsIn)  # inflow area

        # womersley = steady + e^iCt, e^iCt has average 0
        self.pg_normalization_factor.append(
            womersleyBC.average_analytic_pressure_grad(self.factor))
        self.p_normalization_factor.append(
            norm(interpolate(
                womersleyBC.average_analytic_pressure_expr(self.factor),
                self.pSpace),
                 norm_type='L2'))
        self.vel_normalization_factor.append(
            norm(interpolate(
                womersleyBC.average_analytic_velocity_expr(self.factor),
                self.vSpace),
                 norm_type='L2'))

        one = (interpolate(Expression('1.0', degree=1), Q))
        self.outflow_area = assemble(one * self.dsOut)
        print('Outflow area:', self.outflow_area)

    def get_boundary_conditions(self, use_pressure_BC, v_space, p_space):
        # boundary parts: 1 walls, 2 inflow, 3 outflow
        bc0 = DirichletBC(v_space, (0.0, 0.0, 0.0), self.facet_function,
                          1)  # no-slip
        inflow = DirichletBC(v_space, self.v_in, self.facet_function, 2)
        bcu = [inflow, bc0]
        bcp = []
        if use_pressure_BC:
            outflow = DirichletBC(p_space, 0.0, self.facet_function, 3)
            bcp = [outflow]
        return bcu, bcp

    def get_initial_conditions(self, function_list):
        out = []
        for d in function_list:
            if d['type'] == 'v':
                f = Function(self.vSpace)
                if self.ic == 'correct':
                    f = self.assemble_solution(d['time'])
            if d['type'] == 'p':
                f = Function(self.pSpace)
                if self.ic == 'correct':
                    f = interpolate(
                        womersleyBC.analytic_pressure(self.factor, d['time']),
                        self.pSpace)
            out.append(f)
        return out

    def get_outflow_measures(self):
        return [self.dsOut]

    def get_v_solution(self, t):
        v = self.assemble_solution(t)
        return v

    def get_p_solution(self, t):
        p = interpolate(womersleyBC.analytic_pressure(self.factor, t),
                        self.pSpace)
        return p

    def update_time(self, actual_time, step_number):
        super(Problem, self).update_time(actual_time, step_number)
        if self.actual_time > 0.5 and abs(
                math.modf(actual_time)[0]) < 0.5 * self.metadata['dt']:
            self.second_list.append(int(round(self.actual_time)))

        self.solution = self.assemble_solution(self.actual_time)

        # Update boundary condition
        self.tc.start('updateBC')
        self.v_in.assign(self.onset_factor * self.solution)
        self.tc.end('updateBC')

        # construct analytic pressure (used for computing pressure and force errors)
        self.tc.start('analyticP')
        analytic_pressure = womersleyBC.analytic_pressure(
            self.factor, self.actual_time)
        self.sol_p = interpolate(analytic_pressure, self.pSpace)
        self.tc.end('analyticP')

        self.tc.start('analyticVnorms')
        self.analytic_v_norm_L2 = norm(self.solution, norm_type='L2')
        self.analytic_v_norm_H1 = norm(self.solution, norm_type='H1')
        self.analytic_v_norm_H1w = sqrt(
            assemble((inner(grad(self.solution), grad(self.solution)) +
                      inner(self.solution, self.solution)) * self.dsWall))
        self.listDict['av_norm_L2']['list'].append(self.analytic_v_norm_L2)
        self.listDict['av_norm_H1']['list'].append(self.analytic_v_norm_H1)
        self.listDict['av_norm_H1w']['list'].append(self.analytic_v_norm_H1w)
        self.tc.end('analyticVnorms')

    def assemble_solution(self, t):  # returns
        """
        :param t: time
        :return: Womersley flow (analytic solution) at time t
        analytic solution at any time is a steady parabolic flow + linear combination of 8 modes
        modes were precomputed as 8 functions on given mesh and stored in hdf5 file
        """
        if self.tc is not None:
            self.tc.start('assembleSol')
        sol = Function(self.solutionSpace)
        # analytic solution has zero x and y components
        dofs2 = self.solutionSpace.sub(2).dofmap().dofs(
        )  # gives field of indices corresponding to z axis
        sol.assign(Constant(("0.0", "0.0", "0.0")))  # QQ not needed
        sol.vector()[dofs2] += self.factor * self.bessel_parabolic.vector(
        ).array()  # parabolic part of sol
        for idx in range(8):  # add modes of Womersley sol
            sol.vector()[dofs2] += self.factor * cos(
                self.coefs_exp[idx] * pi *
                t) * self.bessel_real[idx].vector().array()
            sol.vector()[dofs2] += self.factor * -sin(
                self.coefs_exp[idx] * pi *
                t) * self.bessel_complex[idx].vector().array()
        if self.tc is not None:
            self.tc.end('assembleSol')
        return sol

    def load_precomputed_bessel_functions(self, PS):
        """ loads precomputed Bessel functions (modes of analytic solution) """
        f = HDF5File(
            mpi_comm_world(),
            'precomputed/precomputed_' + self.precomputed_filename + '.hdf5',
            'r')
        temp = toc()
        fce = Function(PS)
        f.read(fce, "parab")
        self.bessel_parabolic = fce.copy(deepcopy=True)
        for i in range(8):
            f.read(fce, "real%d" % i)
            self.bessel_real.append(fce.copy(deepcopy=True))
            f.read(fce, "imag%d" % i)
            self.bessel_complex.append(fce.copy(deepcopy=True))
        print("Loaded partial solution functions. Time: %f" % (toc() - temp))

    def compute_err(self, is_tent, velocity, t):
        super(Problem, self).compute_err(is_tent, velocity, t)
        er_list_H1w = self.listDict['u2H1w' if is_tent else 'u_H1w']['list']
        errorH1wall = sqrt(
            assemble(
                (inner(grad(velocity - self.solution),
                       grad(velocity - self.solution)) +
                 inner(velocity - self.solution, velocity - self.solution)) *
                self.dsWall))
        er_list_H1w.append(errorH1wall)
        print('  Relative H1wall error:',
              errorH1wall / self.analytic_v_norm_H1w)

    def compute_functionals(self, velocity, pressure, t, step):
        super(Problem, self).compute_functionals(velocity, pressure, t, step)
        self.compute_force(velocity, pressure, t)

    def compute_force(self, velocity, pressure, t):
        self.tc.start('errorForce')
        I = Identity(3)  # Identity tensor

        def T(p, v):
            return -p * I + 2.0 * self.nu * sym(grad(v))

        error_force = sqrt(
            assemble(
                inner((T(pressure, velocity) - T(self.sol_p, self.solution)) *
                      self.normal,
                      (T(pressure, velocity) - T(self.sol_p, self.solution)) *
                      self.normal) * self.dsWall))
        an_force = sqrt(
            assemble(
                inner(
                    T(self.sol_p, self.solution) * self.normal,
                    T(self.sol_p, self.solution) * self.normal) * self.dsWall))
        an_f_normal = sqrt(
            assemble(
                inner(
                    inner(
                        T(self.sol_p, self.solution) * self.normal,
                        self.normal),
                    inner(
                        T(self.sol_p, self.solution) * self.normal,
                        self.normal)) * self.dsWall))
        error_f_normal = sqrt(
            assemble(
                inner(
                    inner(
                        (T(self.sol_p, self.solution) - T(pressure, velocity))
                        * self.normal, self.normal),
                    inner(
                        (T(self.sol_p, self.solution) - T(pressure, velocity))
                        * self.normal, self.normal)) * self.dsWall))
        an_f_shear = sqrt(
            assemble(
                inner(
                    (I - outer(self.normal, self.normal)) *
                    T(self.sol_p, self.solution) * self.normal,
                    (I - outer(self.normal, self.normal)) *
                    T(self.sol_p, self.solution) * self.normal) * self.dsWall))
        error_f_shear = sqrt(
            assemble(
                inner((I - outer(self.normal, self.normal)) *
                      (T(self.sol_p, self.solution) - T(pressure, velocity)) *
                      self.normal, (I - outer(self.normal, self.normal)) *
                      (T(self.sol_p, self.solution) - T(pressure, velocity)) *
                      self.normal) * self.dsWall))
        self.listDict['a_force_wall']['list'].append(an_force)
        self.listDict['a_force_wall_normal']['list'].append(an_f_normal)
        self.listDict['a_force_wall_shear']['list'].append(an_f_shear)
        self.listDict['force_wall']['list'].append(error_force)
        self.listDict['force_wall_normal']['list'].append(error_f_normal)
        self.listDict['force_wall_shear']['list'].append(error_f_shear)
        print('  Relative force error:', error_force / an_force)
        self.tc.end('errorForce')

    def save_pressure(self, is_tent, pressure):
        super(Problem, self).save_pressure(is_tent, pressure)
        self.tc.start('computePG')
        # Report pressure gradient
        p_in = assemble((1.0 / self.area) * pressure * self.dsIn)
        p_out = assemble((1.0 / self.area) * pressure * self.dsOut)
        computed_gradient = (p_out - p_in) / 20.0
        # 20.0 is a length of a pipe NT should depend on mesh length (implement through metadata or function of mesh)
        self.tc.end('computePG')
        self.tc.start('analyticP')
        analytic_gradient = womersleyBC.analytic_pressure_grad(
            self.factor, self.actual_time)
        if not is_tent:
            self.last_analytic_pressure_norm = norm(self.sol_p, norm_type='L2')
            self.listDict['ap_norm']['list'].append(
                self.last_analytic_pressure_norm)
        self.tc.end('analyticP')
        self.tc.start('errorP')
        error = errornorm(self.sol_p, pressure, norm_type="l2", degree_rise=0)
        self.listDict['p2' if is_tent else 'p']['list'].append(error)
        print("Normalized pressure error norm:",
              error / self.p_normalization_factor[0])
        self.listDict['pg2' if is_tent else 'pg']['list'].append(
            computed_gradient)
        if not is_tent:
            self.listDict['apg']['list'].append(analytic_gradient)
        self.listDict['pgE2' if is_tent else 'pgE']['list'].append(
            computed_gradient - analytic_gradient)
        self.listDict['pgEA2' if is_tent else 'pgEA']['list'].append(
            abs(computed_gradient - analytic_gradient))
        self.tc.end('errorP')
        if self.doSaveDiff:
            # sol_pg_expr = Expression(("0", "0", "pg"), pg=analytic_gradient / self.pg_normalization_factor[0])
            # sol_pg = interpolate(sol_pg_expr, self.pgSpace)
            # plot(sol_p, title="sol")
            # plot(pressure, title="p")
            # plot(pressure - sol_p, interactive=True, title="diff")
            # exit()
            self.pFunction.assign(pressure - self.sol_p)
            self.fileDict['p2D' if is_tent else 'pD']['file'] << self.pFunction
Пример #39
0
class ObjectiveFunctional(LinearOperator):
    """
    Provides data misfit, gradient and Hessian information for the data misfit
    part of a time-independent symmetric inverse problem.
    """
    __metaclass__ = abc.ABCMeta

    # Instantiation
    def __init__(self, V, Vm, bc, bcadj, \
    RHSinput=[], ObsOp=[], UD=[], Regul=[], Data=[], plot=False, \
    mycomm=None):
        # Define test, trial and all other functions
        self.trial = TrialFunction(V)
        self.test = TestFunction(V)
        self.mtrial = TrialFunction(Vm)
        self.mtest = TestFunction(Vm)
        self.rhs = Function(V)
        self.m = Function(Vm)
        self.mcopy = Function(Vm)
        self.srchdir = Function(Vm)
        self.delta_m = Function(Vm)
        self.MG = Function(Vm)
        self.Grad = Function(Vm)
        self.Gradnorm = 0.0
        self.lenm = len(self.m.vector().array())
        self.u = Function(V)
        self.ud = Function(V)
        self.diff = Function(V)
        self.p = Function(V)
        # Define weak forms to assemble A, C and E
        self._wkforma()
        self._wkformc()
        self._wkforme()
        # Store other info:
        self.ObsOp = ObsOp
        self.UD = UD
        self.reset()    # Initialize U, C and E to []
        self.Data = Data
        self.GN = 1.0   # GN = 0.0 => GN Hessian; = 1.0 => full Hessian
        # Operators and bc
        LinearOperator.__init__(self, self.delta_m.vector(), \
        self.delta_m.vector()) 
        self.bc = bc
        self.bcadj = bcadj
        self._assemble_solverM(Vm)
        self.assemble_A()
        self.assemble_RHS(RHSinput)
        self.Regul = Regul
        # Counters, tolerances and others
        self.nbPDEsolves = 0    # Updated when solve_A called
        self.nbfwdsolves = 0    # Counter for plots
        self.nbadjsolves = 0    # Counter for plots
        self._set_plots(plot)
        # MPI:
        self.mycomm = mycomm
        try:
            self.myrank = MPI.rank(self.mycomm)
        except:
            self.myrank = 0

    def copy(self):
        """Define a copy method"""
        V = self.trial.function_space()
        Vm = self.mtrial.function_space()
        newobj = self.__class__(V, Vm, self.bc, self.bcadj, [], self.ObsOp, \
        self.UD, self.Regul, self.Data, False)
        newobj.RHS = self.RHS
        newobj.update_m(self.m)
        return newobj

    def mult(self, mhat, y):
        """mult(self, mhat, y): do y = Hessian * mhat
        member self.GN sets full Hessian (=1.0) or GN Hessian (=0.0)"""
        N = self.Nbsrc # Number of sources
        y[:] = np.zeros(self.lenm)
        for C, E in zip(self.C, self.E):
            # Solve for uhat
            C.transpmult(mhat, self.rhs.vector())
            self.bcadj.apply(self.rhs.vector())
            self.solve_A(self.u.vector(), -self.rhs.vector())
            # Solve for phat
            E.transpmult(mhat, self.rhs.vector())
            Etmhat = self.rhs.vector().array()
            self.rhs.vector().axpy(1.0, self.ObsOp.incradj(self.u))
            self.bcadj.apply(self.rhs.vector())
            self.solve_A(self.p.vector(), -self.rhs.vector())
            # Compute Hessian*x:
            y.axpy(1.0/N, C * self.p.vector())
            y.axpy(self.GN/N, E * self.u.vector())
        y.axpy(1.0, self.Regul.hessian(mhat))

    # Getters
    def getm(self): return self.m
    def getmarray(self):    return self.m.vector().array()
    def getmcopyarray(self):    return self.mcopy.vector().array()
    def getVm(self):    return self.mtrial.function_space()
    def getMGarray(self):   return self.MG.vector().array()
    def getMGvec(self):   return self.MG.vector()
    def getGradarray(self):   return self.Grad.vector().array()
    def getGradnorm(self):  return self.Gradnorm
    def getsrchdirarray(self):    return self.srchdir.vector().array()
    def getsrchdirvec(self):    return self.srchdir.vector()
    def getsrchdirnorm(self):
        return np.sqrt((self.MM*self.getsrchdirvec()).inner(self.getsrchdirvec()))
    def getgradxdir(self): return self.gradxdir
    def getcost(self):  return self.cost, self.misfit, self.regul
    def getprecond(self):
        Prec = PETScKrylovSolver("richardson", "amg")
        Prec.parameters["maximum_iterations"] = 1
        Prec.parameters["error_on_nonconvergence"] = False
        Prec.parameters["nonzero_initial_guess"] = False
        Prec.set_operator(self.Regul.get_precond())
        return Prec
    def getMass(self):    return self.MM

    # Setters
    def setsrchdir(self, arr):  self.srchdir.vector()[:] = arr
    def setgradxdir(self, valueloc):   
        """Sum all local results for Grad . Srch_dir"""
        try:
            valueglob = MPI.sum(self.mycomm, valueloc)
        except:
            valueglob = valueloc
        self.gradxdir = valueglob

    # Solve
    def solvefwd(self, cost=False):
        """Solve fwd operators for given RHS"""
        self.nbfwdsolves += 1
        if self.ObsOp.noise:    self.noise = 0.0
        if self.plot:
            self.plotu = PlotFenics(self.plotoutdir)
            self.plotu.set_varname('u{0}'.format(self.nbfwdsolves))
        if cost:    self.misfit = 0.0
        for ii, rhs in enumerate(self.RHS):
            self.solve_A(self.u.vector(), rhs)
            if self.plot:   self.plotu.plot_vtk(self.u, ii)
            u_obs, noiselevel = self.ObsOp.obs(self.u)
            self.U.append(u_obs)
            if self.ObsOp.noise:    self.noise += noiselevel
            if cost:
                self.misfit += self.ObsOp.costfct(u_obs, self.UD[ii])
            self.C.append(assemble(self.c))
        if cost:
            self.misfit /= len(self.U)
            self.regul = self.Regul.cost(self.m)
            self.cost = self.misfit + self.regul
        if self.ObsOp.noise and self.myrank == 0:
            print 'Total noise in data misfit={:.5e}\n'.\
            format(self.noise*.5/len(self.U))
            self.ObsOp.noise = False    # Safety
        if self.plot:   self.plotu.gather_vtkplots()

    def solvefwd_cost(self):
        """Solve fwd operators for given RHS and compute cost fct"""
        self.solvefwd(True)

    def solveadj(self, grad=False):
        """Solve adj operators"""
        self.nbadjsolves += 1
        if self.plot:
            self.plotp = PlotFenics(self.plotoutdir)
            self.plotp.set_varname('p{0}'.format(self.nbadjsolves))
        self.Nbsrc = len(self.UD)
        if grad:    self.MG.vector()[:] = np.zeros(self.lenm)
        for ii, C in enumerate(self.C):
            self.ObsOp.assemble_rhsadj(self.U[ii], self.UD[ii], \
            self.rhs, self.bcadj)
            self.solve_A(self.p.vector(), self.rhs.vector())
            if self.plot:   self.plotp.plot_vtk(self.p, ii)
            self.E.append(assemble(self.e))
            if grad:    self.MG.vector().axpy(1.0/self.Nbsrc, \
                        C * self.p.vector())
        if grad:
            self.MG.vector().axpy(1.0, self.Regul.grad(self.m))
            self.solverM.solve(self.Grad.vector(), self.MG.vector())
            self.Gradnorm = np.sqrt(self.Grad.vector().inner(self.MG.vector()))
        if self.plot:   self.plotp.gather_vtkplots()

    def solveadj_constructgrad(self):
        """Solve adj operators and assemble gradient"""
        self.solveadj(True)

    # Assembler
    def assemble_A(self):
        """Assemble operator A(m)"""
        self.A = assemble(self.a)
        self.bc.apply(self.A)
        self.set_solver()

    def solve_A(self, b, f):
        """Solve system of the form A.b = f, 
        with b and f in form to be used in solver."""
        self.solver.solve(b, f)
        self.nbPDEsolves += 1

    def assemble_RHS(self, RHSin):
        """Assemble RHS for fwd solve"""
        if RHSin == []: self.RHS = None
        else:
            self.RHS = []
            for rhs in RHSin:
                if isinstance(rhs, Expression):
                    L = rhs*self.test*dx
                    b = assemble(L)
                    self.bc.apply(b)
                    self.RHS.append(b)
                else:   raise WrongInstanceError("rhs should be Expression")

    def _assemble_solverM(self, Vm):
        self.MM = assemble(inner(self.mtrial, self.mtest)*dx)
        self.solverM = LUSolver()
        self.solverM.parameters['reuse_factorization'] = True
        self.solverM.parameters['symmetric'] = True
        self.solverM.set_operator(self.MM)

    def _set_plots(self, plot):
        self.plot = plot
        if self.plot:
            filename, ext = splitext(sys.argv[0])
            self.plotoutdir = filename + '/Plots/'
            self.plotvarm = PlotFenics(self.plotoutdir)
            self.plotvarm.set_varname('m')

    def plotm(self, index):
        if self.plot:   self.plotvarm.plot_vtk(self.m, index)

    def gatherm(self):
        if self.plot:   self.plotvarm.gather_vtkplots()

    # Update param
    def update_Data(self, Data):
        """Update Data member"""
        self.Data = Data
        self.assemble_A()
        self.reset()

    def update_m(self, m):
        """Update values of parameter m"""
        if isinstance(m, np.ndarray):
            self.m.vector()[:] = m
        elif isinstance(m, Function):
            self.m.assign(m)
        elif isinstance(m, float):
            self.m.vector()[:] = m
        elif isinstance(m, int):
            self.m.vector()[:] = float(m)
        else:   raise WrongInstanceError('Format for m not accepted')
        self.assemble_A()
        self.reset()

    def backup_m(self):
        self.mcopy.assign(self.m)

    def restore_m(self):
        self.update_m(self.mcopy)

    def reset(self):
        """Reset U, C and E"""
        self.U = []
        self.C = []
        self.E = []

    def set_solver(self):
        """Reset solver for fwd operator"""
        self.solver = LUSolver()
        self.solver.parameters['reuse_factorization'] = True
        self.solver.set_operator(self.A)

    def addPDEcount(self, increment=1):
        """Increase 'nbPDEsolves' by 'increment'"""
        self.nbPDEsolves += increment

    def resetPDEsolves(self):
        self.nbPDEsolves = 0

    # Additional methods for compatibility with CG solver:
    def init_vector(self, x, dim):
        """Initialize vector x to be compatible with parameter
         Does not work in dolfin 1.3.0"""
        self.MM.init_vector(x, 0)

    def init_vector130(self):
        """Initialize vector x to be compatible with parameter"""
        return Vector(Function(self.mcopy.function_space()).vector())

    # Abstract methods
    @abc.abstractmethod
    def _wkforma(self): self.a = []

    @abc.abstractmethod
    def _wkformc(self): self.c = []

    @abc.abstractmethod
    def _wkforme(self): self.e = []
Пример #40
0
def test_assign(V, W):
    for V0, V1, vector_space in [(V, W, False), (W, V, True)]:
        u = Function(V0)
        u0 = Function(V0)
        u1 = Function(V0)
        u2 = Function(V0)
        u3 = Function(V1)

        u.vector()[:] = 1.0
        u0.vector()[:] = 2.0
        u1.vector()[:] = 3.0
        u2.vector()[:] = 4.0
        u3.vector()[:] = 5.0

        uu = Function(V0)
        uu.assign(2 * u)
        assert uu.vector().get_local().sum() == u0.vector().get_local().sum()

        uu = Function(V1)
        uu.assign(3 * u)
        assert uu.vector().get_local().sum() == u1.vector().get_local().sum()

        # Test complex assignment
        expr = 3 * u - 4 * u1 - 0.1 * 4 * u * 4 + u2 + 3 * u0 / 3. / 0.5
        expr_scalar = 3 - 4 * 3 - 0.1 * 4 * 4 + 4. + 3 * 2. / 3. / 0.5
        uu.assign(expr)
        assert (round(
            uu.vector().get_local().sum() -
            float(expr_scalar * uu.vector().size()), 7) == 0)

        # Test expression scaling
        expr = 3 * expr
        expr_scalar *= 3
        uu.assign(expr)
        assert (round(
            uu.vector().get_local().sum() -
            float(expr_scalar * uu.vector().size()), 7) == 0)

        # Test expression scaling
        expr = expr / 4.5
        expr_scalar /= 4.5
        uu.assign(expr)
        assert (round(
            uu.vector().get_local().sum() -
            float(expr_scalar * uu.vector().size()), 7) == 0)

        # Test self assignment
        expr = 3 * u - 5.0 * u2 + u1 - 5 * u
        expr_scalar = 3 - 5 * 4. + 3. - 5
        u.assign(expr)
        assert (round(
            u.vector().get_local().sum() -
            float(expr_scalar * u.vector().size()), 7) == 0)

        # Test zero assignment
        u.assign(-u2 / 2 + 2 * u1 - u1 / 0.5 + u2 * 0.5)
        assert round(u.vector().get_local().sum() - 0.0, 7) == 0

        # Test erroneous assignments
        uu = Function(V1)

        @function.expression.numba_eval
        def expr_eval(values, x, t):
            values[:, 0] = 1.0

        f = Expression(expr_eval)

        with pytest.raises(RuntimeError):
            uu.assign(1.0)
        with pytest.raises(RuntimeError):
            uu.assign(4 * f)

        if not vector_space:
            with pytest.raises(RuntimeError):
                uu.assign(u * u0)
            with pytest.raises(RuntimeError):
                uu.assign(4 / u0)
            with pytest.raises(RuntimeError):
                uu.assign(4 * u * u1)
Пример #41
0
def test(problem, max_num_steps=2, show=False):
    # # Density depends on temperature.
    # material = 'water'
    # rho = params[material]['density'](293.0)
    # mu = params[material]['dynamic viscosity'](293.0)

    rho = 1.0
    mu = 1.0

    # Start time, end time, time step.
    t = 0.0
    T = 8.0
    dt = 1.0e-5
    dt_max = 1.0e-1

    num_subspaces = problem.W.num_sub_spaces()

    if num_subspaces == 2:
        # g = Constant((0.0, 0.0))
        g = Constant((0.0, -9.81))
    elif num_subspaces == 3:
        # g = Constant((0.0, 0.0, 0.0))
        g = Constant((0.0, -9.81, 0.0))
    else:
        raise RuntimeError("Illegal number of subspaces ({}).".format(num_subspaces))

    initial_stokes = False
    if initial_stokes:
        u0, p0 = cyl_stokes.solve(
            problem.W,
            problem.P,
            mu,
            rho,
            problem.u_bcs,
            problem.p_bcs,
            f=rho * g,
            tol=1.0e-10,
            maxiter=2000,
        )
    else:
        # Initial states.
        u0 = Function(problem.W, name="velocity")
        u0.vector().zero()
        p0 = Function(problem.P, name="pressure")
        p0.vector().zero()

    filename = "navier_stokes.xdmf"
    with XDMFFile(mpi_comm_world(), filename) as xdmf_file:
        xdmf_file.parameters["flush_output"] = True
        xdmf_file.parameters["rewrite_function_mesh"] = False

        if show:
            xdmf_file.write(u0, t)
            xdmf_file.write(p0, t)

        stepper = cyl_ns.IPCS(time_step_method="backward euler")
        steps = 0
        while t < T + DOLFIN_EPS and steps < max_num_steps:
            steps += 1
            begin("Time step {:e} -> {:e}...".format(t, t + dt))
            try:
                u1, p1 = stepper.step(
                    Constant(dt),
                    {0: u0},
                    p0,
                    problem.W,
                    problem.P,
                    problem.u_bcs,
                    problem.p_bcs,
                    Constant(rho),
                    Constant(mu),
                    f={0: rho * g, 1: rho * g},
                    tol=1.0e-10,
                )
            except RuntimeError:
                print(
                    "Navier--Stokes solver failed to converge. "
                    "Decrease time step from {} to {} and try again.".format(
                        dt, 0.5 * dt
                    )
                )
                dt *= 0.5
                end()
                end()
                end()
                continue

            u0.assign(u1)
            p0.assign(p1)

            if show:
                # Save to files.
                xdmf_file.write(u0, t + dt)
                xdmf_file.write(p0, t + dt)
                # Plotting for some reason takes up a lot of memory.
                plot(u0, title="velocity", rescale=True)
                plot(p0, title="pressure", rescale=True)
                # interactive()

            begin("Step size adaptation...")
            # unorm = project(abs(u[0]) + abs(u[1]) + abs(u[2]),
            #                 P,
            #                 form_compiler_parameters={'quadrature_degree': 4}
            #                 )
            unorm = project(
                norm(u1), problem.P, form_compiler_parameters={"quadrature_degree": 4}
            )
            unorm = norm(unorm.vector(), "linf")
            # print('||u||_inf = {:e}'.format(unorm))
            # Some smooth step-size adaption.
            target_dt = 0.2 * problem.mesh.hmax() / unorm
            print("current dt: {:e}".format(dt))
            print("target dt:  {:e}".format(target_dt))
            # alpha is the aggressiveness factor. The distance between the
            # current step size and the target step size is reduced by
            # |1-alpha|. Hence, if alpha==1 then dt_next==target_dt. Otherwise
            # target_dt is approached slowlier.
            alpha = 0.5
            dt = min(
                dt_max,
                # At most double the step size from step to step.
                dt * min(2.0, 1.0 + alpha * (target_dt - dt) / dt),
            )
            print("next dt:    {:e}".format(dt))
            t += dt
            end()
            end()
    return
Пример #42
0
class Problem(gp.GeneralProblem):
    def __init__(self, args, tc, metadata):
        self.has_analytic_solution = True
        self.problem_code = 'WCYL'
        super(Problem, self).__init__(args, tc, metadata)

        # TODO check if really used here
        self.tc.init_watch('assembleSol', 'Assembled analytic solution', True)
        self.tc.init_watch('analyticP', 'Analytic pressure', True)
        self.tc.init_watch('analyticVnorms', 'Computed analytic velocity norms', True)
        self.tc.init_watch('errorP', 'Computed pressure error', True)
        self.tc.init_watch('errorV', 'Computed velocity error', True)
        self.tc.init_watch('errorForce', 'Computed force error', True)
        self.tc.init_watch('errorVtest', 'Computed velocity error test', True)
        self.tc.init_watch('computePG', 'Computed pressure gradient', True)

        self.name = 'womersley_cylinder'
        self.status_functional_str = 'last H1 velocity error'

        # input parameters
        self.ic = args.ic
        self.factor = args.factor
        self.metadata['factor'] = self.factor
        self.scale_factor.append(self.factor)

        # fixed parameters (used in analytic solution and in BC)
        self.nu = 3.71 * args.nu # kinematic viscosity
        self.R = 5.0  # cylinder radius

        self.mesh_volume = pi*25.*20.

        # Import gmsh mesh
        self.mesh, self.facet_function = super(Problem, self).loadMesh(args.mesh)
        self.dsIn = Measure("ds", subdomain_id=2, subdomain_data=self.facet_function)
        self.dsOut = Measure("ds", subdomain_id=3, subdomain_data=self.facet_function)
        self.dsWall = Measure("ds", subdomain_id=1, subdomain_data=self.facet_function)
        self.normal = FacetNormal(self.mesh)
        print("Mesh name: ", args.mesh, "    ", self.mesh)
        print("Mesh norm max: ", self.mesh.hmax())
        print("Mesh norm min: ", self.mesh.hmin())

        self.actual_time = None
        self.sol_p = None
        self.last_analytic_pressure_norm = None
        self.v_in = None
        self.area = None

        choose_note = {1.0: '', 0.1: 'nuL10', 0.01: 'nuL100', 10.0: 'nuH10'}
        self.precomputed_filename = args.mesh + choose_note[self.nu_factor]
        print('chosen filename for precomputed solution', self.precomputed_filename)

        # partial Bessel functions and coefficients
        self.bessel_parabolic = None
        self.bessel_real = []
        self.bessel_complex = []
        self.coefs_exp = [-8, -6, -4, -2, 2, 4, 6, 8]

        self.listDict.update({
            'u_H1w': {'list': [], 'name': 'corrected velocity H1 error on wall', 'abrev': 'CE_H1w', 'scale': self.scale_factor,
                     'relative': 'av_norm_H1w', 'slist': []},
            'u2H1w': {'list': [], 'name': 'tentative velocity H1 error on wall', 'abrev': 'TE_H1w', 'scale': self.scale_factor,
                     'relative': 'av_norm_H1w', 'slist': []},
            'av_norm_H1w': {'list': [], 'name': 'analytic velocity H1 norm on wall', 'abrev': 'AVN_H1w'},
            'a_force_wall': {'list': [], 'name': 'analytic force on wall', 'abrev': 'AF'},
            'a_force_wall_normal': {'list': [], 'name': 'analytic force on wall', 'abrev': 'AFN'},
            'a_force_wall_shear': {'list': [], 'name': 'analytic force on wall', 'abrev': 'AFS'},
            'force_wall': {'list': [], 'name': 'force error on wall', 'abrev': 'FE',
                           'relative': 'a_force_wall', 'slist': []},
            'force_wall_normal': {'list': [], 'name': 'normal force error on wall', 'abrev': 'FNE',
                                  'relative': 'a_force_wall', 'slist': []},
            'force_wall_shear': {'list': [], 'name': 'shear force error on wall', 'abrev': 'FSE',
                                 'relative': 'a_force_wall', 'slist': []},
        })

    def __str__(self):
        return 'womersley flow in cylinder'

    @staticmethod
    def setup_parser_options(parser):
        super(Problem, Problem).setup_parser_options(parser)
        # QQ precomputed initial condition?
        # IFNEED smooth initial u0 v_in incompatibility via modification of v_in (options normal, smoothed)
        parser.add_argument('--ic', help='Initial condition', choices=['zero', 'correct'], default='zero')
        parser.add_argument('-F', '--factor', help='Velocity scale factor', type=float, default=1.0)

    def initialize(self, V, Q, PS, D):
        super(Problem, self).initialize(V, Q, PS, D)

        print("IC type: " + self.ic)
        print("Velocity scale factor = %4.2f" % self.factor)
        reynolds = 728.761 * self.factor  # TODO modify by nu_factor
        print("Computing with Re = %f" % reynolds)

        self.v_in = Function(V)
        print('Initializing error control')
        self.load_precomputed_bessel_functions(PS)
        self.solution = self.assemble_solution(0.0)

        # set constants for
        self.area = assemble(interpolate(Expression("1.0"), Q) * self.dsIn)  # inflow area

        # womersley = steady + e^iCt, e^iCt has average 0
        self.pg_normalization_factor.append(womersleyBC.average_analytic_pressure_grad(self.factor))
        self.p_normalization_factor.append(norm(
            interpolate(womersleyBC.average_analytic_pressure_expr(self.factor), self.pSpace), norm_type='L2'))
        self.vel_normalization_factor.append(norm(
            interpolate(womersleyBC.average_analytic_velocity_expr(self.factor), self.vSpace), norm_type='L2'))
        # print('Normalisation factors (vel, p, pg):', self.vel_normalization_factor[0], self.p_normalization_factor[0],
        #       self.pg_normalization_factor[0])

        one = (interpolate(Expression('1.0'), Q))
        self.outflow_area = assemble(one*self.dsOut)
        print('Outflow area:', self.outflow_area)

    def get_boundary_conditions(self, use_pressure_BC, v_space, p_space):
        # boundary parts: 1 walls, 2 inflow, 3 outflow
        # Boundary conditions
        bc0 = DirichletBC(v_space, (0.0, 0.0, 0.0), self.facet_function, 1)
        inflow = DirichletBC(v_space, self.v_in, self.facet_function, 2)
        bcu = [inflow, bc0]
        bcp = []
        if use_pressure_BC:
            outflow = DirichletBC(p_space, 0.0, self.facet_function, 3)
            bcp = [outflow]
        return bcu, bcp

    def get_initial_conditions(self, function_list):
        out = []
        for d in function_list:
            if d['type'] == 'v':
                f = Function(self.vSpace)
                if self.ic == 'correct':
                    f = self.assemble_solution(d['time'])
            if d['type'] == 'p':
                f = Function(self.pSpace)
                if self.ic == 'correct':
                    f = interpolate(womersleyBC.analytic_pressure(self.factor, d['time']), self.pSpace)
            out.append(f)
        return out

    def get_outflow_measures(self):
        return [self.dsOut]

    def get_outflow_measure_form(self):
        return self.dsOut

    def get_v_solution(self, t):
        v = self.assemble_solution(t)
        return v

    def get_p_solution(self, t):
        p = interpolate(womersleyBC.analytic_pressure(self.factor, t), self.pSpace)
        return p

    def update_time(self, actual_time, step_number):
        super(Problem, self).update_time(actual_time, step_number)
        if self.actual_time > 0.5 and int(round(self.actual_time * 1000)) % 1000 == 0:
            self.isWholeSecond = True
            seconds = int(round(self.actual_time))
            self.second_list.append(seconds)
            self.N1 = seconds*self.stepsInSecond
            self.N0 = (seconds-1)*self.stepsInSecond
        else:
            self.isWholeSecond = False

        self.solution = self.assemble_solution(self.actual_time)

        # Update boundary condition
        self.tc.start('updateBC')
        self.v_in.assign(self.solution)
        self.tc.end('updateBC')

        # construct analytic pressure (used for computing pressure and force errors)
        self.tc.start('analyticP')
        analytic_pressure = womersleyBC.analytic_pressure(self.factor, self.actual_time)
        self.sol_p = interpolate(analytic_pressure, self.pSpace)
        self.tc.end('analyticP')

        self.tc.start('analyticVnorms')
        self.analytic_v_norm_L2 = norm(self.solution, norm_type='L2')
        self.analytic_v_norm_H1 = norm(self.solution, norm_type='H1')
        self.analytic_v_norm_H1w = sqrt(assemble((inner(grad(self.solution), grad(self.solution)) +
                                                  inner(self.solution, self.solution)) * self.dsWall))
        self.listDict['av_norm_L2']['list'].append(self.analytic_v_norm_L2)
        self.listDict['av_norm_H1']['list'].append(self.analytic_v_norm_H1)
        self.listDict['av_norm_H1w']['list'].append(self.analytic_v_norm_H1w)
        self.tc.end('analyticVnorms')

    def assemble_solution(self, t):  # returns Womersley sol for time t
        if self.tc is not None:
            self.tc.start('assembleSol')
        sol = Function(self.solutionSpace)
        dofs2 = self.solutionSpace.sub(2).dofmap().dofs()  # gives field of indices corresponding to z axis
        sol.assign(Constant(("0.0", "0.0", "0.0")))  # QQ not needed
        sol.vector()[dofs2] += self.factor * self.bessel_parabolic.vector().array()  # parabolic part of sol
        for idx in range(8):  # add modes of Womersley sol
            sol.vector()[dofs2] += self.factor * cos(self.coefs_exp[idx] * pi * t) * self.bessel_real[idx].vector().array()
            sol.vector()[dofs2] += self.factor * -sin(self.coefs_exp[idx] * pi * t) * self.bessel_complex[idx].vector().array()
        if self.tc is not None:
            self.tc.end('assembleSol')
        return sol

    # load precomputed Bessel functions
    def load_precomputed_bessel_functions(self, PS):
        f = HDF5File(mpi_comm_world(), 'precomputed/precomputed_' + self.precomputed_filename + '.hdf5', 'r')
        temp = toc()
        fce = Function(PS)
        f.read(fce, "parab")
        self.bessel_parabolic = Function(fce)
        for i in range(8):
            f.read(fce, "real%d" % i)
            self.bessel_real.append(Function(fce))
            f.read(fce, "imag%d" % i)
            self.bessel_complex.append(Function(fce))
            # plot(coefs_r_prec[i], title="coefs_r_prec", interactive=True) # reasonable values
            # plot(coefs_i_prec[i], title="coefs_i_prec", interactive=True) # reasonable values
        # plot(c0_prec,title="c0_prec",interactive=True) # reasonable values
        print("Loaded partial solution functions. Time: %f" % (toc() - temp))

    def compute_err(self, is_tent, velocity, t):
        super(Problem, self).compute_err(is_tent, velocity, t)
        er_list_H1w = self.listDict['u2H1w' if is_tent else 'u_H1w']['list']
        errorH1wall = sqrt(assemble((inner(grad(velocity - self.solution), grad(velocity - self.solution)) +
                                     inner(velocity - self.solution, velocity - self.solution)) * self.dsWall))
        er_list_H1w.append(errorH1wall)
        print('  Relative H1wall error:', errorH1wall / self.analytic_v_norm_H1w)
        if self.isWholeSecond:
            self.listDict['u2H1w' if is_tent else 'u_H1w']['slist'].append(
                sqrt(sum([i*i for i in er_list_H1w[self.N0:self.N1]])/self.stepsInSecond))

    def compute_functionals(self, velocity, pressure, t):
        super(Problem, self).compute_functionals(velocity, pressure, t)
        self.compute_force(velocity, pressure, t)

    def compute_force(self, velocity, pressure, t):
        self.tc.start('errorForce')
        I = Identity(3)  # Identity tensor
        def T(p, v):
            return -p * I + 2.0 * self.nu * sym(grad(v))
        error_force = sqrt(
                assemble(inner((T(pressure, velocity) - T(self.sol_p, self.solution)) * self.normal,
                               (T(pressure, velocity) - T(self.sol_p, self.solution)) * self.normal) * self.dsWall))
        an_force = sqrt(assemble(inner(T(self.sol_p, self.solution) * self.normal,
                                            T(self.sol_p, self.solution) * self.normal) * self.dsWall))
        an_f_normal = sqrt(assemble(inner(inner(T(self.sol_p, self.solution) * self.normal, self.normal),
                                               inner(T(self.sol_p, self.solution) * self.normal, self.normal)) * self.dsWall))
        error_f_normal = sqrt(
                assemble(inner(inner((T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, self.normal),
                               inner((T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, self.normal)) * self.dsWall))
        an_f_shear = sqrt(
                assemble(inner((I - outer(self.normal, self.normal)) * T(self.sol_p, self.solution) * self.normal,
                               (I - outer(self.normal, self.normal)) * T(self.sol_p, self.solution) * self.normal) * self.dsWall))
        error_f_shear = sqrt(
                assemble(inner((I - outer(self.normal, self.normal)) *
                               (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal,
                               (I - outer(self.normal, self.normal)) *
                               (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal) * self.dsWall))
        self.listDict['a_force_wall']['list'].append(an_force)
        self.listDict['a_force_wall_normal']['list'].append(an_f_normal)
        self.listDict['a_force_wall_shear']['list'].append(an_f_shear)
        self.listDict['force_wall']['list'].append(error_force)
        self.listDict['force_wall_normal']['list'].append(error_f_normal)
        self.listDict['force_wall_shear']['list'].append(error_f_shear)
        if self.isWholeSecond:
            self.listDict['force_wall']['slist'].append(
                sqrt(sum([i*i for i in self.listDict['force_wall']['list'][self.N0:self.N1]])/self.stepsInSecond))
        print('  Relative force error:', error_force/an_force)
        self.tc.end('errorForce')

    def save_pressure(self, is_tent, pressure):
        super(Problem, self).save_pressure(is_tent, pressure)
        self.tc.start('computePG')
        # Report pressure gradient
        p_in = assemble((1.0/self.area) * pressure * self.dsIn)
        p_out = assemble((1.0/self.area) * pressure * self.dsOut)
        computed_gradient = (p_out - p_in)/20.0
        # 20.0 is a length of a pipe NT should depend on mesh length (implement throuhg metadata or function of mesh)
        self.tc.end('computePG')
        self.tc.start('analyticP')
        analytic_gradient = womersleyBC.analytic_pressure_grad(self.factor, self.actual_time)
        if not is_tent:
            self.last_analytic_pressure_norm = norm(self.sol_p, norm_type='L2')
            self.listDict['ap_norm']['list'].append(self.last_analytic_pressure_norm)
        self.tc.end('analyticP')
        self.tc.start('errorP')
        error = errornorm(self.sol_p, pressure, norm_type="l2", degree_rise=0)
        self.listDict['p2' if is_tent else 'p']['list'].append(error)
        print("Normalized pressure error norm:", error/self.p_normalization_factor[0])
        self.listDict['pg2' if is_tent else 'pg']['list'].append(computed_gradient)
        if not is_tent:
            self.listDict['apg']['list'].append(analytic_gradient)
        self.listDict['pgE2' if is_tent else 'pgE']['list'].append(computed_gradient-analytic_gradient)
        self.listDict['pgEA2' if is_tent else 'pgEA']['list'].append(abs(computed_gradient-analytic_gradient))
        if self.isWholeSecond:
            for key in (['pgE2', 'p2'] if is_tent else ['pgE', 'p']):
                self.listDict[key]['slist'].append(
                    sqrt(sum([i*i for i in self.listDict[key]['list'][self.N0:self.N1]])/self.stepsInSecond))
        self.tc.end('errorP')
        if self.doSaveDiff:
            sol_pg_expr = Expression(("0", "0", "pg"), pg=analytic_gradient / self.pg_normalization_factor[0])
            # sol_pg = interpolate(sol_pg_expr, self.pgSpace)
            # plot(sol_p, title="sol")
            # plot(pressure, title="p")
            # plot(pressure - sol_p, interactive=True, title="diff")
            # exit()
            self.pFunction.assign(pressure-self.sol_p)
            self.fileDict['p2D' if is_tent else 'pD']['file'] << self.pFunction
Пример #43
0
def test_time_step():
    problem = problems.Crucible()

    boundaries = problem.wp_boundaries

    # The melting point of GaAs is 1511 K.
    average_temp = 1520.0

    f = Constant(0.0)

    material = problem.subdomain_materials[problem.wpi]
    rho = material.density(average_temp)
    cp = material.specific_heat_capacity
    kappa = material.thermal_conductivity

    my_ds = Measure("ds")(subdomain_data=boundaries)

    # from dolfin import DirichletBC
    convection = None
    heat = maelstrom.heat.Heat(
        problem.Q,
        kappa,
        rho,
        cp,
        convection,
        source=Constant(0.0),
        dirichlet_bcs=problem.theta_bcs_d,
        neumann_bcs=problem.theta_bcs_n,
        robin_bcs=problem.theta_bcs_r,
        my_dx=dx,
        my_ds=my_ds,
    )

    # create time stepper
    # stepper = parabolic.ExplicitEuler(heat)
    stepper = parabolic.ImplicitEuler(heat)
    # stepper = parabolic.Trapezoidal(heat)

    theta0 = project(Constant(average_temp), problem.Q)
    # theta0 = heat.solve_stationary()
    theta0.rename("theta0", "temperature")

    theta1 = Function(problem.Q)
    theta1 = Function(problem.Q)

    t = 0.0
    dt = 1.0e-3
    end_time = 10 * dt

    with XDMFFile("temperature.xdmf") as f:
        f.parameters["flush_output"] = True
        f.parameters["rewrite_function_mesh"] = False

        f.write(theta0, t)
        while t < end_time:
            theta1.assign(stepper.step(theta0, t, dt))
            theta0.assign(theta1)
            t += dt
            #
            f.write(theta0, t)

    assert abs(maelstrom.helpers.average(theta0) - 1519.81) < 1.0e-2

    return
    def step(self,
             dt,
             u1, p1,
             u, p0,
             u_bcs, p_bcs,
             f0=None, f1=None,
             verbose=True,
             tol=1.0e-10
             ):
        '''General pressure projection scheme as described in section 3.4 of
        :cite:`GMS06`.
        '''
        # Some initial sanity checkups.
        assert dt > 0.0

        # Define coefficients
        k = Constant(dt)

        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        # Compute tentative velocity step.
        # rho (u[-1] + (u.\nabla)u) = mu 1/r \div(r \nabla u) + rho g.
        with Message('Computing tentative velocity'):
            solver = NewtonSolver()
            solver.parameters['maximum_iterations'] = 5
            solver.parameters['absolute_tolerance'] = tol
            solver.parameters['relative_tolerance'] = 0.0
            solver.parameters['report'] = True
            # The nonlinear term makes the problem generally nonsymmetric.
            solver.parameters['linear_solver'] = 'gmres'
            # If the nonsymmetry is too strong, e.g., if u[-1] is large, then
            # AMG preconditioning might not work very well.
            # Use HYPRE-Euclid instead of ILU for parallel computation.
            solver.parameters['preconditioner'] = 'hypre_euclid'
            solver.parameters['krylov_solver']['relative_tolerance'] = tol
            solver.parameters['krylov_solver']['absolute_tolerance'] = 0.0
            solver.parameters['krylov_solver']['maximum_iterations'] = 1000
            solver.parameters['krylov_solver']['monitor_convergence'] = verbose

            ui = Function(self.W)
            step_problem = \
                TentativeVelocityProblem(ui,
                                         self.theta,
                                         self.rho, self.mu,
                                         u, p0, k,
                                         u_bcs,
                                         f0, f1,
                                         stabilization=self.stabilization,
                                         dx=self.dx
                                         )

            # Take u[-1] as initial guess.
            ui.assign(u[-1])
            solver.solve(step_problem, ui.vector())
            #div_u = 1/r * div(r*ui)
            #plot(div_u)
            #interactive()
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        with Message('Computing pressure correction'):
            # The pressure correction is based on the update formula
            #
            #                           (    dphi/dr    )
            #     rho/dt (u_{n+1}-u*) + (    dphi/dz    ) = 0
            #                           (1/r dphi/dtheta)
            #
            # with
            #
            #     phi = p_{n+1} - p*
            #
            # and
            #
            #      1/r d/dr    (r ur_{n+1})
            #    +     d/dz    (  uz_{n+1})
            #    + 1/r d/dtheta(  utheta_{n+1}) = 0
            #
            # With the assumption that u does not change in the direction
            # theta, one derives
            #
            #  - 1/r * div(r * \nabla phi) = 1/r * rho/dt div(r*(u_{n+1} - u*))
            #  - 1/r * n. (r * \nabla phi) = 1/r * rho/dt  n.(r*(u_{n+1} - u*))
            #
            # In its weak form, this is
            #
            #   \int r * \grad(phi).\grad(q) *2*pi =
            #       - rho/dt \int div(r*u*) q *2*p
            #       - rho/dt \int_Gamma n.(r*(u_{n+1}-u*)) q *2*pi.
            #
            # (The terms 1/r cancel with the volume elements 2*pi*r.)
            # If Dirichlet boundary conditions are applied to both u* and u_n
            # (the latter in the final step), the boundary integral vanishes.
            #
            alpha = 1.0
            #alpha = 3.0 / 2.0
            rotational_form = False
            self._pressure_poisson(p1, p0,
                                   self.mu, ui,
                                   self.rho * alpha * ui / k,
                                   p_bcs=p_bcs,
                                   rotational_form=rotational_form,
                                   tol=tol,
                                   verbose=verbose
                                   )
        #plot(ui, title='u intermediate')
        ##plot(f, title='f')
        ##plot(ui[1], title='u intermediate[1]')
        #plot(div(ui), title='div(u intermediate)')
        #plot(p1, title='p1')
        #interactive()
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        # Velocity correction.
        #   U = u[-1] - dt/rho \nabla (p1-p0).
        with Message('Computing velocity correction'):
            u = TrialFunction(self.W)
            v = TestFunction(self.W)
            a3 = dot(u, v) * dx
            phi = Function(self.P)
            phi.assign(p1)
            if p0:
                phi -= p0
            if rotational_form:
                r = Expression('x[0]', degree=1, domain=self.W.mesh())
                div_ui = 1/r * (r * ui[0]).dx(0) + ui[1].dx(1)
                phi += self.mu * div_ui
            L3 = dot(ui, v) * dx \
                - k / self.rho * (phi.dx(0) * v[0] + phi.dx(1) * v[1]) * dx
            # Jacobi preconditioning for mass matrix is order-optimal.
            solve(
                a3 == L3, u1,
                bcs=u_bcs,
                solver_parameters={
                    'linear_solver': 'iterative',
                    'symmetric': True,
                    'preconditioner': 'jacobi',
                    'krylov_solver': {'relative_tolerance': tol,
                                      'absolute_tolerance': 0.0,
                                      'maximum_iterations': 100,
                                      'monitor_convergence': verbose}
                    }
                )
            #u = project(ui - k/rho * grad(phi), V)
            # div_u = 1/r * div(r*u)
            r = Expression('x[0]', degree=1, domain=self.W.mesh())
            div_u1 = 1.0 / r * (r * u1[0]).dx(0) + u1[1].dx(1)
            info('||u||_div = %e' % sqrt(assemble(div_u1 * div_u1 * dx)))
            #plot(div_u)
            #interactive()

            ## Ha! TODO remove
            #u1.vector()[:] = ui.vector()
        return
Пример #45
0
def get_lorentz_joule(problem, input_voltages, show=False):
    submesh_workpiece = problem.W.mesh()

    subdomain_indices = problem.subdomain_materials.keys()

    info("Input voltages:")
    info(repr(input_voltages))

    if input_voltages is None:
        return None, Constant(0.0)

    # Merge coil rings with voltages.
    coils = [
        {"rings": coil_domain, "c_type": "voltage", "c_value": voltage}
        for coil_domain, voltage in zip(problem.coil_domains, input_voltages)
    ]
    # Build subdomain parameter dictionaries for Maxwell
    mu_const = {
        i: problem.subdomain_materials[i].magnetic_permeability
        for i in subdomain_indices
    }
    sigma_const = {
        i: problem.subdomain_materials[i].electrical_conductivity
        for i in subdomain_indices
    }

    # Function space for magnetic scalar potential, Lorentz force etc.
    V = FunctionSpace(problem.mesh, "CG", 1)
    # Compute the magnetic field.
    # The Maxwell equations depend on two parameters that change during the
    # computation: (a) the temperature, and (b) the velocity field u0. We
    # assume though that changes in either of the two will only marginally
    # influence the magnetic field. Consequently, we precompute all associated
    # values.
    dx_subdomains = Measure("dx", subdomain_data=problem.subdomains)
    with Message("Computing magnetic field..."):
        Phi, voltages = cmx.compute_potential(
            coils,
            V,
            dx_subdomains,
            mu_const,
            sigma_const,
            problem.omega,
            convections={}
            # io_submesh=submesh_workpiece
        )
        # Get resulting Lorentz force.
        lorentz = cmx.compute_lorentz(Phi, problem.omega, sigma_const[problem.wpi])

        # Show the Lorentz force in the workpiece.
        # W_element = VectorElement('CG', submesh_workpiece.ufl_cell(), 1)
        # First project onto the entire mesh, then onto the submesh; see bug
        # <https://bitbucket.org/fenics-project/dolfin/issues/869/projecting-grad-onto-submesh-error>.
        W = VectorFunctionSpace(problem.mesh, "CG", 1)
        pl = project(lorentz, W)
        W2 = VectorFunctionSpace(submesh_workpiece, "CG", 1)
        pl = project(pl, W2)
        pl.rename("Lorentz force", "Lorentz force")
        with XDMFFile(submesh_workpiece.mpi_comm(), "lorentz.xdmf") as f:
            f.parameters["flush_output"] = True
            f.write(pl)

        if show:
            tri = plot(pl, title="Lorentz force")
            plt.colorbar(tri)
            plt.show()

        # Get Joule heat source.
        joule = cmx.compute_joule(
            Phi, voltages, problem.omega, sigma_const, mu_const, subdomain_indices
        )

        if show:
            # Show Joule heat source.
            submesh = SubMesh(problem.mesh, problem.subdomains, problem.wpi)
            W_submesh = FunctionSpace(submesh, "CG", 1)
            jp = Function(W_submesh, name="Joule heat source")
            jp.assign(project(joule[problem.wpi], W_submesh))
            tri = plot(jp)
            plt.title("Joule heat source")
            plt.colorbar(tri)
            plt.show()

        joule_wpi = joule[problem.wpi]

    # To work around bug
    # <https://bitbucket.org/fenics-project/dolfin/issues/869/projecting-grad-onto-submesh-error>.
    # return the projection `pl` and not `lorentz` itself.
    # TODO remove this workaround
    return pl, joule_wpi, Phi
Пример #46
0
def compute_tentative_velocity(
    time_step_method, rho, mu, u, p0, dt, u_bcs, f, W, my_dx, tol
):
    """Compute the tentative velocity via

    .. math::
        \\rho (u_0 + (u\\cdot\\nabla)u) =
            \\mu \\frac{1}{r} \\div(r \\nabla u) + \\rho g.
    """

    class TentativeVelocityProblem(NonlinearProblem):
        def __init__(self, ui, time_step_method, rho, mu, u, p0, dt, bcs, f, my_dx):
            super(TentativeVelocityProblem, self).__init__()

            W = ui.function_space()
            v = TestFunction(W)

            self.bcs = bcs

            r = SpatialCoordinate(ui.function_space().mesh())[0]

            def me(uu, ff):
                return _momentum_equation(uu, v, p0, ff, rho, mu, my_dx)

            self.F0 = rho * dot(ui - u[0], v) / dt * 2 * pi * r * my_dx
            if time_step_method == "forward euler":
                self.F0 += me(u[0], f[0])
            elif time_step_method == "backward euler":
                self.F0 += me(ui, f[1])
            else:
                assert (
                    time_step_method == "crank-nicolson"
                ), "Unknown time stepper '{}'".format(
                    time_step_method
                )
                self.F0 += 0.5 * (me(u[0], f[0]) + me(ui, f[1]))

            self.jacobian = derivative(self.F0, ui)
            self.reset_sparsity = True
            return

        # pylint: disable=unused-argument
        def F(self, b, x):
            # We need to evaluate F at x, so we have to make sure that self.F0
            # is assembled for ui=x. We could use a self.ui and set
            #
            #     self.ui.vector()[:] = x
            #
            # here. One way around this copy is to instantiate this class with
            # the same Function ui that is then used for the solver.solve().
            assemble(self.F0, tensor=b, form_compiler_parameters={"optimize": True})
            for bc in self.bcs:
                bc.apply(b, x)
            return

        def J(self, A, x):
            # We can ignore x; see comment at F().
            assemble(
                self.jacobian, tensor=A, form_compiler_parameters={"optimize": True}
            )
            for bc in self.bcs:
                bc.apply(A)
            self.reset_sparsity = False
            return

    solver = NewtonSolver()
    solver.parameters["maximum_iterations"] = 10
    solver.parameters["absolute_tolerance"] = tol
    solver.parameters["relative_tolerance"] = 0.0
    solver.parameters["report"] = True
    # While GMRES+ILU converges if the time step is small enough, increasing
    # the time step slows down convergence dramatically in some cases. This
    # makes the step fail, and the adaptive time stepper will decrease the step
    # size. This size can be _very_ small such that simulation take forever.
    # For now, just use a direct solver. Choose UMFPACK over SuperLU since the
    # docker image doesn't contain SuperLU yet, cf.
    # <https://bitbucket.org/fenics-project/docker/issues/64/add-superlu>.
    # TODO come up with an appropriate GMRES preconditioner here
    solver.parameters["linear_solver"] = "umfpack"

    ui = Function(W)
    step_problem = TentativeVelocityProblem(
        ui, time_step_method, rho, mu, u, p0, dt, u_bcs, f, my_dx
    )

    # Take u[0] as initial guess.
    ui.assign(u[0])
    solver.solve(step_problem, ui.vector())

    # Make sure ui is from W. This should happen anyways, but somehow doesn't.
    # TODO find out why not
    ui = project(ui, W)
    # div_u = 1/r * div(r*ui)
    return ui
Пример #47
0
def test(show=False):
    problem = problems.Crucible()
    # The voltage is defined as
    #
    #     v(t) = Im(exp(i omega t) v)
    #          = Im(exp(i (omega t + arg(v)))) |v|
    #          = sin(omega t + arg(v)) |v|.
    #
    # Hence, for a lagging voltage, arg(v) needs to be negative.
    voltages = [
        38.0 * numpy.exp(-1j * 2 * pi * 2 * 70.0 / 360.0),
        38.0 * numpy.exp(-1j * 2 * pi * 1 * 70.0 / 360.0),
        38.0 * numpy.exp(-1j * 2 * pi * 0 * 70.0 / 360.0),
        25.0 * numpy.exp(-1j * 2 * pi * 0 * 70.0 / 360.0),
        25.0 * numpy.exp(-1j * 2 * pi * 1 * 70.0 / 360.0),
    ]

    lorentz, joule, Phi = get_lorentz_joule(problem, voltages, show=show)

    # Some assertions
    ref = 1.4627674791126285e-05
    assert abs(norm(Phi[0], "L2") - ref) < 1.0e-3 * ref
    ref = 3.161363929287592e-05
    assert abs(norm(Phi[1], "L2") - ref) < 1.0e-3 * ref
    #
    ref = 12.115309575057681
    assert abs(norm(lorentz, "L2") - ref) < 1.0e-3 * ref
    #
    ref = 1406.336109054347
    V = FunctionSpace(problem.submesh_workpiece, "CG", 1)
    jp = project(joule, V)
    jp.rename("s", "Joule heat source")
    assert abs(norm(jp, "L2") - ref) < 1.0e-3 * ref

    # check_currents = False
    # if check_currents:
    #     r = SpatialCoordinate(problem.mesh)[0]
    #     begin('Currents computed after the fact:')
    #     k = 0
    #     with XDMFFile('currents.xdmf') as xdmf_file:
    #         for coil in coils:
    #             for ii in coil['rings']:
    #                 J_r = sigma[ii] * (
    #                     voltages[k].real/(2*pi*r) + problem.omega * Phi[1]
    #                     )
    #                 J_i = sigma[ii] * (
    #                     voltages[k].imag/(2*pi*r) - problem.omega * Phi[0]
    #                     )
    #                 alpha = assemble(J_r * dx(ii))
    #                 beta = assemble(J_i * dx(ii))
    #                 info('J = {:e} + i {:e}'.format(alpha, beta))
    #                 info(
    #                     '|J|/sqrt(2) = {:e}'.format(
    #                         numpy.sqrt(0.5 * (alpha**2 + beta**2))
    #                     ))
    #                 submesh = SubMesh(problem.mesh, problem.subdomains, ii)
    #                 V1 = FunctionSpace(submesh, 'CG', 1)
    #                 # Those projections may take *very* long.
    #                 # TODO find out why
    #                 j_v1 = [
    #                     project(J_r, V1),
    #                     project(J_i, V1)
    #                     ]
    #                 # show=Trueplot(j_v1[0], title='j_r')
    #                 # plot(j_v1[1], title='j_i')
    #                 current = project(as_vector(j_v1), V1*V1)
    #                 current.rename('j{}'.format(ii), 'current {}'.format(ii))
    #                 xdmf_file.write(current)
    #                 k += 1
    #     end()

    filename = "./maxwell.xdmf"
    with XDMFFile(filename) as xdmf_file:
        xdmf_file.parameters["flush_output"] = True
        xdmf_file.parameters["rewrite_function_mesh"] = False

        # Store phi
        info("Writing out Phi to {}...".format(filename))
        V = FunctionSpace(problem.mesh, "CG", 1)
        phi = Function(V, name="phi")
        Phi0 = project(Phi[0], V)
        Phi1 = project(Phi[1], V)
        omega = problem.omega
        for t in numpy.linspace(0.0, 2 * pi / omega, num=100, endpoint=False):
            # Im(Phi * exp(i*omega*t))
            phi.vector().zero()
            phi.vector().axpy(sin(problem.omega * t), Phi0.vector())
            phi.vector().axpy(cos(problem.omega * t), Phi1.vector())
            xdmf_file.write(phi, t)

        # Show the resulting magnetic field
        #
        #   B_r = -dphi/dz,
        #   B_z = 1/r d(rphi)/dr.
        #
        r = SpatialCoordinate(problem.mesh)[0]
        g = 1.0 / r * grad(r * Phi[0])
        V_element = FiniteElement("CG", V.mesh().ufl_cell(), 1)
        VV = FunctionSpace(V.mesh(), V_element * V_element)

        B_r = project(as_vector((-g[1], g[0])), VV)
        g = 1 / r * grad(r * Phi[1])
        B_i = project(as_vector((-g[1], g[0])), VV)
        info("Writing out B to {}...".format(filename))
        B = Function(VV)
        B.rename("B", "magnetic field")
        if abs(problem.omega) < DOLFIN_EPS:
            B.assign(B_r)
            xdmf_file.write(B)
            # plot(B_r, title='Re(B)')
            # plot(B_i, title='Im(B)')
        else:
            # Write those out to a file.
            lspace = numpy.linspace(
                0.0, 2 * pi / problem.omega, num=100, endpoint=False
            )
            for t in lspace:
                # Im(B * exp(i*omega*t))
                B.vector().zero()
                B.vector().axpy(sin(problem.omega * t), B_r.vector())
                B.vector().axpy(cos(problem.omega * t), B_i.vector())
                xdmf_file.write(B, t)

    filename = "./lorentz-joule.xdmf"
    info("Writing out Lorentz force and Joule heat source to {}...".format(filename))
    with XDMFFile(filename) as xdmf_file:
        xdmf_file.write(lorentz, 0.0)
        # xdmf_file.write(jp, 0.0)

    return
Пример #48
0
class FluxModule(object):
  """
  Module responsible for calculating scalar fluxes and keff eigenvalue
  """

  def __init__(self, PD, DD, verbosity):
    """
    Constructor
    :param ProblemData PD: Problem information and various mesh-region <-> xs-material mappings
    :param Discretization DD: Discretization data
    :param int verbosity: Verbosity level.
    """

    super(FluxModule, self).__init__()

    self.verb = verbosity
    self.print_prefix = ""

    self.mat_file_name = dict()

    self.PD = PD
    self.DD = DD
    self.BC = PD.bc

    try:
      self.fixed_source_problem = PD.fixed_source_problem
      self.eigenproblem = PD.eigenproblem
    except AttributeError:
      PD.distribute_material_data(DD.cell_regions, DD.M)

    self.A = PETScMatrix()

    # unused in case of an eigenvalue problem
    self.Q = PETScVector()

    # used only for saving the algebraic system
    self.rows_A = None
    self.cols_A = None
    self.vals_A = None
    self.rows_B = None
    self.cols_B = None
    self.vals_B = None

    if self.fixed_source_problem:
      self.fixed_source = Function(self.DD.V0)
      self.vals_Q = None

    # multigroup scalar fluxes
    self.phi_mg = []
    for g in range(self.DD.G):
      phig = Function(self.DD.Vphi1)
      phig.rename("phi","phi_g{}".format(g))
      self.phi_mg.append(phig)

    self.sln = Function(DD.V)
    self.sln_vec = as_backend_type(self.sln.vector())
    self.local_sln_size = self.sln_vec.local_size()

    # auxiliary function for storing various DG(0) quantities (cross sections, group-integrated reaction rates, etc.)
    self.R = Function(self.DD.V0)

    # fission spectrum
    if 'chi' in self.PD.used_xs:
      self.chi = Function(self.DD.V0)
    else:
      self.chi = None

    if 'eSf' in self.PD.used_xs:
      self.E = numpy.zeros(self.DD.local_ndof0)
    else:
      self.E = None

    self.up_to_date = {"flux" : False, "cell_powers" : False}

    self.bnd_matrix_form = None

    self.parameters = parameters["flux_module"]

    if self.eigenproblem:
      assert self.parameters.has_parameter_set("eigensolver")
      self.eigen_params = self.parameters["eigensolver"]
      self.adaptive_eig_tol_end = 0

      self.B = PETScMatrix()
      self.keff = 1
      self.prev_keff = self.keff
      self.set_initial_approximation(numpy.random.random(self.local_sln_size))
      self.update_phi()

    self.u = TrialFunction(self.DD.V)
    self.v = TestFunction(self.DD.V)

    self.v0 = TestFunction(self.DD.V0)
    self.phig = Function(self.DD.Vphi1) # single group scalar flux
    self.cell_RR_form = self.R * self.phig * self.v0 * dx
    self._cell_RRg_vector = PETScVector()

    self.vis_folder = os.path.join(self.PD.out_folder, "FLUX")

    self.vis_files = dict()
    var = "cell_powers"
    self.vis_files[var] = File(os.path.join(self.vis_folder, var+".pvd"), "compressed")
    var = "flux"
    self.vis_files[var] = [
      File(os.path.join(self.vis_folder, "{}_g{}.pvd".format(var, g)), "compressed") for g in range(self.DD.G)
    ]

    variables = self.parameters["saving"].iterkeys()
    self.save_folder = { k : os.path.join(self.PD.out_folder, k.upper()) for k in variables }

  # noinspection PyTypeChecker
  def save_algebraic_system(self, mat_file_name=None, it=0):
    if not mat_file_name: mat_file_name = {}

    try:
      should_save = divmod(it, self.parameters["saving"]["algebraic_system"])[1] == 0
    except ZeroDivisionError:
      should_save = False

    if not should_save:
      return

    if self.verb > 1: print0("Saving the created matrices.")

    timer = Timer("COO representation + matrix saving")

    self.rows_A, self.cols_A, self.vals_A = coo_rep_on_zero(self.A, self.rows_A, self.cols_A, self.vals_A)

    if self.eigenproblem:
      self.rows_B, self.cols_B, self.vals_B = coo_rep_on_zero(self.B, self.rows_B, self.cols_B, self.vals_B)
    elif self.fixed_source_problem:
      self.vals_Q = self.Q.gather_on_zero()

    if MPI.rank(comm) == 0:
      if not mat_file_name:
        mat_file_name['A'] = 'A'
        if self.eigenproblem: mat_file_name['B'] = 'B'
        if self.fixed_source_problem: mat_file_name['Q'] = 'Q'

      for k,v in mat_file_name.iteritems():
        mat_file_name[k] = os.path.join(self.save_folder["algebraic_system"], v)

      if self.verb > 2: print0( self.print_prefix + "  Saving A to " + mat_file_name['A']+'.mat' )
      savemat(mat_file_name['A']+'.mat',
              { 'rows':numpy.asarray(self.rows_A, dtype='d'),
                'cols':numpy.asarray(self.cols_A, dtype='d'),
                'vals':self.vals_A },
              do_compression=True)

      if self.eigenproblem:
        if self.verb > 2: print0( self.print_prefix + "  Saving B to " + mat_file_name['B']+'.mat' )
        savemat(mat_file_name['B']+'.mat',
                { 'rows':numpy.asarray(self.rows_B, dtype='d'),
                  'cols':numpy.asarray(self.cols_B, dtype='d'),
                  'vals':self.vals_B },
                do_compression=True)

      elif self.fixed_source_problem:
        if self.verb > 2: print0( self.print_prefix + "  Saving Q to " + mat_file_name['Q']+'.mat' )
        savemat(mat_file_name['Q']+'.mat',
                { 'vals':self.vals_Q },
                do_compression=True)

  def update(self, it):
    # Handle adaptive eigensolver convergence tolerance strengthening

    if self.eigen_params["adaptive_eig_tol_start"] > 0:
      if self.adaptive_eig_tol_end == 0:
        # First iteration - set the adaptive tolerance strengthening
        if self.verb > 0:
          s = "Eigenvalue tolerance will be decreased adaptively from {} to {} during adaptivity iterations"
          print0(s.format(self.eigen_params["adaptive_eig_tol_start"], self.eigen_params["tol"]))

        self.adaptive_eig_tol_end = self.eigen_params["tol"]
        self.eigen_params["tol"] = self.eigen_params["adaptive_eig_tol_start"]
      else:
        # Subsequent iterations - strengthen the eigensolver convergence tolerance each adaptivity step until
        # adaptive_eig_tol_end is reached
        if self.eigen_params["tol"] > self.adaptive_eig_tol_end:
          self.eigen_params["tol"] /= 10

  def update_phi(self):
    raise NotImplementedError("Abstract scalar flux update method -- must be overriden in specific flux modules.")

  def get_dg0_fluxes(self):
    """
    Get flux interpolated at DG(0) dofs.
    :return: List of `G` arrays of group-fluxes at local DG(0) dofs.
    :rtype: list[ndarray]
    """
    dg0_fluxes = list()

    for g in xrange(self.DD.G):
      dg0_fluxes.append(interpolate(self.phig, self.DD.V0).vector().get_local())

    return dg0_fluxes

  def visualize(self, it=0):
    var = "cell_powers"
    try:
      should_vis = divmod(it, self.parameters["visualization"][var])[1] == 0
    except ZeroDivisionError:
      should_vis = False

    if should_vis:
      if not self.up_to_date[var]:
        self.calculate_cell_powers()
      else:
        qfun = Function(self.DD.V0)
        qfun.vector()[:] = self.E
        qfun.rename("q", "Power")
        self.vis_files["cell_powers"] << (qfun, float(it))

    var = "flux"
    try:
      should_vis = divmod(it, self.parameters["visualization"][var])[1] == 0
    except ZeroDivisionError:
      should_vis = False

    if should_vis:
      if not self.up_to_date[var]:
        self.update_phi()

      for g in xrange(self.DD.G):
        self.vis_files["flux"][g] << (self.phi_mg[g], float(it))

  def print_results(self):
    if self.verb > 2:
      if self.eigenproblem:
        print0(self.print_prefix + "keff = {}".format(self.keff))
        print0(self.print_prefix + "Residual norm: {}".format(self.residual_norm()))

  def eigenvalue_residual_norm(self, norm_type='l2'):
    r = PETScVector()
    y = PETScVector()

    self.B.mult(self.sln_vec, r)
    self.A.mult(self.sln_vec, y)
    r.apply("insert")
    y.apply("insert")

    r -= self.keff * y

    return norm(r, norm_type)

  def fixed_source_residual_norm(self, norm_type='l2'):
    y = PETScVector()
    self.A.mult(self.sln_vec, y)
    y.apply("insert")
    return norm(self.Q - y, norm_type)

  def residual_norm(self, norm_type='l2'):
    if self.eigenproblem:
      return self.eigenvalue_residual_norm(norm_type)
    else:
      return self.fixed_source_residual_norm(norm_type)

  # FIXME: It doesn't work to define eigensolver only once in the c'tor (we use pointers to matrices, so it should...).
  def solve_keff(self, it=0):
    assert self.A
    assert self.B
    assert self.sln_vec

    self.prev_keff = self.keff

    eigensolver = backend_ext_module.GeneralizedEigenSolver(self.A, self.B)

    if eigensolver.parameters["adaptive_shifting"]:
      eigensolver.set_shift_AB(1. / self.keff)

    if eigensolver.parameters["inner_solver_adaptive_tol_multiplier"] > 0:
      PETScOptions.set("st_ksp_atol",
                       eigensolver.parameters["inner_solver_adaptive_tol_multiplier"] * self.eigenvalue_residual_norm())

    if self.verb > 1: print0(self.print_prefix + "Solving ({})...".format(eigensolver.get_actual_problem_description()))
    solution_timer = Timer("Solver")

    eigensolver.set_initial_space(self.sln_vec)

    eigensolver.solve()

    self.keff = 1. / eigensolver.get_first_eigenpair_AB(self.sln_vec)

    # This is needed in parallel (why not in serial?)
    self.sln.vector()[:] = self.sln_vec.array()

    solution_timer.stop()

    if MPI.rank(comm) == 0:
      if self.verb > 1:
        print "\n" + self.print_prefix + "keff = {}\n".format(self.keff)

      try:
        should_save = divmod(it, self.parameters["saving"]["results"])[1] == 0
      except ZeroDivisionError:
        should_save = False

      if should_save:
        savemat(os.path.join(self.save_folder["results"], "eigensolver_out.mat"),
                {'x': self.sln_vec.array(), 'keff': self.keff})

  def solve_fixed_source(self, it=0):
    assert self.A
    assert self.Q

    dolfin_solve(self.A, self.sln_vec, self.Q, "gmres", "petsc_amg")

  def assemble_algebraic_system(self):
    raise NotImplemented

  def solve(self, it=0):
    """
    Pick the appropriate solver for current problem (eigen/fixed-source) and solve the problem (i.e., update solution
    vector and possibly the eigenvalue
    ).
    """
    self.assemble_algebraic_system()
    self.save_algebraic_system(it)

    if self.eigenproblem:
      self.solve_keff(it)
    else:
      self.solve_fixed_source(it)

    self.up_to_date = {k : False for k in self.up_to_date.iterkeys()}

  def set_initial_approximation(self, x0):
    self.sln_vec[:] = x0

  def calculate_cell_powers(self):
    """
    Calculates cell-integrated powers (sets :attr:`E`). Also performs normalization to the specified core(-fraction)
    power (:attr:`core.power`).

    Note that the array is ordered by the associated DG(0) dof, not by the cell index in the mesh.
    """
    q_calc_timer = Timer("FM: Calculation of cell powers")
    ass_timer = Timer("FM: Assemble cell powers")

    if self.verb > 2:
      print0(self.print_prefix + "  calculating cell-wise powers.")

    self.E.fill(0)

    if not self.up_to_date["flux"]:
      self.update_phi()

    for g in xrange(self.DD.G):
      self.PD.get_xs('eSf', self.R, g)

      ass_timer.start()
      self.phig.assign(self.phi_mg[g])
      assemble(self.cell_RR_form, tensor=self._cell_RRg_vector)
      ass_timer.stop()

      self.E += self._cell_RRg_vector.get_local()

    self.up_to_date["cell_powers"] = True
    self.normalize_cell_powers()

  def normalize_cell_powers(self):
    assert self.up_to_date["cell_powers"]

    P = MPI.sum(numpy.sum(self.E))

    if self.verb > 2:
      print0(self.print_prefix + "  desired power: " + str(self.PD.core.power))
      print0(self.print_prefix + "  actual power: " + str(P))
      print0("")

    assert (P > 0)

    self.E *= self.PD.core.power / P

  # noinspection PyAttributeOutsideInit
  def calculate_cell_reaction_rate(self, reaction_xs, rr_vect=None, return_xs_arrays=False):
    """
    Calculates cell-integrated reaction rate and optionally returns the xs's needed for the calculation.

    Note that the array is ordered by the associated DG(0) dof, not by the cell index in the mesh.

    :param str reaction_xs: Reaction cross-section id.
    :param ndarray rr_vect: (optional) Output vector. If not given, reaction rate vector of the FluxModule class
      (:attr:`cell_RR`) will be updated. Must be pre-allocated to store :attr:`Discretization.local_ndof0`
      elements, but doesn't need to be pre-set to any value.
    :param bool return_xs_arrays: (optional) If True, a list that contains for each group the cell (dof) values array
      of the DG(0) representation of the specified reaction xs will be created and returned.
    :return: List with xs value arrays for each group if `return_xs_arrays == True`, None otherwise (see above)
    :rtype: list[ndarray] | None
    """
    if reaction_xs not in self.PD.used_xs:
      warning("Attempted to calculate cell-wise reaction rate for reaction without loaded cross-section (skipping).")
      return

    if self.verb > 1: print0(self.print_prefix + "Calculating cell-wise '{}' reaction rate.".format(reaction_xs))

    if not rr_vect:
      try:
        self.cell_RR
      except AttributeError:
        self.cell_RR = numpy.zeros(self.DD.local_ndof0)
      finally:
        rr_vect = self.cell_RR
    else:
      assert rr_vect.size == self.DD.local_ndof0

    if return_xs_arrays:
      xs_arrays = [None] * self.DD.G
    else:
      xs_arrays = None

    rr_vect.fill(0)

    if not self.up_to_date["cell_powers"]:
      self.update_phi()

    calc_timer = Timer("FM: Calculation of '{}' reaction rate".format(reaction_xs))
    ass_timer = Timer("FM: Assemble '{}' reaction rate".format(reaction_xs))

    for g in xrange(self.DD.G):
      self.PD.get_xs(reaction_xs, self.R, g)
      if xs_arrays:
        xs_arrays[g] = self.R.vector().get_local().copy()

      ass_timer.start()
      self.phig.assign(self.phi_mg[g])
      assemble(self.cell_RR_form, tensor=self._cell_RRg_vector)
      ass_timer.stop()

      rr_vect += self._cell_RRg_vector.get_local()

    return xs_arrays
Пример #49
0
class ObjectiveFunctional(LinearOperator):
    """
    Provides data misfit, gradient and Hessian information for the data misfit
    part of a time-independent symmetric inverse problem.
    """
    __metaclass__ = abc.ABCMeta

    # Instantiation
    def __init__(self, V, Vm, bc, bcadj, \
    RHSinput=[], ObsOp=[], UD=[], Regul=[], Data=[], plot=False, \
    mycomm=None):
        # Define test, trial and all other functions
        self.trial = TrialFunction(V)
        self.test = TestFunction(V)
        self.mtrial = TrialFunction(Vm)
        self.mtest = TestFunction(Vm)
        self.rhs = Function(V)
        self.m = Function(Vm)
        self.mcopy = Function(Vm)
        self.srchdir = Function(Vm)
        self.delta_m = Function(Vm)
        self.MG = Function(Vm)
        self.MGv = self.MG.vector()
        self.Grad = Function(Vm)
        self.Gradnorm = 0.0
        self.lenm = len(self.m.vector().array())
        self.u = Function(V)
        self.ud = Function(V)
        self.diff = Function(V)
        self.p = Function(V)
        # Store other info:
        self.ObsOp = ObsOp
        self.UD = UD
        self.reset()  # Initialize U, C and E to []
        self.Data = Data
        self.GN = 1.0  # GN = 0.0 => GN Hessian; = 1.0 => full Hessian
        # Define weak forms to assemble A, C and E
        self._wkforma()
        self._wkformc()
        self._wkforme()
        # Operators and bc
        LinearOperator.__init__(self, self.delta_m.vector(), \
        self.delta_m.vector())
        self.bc = bc
        self.bcadj = bcadj
        self._assemble_solverM(Vm)
        self.assemble_A()
        self.assemble_RHS(RHSinput)
        self.Regul = Regul
        self.regparam = 1.0
        if Regul != []:
            self.PD = self.Regul.isPD()
        # Counters, tolerances and others
        self.nbPDEsolves = 0  # Updated when solve_A called
        self.nbfwdsolves = 0  # Counter for plots
        self.nbadjsolves = 0  # Counter for plots
        # MPI:
        self.mycomm = mycomm

    def copy(self):
        """Define a copy method"""

        V = self.trial.function_space()
        Vm = self.mtrial.function_space()
        newobj = self.__class__(V, Vm, self.bc, self.bcadj, [], self.ObsOp, \
        self.UD, self.Regul, self.Data, False)
        newobj.RHS = self.RHS
        newobj.update_m(self.m)
        return newobj

    def mult(self, mhat, y):
        """mult(self, mhat, y): do y = Hessian * mhat
        member self.GN sets full Hessian (=1.0) or GN Hessian (=0.0)"""

        N = self.Nbsrc  # Number of sources
        y[:] = np.zeros(self.lenm)

        for C, E in zip(self.C, self.E):
            C.transpmult(mhat, self.rhs.vector())
            if self.bcadj is not None:
                self.bcadj.apply(self.rhs.vector())
            self.solve_A(self.u.vector(), -self.rhs.vector())

            E.transpmult(mhat, self.rhs.vector())
            Etmhat = self.rhs.vector().array()
            self.rhs.vector().axpy(1.0, self.ObsOp.incradj(self.u))
            if self.bcadj is not None:
                self.bcadj.apply(self.rhs.vector())
            self.solve_A(self.p.vector(), -self.rhs.vector())

            y.axpy(1.0 / N, C * self.p.vector())
            y.axpy(self.GN / N, E * self.u.vector())

        y.axpy(self.regparam, self.Regul.hessian(mhat))

    # Getters
    def getm(self):
        return self.m

    def getmarray(self):
        return self.m.vector().array()

    def getmcopyarray(self):
        return self.mcopy.vector().array()

    def getVm(self):
        return self.mtrial.function_space()

    def getMGarray(self):
        return self.MG.vector().array()

    def getMGvec(self):
        return self.MGv

    def getGradarray(self):
        return self.Grad.vector().array()

    def getGradnorm(self):
        return self.Gradnorm

    def getsrchdirarray(self):
        return self.srchdir.vector().array()

    def getsrchdirvec(self):
        return self.srchdir.vector()

    def getsrchdirnorm(self):
        return np.sqrt(
            (self.MM * self.getsrchdirvec()).inner(self.getsrchdirvec()))

    def getgradxdir(self):
        return self.gradxdir

    def getcost(self):
        return self.cost, self.misfit, self.regul

    def getprecond(self):
        return self.Regul.getprecond()
#        Prec = PETScKrylovSolver("richardson", "amg")
#        Prec.parameters["maximum_iterations"] = 1
#        Prec.parameters["error_on_nonconvergence"] = False
#        Prec.parameters["nonzero_initial_guess"] = False
#        Prec.set_operator(self.Regul.get_precond())
#        return Prec

    def getMass(self):
        return self.MM

    # Setters
    def setsrchdir(self, arr):
        self.srchdir.vector()[:] = arr

    def setgradxdir(self, valueloc):
        """Sum all local results for Grad . Srch_dir"""
        try:
            valueglob = MPI.sum(self.mycomm, valueloc)
        except:
            valueglob = valueloc
        self.gradxdir = valueglob

    # Solve
    def solvefwd(self, cost=False):
        """Solve fwd operators for given RHS"""

        self.nbfwdsolves += 1
        if cost: self.misfit = 0.0
        self.U = []
        self.C = []
        for ii, rhs in enumerate(self.RHS):
            self.solve_A(self.u.vector(), rhs)
            u_obs, noiselevel = self.ObsOp.obs(self.u)
            self.U.append(u_obs)
            if cost:
                self.misfit += self.ObsOp.costfct(u_obs, self.UD[ii])
            self.C.append(assemble(self.c))
        if cost:
            self.misfit /= len(self.U)
            self.regul = self.Regul.cost(self.m)
            self.cost = self.misfit + self.regparam * self.regul

    def solvefwd_cost(self):
        """Solve fwd operators for given RHS and compute cost fct"""

        self.solvefwd(True)

    def solveadj(self, grad=False):
        """Solve adj operators"""

        self.nbadjsolves += 1
        self.Nbsrc = len(self.UD)
        if grad:
            self.MG.vector().zero()
        self.E = []

        for ii, C in enumerate(self.C):
            self.ObsOp.assemble_rhsadj(self.U[ii], self.UD[ii], \
            self.rhs, self.bcadj)
            self.solve_A(self.p.vector(), self.rhs.vector())
            self.E.append(assemble(self.e))
            if grad:
                self.MG.vector().axpy(1.0 / self.Nbsrc, C * self.p.vector())

        if grad:
            self.MG.vector().axpy(self.regparam, self.Regul.grad(self.m))
            self.solverM.solve(self.Grad.vector(), self.MG.vector())
            self.Gradnorm = np.sqrt(self.Grad.vector().inner(self.MG.vector()))

    def solveadj_constructgrad(self):
        """Solve adj operators and assemble gradient"""

        self.solveadj(True)

    # Assembler
    def assemble_A(self):
        """Assemble operator A(m)"""

        self.A = assemble(self.a)
        if self.bc is not None:
            self.bc.apply(self.A)
        compute_eigfenics(self.A, 'eigA.txt')
        self.set_solver()

    def solve_A(self, b, f):
        """Solve system of the form A.b = f, 
        with b and f in form to be used in solver."""

        self.solver.solve(b, f)
        self.nbPDEsolves += 1

    def assemble_RHS(self, RHSin):
        """Assemble RHS for fwd solve"""

        if RHSin == []: self.RHS = None
        else:
            self.RHS = []
            for rhs in RHSin:
                if isinstance(rhs, Expression):
                    L = rhs * self.test * dx
                    b = assemble(L)
                    if self.bc is not None:
                        self.bc.apply(b)
                    self.RHS.append(b)
                elif isinstance(rhs, GenericVector):
                    self.RHS.append(rhs)
                else:
                    raise WrongInstanceError(
                        "rhs should be an Expression or a GenericVector")

    def _assemble_solverM(self, Vm):

        self.MM = assemble(inner(self.mtrial, self.mtest) * dx)
        self.solverM = PETScKrylovSolver('cg', 'jacobi')
        self.solverM.parameters["maximum_iterations"] = 1000
        self.solverM.parameters["relative_tolerance"] = 1e-12
        self.solverM.parameters["error_on_nonconvergence"] = True
        self.solverM.parameters["nonzero_initial_guess"] = False
        #        self.solverM = LUSolver()
        #        self.solverM.parameters['reuse_factorization'] = True
        #        self.solverM.parameters['symmetric'] = True
        self.solverM.set_operator(self.MM)

    # Update param
    def update_Data(self, Data):
        """Update Data member"""

        self.Data = Data
        self.assemble_A()
        self.reset()

    def update_m(self, m):
        """Update values of parameter m"""

        if isinstance(m, np.ndarray):
            self.m.vector()[:] = m
        elif isinstance(m, Function):
            self.m.assign(m)
        elif isinstance(m, float):
            self.m.vector()[:] = m
        elif isinstance(m, int):
            self.m.vector()[:] = float(m)
        else:
            raise WrongInstanceError('Format for m not accepted')
        self.assemble_A()
        self.reset()

    def backup_m(self):
        self.mcopy.assign(self.m)

    def restore_m(self):
        self.update_m(self.mcopy)

    def reset(self):
        """Reset U, C and E"""
        self.U = []
        self.C = []
        self.E = []

    def set_solver(self):
        """Reset solver for fwd operator"""

        #self.solver = LUSolver()
        #self.solver.parameters['reuse_factorization'] = True
        self.solver = PETScKrylovSolver("cg", "amg")
        self.solver.parameters["maximum_iterations"] = 1000
        self.solver.parameters["relative_tolerance"] = 1e-12
        self.solver.parameters["error_on_nonconvergence"] = True
        self.solver.parameters["nonzero_initial_guess"] = False
        self.solver.set_operator(self.A)

    def addPDEcount(self, increment=1):
        """Increase 'nbPDEsolves' by 'increment'"""
        self.nbPDEsolves += increment

    def resetPDEsolves(self):
        self.nbPDEsolves = 0

    # Additional methods for compatibility with CG solver:
    def init_vector(self, x, dim):
        """Initialize vector x to be compatible with parameter
         Does not work in dolfin 1.3.0"""
        self.MM.init_vector(x, 0)

    def init_vector130(self):
        """Initialize vector x to be compatible with parameter"""
        return Vector(Function(self.mcopy.function_space()).vector())

    # Abstract methods
    @abc.abstractmethod
    def _wkforma(self):
        self.a = []

    @abc.abstractmethod
    def _wkformc(self):
        self.c = []

    @abc.abstractmethod
    def _wkforme(self):
        self.e = []


    def inversion(self, initial_medium, target_medium, mpicomm, \
    parameters_in=[], myplot=None):
        """ solve inverse problem with that objective function """

        parameters = {'tolgrad':1e-10, 'tolcost':1e-14, 'maxnbNewtiter':50, \
        'maxtolcg':0.5}
        parameters.update(parameters_in)
        maxnbNewtiter = parameters['maxnbNewtiter']
        tolgrad = parameters['tolgrad']
        tolcost = parameters['tolcost']
        tolcg = parameters['maxtolcg']
        mpirank = MPI.rank(mpicomm)

        self.update_m(initial_medium)
        self._plotm(myplot, 'init')

        if mpirank == 0:
            print '\t{:12s} {:10s} {:12s} {:12s} {:12s} {:10s} \t{:10s} {:12s} {:12s}'.format(\
            'iter', 'cost', 'misfit', 'reg', '|G|', 'medmisf', 'a_ls', 'tol_cg', 'n_cg')
        dtruenorm = np.sqrt(target_medium.vector().\
        inner(self.MM*target_medium.vector()))

        self.solvefwd_cost()
        for it in xrange(maxnbNewtiter):
            self.solveadj_constructgrad()  # compute gradient

            if it == 0: gradnorm0 = self.Gradnorm
            diff = self.m.vector() - target_medium.vector()
            medmisfit = np.sqrt(diff.inner(self.MM * diff))
            if mpirank == 0:
                print '{:12d} {:12.4e} {:12.2e} {:12.2e} {:11.4e} {:10.2e} ({:4.2f})'.\
                format(it, self.cost, self.misfit, self.regul, \
                self.Gradnorm, medmisfit, medmisfit/dtruenorm),
            self._plotm(myplot, str(it))
            self._plotgrad(myplot, str(it))

            if self.Gradnorm < gradnorm0 * tolgrad or self.Gradnorm < 1e-12:
                if mpirank == 0:
                    print '\nGradient sufficiently reduced -- optimization stopped'
                break

            # Compute search direction:
            tolcg = min(tolcg, np.sqrt(self.Gradnorm / gradnorm0))
            self.assemble_hessian()  # for regularization
            cgiter, cgres, cgid, tolcg = compute_searchdirection(
                self, 'Newt', tolcg)
            self._plotsrchdir(myplot, str(it))

            # Line search:
            cost_old = self.cost
            statusLS, LScount, alpha = bcktrcklinesearch(self, 12)
            if mpirank == 0:
                print '{:11.3f} {:12.2e} {:10d}'.format(alpha, tolcg, cgiter)
            if self.PD: self.Regul.update_w(self.srchdir.vector(), alpha)

            if np.abs(self.cost - cost_old) / np.abs(cost_old) < tolcost:
                if mpirank == 0:
                    if tolcg < 1e-14:
                        print 'Cost function stagnates -- optimization aborted'
                        break
                    tolcg = 0.001 * tolcg

    def assemble_hessian(self):
        self.Regul.assemble_hessian(self.m)

    def _plotm(self, myplot, index):
        """ plot media during inversion """
        if not myplot == None:
            myplot.set_varname('m' + index)
            myplot.plot_vtk(self.m)

    def _plotgrad(self, myplot, index):
        """ plot grad during inversion """
        if not myplot == None:
            myplot.set_varname('Grad_m' + index)
            myplot.plot_vtk(self.Grad)

    def _plotsrchdir(self, myplot, index):
        """ plot srchdir during inversion """
        if not myplot == None:
            myplot.set_varname('srchdir_m' + index)
            myplot.plot_vtk(self.srchdir)
Пример #50
0
class RungeKuttaDGTimestepping(object):
    def __init__(
        self,
        simulation,
        a,
        L,
        u,
        up,
        func_name,
        order=None,
        explicit_funcs=None,
        bcs=None,
    ):
        """
        RKDG timestepping. A is a block diagonal mass matrix form (u*v*dx),
        L is the form of the right hand side of du/dt = L. The functions
        u and up are the result and the previous value respectively. If order
        is not given it is taken as P+1 where P is the element degree of u.

        It is assumed that up is used in the form L so that changing it causes
        L to change. L should not depend on u or t, only up. The up function
        *will be modified* when running .step()! It will not contain a sensible
        value afterwords, while the u function will contain the time integrated
        value after the time step. The up function should contain the value of
        u at the beginning of the time step before running .step()

        Explicit functions are other functions that are a part of L. They will
        be extrapolated. The API is: give [ux, up, upp, ... uppp] where ux is the
        function that is explicit in L and will be extrapolated to the fractional
        RK time step based on the values at previous time steps. The order of
        extrapolation depends on the number of previous values upp...pps given.
        """
        self.simulation = simulation

        V = u.function_space()
        if order is None:
            order = V.ufl_element().degree() + 1
        self.order = order

        # Number of stages
        if order <= 3:
            S = order
        else:
            self.A, self.B, _C = get_ssp_rk_coefficients(order)
            S = len(self.A)
        simulation.log.info(
            '    Preparing SSP RK method of order %d with %d stages' %
            (order, S))

        self.funcs_to_extrapolate = explicit_funcs
        self.bcs = bcs
        self.u = u
        self.up = up
        self.us = [Function(V) for _ in range(S - 1)]
        self.dus = [Function(V) for _ in range(S)]
        self.solver = LocalSolver(a, L)
        self.solver.factorize()
        self.slope_limiters = {
            du: SlopeLimiter(simulation, func_name, du)
            for du in self.dus
        }

        if self.order > 3:
            # Need one extra function storage when running the generic code
            self.up_store = Function(V)

    def _solve(self, du, fdt, uexpl):
        """
        Assemble L and use the block diagonality of the mass matrix to run
        the pre-factorized local solver instead of a global solve

        We first update any explicit functions (uexpl which we get from the
        previous RK step and self.funcs_to_extrapolate which we extrapolate).
        We also update the boundary conditions in case they are time dependent
        """
        if uexpl is not self.up:
            self.up.assign(uexpl)

        # The RK sub time step
        orig_t = self.simulation.time
        t = orig_t - (1 - fdt) * self.simulation.dt
        self.simulation.time = t

        # Update time dependent explicit functions in L to the RK sub time step
        for funcs in self.funcs_to_extrapolate:
            ux = funcs[0]
            ups = funcs[1:]

            ux.vector().zero()
            if len(ups) == 2:
                old1, old2 = ups
                ux.vector().axpy(fdt + 1, old1.vector())
                ux.vector().axpy(-fdt, old2.vector())

            elif len(ups) == 3:
                old1, old2, old3 = ups
                ux.vector().axpy(fdt**2 / 2 + 3 * fdt / 2 + 1, old1.vector())
                ux.vector().axpy(-fdt**2 - 2 * fdt, old2.vector())
                ux.vector().axpy(fdt**2 / 2 + fdt / 2, old3.vector())

            else:
                raise NotImplementedError(
                    'Extrapolation of degree %d not implemented' %
                    (len(ups) - 1))

        # Update time dependent BCs in L to the RK sub time step
        for bc in self.bcs:
            bc.update()

        self.solver.solve_local_rhs(du)
        self.simulation.time = orig_t

        self.slope_limiters[du].run()

    def step(self, dt):
        """
        Use Runge-Kutta to step dt forward in time
        """
        u, up = self.u, self.up
        us, dus = self.us, self.dus
        K, ls = self.order, self.solver

        if K == 1:
            u.assign(up)
            ls.solve_global_rhs(dus[0])
            u.vector().axpy(dt, dus[0].vector())

        elif K == 2:
            u.assign(up)
            self._solve(dus[0], 0.0, up)
            us[0].assign(u)
            us[0].vector().axpy(dt, dus[0].vector())

            self._solve(dus[1], 1.0, us[0])
            u.vector().axpy(0.5 * dt, dus[0].vector())
            u.vector().axpy(0.5 * dt, dus[1].vector())

        elif K == 3:
            u.assign(up)
            self._solve(dus[0], 0.0, up)
            us[0].assign(u)
            us[0].vector().axpy(dt, dus[0].vector())

            self._solve(dus[1], 0.5, us[0])
            us[1].assign(u)
            us[1].vector().axpy(0.25 * dt, dus[0].vector())
            us[1].vector().axpy(0.25 * dt, dus[1].vector())

            self._solve(dus[2], 1.0, us[1])
            u.vector().axpy(1 / 6 * dt, dus[0].vector())
            u.vector().axpy(1 / 6 * dt, dus[1].vector())
            u.vector().axpy(2 / 3 * dt, dus[2].vector())

        else:
            # Generic implementation
            self.up_store.assign(up)
            A, B = self.A, self.B
            S = len(A)  # number of stages

            for i in range(S):
                # FIXME: use the _solve() method ... we must first calculate the effective time step
                if i > 0:
                    up.assign(us[i - 1])
                ls.solve_local_rhs(dus[i])

                un = us[i] if i < S - 1 else u
                un.vector().zero()

                for j in range(i + 1):
                    a, b = A[i][j], B[i][j]

                    if a != 0:
                        uo = us[j - 1] if j > 0 else self.up_store
                        un.vector().axpy(a, uo.vector())
                    if b != 0:
                        un.vector().axpy(b * dt, dus[j].vector())
Пример #51
0
    def solve(self, problem):
        self.problem = problem
        doSave = problem.doSave
        save_this_step = False
        onlyVel = problem.saveOnlyVel
        dt = self.metadata['dt']

        nu = Constant(self.problem.nu)
        self.tc.init_watch('init', 'Initialization', True, count_to_percent=False)
        self.tc.init_watch('rhs', 'Assembled right hand side', True, count_to_percent=True)
        self.tc.init_watch('applybc1', 'Applied velocity BC 1st step', True, count_to_percent=True)
        self.tc.init_watch('applybc3', 'Applied velocity BC 3rd step', True, count_to_percent=True)
        self.tc.init_watch('applybcP', 'Applied pressure BC or othogonalized rhs', True, count_to_percent=True)
        self.tc.init_watch('assembleMatrices', 'Initial matrix assembly', False, count_to_percent=True)
        self.tc.init_watch('solve 1', 'Running solver on 1st step', True, count_to_percent=True)
        self.tc.init_watch('solve 2', 'Running solver on 2nd step', True, count_to_percent=True)
        self.tc.init_watch('solve 3', 'Running solver on 3rd step', True, count_to_percent=True)
        self.tc.init_watch('solve 4', 'Running solver on 4th step', True, count_to_percent=True)
        self.tc.init_watch('assembleA1', 'Assembled A1 matrix (without stabiliz.)', True, count_to_percent=True)
        self.tc.init_watch('assembleA1stab', 'Assembled A1 stabilization', True, count_to_percent=True)
        self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True)
        self.tc.init_watch('saveVel', 'Saved velocity', True)

        self.tc.start('init')

        # Define function spaces (P2-P1)
        mesh = self.problem.mesh
        self.V = VectorFunctionSpace(mesh, "Lagrange", 2)  # velocity
        self.Q = FunctionSpace(mesh, "Lagrange", 1)  # pressure
        self.PS = FunctionSpace(mesh, "Lagrange", 2)  # partial solution (must be same order as V)
        self.D = FunctionSpace(mesh, "Lagrange", 1)  # velocity divergence space

        problem.initialize(self.V, self.Q, self.PS, self.D)

        # Define trial and test functions
        u = TrialFunction(self.V)
        v = TestFunction(self.V)
        p = TrialFunction(self.Q)
        q = TestFunction(self.Q)

        n = FacetNormal(mesh)
        I = Identity(find_geometric_dimension(u))

        # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure
        [u1, u0, p0] = self.problem.get_initial_conditions([{'type': 'v', 'time': -dt},
                                                            {'type': 'v', 'time': 0.0},
                                                            {'type': 'p', 'time': 0.0}])

        u_ = Function(self.V)  # current tentative velocity
        u_cor = Function(self.V)  # current corrected velocity
        p_ = Function(self.Q)  # current pressure or pressure help function from rotation scheme
        p_mod = Function(self.Q)  # current modified pressure from rotation scheme

        # Define coefficients
        k = Constant(self.metadata['dt'])
        f = Constant((0, 0, 0))

        # Define forms
        # step 1: Tentative velocity, solve to u_
        u_ext = 1.5 * u0 - 0.5 * u1  # extrapolation for convection term

        # Stabilisation
        h = CellSize(mesh)
        if self.args.cbc_tau:
            # used in Simula cbcflow project
            tau = Constant(self.stabCoef) * h / (sqrt(inner(u_ext, u_ext)) + h)
        else:
            # proposed in R. Codina: On stabilized finite element methods for linear systems of
            # convection-diffusion-reaction equations.
            tau = Constant(self.stabCoef) * k * h ** 2 / (
            2 * nu * k + k * h * sqrt(DOLFIN_EPS + inner(u_ext, u_ext)) + h ** 2)
            # DOLFIN_EPS is added because of FEniCS bug that inner(u_ext, u_ext) can be negative when u_ext = 0

        if self.use_full_SUPG:
            v1 = v + tau * 0.5 * dot(grad(v), u_ext)
            parameters['form_compiler']['quadrature_degree'] = 6
        else:
            v1 = v

        def nonlinearity(function):
            if self.args.ema:
                return 2 * inner(dot(sym(grad(function)), u_ext), v1) * dx + inner(div(function) * u_ext, v1) * dx
            else:
                return inner(dot(grad(function), u_ext), v1) * dx

        def diffusion(fce):
            if self.useLaplace:
                return nu * inner(grad(fce), grad(v1)) * dx
            else:
                form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx
                if self.bcv == 'CDN':
                    return form
                if self.bcv == 'LAP':
                    return form - inner(nu * dot(grad(fce).T, n), v1) * problem.get_outflow_measure_form()
                if self.bcv == 'DDN':
                    return form  # additional term must be added to non-constant part

        def pressure_rhs():
            if self.args.bc == 'outflow':
                return inner(p0, div(v1)) * dx
            else:
                return inner(p0, div(v1)) * dx - inner(p0 * n, v1) * problem.get_outflow_measure_form()

        a1_const = (1. / k) * inner(u, v1) * dx + diffusion(0.5 * u)
        a1_change = nonlinearity(0.5 * u)
        if self.bcv == 'DDN':
            # does not penalize influx for current step, only for the next one
            # this can lead to oscilation:
            # DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx...
            # u and u_ext cannot be switched, min_value is nonlinear function
            a1_change += -0.5 * min_value(Constant(0.), inner(u_ext, n)) * inner(u,
                                                                                 v1) * problem.get_outflow_measure_form()
            # NT works only with uflacs compiler

        L1 = (1. / k) * inner(u0, v1) * dx - nonlinearity(0.5 * u0) - diffusion(0.5 * u0) + pressure_rhs()
        if self.bcv == 'DDN':
            L1 += 0.5 * min_value(0., inner(u_ext, n)) * inner(u0, v1) * problem.get_outflow_measure_form()

        # Non-consistent SUPG stabilisation
        if self.stabilize and not self.use_full_SUPG:
            # a1_stab = tau*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx
            a1_stab = 0.5 * tau * inner(dot(grad(u), u_ext), dot(grad(v), u_ext)) * dx(None, {'quadrature_degree': 6})
            # optional: to use Crank Nicolson in stabilisation term following change of RHS is needed:
            # L1 += -0.5*tau*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6})

        outflow_area = Constant(problem.outflow_area)
        need_outflow = Constant(0.0)
        if self.useRotationScheme:
            # Rotation scheme
            F2 = inner(grad(p), grad(q)) * dx + (1. / k) * q * div(u_) * dx
        else:
            # Projection, solve to p_
            if self.forceOutflow and problem.can_force_outflow:
                info('Forcing outflow.')
                F2 = inner(grad(p - p0), grad(q)) * dx + (1. / k) * q * div(u_) * dx
                for m in problem.get_outflow_measures():
                    F2 += (1. / k) * (1. / outflow_area) * need_outflow * q * m
            else:
                F2 = inner(grad(p - p0), grad(q)) * dx + (1. / k) * q * div(u_) * dx
        a2, L2 = system(F2)

        # step 3: Finalize, solve to u_
        if self.useRotationScheme:
            # Rotation scheme
            F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_), v) * dx
        else:
            F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_ - p0), v) * dx
        a3, L3 = system(F3)

        if self.useRotationScheme:
            # Rotation scheme: modify pressure
            F4 = (p - p0 - p_ + nu * div(u_)) * q * dx
            a4, L4 = system(F4)

        # Assemble matrices
        self.tc.start('assembleMatrices')
        A1_const = assemble(a1_const)  # must be here, so A1 stays one Python object during repeated assembly
        A1_change = A1_const.copy()  # copy to get matrix with same sparse structure (data will be overwritten)
        if self.stabilize and not self.use_full_SUPG:
            A1_stab = A1_const.copy()  # copy to get matrix with same sparse structure (data will be overwritten)
        A2 = assemble(a2)
        A3 = assemble(a3)
        if self.useRotationScheme:
            A4 = assemble(a4)
        self.tc.end('assembleMatrices')

        if self.solvers == 'direct':
            self.solver_vel_tent = LUSolver('mumps')
            self.solver_vel_cor = LUSolver('mumps')
            self.solver_p = LUSolver('mumps')
            if self.useRotationScheme:
                self.solver_rot = LUSolver('mumps')
        else:
            # NT 2016-1  KrylovSolver >> PETScKrylovSolver

            # not needed, chosen not to use hypre_parasails:
            # if self.prec_v == 'hypre_parasails':  # in FEniCS 1.6.0 inaccessible using KrylovSolver class
            #     self.solver_vel_tent = PETScKrylovSolver('gmres')   # PETSc4py object
            #     self.solver_vel_tent.ksp().getPC().setType('hypre')
            #     PETScOptions.set('pc_hypre_type', 'parasails')
            #     # this is global setting, but preconditioners for pressure solvers are set by their constructors
            # else:
            self.solver_vel_tent = PETScKrylovSolver('gmres', self.args.precV)  # nonsymetric > gmres
            # cannot use 'ilu' in parallel
            self.solver_vel_cor = PETScKrylovSolver('cg', self.args.precVC)
            self.solver_p = PETScKrylovSolver(self.args.solP, self.args.precP)  # almost (up to BC) symmetric > CG
            if self.useRotationScheme:
                self.solver_rot = PETScKrylovSolver('cg', 'hypre_amg')

        # setup Krylov solvers
        if self.solvers == 'krylov':
            # Get the nullspace if there are no pressure boundary conditions
            foo = Function(self.Q)  # auxiliary vector for setting pressure nullspace
            if self.args.bc == 'nullspace':
                null_vec = Vector(foo.vector())
                self.Q.dofmap().set(null_vec, 1.0)
                null_vec *= 1.0 / null_vec.norm('l2')
                self.null_space = VectorSpaceBasis([null_vec])
                as_backend_type(A2).set_nullspace(self.null_space)

            # apply global options for Krylov solvers
            solver_options = {'monitor_convergence': True, 'maximum_iterations': 10000, 'nonzero_initial_guess': True}
            # 'nonzero_initial_guess': True   with  solver.solve(A, u, b) means that
            # Solver will use anything stored in u as an initial guess
            for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_rot, self.solver_p] if \
                    self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]:
                for key, value in solver_options.items():
                    try:
                        solver.parameters[key] = value
                    except KeyError:
                        info('Invalid option %s for KrylovSolver' % key)
                        return 1

            if self.args.solP == 'richardson':
                self.solver_p.parameters['monitor_convergence'] = False

            self.solver_vel_tent.parameters['relative_tolerance'] = 10 ** (-self.args.prv1)
            self.solver_vel_tent.parameters['absolute_tolerance'] = 10 ** (-self.args.pav1)
            self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12
            self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4
            self.solver_p.parameters['relative_tolerance'] = 10 ** (-self.args.prp)
            self.solver_p.parameters['absolute_tolerance'] = 10 ** (-self.args.pap)
            if self.useRotationScheme:
                self.solver_rot.parameters['relative_tolerance'] = 10E-10
                self.solver_rot.parameters['absolute_tolerance'] = 10E-10

            if self.args.Vrestart > 0:
                self.solver_vel_tent.parameters['gmres']['restart'] = self.args.Vrestart

            if self.args.solP == 'gmres' and self.args.Prestart > 0:
                self.solver_p.parameters['gmres']['restart'] = self.args.Prestart

        # boundary conditions
        bcu, bcp = problem.get_boundary_conditions(self.args.bc == 'outflow', self.V, self.Q)
        self.tc.end('init')
        # Time-stepping
        info("Running of Incremental pressure correction scheme n. 1")
        ttime = self.metadata['time']
        t = dt
        step = 1

        # debug function
        if problem.args.debug_rot:
            plot_cor_v = Function(self.V)

        while t < (ttime + dt / 2.0):
            self.problem.update_time(t, step)
            if self.MPI_rank == 0:
                problem.write_status_file(t)

            if doSave:
                save_this_step = problem.save_this_step

            # assemble matrix (it depends on solution)
            self.tc.start('assembleA1')
            assemble(a1_change, tensor=A1_change)  # assembling into existing matrix is faster than assembling new one
            A1 = A1_const.copy()  # we dont want to change A1_const
            A1.axpy(1, A1_change, True)
            self.tc.end('assembleA1')
            self.tc.start('assembleA1stab')
            if self.stabilize and not self.use_full_SUPG:
                assemble(a1_stab, tensor=A1_stab)  # assembling into existing matrix is faster than assembling new one
                A1.axpy(1, A1_stab, True)
            self.tc.end('assembleA1stab')

            # Compute tentative velocity step
            begin("Computing tentative velocity")
            self.tc.start('rhs')
            b = assemble(L1)
            self.tc.end('rhs')
            self.tc.start('applybc1')
            [bc.apply(A1, b) for bc in bcu]
            self.tc.end('applybc1')
            try:
                self.tc.start('solve 1')
                self.solver_vel_tent.solve(A1, u_.vector(), b)
                self.tc.end('solve 1')
                if save_this_step:
                    self.tc.start('saveVel')
                    problem.save_vel(True, u_)
                    self.tc.end('saveVel')
                if save_this_step and not onlyVel:
                    problem.save_div(True, u_)
                problem.compute_err(True, u_, t)
                problem.compute_div(True, u_)
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            end()

            if self.useRotationScheme:
                begin("Computing tentative pressure")
            else:
                begin("Computing pressure")
            if self.forceOutflow and problem.can_force_outflow:
                out = problem.compute_outflow(u_)
                info('Tentative outflow: %f' % out)
                n_o = -problem.last_inflow - out
                info('Needed outflow: %f' % n_o)
                need_outflow.assign(n_o)
            self.tc.start('rhs')
            b = assemble(L2)
            self.tc.end('rhs')
            self.tc.start('applybcP')
            [bc.apply(A2, b) for bc in bcp]
            if self.args.bc == 'nullspace':
                self.null_space.orthogonalize(b)
            self.tc.end('applybcP')
            try:
                self.tc.start('solve 2')
                self.solver_p.solve(A2, p_.vector(), b)
                self.tc.end('solve 2')
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            if self.useRotationScheme:
                foo = Function(self.Q)
                foo.assign(p_ + p0)
                if save_this_step and not onlyVel:
                    problem.averaging_pressure(foo)
                    problem.save_pressure(True, foo)
            else:
                foo = Function(self.Q)
                foo.assign(p_)  # we do not want to change p_ by averaging
                if save_this_step and not onlyVel:
                    problem.averaging_pressure(foo)
                    problem.save_pressure(False, foo)
            end()

            begin("Computing corrected velocity")
            self.tc.start('rhs')
            b = assemble(L3)
            self.tc.end('rhs')
            if not self.args.B:
                self.tc.start('applybc3')
                [bc.apply(A3, b) for bc in bcu]
                self.tc.end('applybc3')
            try:
                self.tc.start('solve 3')
                self.solver_vel_cor.solve(A3, u_cor.vector(), b)
                self.tc.end('solve 3')
                problem.compute_err(False, u_cor, t)
                problem.compute_div(False, u_cor)
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            if save_this_step:
                self.tc.start('saveVel')
                problem.save_vel(False, u_cor)
                self.tc.end('saveVel')
            if save_this_step and not onlyVel:
                problem.save_div(False, u_cor)
            end()

            if self.useRotationScheme:
                begin("Rotation scheme pressure correction")
                self.tc.start('rhs')
                b = assemble(L4)
                self.tc.end('rhs')
                try:
                    self.tc.start('solve 4')
                    self.solver_rot.solve(A4, p_mod.vector(), b)
                    self.tc.end('solve 4')
                except RuntimeError as inst:
                    problem.report_fail(t)
                    return 1
                if save_this_step and not onlyVel:
                    problem.averaging_pressure(p_mod)
                    problem.save_pressure(False, p_mod)
                end()

                if problem.args.debug_rot:
                    # save applied pressure correction (expressed as a term added to RHS of next tentative vel. step)
                    # see comment next to argument definition
                    plot_cor_v.assign(project(k * grad(nu * div(u_)), self.V))
                    problem.fileDict['grad_cor']['file'].write(plot_cor_v, t)

            # compute functionals (e. g. forces)
            problem.compute_functionals(u_cor, p_mod if self.useRotationScheme else p_, t, step)

            # Move to next time step
            self.tc.start('next')
            u1.assign(u0)
            u0.assign(u_cor)
            u_.assign(u_cor)  # use corrected velocity as initial guess in first step

            if self.useRotationScheme:
                p0.assign(p_mod)
            else:
                p0.assign(p_)

            t = round(t + dt, 6)  # round time step to 0.000001
            step += 1
            self.tc.end('next')

        info("Finished: Incremental pressure correction scheme n. 1")
        problem.report()
        return 0