Пример #1
0
    def test_multiplication(self):
        print(
            '\n== testing multiplication of system matrix for problem of weighted projection ===='
        )
        for dim, pol_order in itertools.product([2, 3], [1, 2]):
            N = 2  # no. of elements

            print('dim={0}, pol_order={1}, N={2}'.format(dim, pol_order, N))

            # creating MESH and defining MATERIAL
            if dim == 2:
                mesh = UnitSquareMesh(N, N)
                m = Expression("1+10*16*x[0]*(1-x[0])*x[1]*(1-x[1])",
                               degree=2)  # material coefficients
            elif dim == 3:
                mesh = UnitCubeMesh(N, N, N)
                m = Expression("1+10*16*x[0]*(1-x[0])*(1-x[1])*x[2]",
                               degree=2)  # material coefficients

            mesh.coordinates()[:] += 0.1 * np.random.random(
                mesh.coordinates().shape)  # mesh perturbation

            V = FunctionSpace(mesh, "CG", pol_order)  # original FEM space
            W = FunctionSpace(mesh, "CG", 2 * pol_order)  # double-grid space

            print('assembling local matrices for DoGIP...')
            Bhat = get_Bhat(
                dim, pol_order,
                problem=0)  # projection between V on W on a reference element
            AT_dogip = get_A_T(m, V, W, problem=0)

            dofmapV = V.dofmap()

            def system_multiplication_DoGIP(AT_dogip, Bhat, u_vec):
                # mutliplication with DoGIP decomposition
                Au = np.zeros_like(u_vec)
                for ii, cell in enumerate(cells(mesh)):
                    ind = dofmapV.cell_dofs(ii)  # local to global map
                    Au[ind] += Bhat.T.dot(AT_dogip[ii] * Bhat.dot(u_vec[ind]))
                return Au

            print('assembling FEM sparse matrix')
            u, v = TrialFunction(V), TestFunction(V)
            Asp = assemble(m * u * v * dx, tensor=EigenMatrix())  #
            Asp = Asp.sparray()

            print('multiplication...')
            ur = Function(V)  # creating random vector
            ur_vec = 5 * np.random.random(V.dim())
            ur.vector().set_local(ur_vec)

            Au_DoGIP = system_multiplication_DoGIP(
                AT_dogip, Bhat, ur_vec)  # DoGIP multiplication
            Auex = Asp.dot(ur_vec)  # FEM multiplication with sparse matrix

            # testing the difference between DoGIP and FEniCS
            self.assertAlmostEqual(0, np.linalg.norm(Auex - Au_DoGIP))
            print('...ok')
Пример #2
0
def function_interpolate(fin, fsout, coords=None, method='nearest'):
    """Copy a fenics Function

    Required arguments:
    fin: the input Function to copy
    fsout: the output FunctionSpace onto which to copy it

    Optional arguments:
    coords: the global coordinates of the DOFs of the FunctionSpace on
    which fin is defined. If not provided, gather_dof_coords will be
    called to get them. If you intend to do several interpolations
    from functions defined on the same FunctionSpace, you can avoid
    multiple calls to gather_dof_coords by supplying this argument.
    method='nearest': the method argument of
    scipy.interpolate.griddata.

    Returns:
    The values from fin are interpolated into fout, a Function defined
    on fsout. fout is returned.
    """
    comm = fsout.mesh().mpi_comm()
    logGATHER('comm.size', comm.size)
    try:
        fsin = fin.function_space()
    except AttributeError:  # fallback for Constant
        fout = fe.interpolate(fin, fsout)
        return (fout)
    vlen = fsin.dim()  # # dofs
    logGATHER('vlen', vlen)
    if coords is None:
        coords = gather_dof_coords(fsin)
    logGATHER('fsin.mesh().mpi_comm().size', fsin.mesh().mpi_comm().size)
    try:
        vec0 = fin.vector().gather_on_zero()
    except AttributeError:  # fallback for Constant
        fout = fe.interpolate(fin, fsout)
        return (fout)
    if comm.rank == 0:
        vec = vec0.copy()
    else:
        vec = np.empty(vlen)
    comm.Bcast(vec, root=0)
    logGATHER('vec', vec)
    fout = Function(fsout)
    fout.vector()[:] = griddata(coords,
                                vec,
                                fsout.tabulate_dof_coordinates(),
                                method=method).flatten()
    fout.vector().apply('insert')
    return (fout)
Пример #3
0
def solve():
    mesh = UnitIntervalMesh(10)
    V = FunctionSpace(mesh, "Lagrange", 1)
    dt = 0.01
    bt_params = bulk_temperature.BulkTemperatureParameters(V=V,
                                                           mesh=mesh,
                                                           dt=dt)
    bt = bulk_temperature.BulkTemperature(params=bt_params)
    a_T, L_T = bt.construct_variation_problem()
    T = Function(V)
    bt_params.T_prev.vector().set_local(
        np.random.uniform(0, -10,
                          bt_params.T_prev.vector().size()))
    map_params = mass_air_phase.MassAirPhaseParameters(V=V, mesh=mesh, dt=dt)
    map_params.T_s = T
    ma_ph = mass_air_phase.MassAirPhase(params=map_params)
    a_P, L_P = ma_ph.construct_variation_problem()
    P = Function(V)
    num_steps = 10
    t = 0
    T_sols = [bt_params.T_prev.vector().array()]
    P_sols = []
    for n in range(num_steps):
        bc = bt.make_bcs(V, 0, -10)
        _, bt_params.T_prev, T = bulk_temperature.step(time=t,
                                                       dt=dt,
                                                       t_prev=bt_params.T_prev,
                                                       t=T,
                                                       a=a_T,
                                                       l=L_T,
                                                       bc=bc)
        _, map_params.P_prev, P = mass_air_phase.step(time=t,
                                                      dt=dt,
                                                      p_prev=map_params.P_prev,
                                                      p=P,
                                                      a=a_P,
                                                      l=L_P)
        t += dt
        T_sols.append(T.vector().array())
        P_sols.append(P.vector().array())
    for T_s in T_sols:
        plt.plot(T_s)
    plt.figure()
    for P_s in P_sols:
        plt.plot(P_s)

    plt.show()
Пример #4
0
def main():
    np.random.seed(793817931)
    degree = 3
    mesh = UnitSquareMesh(2, 2)
    gdim = mesh.geometry().dim()
    fs = FunctionSpace(mesh, 'CG', degree)
    f = Function(fs)
    random_function(f)
    print(f.vector()[:])
Пример #5
0
    def test_DoGIP_vs_FEniCS(self):
        print(
            '\n== testing DoGIP vs. FEniCS for problem of weighted projection ===='
        )

        for dim, pol_order in itertools.product([2, 3], [1, 2]):
            print('dim={}; pol_order={}'.format(dim, pol_order))
            N = 2
            # creating MESH, defining MATERIAL and SOURCE
            if dim == 2:
                mesh = UnitSquareMesh(N, N)
                m = Expression("1+10*16*x[0]*(1-x[0])*x[1]*(1-x[1])",
                               degree=3)  # material coefficients
                f = Expression("x[0]*x[0]*x[1]", degree=2)
            elif dim == 3:
                mesh = UnitCubeMesh(N, N, N)
                m = Expression("1+100*x[0]*(1-x[0])*x[1]*x[2]",
                               degree=2)  # material coefficients
                f = Expression("(1-x[0])*x[1]*x[2]", degree=2)

            mesh.coordinates()[:] += 0.1 * np.random.random(
                mesh.coordinates().shape)  # mesh perturbation

            ## standard approach with FEniCS #############################################
            V = FunctionSpace(mesh, "CG", pol_order)  # original FEM space
            u, v = TrialFunction(V), TestFunction(V)
            u_fenics = Function(V)
            solve(m * u * v * dx == m * f * v * dx, u_fenics)

            ## DoGIP - double-grid integration with interpolation-projection #############
            W = FunctionSpace(mesh, "CG", 2 * pol_order)  # double-grid space
            w = TestFunction(W)
            A_dogip = assemble(
                m * w *
                dx).get_local()  # diagonal matrix of material coefficients
            b = assemble(m * f * v * dx)  # vector of right-hand side

            # assembling interpolation-projection matrix B
            B = get_B(V, W, problem=0)

            # # linear solver on double grid, standard
            Afun = lambda x: B.T.dot(A_dogip * B.dot(x))

            Alinoper = linalg.LinearOperator((V.dim(), V.dim()),
                                             matvec=Afun,
                                             dtype=np.float)
            x, info = linalg.cg(Alinoper,
                                b.get_local(),
                                x0=np.zeros(V.dim()),
                                tol=1e-10,
                                maxiter=1e3,
                                callback=None)

            # testing the difference between DoGIP and FEniCS
            self.assertAlmostEqual(
                0, np.linalg.norm(u_fenics.vector().get_local() - x))
            print('...ok')
Пример #6
0
def fluxes_from_temperature_full_domain(F, V):
    """
    compute flux from weak form (see p.3 in Toselli, Andrea, and Olof Widlund. Domain decomposition methods-algorithms and theory. Vol. 34. Springer Science & Business Media, 2006.)
    :param F: weak form with known u^{n+1}
    :param V: function space
    :param hy: spatial resolution perpendicular to flux direction
    :return:
    """
    fluxes_vector = assemble(F)  # assemble weak form -> evaluate integral
    v = TestFunction(V)
    fluxes = Function(V)  # create function for flux
    area = assemble(v * ds).get_local()
    for i in range(area.shape[0]):
        if area[i] != 0:  # put weight from assemble on function
            fluxes.vector()[i] = fluxes_vector[i] / area[i]  # scale by surface area
        else:
            assert(abs(fluxes_vector[i]) < 10**-10)  # for non surface parts, we expect zero flux
            fluxes.vector()[i] = fluxes_vector[i]
    return fluxes
Пример #7
0
def fluxes_from_temperature_full_domain(f, v_vec, k):
    """Computes flux from weak form (see p.3 in Toselli, Andrea, and Olof
    Widlund. Domain decomposition methods-algorithms and theory. Vol. 34.
    Springer Science & Business Media, 2006.).

    :param f: weak form with known u^{n+1}
    :param v_vec: vector function space
    :param k: thermal conductivity
    :return: fluxes function
    """
    fluxes_vector = assemble(f)  # assemble weak form -> evaluate integral
    v = TestFunction(v_vec)
    fluxes = Function(v_vec)  # create function for flux
    area = assemble(v * ds).get_local()
    for i in range(area.shape[0]):
        if area[i] != 0:  # put weight from assemble on function
            fluxes.vector()[i] = - k * fluxes_vector[i] / area[i]  # scale by surface area
        else:
            assert (abs(fluxes_vector[i]) < 1E-9)  # for non surface parts, we expect zero flux
            fluxes.vector()[i] = - k * fluxes_vector[i]
    return fluxes
Пример #8
0
  def get_nearest(self, fn):
    """
    returns a dolfin Function object with values given by interpolated
    nearest-neighbor data <fn>.
    """
    #FIXME: get to work with a change of projection.
    # get the dofmap to map from mesh vertex indices to function indicies :
    df    = self.func_space.dofmap()
    dfmap = df.vertex_to_dof_map(self.mesh)

    unew  = Function(self.func_space)         # existing dataset projection
    uocom = unew.vector().array()             # mesh indexed main vertex values

    d     = float64(self.data[fn])            # original matlab spec dataset

    # get arrays of x-values for specific domain
    xs    = self.x
    ys    = self.y

    for v in vertices(self.mesh):
      # mesh vertex x,y coordinate :
      i   = v.index()
      p   = v.point()
      x   = p.x()
      y   = p.y()

      # indexes of closest datapoint to specific dataset's x and y domains :
      idx = abs(xs - x).argmin()
      idy = abs(ys - y).argmin()

      # data value for closest value :
      dv  = d[idy, idx]
      if dv > 0:
        dv = 1.0
      uocom[i] = dv

    # set the values of the empty function's vertices to the data values :
    unew.vector().set_local(uocom[dfmap])
    return unew
Пример #9
0
  def get_nearest(self, fn):
    """
    returns a dolfin Function object with values given by interpolated 
    nearest-neighbor data <fn>.
    """
    #FIXME: get to work with a change of projection.
    # get the dofmap to map from mesh vertex indices to function indicies :
    df    = self.func_space.dofmap()
    dfmap = df.vertex_to_dof_map(self.mesh)
    
    unew  = Function(self.func_space)         # existing dataset projection
    uocom = unew.vector().array()             # mesh indexed main vertex values
    
    d     = float64(self.data[fn])            # original matlab spec dataset

    # get arrays of x-values for specific domain
    xs    = self.x
    ys    = self.y
    
    for v in vertices(self.mesh):
      # mesh vertex x,y coordinate :
      i   = v.index()
      p   = v.point()
      x   = p.x()
      y   = p.y()
      
      # indexes of closest datapoint to specific dataset's x and y domains :
      idx = abs(xs - x).argmin()
      idy = abs(ys - y).argmin()
      
      # data value for closest value :
      dv  = d[idy, idx] 
      if dv > 0:
        dv = 1.0
      uocom[i] = dv
    
    # set the values of the empty function's vertices to the data values :
    unew.vector().set_local(uocom[dfmap])
    return unew
Пример #10
0
def read_dem(bounds, res):
    """
    Function to read in a DEM from SRTM amd interplolate it onto a dolphyn mesh. This function uses the python package
    'elevation' (http://elevation.bopen.eu/en/stable/) and the gdal libraries.

    I will assume you want the 30m resolution SRTM model.

    :param bounds: west, south, east, north coordinates
    :return u_n, lx, ly: the elevation interpolated onto the dolphyn mesh and the lengths of the domain
    """
    west, south, east, north = bounds

    # Create a temporary file to store the DEM and go get it using elevation
    dem_path = 'tmp.tif'
    output = os.getcwd() + '/' + dem_path
    elv.clip(bounds=bounds, output=output, product='SRTM1')

    # read in the DEM into a numpy array
    gdal_data = gdal.Open(output)
    data_array = gdal_data.ReadAsArray().astype(np.float)

    # The DEM is 30m per pixel, so lets make a array for x and y at 30 m
    ny, nx = np.shape(data_array)
    lx = nx * 30
    ly = ny * 30

    x, y = np.meshgrid(np.linspace(0, lx / ly, nx), np.linspace(0, 1, ny))

    # Create mesh and define function space
    domain = Rectangle(Point(0, 0), Point(lx / ly, 1))
    mesh = generate_mesh(domain, res)
    V = FunctionSpace(mesh, 'P', 1)
    u_n = Function(V)

    # Get the global coordinates
    gdim = mesh.geometry().dim()
    gc = V.tabulate_dof_coordinates().reshape((-1, gdim))

    # Interpolate elevation into the initial condition
    elevation = interpolate.griddata((x.flatten(), y.flatten()),
                                     data_array.flatten(),
                                     (gc[:, 0], gc[:, 1]),
                                     method='nearest')
    u_n.vector()[:] = elevation

    # remove tmp DEM
    os.remove(output)

    return u_n, lx, ly, mesh, V
    def compute_steady_state(self):

        names = {'Cl', 'Na', 'K'}

        P1 = FiniteElement('P', fe.triangle, 1)
        element = MixedElement([P1, P1, P1])
        V = FunctionSpace(self.mesh, element)
        self.V_conc = V

        (u_cl, u_na, u_k) = TrialFunction(V)
        (v_cl, v_na, v_k) = TestFunction(V)

        assert (self.flow is not None)

        n = fe.FacetNormal(self.mesh)

        # F = ( self.F_diff_conv(u_cl, v_cl, n, grad(self.phi), 1. ,1., 0.)
        #    + self.F_diff_conv(u_na, v_na, n, grad(self.phi), 1. ,1., 0.)
        #    + self.F_diff_conv(u_k , v_k , n, grad(self.phi), 1. ,1., 0.) )

        dx, ds = self.dx, self.ds
        flow = self.flow
        F = inner(grad(u_cl), grad(v_cl)) * dx \
            + inner(flow, grad(u_cl)) * v_cl * dx \
            + inner(grad(u_na), grad(v_na)) * dx \
            + inner(flow, grad(u_na)) * v_na * dx \
            + inner(grad(u_k), grad(v_k)) * dx \
            + inner(flow, grad(u_k)) * v_k * dx

        a, L = fe.lhs(F), fe.rhs(F)
        a_mat = fe.assemble(a)
        L_vec = fe.assemble(L)
        # solve
        u = Function(V)
        fe.solve(a_mat, u.vector(), L_vec)

        u_cl, u_na, u_k = u.split()

        output1 = fe.File('/tmp/steady_state_cl.pvd')
        output1 << u_cl
        output2 = fe.File('/tmp/steady_state_na.pvd')
        output2 << u_na
        output3 = fe.File('/tmp/steady_state_k.pvd')
        output3 << u_k

        self.u_cl = u_cl
        self.u_na = u_na
        self.u_k = u_k
Пример #12
0
def test_solve() -> None:
    dt = 0.01
    mesh = UnitIntervalMesh(10)
    params = MassAirPhaseParameters(V=None, mesh=mesh, dt=dt)
    mass_air_phase = MassAirPhase(params=params)
    a, L = mass_air_phase.construct_variation_problem()
    print(type(a))
    print(type(L))
    P = Function(params.V)
    P_prev = params.P_prev
    num_steps = 10
    t = 0
    sols = []
    for n in range(num_steps):
        t, P_prev, P = step(time=t, dt=dt, p_prev=P_prev, p=P, a=a, l=L)
        sols.append(P.vector().get_local())
    for s in sols:
        plt.plot(s)

    plt.show()
Пример #13
0
def test_solve() -> None:
    mesh = UnitIntervalMesh(10)
    p = BulkTemperatureParameters(V=None, mesh=mesh, dt=0.01)
    bulk_temp = BulkTemperature(params=p)
    V = p.V
    dt = p.dt
    T_prev = p.T_prev
    a, L = bulk_temp.construct_variation_problem()

    T = Function(V)
    num_steps = 10
    t = 0
    sols = []
    for n in range(num_steps):
        bc = bulk_temp.make_bcs(V, 0, -10)
        t, T_prev, T = step(time=t, dt=dt, t_prev=T_prev, t=T, a=a, l=L, bc=bc)
        sols.append(T.vector().array())
    for s in sols:
        plt.plot(s)
    plt.show()
Пример #14
0
def fluid_to_solid(function, solid: Space, fluid: Space, param: Parameters,
                   subspace_index):

    function_vector = function.vector()
    vertex_to_dof_fluid = vertex_to_dof_map(
        fluid.function_space_split[subspace_index])
    result = Function(solid.function_space_split[subspace_index])
    result_vector = result.vector()
    vector_to_dif_solid = vertex_to_dof_map(
        solid.function_space_split[subspace_index])
    horizontal = param.NUMBER_ELEMENTS_HORIZONTAL + 1
    vertical = param.NUMBER_ELEMENTS_VERTICAL + 1
    for i in range(2):

        for j in range(horizontal):

            result_vector[vector_to_dif_solid[
                (vertical - i - 1) * horizontal +
                j]] = function_vector[vertex_to_dof_fluid[i * horizontal + j]]

    return result
Пример #15
0
def solve_wave_equation(u0, u1, u_boundary, f, domain, mesh, degree):
    """Solving the wave equation using CG-CG method.

    Args:
        u0: Initial data.
        u1: Initial velocity.
        u_boundary: Dirichlet boundary condition.
        f: Right-hand side.
        domain: Space-time domain.
        mesh: Computational mesh.
        degree: CG(degree) will be used as the finite element.
    Outputs:
        uh: Numerical solution.
    """
    # Element
    V = FunctionSpace(mesh, "CG", degree)
    # Measures on the initial and terminal slice
    mask = MeshFunction("size_t", mesh, mesh.topology().dim() - 1, 0)
    domain.get_initial_slice().mark(mask, 1)
    ends = ds(subdomain_data=mask)
    # Form
    g = Constant(((-1.0, 0.0), (0.0, 1.0)))
    u = TrialFunction(V)
    v = TestFunction(V)
    a = dot(grad(v), dot(g, grad(u))) * dx
    L = f * v * dx + u1 * v * ends(1)
    # Assembled matrices
    A = assemble(a, keep_diagonal=True)
    b = assemble(L, keep_diagonal=True)
    # Spatial boundary condition
    bc = DirichletBC(V, u_boundary, domain.get_spatial_boundary())
    bc.apply(A, b)
    # Temporal boundary conditions (by hand)
    (A, b) = apply_time_boundary_conditions(domain, V, u0, A, b)
    # Solve
    solver = LUSolver()
    solver.set_operator(A)
    uh = Function(V)
    solver.solve(uh.vector(), b)
    return uh
Пример #16
0
def morph_fenics(mesh, nodes, u, other_fix=[]):
    """
    Morph using FEniCS Functions.
    Returns a CG0 Function of DeltaX, such that
    w = DeltaX / dt
    """
    X_orig = mesh.coordinates().copy()
    X_defo = X_orig.copy()
    uN = u.compute_vertex_values().reshape(u.geometric_dimension(),
                                           len(nodes)).T
    X_defo[list(nodes), :] += uN
    # Warp the mesh
    X_new = do_tri_map(list(nodes) + list(other_fix), X_defo, X_orig)
    mesh.coordinates()[:] = X_new
    # Calculate w
    from fenics import VectorFunctionSpace, Function
    V = VectorFunctionSpace(mesh, "CG", 1)
    DeltaX = Function(V)
    nodeorder = V.dofmap().dofs(mesh, 0)
    utot = (X_new - X_orig).ravel()
    for i, l in enumerate(nodeorder):
        DeltaX.vector()[l] = utot[i]

    return DeltaX  # w = DeltaX / Dt
Пример #17
0
def get_B(V, W, problem=1, sparse=True, threshold=1e-14):
    """
    Projection/interpolation operator between spaces V and W on the whole computational domain.

    Parameters
    ----------
    V : FunctionSpace
        original finite element space
    W : FunctionSpace
        double-grid finite element space
    problem : {int, string}
        parameter defining problem: 0 - weighted projection, 1 - elliptic problem
    sparse : boolean
        determining the format of output matrix
    threshold : float
        parameter under which the values are considered to be zero

    Returns
    -------
    B : ndarray
        interpolation matrix between original and double-grid finite element basis
    """
    if problem in [1, 'elliptic']:  # elliptic problem
        operator = grad
        try:
            IP = Project_multiple(W)
        except:
            IP = project
    elif problem in [0, 'projection']:  # weighted projection
        operator = lambda x: x
        IP = interpolate

    if sparse:  # sparse version
        col = np.array([])
        row = np.array([])
        data = np.array([])
        for ii in range(V.dim()):
            bfun = Function(V)
            bfun.vector()[ii] = 1.
            bfund = IP(operator(bfun), W)
            vals = bfund.vector().get_local()
            indc = np.where(np.abs(vals) > threshold)[0]
            col = np.hstack([col, indc])
            row = np.hstack([row, ii * np.ones(indc.size)])
            data = np.hstack([data, vals[indc]])

        col = np.array(col)
        row = np.array(row)
        data = np.array(data)

        B = scipy.sparse.csr_matrix((data, (row, col)),
                                    shape=(V.dim(), W.dim()))

    else:  # dense version
        B = np.zeros([V.dim(), W.dim()])
        for ii in range(V.dim()):
            bfun = Function(V)
            bfun.vector()[ii] = 1.
            bfund = IP(operator(bfun), W)
            B[ii] = bfund.vector().get_local()()
        np.putmask(B, np.abs(B) < threshold, 0)
        B = scipy.sparse.csr_matrix(B)
        B.eliminate_zeros()

    return B.T
Пример #18
0
def main():
    nelements = 8
    dim = 1
    degree = 2
    params = {
        'alpha': 1,
        'beta': 1,
        'mu': 0.4,
        'Umax': 1,
        'Ufac': 4,
        'sU': 1,
        'sigma': 1,
        'N': 1,
        'M': 1,
        's': 10,
        'gamma': 1,
        'D': 0.01,
        'srho0': 0.01,
        'grhopen': 10,
    }
    U0str = 'Ufac/dim * (0.25 - pow(x[0] - mu, 2)/(2*sU*sU))'
    rho0str = 'N*exp(-beta*log((%s)+alpha)/(sigma*sigma/2))' % U0str

    def Vfunc(U):
        return -params['beta'] * ufl.ln(U + params['alpha'])

    mesh = unit_mesh(nelements, dim)
    fe.parameters["form_compiler"]["representation"] = "uflacs"
    fe.parameters['linear_algebra_backend'] = 'PETSc'
    solver = makeKSDGSolver(dim=dim,
                            degree=degree,
                            nelements=nelements,
                            parameters=params,
                            V=Vfunc,
                            U0=U0str,
                            rho0=rho0str,
                            debug=True)
    print(str(solver))
    print(solver.ddt(debug=True))
    print("dsol/dt components:", solver.dsol.vector()[:])

    eksolver = EKKSDGSolver(dim=dim,
                            degree=degree,
                            nelements=nelements,
                            parameters=params,
                            V=Vfunc,
                            U0=U0str,
                            rho0=rho0str,
                            debug=True)
    print(str(eksolver))
    print(eksolver.ddt(debug=True))
    print("dsol/dt components:", eksolver.dsol.vector()[:])
    #
    # try out the time-stepper
    #
    np.random.seed(793817931)
    murho0 = params['N'] / params['M']
    Cd1 = fe.FunctionSpace(mesh, 'CG', 1)
    rho0 = Function(Cd1)
    rho0.vector()[:] = np.random.normal(murho0, params['srho0'],
                                        rho0.vector()[:].shape)
    U0 = Function(Cd1)
    U0.vector()[:] = (params['s'] / params['gamma']) * rho0.vector()
    ksdg = makeKSDGSolver(degree=degree,
                          mesh=mesh,
                          parameters=params,
                          V=Vfunc,
                          U0=U0,
                          rho0=rho0)
    ksdg.implicitTS()
    print('Final time point')
    print(ksdg.history[-1])
    ksdg.imExTS()
    print('Final time point')
    print(ksdg.history[-1])
    ksdg.explicitTS()
    print('Final time point')
    print(ksdg.history[-1])
Пример #19
0
class KSDGSolverPeriodic(KSDGSolver):
    default_params = dict(
        rho_min = 1e-7,
        U_min = 1e-7,
        width = 1.0,
        rhopen = 10,
        Upen = 1,
        grhopen = 1,
        gUpen = 1,
    )

    def __init__(
            self,
            mesh=None,
            width=1.0,
            dim=1,
            nelements=8,
            degree=2,
            parameters={},
            V=(lambda U: U),
            U0=None,
            rho0=None,
            t0=0.0,
            debug=False,
            solver_type = 'lu',
            preconditioner_type = 'default',
            periodic=True,
            ligands=None
            ):
        """DG solver for the periodic Keller-Segel PDE system

        Keyword parameters:
        mesh=None: the mesh on which to solve the problem
        width=1.0: the width of the domain
        dim=1: # of spatial dimensions.
        nelements=8: If mesh is not supplied, one will be
        contructed using UnitIntervalMesh, UnitSquareMesh, or
        UnitCubeMesh (depending on dim). dim and nelements are not
        needed if mesh is supplied.
        degree=2: degree of the polynomial approximation
        parameters={}: a dict giving the values of scalar parameters of
            .V, U0, and rho0 Expressions. This dict needs to also
            define numerical parameters that appear in the PDE. Some
            of these have defaults:
            dim = dim: # of spatial dimensions
            sigma: organism movement rate
            s: attractant secretion rate
            gamma: attractant decay rate
            D: attractant diffusion constant
            rho_min=10.0**-7: minimum feasible worm density
            U_min=10.0**-7: minimum feasible attractant concentration
            rhopen=10: penalty for discontinuities in rho
            Upen=1: penalty for discontinuities in U
            grhopen=1, gUpen=1: penalties for discontinuities in gradients
        V=(lambda U: U): a callable taking two numerical arguments, U
            and rho, or a single argument, U, and returning a single
            number, V, the potential corresponding to U. Use fenics
            versions of mathematical functions, e.g. fe.ln, abs,
            fe.exp.
        U0, rho0: Expressions, Functions, or strs specifying the
            initial condition.
        t0=0.0: initial time
        solver_type='lu'
        preconditioner_type='default'
        periodic=True: Allowed for compatibility, but ignored
        ligands=None: ignored for compatibility
        """
        logPERIODIC('creating KSDGSolverPeriodic')
        self.args = dict(
            mesh=mesh,
            width=width,
            dim=dim,
            nelements=nelements,
            degree=degree,
            parameters=parameters,
            V=V,
            U0=U0,
            rho0=rho0,
            t0=t0,
            debug=debug,
            solver_type = solver_type,
            preconditioner_type = preconditioner_type,
            periodic=True,
            ligands=ligands
        )
        self.debug = debug
        self.solver_type = solver_type
        self.preconditioner_type = preconditioner_type
        self.periodic = True
        self.params = self.default_params.copy()
        #
        # Store the original mesh in self.omesh. self.mesh will be the
        # corner mesh.
        #
        if (mesh):
            self.omesh = mesh
        else:
            self.omesh = box_mesh(width=width, dim=dim, nelements=nelements)
            self.nelements = nelements
        try:
            comm = self.omesh.mpi_comm().tompi4py()
        except AttributeError:
            comm = self.omesh.mpi_comm()
        self.lmesh = gather_mesh(self.omesh)
        omeshstats = mesh_stats(self.omesh)
        logPERIODIC('omeshstats', omeshstats)
        self.xmin = omeshstats['xmin']
        self.xmax = omeshstats['xmax']
        self.xmid = omeshstats['xmid']
        self.delta_ = omeshstats['dx']
        self.mesh = corner_submesh(self.lmesh)
        meshstats = mesh_stats(self.mesh)
        logPERIODIC('meshstats', meshstats)
        logPERIODIC('self.omesh', self.omesh)
        logPERIODIC('self.mesh', self.mesh)
        logPERIODIC('self.mesh.mpi_comm().size', self.mesh.mpi_comm().size)
        self.nelements = nelements
        self.degree = degree
        self.dim = self.mesh.geometry().dim()
        self.params['dim'] = self.dim
        self.params.update(parameters)
        # 
        # Solution spaces and Functions
        #
        # The solution function space is a vector space with
        # 2*(2**dim) elements. The first 2**dim components are even
        # and odd parts of rho; These are followed by even and
        # odd parts of U. The array self.evenodd identifies even
        # and odd components. Each row is a length dim sequence 0s and
        # 1s and represnts one component. For instance, if evenodd[i]
        # is [0, 1, 0], then component i of the vector space is even
        # in dimensions 0 and 2 (x and z conventionally) and off in
        # dimension 1 (y).
        #
        self.symmetries = evenodd_symmetries(self.dim)
        self.signs = [fe.as_matrix(np.diagflat(1.0 - 2.0*eo))
                      for eo in self.symmetries]
        self.eomat = evenodd_matrix(self.symmetries)
        fss = self.make_function_space()
        (self.SE, self.SS, self.VE, self.VS) = [
            fss[fs] for fs in ('SE', 'SS', 'VE', 'VS')
        ]
        (self.SE, self.SS, self.VE, self.VS) = self.make_function_space()
        self.sol = Function(self.VS)                  # sol, current soln
        logPERIODIC('self.sol', self.sol)
        # srhos and sUs are fcuntions defiend on subspaces
        self.srhos = self.sol.split()[:2**self.dim]
        self.sUs = self.sol.split()[2**self.dim:]
        # irhos and iUs are Indexed UFL expressions
        self.irhos = fe.split(self.sol)[:2**self.dim]
        self.iUs = fe.split(self.sol)[2**self.dim:]
        self.wrhos = TestFunctions(self.VS)[: 2**self.dim]
        self.wUs = TestFunctions(self.VS)[2**self.dim :]
        self.tdsol = TrialFunction(self.VS) # time derivatives
        self.tdrhos = fe.split(self.tdsol)[: 2**self.dim]
        self.tdUs = fe.split(self.tdsol)[2**self.dim :]
        bc_method = 'geometric' if self.dim > 1 else 'pointwise'
        rhobcs = [DirichletBC(
            self.VS.sub(i),
            Constant(0),
            FacesDomain(self.mesh, self.symmetries[i]),
            method=bc_method
        ) for i in range(2**self.dim) if np.any(self.symmetries[i] != 0.0)]
        Ubcs = [DirichletBC(
            self.VS.sub(i + 2**self.dim),
            Constant(0),
            FacesDomain(self.mesh, self.symmetries[i]),
            method=bc_method
        ) for i in range(2**self.dim)  if np.any(self.symmetries[i] != 0.0)]
        self.bcs = rhobcs + Ubcs
        self.n = FacetNormal(self.mesh)
        self.h = CellDiameter(self.mesh)
        self.havg = fe.avg(self.h)
        self.dx = fe.dx
        self.dS = fe.dS
        #
        # record initial state
        #
        if not U0:
            U0 = Constant(0.0)
        if isinstance(U0, ufl.coefficient.Coefficient):
            self.U0 = U0
        else:
            self.U0 = Expression(U0, **self.params,
                                 degree=self.degree, domain=self.mesh)
        if not rho0:
            rho0 = Constant(0.0)
        if isinstance(rho0, ufl.coefficient.Coefficient):
            self.rho0 = rho0
        else:
            self.rho0 = Expression(rho0, **self.params,
                                   degree=self.degree, domain=self.mesh)
        try:
            V(self.U0, self.rho0)
            def realV(U, rho):
                return V(U, rho)
        except TypeError:
            def realV(U, rho):
                return V(U)
        self.V = realV
        self.t0 = t0
        #
        # initialize state
        #
        # cache assigners
        logPERIODIC('restarting')
        self.restart()
        logPERIODIC('restart returned')
        return(None)

    def make_function_space(self,
                            mesh=None,
                            dim=None,
                            degree=None
                            ):
        if not mesh: mesh = self.mesh
        if not dim: dim = self.dim
        if not degree: degree = self.degree
        SE = FiniteElement('DG', cellShapes[dim-1], degree)
        SS = FunctionSpace(mesh, SE) # scalar space
        elements = [SE] * (2*2**self.dim)
        VE = MixedElement(elements)
        VS = FunctionSpace(mesh, VE)   # vector space
        logPERIODIC('VS', VS)
        return dict(SE=SE, SS=SS, VE=VE, VS=VS)


    def restart(self):
        logPERIODIC('restart')
        self.t = self.t0
        U0comps = evenodd_functions(
            omesh=self.omesh,
            degree=self.degree,
            func=self.U0,
            evenodd=self.symmetries,
            width=self.xmax
        )
        rho0comps = evenodd_functions(
            omesh=self.omesh,
            degree=self.degree,
            func=self.rho0,
            evenodd=self.symmetries,
            width=self.xmax
        )
        coords = gather_dof_coords(rho0comps[0].function_space())
        for i in range(2**self.dim):
            fe.assign(self.sol.sub(i),
                      function_interpolate(rho0comps[i],
                                           self.SS,
                                           coords=coords))
            fe.assign(self.sol.sub(i + 2**self.dim),
                      function_interpolate(U0comps[i],
                                           self.SS,
                                           coords=coords))
        
    def setup_problem(self, debug=False):
        #
        # assemble the matrix, if necessary (once for all time points)
        #
        if not hasattr(self, 'A'):
            drho_integral = vectotal(
                [tdrho*wrho*self.dx for tdrho,wrho in
                 zip(self.tdrhos, self.wrhos)]
            )
            dU_integral = vectotal(
                [tdU*wU*self.dx
                 for tdU,wU in zip(self.tdUs, self.wUs)
                ]
            )
            self.A = fe.assemble(drho_integral + dU_integral)
            for bc in self.bcs:
                bc.apply(self.A)
            # if self.solver_type == 'lu':
            #     self.solver = fe.LUSolver(
            #         self.A,
            #     )
            #     self.solver.parameters['reuse_factorization'] = True
            # else:
            #     self.solver = fe.KrylovSolver(
            #         self.A,
            #         self.solver_type,
            #         self.preconditioner_type
            #     )
            self.dsol = Function(self.VS)
            self.drhos = self.dsol.split()[: 2**self.dim]
            self.dUs = self.dsol.split()[2**self.dim :]
        #
        # These are the values of rho and U themselves (not their
        # symmetrized versions) on all subdomains of the original
        # domain.
        #
        if not hasattr(self, 'rhosds'):
            self.rhosds = matmul(self.eomat, self.irhos)
        if not hasattr(self, 'Usds'):
            self.Usds = matmul(self.eomat, self.iUs)
        #
        # assemble RHS (for each time point, but compile only once)
        #
        if not hasattr(self, 'rho_terms'):
            self.sigma = self.params['sigma']
            self.s2 = self.sigma * self.sigma / 2
            self.rho_min = self.params['rho_min']
            self.rhopen = self.params['rhopen']
            self.grhopen = self.params['grhopen']
            #
            # Compute fluxes on subdomains.
            #
            self.Vsds = [self.V(Usd, rhosd) for Usd,rhosd in
                         zip(self.Usds, self.rhosds)]
            #
            # I may need to adjust the signs of the subdomain vs by
            # the symmetries of the combinations
            #
            self.vsds = [-ufl.grad(Vsd) - (
                self.s2*ufl.grad(rhosd)/ufl.max_value(rhosd, self.rho_min)
            ) for Vsd,rhosd in zip(self.Vsds, self.rhosds)]
            self.fluxsds = [vsd * rhosd for vsd,rhosd in
                            zip(self.vsds, self.rhosds)]
            self.vnsds = [ufl.max_value(ufl.dot(vsd, self.n), 0)
                          for vsd in self.vsds]
            self.facet_fluxsds = [(
                vnsd('+')*ufl.max_value(rhosd('+'), 0.0) -
                vnsd('-')*ufl.max_value(rhosd('-'), 0.0)
            ) for vnsd,rhosd in zip(self.vnsds, self.rhosds)]
            #
            # Now combine the subdomain fluxes to get the fluxes for
            # the symmetrized functions
            #
            self.fluxs = matmul((2.0**-self.dim)*self.eomat,
                                self.fluxsds)
            self.facet_fluxs = matmul((2.0**-self.dim)*self.eomat,
                                      self.facet_fluxsds)
            self.rho_flux_jump = vectotal(
                [-facet_flux*ufl.jump(wrho)*self.dS
                 for facet_flux,wrho in
                 zip(self.facet_fluxs, self.wrhos)]
            )
            self.rho_grad_move = vectotal(
                [ufl.dot(flux, ufl.grad(wrho))*self.dx
                 for flux,wrho in
                 zip(self.fluxs, self.wrhos)]
            )
            self.rho_penalty = vectotal(
                [-(self.rhopen * self.degree**2 / self.havg) *
                 ufl.dot(ufl.jump(rho, self.n),
                        ufl.jump(wrho, self.n)) * self.dS
                 for rho,wrho in zip(self.irhos, self.wrhos)]
            )
            self.grho_penalty = vectotal(
                [-self.grhopen * self.degree**2 *
                 (ufl.jump(ufl.grad(rho), self.n) *
                  ufl.jump(ufl.grad(wrho), self.n)) * self.dS
                 for rho,wrho in zip(self.irhos, self.wrhos)]
            )
            self.rho_terms = (
                self.rho_flux_jump + self.rho_grad_move +
                self.rho_penalty + self.grho_penalty
            )
        if not hasattr(self, 'U_terms'):
            self.U_min = self.params['U_min']
            self.gamma = self.params['gamma']
            self.s = self.params['s']
            self.D = self.params['D']
            self.Upen = self.params['Upen']
            self.gUpen = self.params['gUpen']
            self.U_decay = vectotal(
                [-self.gamma * U * wU * self.dx
                 for U,wU in zip(self.iUs, self.wUs)]
            )
            self.U_secretion = vectotal(
                [self.s * rho * wU * self.dx
                 for rho, wU in zip(self.irhos, self.wUs)]
            )
            self.jump_gUw = vectotal(
                [self.D * ufl.jump(wU * ufl.grad(U), self.n) * self.dS
                 for wU, U in zip(self.wUs, self.iUs)
                ]
            )
            self.U_diffusion = vectotal(
                [-self.D
                 * ufl.dot(ufl.grad(U), ufl.grad(wU))*self.dx
                 for U,wU in zip(self.iUs, self.wUs)
                ]
            )
            self.U_penalty = vectotal(
                [-(self.Upen * self.degree**2 / self.havg)
                 * ufl.dot(ufl.jump(U, self.n), ufl.jump(wU, self.n))*self.dS
                 for U,wU in zip(self.iUs, self.wUs)
                ]
            )
            self.gU_penalty = vectotal(
                [-self.gUpen * self.degree**2 *
                 ufl.jump(ufl.grad(U), self.n) *
                 ufl.jump(ufl.grad(wU), self.n) * self.dS
                 for U,wU in zip(self.iUs, self.wUs)
                ]
            )
            self.U_terms = (
                # decay and secretion
                self.U_decay + self.U_secretion +
                # diffusion
                self.jump_gUw + self.U_diffusion +
                # penalties (to enforce continuity)
                self.U_penalty + self.gU_penalty
            )
        if not hasattr(self, 'all_terms'):
            self.all_terms = self.rho_terms + self.U_terms
        if not hasattr(self, 'J_terms'):
            self.J_terms = fe.derivative(self.all_terms, self.sol)
        # if not hasattr(self, 'JU_terms'):
        #     self.JU_terms = [fe.derivative(self.all_terms, U)
        #                      for U in self.Us]
        # if not hasattr(self, 'Jrho_terms'):
        #     self.Jrho_terms = [fe.derivative(self.all_terms, rho)
        #                        for rho in self.rhos]


    def ddt(self, debug=False):
        """Calculate time derivative of rho and U

        Results are left in self.dsol as a two-component vector function.
        """
        self.setup_problem(debug)
        self.b = fe.assemble(self.all_terms)
        for bc in self.bcs:
            bc.apply(self.b)
        return fe.solve(self.A, self.dsol.vector(), self.b,
                        self.solver_type)
Пример #20
0
def theta_method(A, f, ah, bh, θ, k, T):
    """θ-method time-stepper.

    Abstract problem:

        u'' + Au = f,  u(0) = ah, u'(0) = bh

    is formulated as a first-order system:

        u' - v = 0,
        v' + Au = f,
        u(0) = ah, v(0) = bh.

    Args:
        A (Function -> Function -> Form): Possibly nonlinear functional A(u, v)
        f (float -> Function): Right-hand side
        ah (Function): Initial value
        bh (Function): Initial velocity
        k (float): Time step
        T (float): Max time

    Returns:
        list(float): Times
        list(Function): Solution at each time
    """
    # pylint: disable=invalid-name, too-many-arguments, too-many-locals
    # Basic setup
    V = ah.function_space()
    u = TrialFunction(V)
    w = TestFunction(V)
    v = TrialFunction(V)
    y = TestFunction(V)
    α = k * θ
    β = k * (1.0 - θ)
    # Prepare initial condition
    u0 = Function(V)
    u0.vector()[:] = ah.vector()
    v0 = Function(V)
    v0.vector()[:] = bh.vector()
    # Initialize time stepper
    t = k
    u1 = Function(V)
    u1.vector()[:] = u0.vector()
    v1 = Function(V)
    v1.vector()[:] = v0.vector()
    ts = [0]
    uh = [interpolate(u0, V)]
    progress = -1
    print("Progress: ", end="")
    while t < T:
        # Print progress
        pct = int(t / T * 100) // 10 * 10
        if pct > progress:
            print("{}%..".format(pct), end="")
            progress = pct
        # Solve for the next u (nonlinear)
        lhs = (dot(u, w) + α**2 * A(u, w)) * dx
        rhs = (dot(u0, w) - α * β * A(u0, w) + k * dot(v0, w) +
               α**2 * f(t + k, w) + α * β * f(t, w)) * dx
        act = action(lhs - rhs, u1)
        J = derivative(act, u1)
        problem = NonlinearVariationalProblem(act, u1, [], J)
        solver = NonlinearVariationalSolver(problem)
        solver.parameters["newton_solver"]["linear_solver"] = "gmres"
        solver.parameters["newton_solver"]["preconditioner"] = "ilu"
        try:
            solver.solve()
        except RuntimeError:
            print("blowup at t={}.".format(t))
            return (ts, uh)
        # Solve for the next v (linear)
        lhs = dot(v, y) * dx
        rhs = (dot(u1 - u0, y) - β * dot(v0, w)) / α * dx
        problem = LinearVariationalProblem(lhs, rhs, v1)
        solver = LinearVariationalSolver(problem)
        solver.parameters["linear_solver"] = "cg"
        solver.parameters["preconditioner"] = "hypre_amg"
        solver.solve()
        # Update
        t += k
        u0.vector()[:] = u1.vector()
        v0.vector()[:] = v1.vector()
        # Record result
        ts.append(t)
        uh.append(interpolate(u1, V))
    print("done")
    return (ts, uh)
Пример #21
0
def leapfrog(A, f, ah, bh, k, T):
    """Leapfrog time-stepper.

    Abstract problem:

        u'' + Au = f,  u(0) = ah, u'(0) = bh

    Args:
        A (Function -> Function -> Form): Possibly nonlinear functional A(u, v)
        f (float -> Function): Right-hand side
        ah (Function): Initial value
        bh (Function): Initial velocity
        k (float): Time step
        T (float): Max time

    Returns:
        list(float): Times
        list(Function): Solution at each time
    """
    # pylint: disable=invalid-name, too-many-arguments, too-many-locals
    # Basic setup
    V = ah.function_space()
    u = TrialFunction(V)
    v = TestFunction(V)
    # Prepare initial condition
    u0 = Function(V)
    u0.vector()[:] = ah.vector()
    u1 = Function(V)
    u1.vector()[:] = ah.vector() + k * bh.vector()
    # Initialize time stepper
    t = k
    u2 = Function(V)
    u2.vector()[:] = u1.vector()
    ts = [0, t]
    uh = [interpolate(u0, V), interpolate(u1, V)]
    progress = -1
    print("Progress: ", end="")
    while t < T:
        # Print progress
        pct = int(t / T * 100) // 10 * 10
        if pct > progress:
            print("{}%..".format(pct), end="")
            progress = pct
        # Solve
        lhs = dot(u, v) * dx
        rhs = (dot(2 * u1 - u0, v) + k * k * (f(t, v) - A(u1, v))) * dx
        problem = LinearVariationalProblem(lhs, rhs, u2)
        solver = LinearVariationalSolver(problem)
        solver.parameters["linear_solver"] = "cg"
        solver.parameters["preconditioner"] = "hypre_amg"
        try:
            solver.solve()
        except RuntimeError:
            print("blowup at t={}.".format(t))
            return (ts, uh)
        # Update
        t += k
        u0.vector()[:] = u1.vector()
        u1.vector()[:] = u2.vector()
        # Record result
        ts.append(t)
        uh.append(interpolate(u1, V))
    print("done")
    return (ts, uh)
Пример #22
0
    def solve(self):
        """
    Performs the physics, evaluating and updating the enthalpy and age as 
    well as storing the velocity, temperature, and the age in vtk files.

    """
        model = self.model
        config = self.config

        t = config['t_start']
        t_end = config['t_end']
        dt = config['time_step']
        thklim = config['free_surface']['thklim']

        mesh = model.mesh
        smb = model.smb
        sigma = model.sigma

        S = model.S
        B = model.B

        smb.interpolate(config['free_surface']['observed_smb'])

        if config['periodic_boundary_conditions']:
            d2v = dof_to_vertex_map(model.Q_non_periodic)
            mhat_non = Function(model.Q_non_periodic)
        else:
            d2v = dof_to_vertex_map(model.Q)

        # Loop over all times
        while t <= t_end:

            B_a = B.compute_vertex_values()
            S_v = S.compute_vertex_values()

            tic = time()

            S_0 = S_v
            f_0 = self.rhs_func_explicit(t, S_0)
            S_1 = S_0 + dt * f_0
            S_1[(S_1 - B_a) < thklim] = thklim + B_a[(S_1 - B_a) < thklim]
            S.vector().set_local(S_1[d2v])
            S.vector().apply('')

            f_1 = self.rhs_func_explicit(t, S_1)
            S_2 = 0.5 * S_0 + 0.5 * S_1 + 0.5 * dt * f_1
            S_2[(S_2 - B_a) < thklim] = thklim + B_a[(S_2 - B_a) < thklim]
            S.vector().set_local(S_2[d2v])
            S.vector().apply('')

            mesh.coordinates(
            )[:, 2] = sigma.compute_vertex_values() * (S_2 - B_a) + B_a
            if config['periodic_boundary_conditions']:
                temp = (S_2[d2v] - S_0[d2v]) / dt * sigma.vector().get_local()
                mhat_non.vector().set_local(temp)
                mhat_non.vector().apply('')
                m_temp = project(mhat_non, model.Q)
                model.mhat.vector().set_local(m_temp.vector().get_local())
                model.mhat.vector().apply('')
            else:
                temp = (S_2[d2v] - S_0[d2v]) / dt * sigma.vector().get_local()
                model.mhat.vector().set_local(temp)
                model.mhat.vector().apply('')
            # Calculate enthalpy update
            if self.config['enthalpy']['on']:
                self.enthalpy_instance.solve(H0=model.H,
                                             Hhat=model.H,
                                             uhat=model.u,
                                             vhat=model.v,
                                             what=model.w,
                                             mhat=model.mhat)
                if self.config['log']:
                    self.file_T << (model.T, t)

            # Calculate age update
            if self.config['age']['on']:
                self.age_instance.solve(A0=model.A,
                                        Ahat=model.A,
                                        uhat=model.u,
                                        vhat=model.v,
                                        what=model.w,
                                        mhat=model.mhat)
                if config['log']:
                    self.file_a << (model.age, t)

            # Store velocity, temperature, and age to vtk files
            if self.config['log']:
                self.t_log.append(t)
                M = assemble(self.surface_instance.M)
                self.mass.append(M)

            # Increment time step
            if MPI.rank(mpi_comm_world()) == 0:
                string = 'Time: {0}, CPU time for last time step: {1}, Mass: {2}'
                print string.format(t, time() - tic, M / self.M_prev)

            self.M_prev = M
            t += dt
            self.step_time.append(time() - tic)
Пример #23
0
def mfd_cellcell(mesh, V, u_n, De, nexp):
    """
    MFD cell-to-cell
    Flow routing distributed from cell-to-cell

    :param mesh: mesh object generated using mshr (fenics)
    :param V: finite element function space
    :param u_n: solution (trial function) for water flux
    :param De: dimensionless diffusion coefficient
    :param nexp: water flux exponent
    :return:
    """

    # get a map of neighbours (thanks google!)
    tdim = mesh.topology().dim()
    mesh.init(tdim - 1, tdim)
    cell_neighbors = np.array([
        sum((filter(lambda ci: ci != cell.index(), facet.entities(tdim))
             for facet in facets(cell)), []) for cell in cells(mesh)
    ])

    # first get the elevation area of each element
    dofmap = V.dofmap()
    elevation = []
    area = []
    xm = []
    ym = []
    for cell in cells(mesh):
        cellnodes = dofmap.cell_dofs(cell.index())
        elevation.append(sum(u_n.vector()[cellnodes]) / 3)
        area.append(cell.volume())
        p = cell.midpoint()
        xm.append(p.x())
        ym.append(p.y())
    elevation = np.array(elevation)
    area = np.array(area)
    xm = np.array(xm)
    ym = np.array(ym)

    # now sort the vector of elevations by decending topography
    ind = np.argsort(-elevation)
    sorted_neighbors = cell_neighbors[ind]

    # determine length between elements
    steep_len = []
    for cell in cells(mesh):
        xh = xm[cell.index()]
        yh = ym[cell.index()]

        neicells = cell_neighbors[cell.index()]
        tnei = elevation[neicells]
        imin = np.argmin(tnei)
        ncell = neicells[imin]
        xn = xm[ncell]
        yn = ym[ncell]

        steep_len.append(np.sqrt((xh - xn) * (xh - xn) + (yh - yn) *
                                 (yh - yn)))
    steep_len = np.array(steep_len)

    flux = area / steep_len

    # determine flux from highest to lowest cells
    for cell in cells(mesh):
        neicells = sorted_neighbors[cell.index()]
        tnei = elevation[neicells]
        imin = np.argmin(tnei)
        ncell = neicells[imin]

        weight = np.zeros(len(neicells))
        i = 0
        for neicell in neicells:
            weight[i] = elevation[ind[cell.index()]] - elevation[neicell]
            # downhill only
            if weight[i] < 0:
                weight[i] = 0
            i += 1

        # weight flux by the sum of the lengths down slope
        if max(weight) > 0:
            weight = weight / sum(weight)
        else:
            weight[:] = 0
        i = 0
        for neicell in neicells:
            flux[neicell] = flux[neicell] + flux[ind[cell.index()]] * weight[i]
            i += 1

    # interpolate to the nodes
    gc = mesh.coordinates()

    flux_node = np.zeros(len(gc))
    for cell in cells(mesh):
        cellnodes = dofmap.cell_dofs(cell.index())
        for nod in cellnodes:
            flux_node[nod] = flux_node[nod] + flux[cell.index()] / 3

    q = Function(V)
    q.vector()[:] = 1 + De * pow(flux_node, nexp)

    return (q)
Пример #24
0
def sd_nodenode(mesh, V, u_n, De, nexp):
    """
    SD node-to-node
    Flow routing from node-to-node based on the steepest route of descent

    :param mesh: mesh object generated using mshr (fenics)
    :param V: finite element function space
    :param u_n: solution (trial function) for water flux
    :param De: dimensionless diffusion coefficient
    :param nexp: water flux exponent
    :return:
    """

    # get the global coordinates
    gdim = mesh.geometry().dim()
    if dolfin.dolfin_version() == '1.6.0':
        dofmap = V.dofmap()
        gc = dofmap.tabulate_all_coordinates(mesh).reshape((-1, gdim))
    else:
        gc = V.tabulate_dof_coordinates().reshape((-1, gdim))
    vtd = vertex_to_dof_map(V)

    # first get the elevation of each vertex
    elevation = np.zeros(len(gc))
    elevation = u_n.compute_vertex_values(mesh)

    # loop to get the local flux
    mesh.init(0, 1)
    flux = np.zeros(len(gc))
    neighbors = []
    for v in vertices(mesh):
        idx = v.index()

        # get the local neighbourhood
        neighborhood = [Edge(mesh, i).entities(0) for i in v.entities(1)]
        neighborhood = np.array(neighborhood).flatten()

        # Remove own index from neighborhood
        neighborhood = neighborhood[np.where(neighborhood != idx)[0]]
        neighbors.append(neighborhood)

        # get location
        xh = v.x(0)
        yh = v.x(1)

        # get distance to neighboring vertices
        length = np.zeros(len(neighborhood))
        weight = np.zeros(len(neighborhood))
        i = 0
        for vert in neighborhood:
            nidx = vtd[vert]
            xn = gc[nidx, 0]
            yn = gc[nidx, 1]
            length[i] = np.sqrt((xh - xn) * (xh - xn) + (yh - yn) * (yh - yn))
            flux[vert] = length[i]
#            weight[i] = elevation[idx] - elevation[vert]
#            # downhill only
#            if weight[i] < 0:
#              weight[i] = 0
#            i += 1
#
#        # find steepest slope
#        steepest = len(neighborhood)+2
#        if max(weight) > 0:
#            steepest = np.argmax(weight)
#        else:
#            weight[:] = 0
#        i = 0
#        for vert in neighborhood:
#            if i == steepest:
#                weight[i] = 1
#            else:
#                weight[i] = 0
#            flux[vert] = flux[vert] + length[i]*weight[i]
#            i += 1

# sort from top to botton
    sortedidx = np.argsort(-elevation)

    # accumulate fluxes from top to bottom
    for idx in sortedidx:
        neighborhood = neighbors[idx]
        weight = np.zeros(len(neighborhood))
        i = 0
        for vert in neighborhood:
            weight[i] = elevation[idx] - elevation[vert]
            # downhill only
            if weight[i] < 0:
                weight[i] = 0
            i += 1

        # find steepest slope
        steepest = len(neighborhood) + 2
        if max(weight) > 0:
            steepest = np.argmax(weight)
        else:
            weight[:] = 0
        i = 0
        for vert in neighborhood:
            if i == steepest:
                weight[i] = 1
            else:
                weight[i] = 0
            flux[vert] = flux[vert] + flux[idx] * weight[i]
            i += 1

    # calculate the diffusion coefficient
    q0 = 1 + De * pow(flux, nexp)
    q = Function(V)
    q.vector()[:] = q0[dof_to_vertex_map(V)]

    return (q)
Пример #25
0
    forces_x, forces_y, forces_z = precice.get_point_sources(read_data)

    A, b = assemble_system(a_form, L_form, bc)

    b_forces = b.copy(
    )  # b is the same for every iteration, only forces change

    for ps in forces_x:
        ps.apply(b_forces)
    for ps in forces_y:
        ps.apply(b_forces)
    for ps in forces_z:
        ps.apply(b_forces)

    assert (b is not b_forces)
    solve(A, u_np1.vector(), b_forces)

    dt = Constant(np.min([precice_dt, fenics_dt]))

    # Write relative displacements to preCICE
    u_delta.vector()[:] = u_np1.vector()[:] - u_n.vector()[:]
    precice.write_data(u_delta)

    # Call to advance coupling, also returns the optimum time step value
    precice_dt = precice.advance(dt(0))

    # Either revert to old step if timestep has not converged or move to next timestep
    if precice.is_action_required(precice.action_read_iteration_checkpoint()
                                  ):  # roll back to checkpoint
        u_cp, t_cp, n_cp = precice.retrieve_checkpoint()
        u_n.assign(u_cp)
Пример #26
0
    # Update the point sources on the coupling boundary with the new read data
    Forces_x, Forces_y = precice.get_point_sources(read_data)

    A, b = assemble_system(a_form, L_form, bc)

    b_forces = b.copy(
    )  # b is the same for every iteration, only forces change

    for ps in Forces_x:
        ps.apply(b_forces)
    for ps in Forces_y:
        ps.apply(b_forces)

    assert (b is not b_forces)
    solve(A, u_np1.vector(), b_forces)

    dt = Constant(np.min([precice_dt, fenics_dt]))

    # Write new displacements to preCICE
    precice.write_data(u_np1)

    # Call to advance coupling, also returns the optimum time step value
    precice_dt = precice.advance(dt(0))

    # Either revert to old step if timestep has not converged or move to next timestep
    if precice.is_action_required(precice.action_read_iteration_checkpoint()
                                  ):  # roll back to checkpoint
        u_cp, t_cp, n_cp = precice.retrieve_checkpoint()
        u_n.assign(u_cp)
        t = t_cp
Пример #27
0
class KSDGSolverVariablePeriodic(KSDGSolverVariable, KSDGSolverPeriodic):
    default_params = collections.OrderedDict(
        sigma=1.0,
        rhomin=1e-7,
        Umin=1e-7,
        width=1.0,
        rhopen=10.0,
        Upen=1.0,
        grhopen=1.0,
        gUpen=1.0,
    )

    def __init__(self,
                 mesh=None,
                 width=1.0,
                 dim=1,
                 nelements=8,
                 degree=2,
                 parameters={},
                 param_funcs={},
                 V=(lambda U, params={}: sum(U)),
                 U0=[],
                 rho0=None,
                 t0=0.0,
                 debug=False,
                 solver_type='petsc',
                 preconditioner_type='default',
                 periodic=True,
                 ligands=None):
        """Discontinuous Galerkin solver for the Keller-Segel PDE system

        Like KSDGSolverVariable, but with periodic boundary conditions.
        """
        logVARIABLE('creating KSDGSolverVariablePeriodic')
        if not ligands:
            ligands = LigandGroups()
        else:
            ligands = copy.deepcopy(ligands)
        self.args = dict(mesh=mesh,
                         width=width,
                         dim=dim,
                         nelements=nelements,
                         degree=degree,
                         parameters=parameters,
                         param_funcs=param_funcs,
                         V=V,
                         U0=U0,
                         rho0=rho0,
                         t0=t0,
                         debug=debug,
                         solver_type=solver_type,
                         preconditioner_type=preconditioner_type,
                         periodic=True,
                         ligands=ligands)
        self.t0 = t0
        self.debug = debug
        self.solver_type = solver_type
        self.preconditioner_type = preconditioner_type
        self.periodic = True
        self.ligands = ligands
        self.nligands = ligands.nligands()
        self.init_params(parameters, param_funcs)
        if nelements is None:
            self.nelements = 8
        else:
            self.nelements = nelements
        if (mesh):
            self.omesh = self.mesh = mesh
        else:
            self.omesh = self.mesh = box_mesh(width=width,
                                              dim=dim,
                                              nelements=self.nelements)
            self.nelements = nelements
        omeshstats = mesh_stats(self.omesh)
        try:
            comm = self.omesh.mpi_comm().tompi4py()
        except AttributeError:
            comm = self.omesh.mpi_comm()
        self.lmesh = gather_mesh(self.omesh)
        logVARIABLE('omeshstats', omeshstats)
        self.xmin = omeshstats['xmin']
        self.xmax = omeshstats['xmax']
        self.xmid = omeshstats['xmid']
        self.delta_ = omeshstats['dx']
        if nelements is None:
            self.nelements = (self.xmax - self.xmin) / self.delta_
        self.mesh = corner_submesh(self.lmesh)
        meshstats = mesh_stats(self.mesh)
        self.degree = degree
        self.dim = self.mesh.geometry().dim()
        #
        # Solution spaces and Functions
        #
        self.symmetries = evenodd_symmetries(self.dim)
        self.signs = [
            fe.as_matrix(np.diagflat(1.0 - 2.0 * eo)) for eo in self.symmetries
        ]
        self.eomat = evenodd_matrix(self.symmetries)
        fss = self.make_function_space()
        (self.SE, self.SS, self.VE,
         self.VS) = [fss[fs] for fs in ('SE', 'SS', 'VE', 'VS')]
        logVARIABLE('self.VS', self.VS)
        self.sol = Function(self.VS)  # sol, current soln
        logVARIABLE('self.sol', self.sol)
        splitsol = self.sol.split()
        self.srhos = splitsol[:2**self.dim]
        self.sUs = splitsol[2**self.dim:]
        splitsol = list(fe.split(self.sol))
        self.irhos = splitsol[:2**self.dim]
        self.iUs = splitsol[2**self.dim:]
        self.iPs = list(fe.split(self.PSf))
        self.iparams = collections.OrderedDict(zip(self.param_names, self.iPs))
        self.iligands = copy.deepcopy(self.ligands)
        self.iligand_params = ParameterList(
            [p for p in self.iligands.params() if p[0] in self.param_numbers])
        for k in self.iligand_params.keys():
            i = self.param_numbers[k]
            self.iligand_params[k] = self.iPs[i]
        tfs = list(TestFunctions(self.VS))
        self.wrhos, self.wUs = tfs[:2**self.dim], tfs[2**self.dim:]
        tfs = list(TrialFunctions(self.VS))
        self.tdrhos, self.tdUs = tfs[:2**self.dim], tfs[2**self.dim:]
        bc_method = 'geometric' if self.dim > 1 else 'pointwise'
        rhobcs = [
            DirichletBC(self.VS.sub(i),
                        Constant(0),
                        FacesDomain(self.mesh, self.symmetries[i]),
                        method=bc_method) for i in range(2**self.dim)
            if np.any(self.symmetries[i] != 0.0)
        ]
        Ubcs = list(
            itertools.chain(*[[
                DirichletBC(self.VS.sub(i + (lig + 1) * 2**self.dim),
                            Constant(0),
                            FacesDomain(self.mesh, self.symmetries[i]),
                            method=bc_method) for i in range(2**self.dim)
                if np.any(self.symmetries[i] != 0.0)
            ] for lig in range(self.nligands)]))
        self.bcs = rhobcs + Ubcs
        self.n = FacetNormal(self.mesh)
        self.h = CellDiameter(self.mesh)
        self.havg = fe.avg(self.h)
        self.dx = fe.dx
        self.dS = fe.dS
        #
        # record initial state
        #
        if not U0:
            U0 = [Constant(0.0)] * self.nligands
        self.U0s = [Constant(0.0)] * self.nligands
        for i, U0i in enumerate(U0):
            if isinstance(U0i, ufl.coefficient.Coefficient):
                self.U0s[i] = U0i
            else:
                self.U0s[i] = Expression(U0i,
                                         **self.params,
                                         degree=self.degree,
                                         domain=self.mesh)
        if not rho0:
            rho0 = Constant(0.0)
        if isinstance(rho0, ufl.coefficient.Coefficient):
            self.rho0 = rho0
        else:
            self.rho0 = Expression(rho0,
                                   **self.params,
                                   degree=self.degree,
                                   domain=self.mesh)
        self.set_time(t0)
        #
        # work out how to call V
        #
        try:
            V(self.U0s, self.rho0, params=self.iparams)

            def realV(Us, rho):
                return V(Us, rho, params=self.iparams)
        except TypeError:

            def realV(Us, rho):
                return V(Us, self.iparams)

        self.V = realV
        #
        # initialize state
        #
        self.restart()
        return None

    def make_function_space(self, mesh=None, dim=None, degree=None):
        if not mesh: mesh = self.mesh
        if not dim: dim = self.dim
        if not degree: degree = self.degree
        SE = FiniteElement('DG', cellShapes[dim - 1], degree)
        SS = FunctionSpace(mesh, SE)  # scalar space
        elements = [SE] * ((self.nligands + 1) * 2**self.dim)
        VE = MixedElement(elements)
        VS = FunctionSpace(mesh, VE)  # vector space
        return dict(SE=SE, SS=SS, VE=VE, VS=VS)

    def restart(self):
        logVARIABLE('restart')
        self.set_time(self.t0)
        U0comps = [None] * self.nligands * 2**self.dim
        for i, U0i in enumerate(self.U0s):
            eofuncs = evenodd_functions(omesh=self.omesh,
                                        degree=self.degree,
                                        func=U0i,
                                        evenodd=self.symmetries,
                                        width=self.xmax)
            U0comps[i * 2**self.dim:(i + 1) * 2**self.dim] = eofuncs
        rho0comps = evenodd_functions(omesh=self.omesh,
                                      degree=self.degree,
                                      func=self.rho0,
                                      evenodd=self.symmetries,
                                      width=self.xmax)
        coords = gather_dof_coords(rho0comps[0].function_space())
        for i in range(2**self.dim):
            fe.assign(
                self.sol.sub(i),
                function_interpolate(rho0comps[i], self.SS, coords=coords))
        for i in range(self.nligands * 2**self.dim):
            fe.assign(self.sol.sub(i + 2**self.dim),
                      function_interpolate(U0comps[i], self.SS, coords=coords))

    def setup_problem(self, t, debug=False):
        self.set_time(t)
        #
        # assemble the matrix, if necessary (once for all time points)
        #
        if not hasattr(self, 'A'):
            logVARIABLE('making matrix A')
            self.drho_integral = sum([
                tdrho * wrho * self.dx
                for tdrho, wrho in zip(self.tdrhos, self.wrhos)
            ])
            self.dU_integral = sum(
                [tdU * wU * self.dx for tdU, wU in zip(self.tdUs, self.wUs)])
            logVARIABLE('assembling A')
            self.A = fe.PETScMatrix()
            logVARIABLE('self.A', self.A)
            fe.assemble(self.drho_integral + self.dU_integral, tensor=self.A)
            logVARIABLE('A assembled. Applying BCs')
            pA = fe.as_backend_type(self.A).mat()
            Adiag = pA.getDiagonal()
            logVARIABLE('Adiag.array', Adiag.array)
            # self.A = fe.assemble(self.drho_integral + self.dU_integral +
            #                      self.dP_integral)
            for bc in self.bcs:
                bc.apply(self.A)
            Adiag = pA.getDiagonal()
            logVARIABLE('Adiag.array', Adiag.array)
            self.dsol = Function(self.VS)
            dsolsplit = self.dsol.split()
            self.drhos, self.dUs = (dsolsplit[:2**self.dim],
                                    dsolsplit[2**self.dim:])
        #
        # assemble RHS (for each time point, but compile only once)
        #
        #
        # These are the values of rho and U themselves (not their
        # symmetrized versions) on all subdomains of the original
        # domain.
        #
        if not hasattr(self, 'rhosds'):
            self.rhosds = matmul(self.eomat, self.irhos)
        # self.Usds is a list of nligands lists. Sublist i is of
        # length 2**dim and lists the value of ligand i on each of the
        # 2**dim subdomains.
        #
        if not hasattr(self, 'Usds'):
            self.Usds = [
                matmul(self.eomat,
                       self.iUs[i * 2**self.dim:(i + 1) * 2**self.dim])
                for i in range(self.nligands)
            ]
        if not hasattr(self, 'rho_terms'):
            logVARIABLE('making rho_terms')
            self.sigma = self.iparams['sigma']
            self.s2 = self.sigma * self.sigma / 2
            self.rhomin = self.iparams['rhomin']
            self.rhopen = self.iparams['rhopen']
            self.grhopen = self.iparams['grhopen']
            #
            # Compute fluxes on subdomains.
            # Vsds is a list of length 2**dim, the value of V on each
            # subdomain.
            #
            self.Vsds = []
            for Usd, rhosd in zip(zip(*self.Usds), self.rhosds):
                self.Vsds.append(self.V(Usd, ufl.max_value(rhosd,
                                                           self.rhomin)))
            self.vsds = [
                -ufl.grad(Vsd) -
                (self.s2 * ufl.grad(rhosd) / ufl.max_value(rhosd, self.rhomin))
                for Vsd, rhosd in zip(self.Vsds, self.rhosds)
            ]
            self.fluxsds = [
                vsd * rhosd for vsd, rhosd in zip(self.vsds, self.rhosds)
            ]
            self.vnsds = [
                ufl.max_value(ufl.dot(vsd, self.n), 0) for vsd in self.vsds
            ]
            self.facet_fluxsds = [
                (vnsd('+') * ufl.max_value(rhosd('+'), 0.0) -
                 vnsd('-') * ufl.max_value(rhosd('-'), 0.0))
                for vnsd, rhosd in zip(self.vnsds, self.rhosds)
            ]
            #
            # Now combine the subdomain fluxes to get the fluxes for
            # the symmetrized functions
            #
            self.fluxs = matmul((2.0**-self.dim) * self.eomat, self.fluxsds)
            self.facet_fluxs = matmul((2.0**-self.dim) * self.eomat,
                                      self.facet_fluxsds)
            self.rho_flux_jump = sum([
                -facet_flux * ufl.jump(wrho) * self.dS
                for facet_flux, wrho in zip(self.facet_fluxs, self.wrhos)
            ])
            self.rho_grad_move = sum([
                ufl.dot(flux, ufl.grad(wrho)) * self.dx
                for flux, wrho in zip(self.fluxs, self.wrhos)
            ])
            self.rho_penalty = sum([
                -(self.degree**2 / self.havg) *
                ufl.dot(ufl.jump(rho, self.n),
                        ufl.jump(self.rhopen * wrho, self.n)) * self.dS
                for rho, wrho in zip(self.irhos, self.wrhos)
            ])
            self.grho_penalty = sum([
                self.degree**2 *
                (ufl.jump(ufl.grad(rho), self.n) *
                 ufl.jump(ufl.grad(-self.grhopen * wrho), self.n)) * self.dS
                for rho, wrho in zip(self.irhos, self.wrhos)
            ])
            self.rho_terms = (self.rho_flux_jump + self.rho_grad_move +
                              self.rho_penalty + self.grho_penalty)
            logVARIABLE('rho_terms made')
        if not hasattr(self, 'U_terms'):
            logVARIABLE('making U_terms')
            self.Umin = self.iparams['Umin']
            self.Upen = self.iparams['Upen']
            self.gUpen = self.iparams['gUpen']
            self.U_decay = 0.0
            self.U_secretion = 0.0
            self.jump_gUw = 0.0
            self.U_diffusion = 0.0
            self.U_penalty = 0.0
            self.gU_penalty = 0.0
            for j, lig in enumerate(self.iligands.ligands()):
                sl = slice(j * 2**self.dim, (j + 1) * 2**self.dim)
                self.U_decay += sum([
                    -lig.gamma * iUi * wUi * self.dx
                    for iUi, wUi in zip(self.iUs[sl], self.wUs[sl])
                ])
                self.U_secretion += sum([
                    lig.s * rho * wU * self.dx
                    for rho, wU in zip(self.irhos, self.wUs[sl])
                ])
                self.jump_gUw += sum([
                    ufl.jump(lig.D * wU * ufl.grad(U), self.n) * self.dS
                    for wU, U in zip(self.wUs[sl], self.iUs[sl])
                ])
                self.U_diffusion += sum([
                    -lig.D * ufl.dot(ufl.grad(U), ufl.grad(wU)) * self.dx
                    for U, wU in zip(self.iUs[sl], self.wUs[sl])
                ])
                self.U_penalty += sum([
                    (-self.degree**2 / self.havg) *
                    ufl.dot(ufl.jump(U, self.n),
                            ufl.jump(self.Upen * wU, self.n)) * self.dS
                    for U, wU in zip(self.iUs[sl], self.wUs[sl])
                ])
                self.gU_penalty += sum([
                    -self.degree**2 * ufl.jump(ufl.grad(U), self.n) *
                    ufl.jump(ufl.grad(self.gUpen * wU), self.n) * self.dS
                    for U, wU in zip(self.iUs[sl], self.wUs[sl])
                ])
            self.U_terms = (
                # decay and secretion
                self.U_decay + self.U_secretion +
                # diffusion
                self.jump_gUw + self.U_diffusion +
                # penalties (to enforce continuity)
                self.U_penalty + self.gU_penalty)
            logVARIABLE('U_terms made')
        if not hasattr(self, 'all_terms'):
            logVARIABLE('making all_terms')
            self.all_terms = self.rho_terms + self.U_terms
        if not hasattr(self, 'J_terms'):
            logVARIABLE('making J_terms')
            self.J_terms = fe.derivative(self.all_terms, self.sol)

    def ddt(self, t, debug=False):
        """Calculate time derivative of rho and U

        Results are left in self.dsol as a two-component vector function.
        """
        self.setup_problem(t, debug=debug)
        self.b = fe.assemble(self.all_terms)
        for bc in self.bcs:
            bc.apply(self.b)
        return fe.solve(self.A, self.dsol.vector(), self.b, self.solver_type)
Пример #28
0
class ExpandedMesh:
    def __init__(self, function):
        """Expand a Function defined on a corner mesh to a full mesh.

        Required parameter:
        funcion: the Function to be expanded.
        """
        self.VS = function.function_space()
        self.submesh = self.VS.mesh()
        self.dim = self.submesh.geometric_dimension()
        self.nss = self.VS.num_sub_spaces()
        self.nfields = self.nss // (2**self.dim)
        self.subcoords = self.submesh.coordinates()
        self.vtrans = integerify_transform(self.subcoords)
        self.icols = ['i' + str(i) for i in range(self.dim)]
        self.emesh, self.maps, self.cellmaps, self.vertexmaps = (expand_mesh(
            self.submesh, self.vtrans))
        self.dcoords = np.reshape(self.VS.tabulate_dof_coordinates(),
                                  (-1, self.dim))
        self.dtrans = integerify_transform(self.dcoords)
        self.eSE = scalar_element(self.VS)
        self.degree = self.eSE.degree()
        self.eVE = MixedElement([self.eSE] * self.nfields)
        self.eVS = FunctionSpace(self.emesh, self.eVE)
        self.sub_dof_list = dof_list(self.VS, self.dtrans)
        subs = self.sub_dof_list['sub'].values
        self.sub_dof_list['submesh'] = subs % (2**self.dim)
        self.sub_dof_list['field'] = subs // (2**self.dim)
        sc = self.sub_dof_list[['submesh', 'cell']].values
        self.sub_dof_list['ecell'] = self.cellmaps[(sc[:, 0], sc[:, 1])]
        self.e_dof_list = dof_list(self.eVS, self.dtrans)
        self.remap = self.sub2e_map()
        self.symmetries = evenodd_symmetries(self.dim)
        self.eomat = evenodd_matrix(self.symmetries)
        self.sub_function = Function(self.VS)
        self.expanded_function = Function(self.eVS)

    def expand(self):
        fvec = self.sub_function.vector()[:]
        fvec = np.reshape(fvec, (-1, 2**self.dim))
        fvec = np.matmul(fvec, self.eomat)
        fvec = fvec.flatten()
        fvec = fvec[self.remap]
        self.expanded_function.vector()[:] = fvec

    def sub2e_map(self):
        cs = list(range(self.dim))
        sdlcols = ['dof', 'submesh', 'field', 'ecell'] + self.icols
        sdls = pd.DataFrame(columns=sdlcols + cs)
        sdl = self.sub_dof_list.copy()
        for submesh in range(2**self.dim):
            sdlsm = sdl[sdl['submesh'] == submesh].copy()
            sdlsm[cs] = self.maps[submesh](sdlsm[cs])
            sdls = sdls.append(sdlsm[sdlcols + cs], sort=False)
        sdls[self.icols] = integerify(sdls[cs].values, transform=self.dtrans)
        sdls.sort_values('dof', inplace=True)
        sdls[sdlcols] = np.array(sdls[sdlcols].values, dtype='int')
        remapdf = self.e_dof_list.merge(right=sdls,
                                        left_on=['sub', 'cell'] + self.icols,
                                        right_on=['field', 'ecell'] +
                                        self.icols)
        assert (self.e_dof_list.shape[0] == sdls.shape[0]
                and remapdf.shape[0] == sdls.shape[0])
        remapdf.sort_values(['dof_x'], inplace=True)
        return remapdf['dof_y'].values
Пример #29
0
class KSDGSolver:
    default_params = dict(
        rho_min=1e-7,
        U_min=1e-7,
        width=1.0,
        rhopen=10,
        Upen=1,
        grhopen=1,
        gUpen=1,
    )

    def __init__(self,
                 mesh=None,
                 width=1.0,
                 dim=1,
                 nelements=8,
                 degree=2,
                 parameters={},
                 V=(lambda U: U),
                 U0=None,
                 rho0=None,
                 t0=0.0,
                 debug=False,
                 solver_type='gmres',
                 preconditioner_type='default',
                 periodic=False,
                 ligands=None):
        """Discontinuous Galerkin solver for the Keller-Segel PDE system

        Keyword parameters:
        mesh=None: the mesh on which to solve the problem
        width=1.0: the width of the domain
        dim=1: # of spatial dimensions.
        nelements=8: If mesh is not supplied, one will be
        contructed using UnitIntervalMesh, UnitSquareMesh, or
        UnitCubeMesh (depending on dim). dim and nelements are not
        needed if mesh is supplied.
        degree=2: degree of the polynomial approximation
        parameters={}: a dict giving the values of scalar parameters of
            .V, U0, and rho0 Expressions. This dict needs to also
            define numerical parameters that appear in the PDE. Some
            of these have defaults:
            dim = dim: # of spatial dimensions
            sigma: organism movement rate
            s: attractant secretion rate
            gamma: attractant decay rate
            D: attractant diffusion constant
            rho_min=10.0**-7: minimum feasible worm density
            U_min=10.0**-7: minimum feasible attractant concentration
            rhopen=10: penalty for discontinuities in rho
            Upen=1: penalty for discontinuities in U
            grhopen=1, gUpen=1: penalties for discontinuities in gradients
        V=(lambda U: U): a callable taking two numerical arguments, U
            and rho, or a single argument, U, and returning a single
            number, V, the potential corresponding to U. Use fenics
            versions of mathematical functions, e.g. ufl.ln, abs,
            ufl.exp.
        U0, rho0: Expressions, Functions, or strs specifying the
            initial condition.
        t0=0.0: initial time
        solver_type='gmres'
        preconditioner_type='default'
        periodic, ligands: ignored for caompatibility
        """
        logSOLVER('creating KSDGSolver')
        self.args = dict(mesh=mesh,
                         width=width,
                         dim=dim,
                         nelements=nelements,
                         degree=degree,
                         parameters=parameters,
                         V=V,
                         U0=U0,
                         rho0=rho0,
                         t0=t0,
                         debug=debug,
                         solver_type=solver_type,
                         preconditioner_type=preconditioner_type,
                         periodic=periodic,
                         ligands=ligands)
        self.debug = debug
        self.solver_type = solver_type
        self.preconditioner_type = preconditioner_type
        self.periodic = False
        self.ligands = ligands
        self.params = self.default_params.copy()
        if (mesh):
            self.omesh = self.mesh = mesh
        else:
            self.omesh = self.mesh = box_mesh(width=width,
                                              dim=dim,
                                              nelements=nelements)
            self.nelements = nelements
        logSOLVER('self.mesh', self.mesh)
        logSOLVER('self.mesh.mpi_comm().size', self.mesh.mpi_comm().size)
        self.nelements = nelements
        self.degree = degree
        self.dim = self.mesh.geometry().dim()
        self.params['dim'] = self.dim
        self.params.update(parameters)
        #
        # Solution spaces and Functions
        #
        fss = self.make_function_space()
        (self.SE, self.SS, self.VE,
         self.VS) = [fss[fs] for fs in ('SE', 'SS', 'VE', 'VS')]
        logSOLVER('self.VS', self.VS)
        self.sol = Function(self.VS)  # sol, current soln
        logSOLVER('self.sol', self.sol)
        self.srho, self.sU = self.sol.sub(0), self.sol.sub(1)
        self.irho, self.iU = fe.split(self.sol)
        self.wrho, self.wU = TestFunctions(self.VS)
        self.tdsol = TrialFunction(self.VS)
        self.tdrho, self.tdU = fe.split(self.tdsol)
        self.n = FacetNormal(self.mesh)
        self.h = CellDiameter(self.mesh)
        self.havg = fe.avg(self.h)
        self.dx = fe.dx
        #        self.dx = fe.dx(metadata={'quadrature_degree': min(degree, 10)})
        self.dS = fe.dS
        #        self.dS = fe.dS(metadata={'quadrature_degree': min(degree, 10)})
        #
        # record initial state
        #
        try:
            V(self.iU, self.irho)

            def realV(U, rho):
                return V(U, rho)
        except TypeError:

            def realV(U, rho):
                return V(U)

        self.V = realV
        if not U0:
            U0 = Constant(0.0)
        if isinstance(U0, ufl.coefficient.Coefficient):
            self.U0 = U0
        else:
            self.U0 = Expression(U0,
                                 **self.params,
                                 degree=self.degree,
                                 domain=self.mesh)
        if not rho0:
            rho0 = Constant(0.0)
        if isinstance(rho0, ufl.coefficient.Coefficient):
            self.rho0 = rho0
        else:
            self.rho0 = Expression(rho0,
                                   **self.params,
                                   degree=self.degree,
                                   domain=self.mesh)
        self.t0 = t0
        #
        # initialize state
        #
        # cache assigners
        logSOLVER('restarting')
        self.restart()
        logSOLVER('restart returned')
        return (None)

    def make_function_space(self, mesh=None, dim=None, degree=None):
        if not mesh: mesh = self.mesh
        if not dim: dim = self.dim
        if not degree: degree = self.degree
        SE = FiniteElement('DG', cellShapes[dim - 1], degree)
        SS = FunctionSpace(mesh, SE)  # scalar space
        VE = MixedElement(SE, SE)
        VS = FunctionSpace(mesh, VE)  # vector space
        return dict(SE=SE, SS=SS, VE=VE, VS=VS)

    def restart(self):
        logSOLVER('restart')
        self.t = self.t0
        CE = FiniteElement('CG', cellShapes[self.dim - 1], self.degree)
        CS = FunctionSpace(self.mesh, CE)  # scalar space
        coords = gather_dof_coords(CS)
        logSOLVER('function_interpolate(self.U0, self.SS, coords=coords)',
                  function_interpolate(self.U0, self.SS, coords=coords))
        fe.assign(self.sol.sub(1),
                  function_interpolate(self.U0, self.SS, coords=coords))
        logSOLVER('U0 assign returned')
        fe.assign(self.sol.sub(0),
                  function_interpolate(self.rho0, self.SS, coords=coords))

    def set_time(t):
        """Stub for derived classes to override"""
        self.t = t

    def setup_problem(self, debug=False):
        #
        # assemble the matrix, if necessary (once for all time points)
        #
        if not hasattr(self, 'A'):
            self.drho_integral = self.tdrho * self.wrho * self.dx
            self.dU_integral = self.tdU * self.wU * self.dx
            self.A = fe.assemble(self.drho_integral + self.dU_integral)
            # if self.solver_type == 'lu':
            #     self.solver = fe.LUSolver(
            #         self.A,
            #     )
            #     self.solver.parameters['reuse_factorization'] = True
            # else:
            #     self.solver = fe.KrylovSolver(
            #         self.A,
            #         self.solver_type,
            #         self.preconditioner_type
            #     )
            # self.solver.parameters.add('linear_solver', self.solver_type)
            # kparams = fe.Parameters('krylov_solver')
            # kparams.add('report', True)
            # kparams.add('nonzero_initial_guess', True)
            # self.solver.parameters.add(kparams)
            # lparams = fe.Parameters('lu_solver')
            # lparams.add('report', True)
            # lparams.add('reuse_factorization', True)
            # lparams.add('verbose', True)
            # self.solver.parameters.add(lparams)
            self.dsol = Function(self.VS)
            self.drho, self.dU = self.dsol.sub(0), self.dsol.sub(1)
        #
        # assemble RHS (for each time point, but compile only once)
        #
        if not hasattr(self, 'rho_terms'):
            self.sigma = self.params['sigma']
            self.s2 = self.sigma * self.sigma / 2
            self.rho_min = self.params['rho_min']
            self.rhopen = self.params['rhopen']
            self.grhopen = self.params['grhopen']
            self.v = -ufl.grad(self.V(self.iU, self.irho)) - (
                self.s2 * ufl.grad(self.irho) /
                ufl.max_value(self.irho, self.rho_min))
            self.flux = self.v * self.irho
            self.vn = ufl.max_value(ufl.dot(self.v, self.n), 0)
            self.facet_flux = (
                self.vn('+') * ufl.max_value(self.irho('+'), 0.0) -
                self.vn('-') * ufl.max_value(self.irho('-'), 0.0))
            self.rho_flux_jump = -self.facet_flux * ufl.jump(
                self.wrho) * self.dS
            self.rho_grad_move = ufl.dot(self.flux, ufl.grad(
                self.wrho)) * self.dx
            self.rho_penalty = -(
                (self.rhopen * self.degree**2 / self.havg) * ufl.dot(
                    ufl.jump(self.irho, self.n), ufl.jump(self.wrho, self.n)) *
                self.dS)
            self.grho_penalty = -(self.grhopen * self.degree**2 *
                                  (ufl.jump(ufl.grad(self.irho), self.n) *
                                   ufl.jump(ufl.grad(self.wrho), self.n)) *
                                  self.dS)
            self.rho_terms = (self.rho_flux_jump + self.rho_grad_move +
                              self.rho_penalty + self.grho_penalty)
        if not hasattr(self, 'U_terms'):
            self.U_min = self.params['U_min']
            self.gamma = self.params['gamma']
            self.s = self.params['s']
            self.D = self.params['D']
            self.Upen = self.params['Upen']
            self.gUpen = self.params['gUpen']
            self.U_decay = -self.gamma * self.iU * self.wU * self.dx
            self.U_secretion = self.s * self.irho * self.wU * self.dx
            self.jump_gUw = (self.D *
                             ufl.jump(self.wU * ufl.grad(self.iU), self.n) *
                             self.dS)
            self.U_diffusion = -self.D * ufl.dot(ufl.grad(self.iU),
                                                 ufl.grad(self.wU)) * self.dx
            self.U_penalty = -(
                (self.Upen * self.degree**2 / self.havg) *
                ufl.dot(ufl.jump(self.iU, self.n), ufl.jump(self.wU, self.n)) *
                self.dS)
            self.gU_penalty = -(self.gUpen * self.degree**2 *
                                (ufl.jump(ufl.grad(self.iU), self.n) *
                                 ufl.jump(ufl.grad(self.wU), self.n)) *
                                self.dS)
            self.U_terms = (
                # decay and secretion
                self.U_decay + self.U_secretion +
                # diffusion
                self.jump_gUw + self.U_diffusion +
                # penalties (to enforce continuity)
                self.U_penalty + self.gU_penalty)
        if not hasattr(self, 'all_terms'):
            self.all_terms = self.rho_terms + self.U_terms
        if not hasattr(self, 'J_terms'):
            self.J_terms = fe.derivative(self.all_terms, self.sol)
        # if not hasattr(self, 'JU_terms'):
        #     self.JU_terms = fe.derivative(self.all_terms, self.sU)
        # if not hasattr(self, 'Jrho_terms'):
        #     self.Jrho_terms = fe.derivative(self.all_terms, self.srho)

    def ddt(self, debug=False):
        """Calculate time derivative of rho and U

        Results are left in self.dsol as a two-component vector function.
        """
        self.setup_problem(debug)
        self.b = fe.assemble(self.all_terms)
        return fe.solve(self.A, self.dsol.vector(), self.b, self.solver_type)

    #
    # The following member functions should not really exist -- use
    # the TS classes in ts instead. These are here only to allow some
    # old code to work.

    def implicitTS(self,
                   t0=0.0,
                   dt=0.001,
                   tmax=20,
                   maxsteps=100,
                   rtol=1e-5,
                   atol=1e-5,
                   prt=True,
                   restart=True,
                   tstype=PETSc.TS.Type.ROSW,
                   finaltime=PETSc.TS.ExactFinalTime.STEPOVER):
        """
        Create an implicit timestepper and solve the DE

        Keyword arguments:
        t0=0.0: the initial time.
        dt=0.001: the initial time step.
        tmax=20: the final time.
        maxsteps=100: maximum number of steps to take.
        rtol=1e-5: relative error tolerance.
        atol=1e-5: absolute error tolerance.
        prt=True: whether to print results as solution progresses
        restart=True: whether to set the initial condition to rho0, U0
        tstype=PETSc.TS.Type.ROSW: implicit solver to use.
        finaltime=PETSc.TS.ExactFinalTime.STEPOVER: how to handle
            final time step.

        Other options can be set by modifying the PETSc Options
        database.
        """
        #        print("KSDGSolver.implicitTS __init__ entered")
        from .ts import implicitTS  # done here to avoid circular imports
        self.ts = implicitTS(
            self,
            t0=t0,
            dt=dt,
            tmax=tmax,
            maxsteps=maxsteps,
            rtol=rtol,
            atol=atol,
            restart=restart,
            tstype=tstype,
            finaltime=finaltime,
        )
        self.ts.setMonitor(self.ts.historyMonitor)
        if prt:
            self.ts.setMonitor(self.ts.printMonitor)
        self.ts.solve()
        self.ts.cleanup()

    def cleanupTS():
        """Should be called when finished with a TS

        Leaves history unchanged.
        """
        del self.ts, self.tsparams

    def imExTS(self,
               t0=0.0,
               dt=0.001,
               tmax=20,
               maxsteps=100,
               rtol=1e-5,
               atol=1e-5,
               prt=True,
               restart=True,
               tstype=PETSc.TS.Type.ARKIMEX,
               finaltime=PETSc.TS.ExactFinalTime.STEPOVER):
        """
        Create an implicit/explicit timestepper and solve the DE

        Keyword arguments:
        t0=0.0: the initial time.
        dt=0.001: the initial time step.
        tmax=20: the final time.
        maxsteps=100: maximum number of steps to take.
        rtol=1e-5: relative error tolerance.
        atol=1e-5: absolute error tolerance.
        prt=True: whether to print results as solution progresses
        restart=True: whether to set the initial condition to rho0, U0
        tstype=PETSc.TS.Type.ARKIMEX: implicit solver to use.
        finaltime=PETSc.TS.ExactFinalTime.STEPOVER: how to handle
            final time step.

        Other options can be set by modifying the PETSc options
        database.
        """
        from .ts import imExTS  # done here to avoid circular imports
        self.ts = imExTS(
            self,
            t0=t0,
            dt=dt,
            tmax=tmax,
            maxsteps=maxsteps,
            rtol=rtol,
            atol=atol,
            restart=restart,
            tstype=tstype,
            finaltime=finaltime,
        )
        self.ts.setMonitor(self.ts.historyMonitor)
        if prt:
            self.ts.setMonitor(self.ts.printMonitor)
        self.ts.solve()
        self.ts.cleanup()

    def explicitTS(self,
                   t0=0.0,
                   dt=0.001,
                   tmax=20,
                   maxsteps=100,
                   rtol=1e-5,
                   atol=1e-5,
                   prt=True,
                   restart=True,
                   tstype=PETSc.TS.Type.RK,
                   finaltime=PETSc.TS.ExactFinalTime.STEPOVER):
        """
        Create an explicit timestepper and solve the DE

        Keyword arguments:
        t0=0.0: the initial time.
        dt=0.001: the initial time step.
        tmax=20: the final time.
        maxsteps=100: maximum number of steps to take.
        rtol=1e-5: relative error tolerance.
        atol=1e-5: absolute error tolerance.
        prt=True: whether to print results as solution progresses
        restart=True: whether to set the initial condition to rho0, U0
        tstype=PETSc.TS.Type.RK: explicit solver to use.
        finaltime=PETSc.TS.ExactFinalTime.STEPOVER: how to handle
            final time step.

        Other options can be set by modifyign the PETSc options
        database.
        """
        from .ts import explicitTS  # done here to avoid circular imports
        self.ts = explicitTS(
            self,
            t0=t0,
            dt=dt,
            tmax=tmax,
            maxsteps=maxsteps,
            rtol=rtol,
            atol=atol,
            restart=restart,
            tstype=tstype,
            finaltime=finaltime,
        )
        self.ts.setMonitor(self.ts.historyMonitor)
        if prt:
            self.ts.setMonitor(self.ts.printMonitor)
        self.ts.solve()
        self.ts.cleanup()
Пример #30
0
def solve_linear_pde(
    u_D_array,
    T,
    D=1,
    C1=0,
    num_r=100,
    min_r=0.001,
    tol=1e-14,
    degree=1,
):
    # disable logging
    set_log_active(False)

    num_t = len(u_D_array)
    dt = T / num_t  # time step size

    mesh = IntervalMesh(num_r, min_r, 1)
    r = mesh.coordinates().flatten()
    r_args = np.argsort(r)
    V = FunctionSpace(mesh, "P", 1)

    # Define boundary conditions
    # Dirichlet condition at R
    def boundary_at_R(x, on_boundary):
        return on_boundary and near(x[0], 1, tol)

    D = Constant(D)
    u_D = Constant(u_D_array[0])
    bc_at_R = DirichletBC(V, u_D, boundary_at_R)

    # Define initial values for free c
    c_0 = Expression("C1", C1=C1, degree=degree)
    c_n = interpolate(c_0, V)

    # Define variational problem
    c = TrialFunction(V)
    v = TestFunction(V)

    # define Constants
    r_squ = Expression("4*pi*pow(x[0],2)", degree=degree)

    F_tmp = (D * dt * inner(grad(c), grad(v)) * r_squ * dx +
             c * v * r_squ * dx - c_n * v * r_squ * dx)
    a, L = lhs(F_tmp), rhs(F_tmp)
    u = Function(V)

    data_c = np.zeros((num_t, len(r)), dtype=np.double)

    for n in range(num_t):
        u_D.assign(u_D_array[n])

        # Compute solution
        solve(a == L, u, bc_at_R)
        data_c[n, :] = u.vector().vec().array

        c_n.assign(u)

    data_c = data_c[:, r_args[::-1]]
    r = r[r_args]

    return data_c, r
Пример #31
0
class KSDGSolverVariable(KSDGSolverMultiple):
    default_params = collections.OrderedDict(
        sigma=1.0,
        rhomin=1e-7,
        Umin=1e-7,
        width=1.0,
        rhopen=10.0,
        Upen=1.0,
        grhopen=1.0,
        gUpen=1.0,
    )

    def __init__(self,
                 mesh=None,
                 width=1.0,
                 dim=1,
                 nelements=8,
                 degree=2,
                 parameters={},
                 param_funcs={},
                 V=(lambda U, params={}: sum(U)),
                 U0=[],
                 rho0=None,
                 t0=0.0,
                 debug=False,
                 solver_type='petsc',
                 preconditioner_type='default',
                 periodic=False,
                 ligands=None):
        """Discontinuous Galerkin solver for the Keller-Segel PDE system

        Keyword parameters:
        mesh=None: the mesh on which to solve the problem
        width=1.0: the width of the domain
        dim=1: # of spatial dimensions.
        nelements=8: If mesh is not supplied, one will be
        contructed using UnitIntervalMesh, UnitSquareMesh, or
        UnitCubeMesh (depending on dim). dim and nelements are not
        needed if mesh is supplied.
        degree=2: degree of the polynomial approximation
        parameters={}: a dict giving the initial values of scalar
            parameters of .V, U0, and rho0 Expressions. This dict
            needs to also define numerical parameters that appear in
            the PDE. Some of these have defaults: dim = dim: # of
            spatial dimensions sigma: organism movement rate
            rhomin=10.0**-7: minimum feasible worm density
            Umin=10.0**-7: minimum feasible attractant concentration
            rhopen=10: penalty for discontinuities in rho Upen=1:
            penalty for discontinuities in U grhopen=1, gUpen=1:
            penalties for discontinuities in gradients nligands=1,
            number of ligands.
        V=(lambda Us, params={}: sum(Us)): a callable taking two
            arguments, Us and rho, or a single argument, Us. Us is a
            list of length nligands. rho is a single expression. V
            returns a single number, V, the potential corresponding to
            Us (and rho). Use ufl versions of mathematical functions,
            e.g. ufl.ln, abs, ufl.exp.
        rho0: Expressions, Functions, or strs specifying the
            initial condition for rho.
        U0: a list of nligands Expressions, Functions or strs
            specifying the initial conditions for the ligands.
        t0=0.0: initial time
        solver_type='gmres'
        preconditioner_type='default'
        ligands=LigandGroups(): ligand list
        periodic=False: ignored for compatibility
        """
        logVARIABLE('creating KSDGSolverVariable')
        if not ligands:
            ligands = LigandGroups()
        else:
            ligands = copy.deepcopy(ligands)
        self.args = dict(mesh=mesh,
                         width=width,
                         dim=dim,
                         nelements=nelements,
                         degree=degree,
                         parameters=parameters,
                         param_funcs=param_funcs,
                         V=V,
                         U0=U0,
                         rho0=rho0,
                         t0=t0,
                         debug=debug,
                         solver_type=solver_type,
                         preconditioner_type=preconditioner_type,
                         periodic=periodic,
                         ligands=ligands)
        self.t0 = t0
        self.debug = debug
        self.solver_type = solver_type
        self.preconditioner_type = preconditioner_type
        self.periodic = False
        self.ligands = ligands
        self.nligands = ligands.nligands()
        self.init_params(parameters, param_funcs)
        if (mesh):
            self.omesh = self.mesh = mesh
        else:
            self.omesh = self.mesh = box_mesh(width=width,
                                              dim=dim,
                                              nelements=nelements)
            self.nelements = nelements
        logVARIABLE('self.mesh', self.mesh)
        logVARIABLE('self.mesh.mpi_comm().size', self.mesh.mpi_comm().size)
        self.nelements = nelements
        self.degree = degree
        self.dim = self.mesh.geometry().dim()
        #
        # Solution spaces and Functions
        #
        fss = self.make_function_space()
        (self.SE, self.SS, self.VE,
         self.VS) = [fss[fs] for fs in ('SE', 'SS', 'VE', 'VS')]
        logVARIABLE('self.VS', self.VS)
        self.sol = Function(self.VS)  # sol, current soln
        logVARIABLE('self.sol', self.sol)
        splitsol = self.sol.split()
        self.srho, self.sUs = splitsol[0], splitsol[1:]
        splitsol = list(fe.split(self.sol))
        self.irho, self.iUs = splitsol[0], splitsol[1:]
        self.iPs = list(fe.split(self.PSf))
        self.iparams = collections.OrderedDict(zip(self.param_names, self.iPs))
        self.iligands = copy.deepcopy(self.ligands)
        self.iligand_params = ParameterList(
            [p for p in self.iligands.params() if p[0] in self.param_numbers])
        for k in self.iligand_params.keys():
            i = self.param_numbers[k]
            self.iligand_params[k] = self.iPs[i]
        tfs = list(TestFunctions(self.VS))
        self.wrho, self.wUs = tfs[0], tfs[1:]
        tfs = list(TrialFunctions(self.VS))
        self.tdrho, self.tdUs = tfs[0], tfs[1:]
        self.n = FacetNormal(self.mesh)
        self.h = CellDiameter(self.mesh)
        self.havg = fe.avg(self.h)
        self.dx = fe.dx
        #        self.dx = fe.dx(metadata={'quadrature_degree': min(degree, 10)})
        self.dS = fe.dS
        #        self.dS = fe.dS(metadata={'quadrature_degree': min(degree, 10)})
        #
        # record initial state
        #
        try:
            V(self.iUs, self.irho, params=self.iparams)

            def realV(Us, rho):
                return V(Us, rho, params=self.iparams)
        except TypeError:

            def realV(Us, rho):
                return V(Us, self.iparams)

        self.V = realV
        if not U0:
            U0 = [Constant(0.0)] * self.nligands
        self.U0s = [Constant(0.0)] * self.nligands
        for i, U0i in enumerate(U0):
            if isinstance(U0i, ufl.coefficient.Coefficient):
                self.U0s[i] = U0i
            else:
                self.U0s[i] = Expression(U0i,
                                         **self.params,
                                         degree=self.degree,
                                         domain=self.mesh)
        if not rho0:
            rho0 = Constant(0.0)
        if isinstance(rho0, ufl.coefficient.Coefficient):
            self.rho0 = rho0
        else:
            self.rho0 = Expression(rho0,
                                   **self.params,
                                   degree=self.degree,
                                   domain=self.mesh)
        self.set_time(t0)
        #
        # initialize state
        #
        self.restart()
        return None

    def init_params(self, parameters, param_funcs):
        """Initialize parameter attributes from __init__ arguments

        The attributes initialized are:
        self.params0: a dict giving initial values of all parameters
        (not just floats). This is basically a copy of the parameters
        argument to __init__, with the insertion of 't' as a new
        parameter (always param_names[-1]).
        self.param_names: a list of the names of the time-varying
        parameters. This is the keys of params0 whose corrsponding
        values are of type float. The order is the order of the
        parameters in self.PSf.
        self.nparams: len(self.param_names)
        self.param_numbers: a dict mapping param names to numbers
        (ints) in the list param_names and the parameters subspace of
        the solution FunctionSpace.
        self.param_funcs: a dict whose keys are the param_names and
        whose values are functions to determine their values as a
        function of time, as explained above. These are copied from
        the param_funcs argument of __init__, except that the default
        initial value function is filled in for parameters not present
        in the argument. Also, the function defined for 't' always
        returns t.
        self.PSf: a Constant object of dimension self.nparams, holding
        the initial values of the parameters.
        """
        self.param_names = [
            n for n, v in parameters.items() if (type(v) is float and n != 't')
        ]
        self.param_names.append('t')
        self.nparams = len(self.param_names)
        logVARIABLE('self.param_names', self.param_names)
        logVARIABLE('self.nparams', self.nparams)
        self.param_numbers = collections.OrderedDict(
            zip(self.param_names, itertools.count()))
        self.params0 = collections.OrderedDict(parameters)
        self.params0['t'] = 0.0
        self.param_funcs = param_funcs.copy()

        def identity(t, params={}):
            return t

        self.param_funcs['t'] = identity
        for n in self.param_names:
            if n not in self.param_funcs:

                def value0(t, params={}, v0=self.params0[n]):
                    return v0

                self.param_funcs[n] = value0
        self.PSf = Constant([self.params0[n] for n in self.param_names])
        return

    def set_time(self, t):
        self.t = t
        params = collections.OrderedDict(
            zip(self.param_names, self.PSf.values()))
        self.PSf.assign(
            Constant([
                self.param_funcs[n](t, params=params) for n in self.param_names
            ]))
        logVARIABLE('self.t', self.t)
        logVARIABLE(
            'collections.OrderedDict(zip(self.param_names, self.PSf.values()))',
            collections.OrderedDict(zip(self.param_names, self.PSf.values())))

    def make_function_space(self, mesh=None, dim=None, degree=None):
        if not mesh: mesh = self.mesh
        if not dim: dim = self.dim
        if not degree: degree = self.degree
        SE = FiniteElement('DG', cellShapes[dim - 1], degree)
        SS = FunctionSpace(mesh, SE)  # scalar space
        elements = [SE] * (self.nligands + 1)
        VE = MixedElement(elements)
        VS = FunctionSpace(mesh, VE)  # vector space
        return dict(SE=SE, SS=SS, VE=VE, VS=VS)

    def restart(self):
        logVARIABLE('restart')
        self.set_time(self.t0)
        CE = FiniteElement('CG', cellShapes[self.dim - 1], self.degree)
        CS = FunctionSpace(self.mesh, CE)  # scalar space
        coords = gather_dof_coords(CS)
        fe.assign(self.sol.sub(0),
                  function_interpolate(self.rho0, self.SS, coords=coords))
        for i, U0i in enumerate(self.U0s):
            fe.assign(self.sol.sub(i + 1),
                      function_interpolate(U0i, self.SS, coords=coords))

    def setup_problem(self, t, debug=False):
        self.set_time(t)
        #
        # assemble the matrix, if necessary (once for all time points)
        #
        if not hasattr(self, 'A'):
            self.drho_integral = self.tdrho * self.wrho * self.dx
            self.dU_integral = sum([
                tdUi * wUi * self.dx for tdUi, wUi in zip(self.tdUs, self.wUs)
            ])
            logVARIABLE('assembling A')
            self.A = PETScMatrix()
            logVARIABLE('self.A', self.A)
            fe.assemble(self.drho_integral + self.dU_integral, tensor=self.A)
            logVARIABLE('A assembled. Applying BCs')
            self.dsol = Function(self.VS)
            dsolsplit = self.dsol.split()
            self.drho, self.dUs = dsolsplit[0], dsolsplit[1:]
        #
        # assemble RHS (for each time point, but compile only once)
        #
        if not hasattr(self, 'rho_terms'):
            self.sigma = self.iparams['sigma']
            self.s2 = self.sigma * self.sigma / 2
            self.rhomin = self.iparams['rhomin']
            self.rhopen = self.iparams['rhopen']
            self.grhopen = self.iparams['grhopen']
            self.v = -ufl.grad(
                self.V(self.iUs, ufl.max_value(self.irho, self.rhomin)) -
                (self.s2 * ufl.grad(self.irho) /
                 ufl.max_value(self.irho, self.rhomin)))
            self.flux = self.v * self.irho
            self.vn = ufl.max_value(ufl.dot(self.v, self.n), 0)
            self.facet_flux = ufl.jump(self.vn * ufl.max_value(self.irho, 0.0))
            self.rho_flux_jump = -self.facet_flux * ufl.jump(
                self.wrho) * self.dS
            self.rho_grad_move = ufl.dot(self.flux, ufl.grad(
                self.wrho)) * self.dx
            self.rho_penalty = -(
                (self.degree**2 / self.havg) *
                ufl.dot(ufl.jump(self.irho, self.n),
                        ufl.jump(self.rhopen * self.wrho, self.n)) * self.dS)
            self.grho_penalty = -(
                self.degree**2 *
                (ufl.jump(ufl.grad(self.irho), self.n) * ufl.jump(
                    ufl.grad(self.grhopen * self.wrho), self.n)) * self.dS)
            self.rho_terms = (self.rho_flux_jump + self.rho_grad_move +
                              self.rho_penalty + self.grho_penalty)
        if not hasattr(self, 'U_terms'):
            self.Umin = self.iparams['Umin']
            self.Upen = self.iparams['Upen']
            self.gUpen = self.iparams['gUpen']
            self.U_decay = sum([
                -lig.gamma * iUi * wUi * self.dx for lig, iUi, wUi in zip(
                    self.iligands.ligands(), self.iUs, self.wUs)
            ])
            self.U_secretion = sum([
                lig.s * self.irho * wUi * self.dx
                for lig, wUi in zip(self.iligands.ligands(), self.wUs)
            ])
            self.jump_gUw = sum([
                ufl.jump(lig.D * wUi * ufl.grad(iUi), self.n) * self.dS
                for lig, wUi, iUi in zip(self.iligands.ligands(), self.wUs,
                                         self.iUs)
            ])
            self.U_diffusion = sum([
                -lig.D * ufl.dot(ufl.grad(iUi), ufl.grad(wUi)) * self.dx
                for lig, iUi, wUi in zip(self.iligands.ligands(), self.iUs,
                                         self.wUs)
            ])
            self.U_penalty = sum([
                -(self.degree**2 / self.havg) * ufl.dot(
                    ufl.jump(iUi, self.n), ufl.jump(self.Upen * wUi, self.n)) *
                self.dS for iUi, wUi in zip(self.iUs, self.wUs)
            ])
            self.gU_penalty = sum([
                -self.degree**2 * ufl.jump(ufl.grad(iUi), self.n) *
                ufl.jump(ufl.grad(self.gUpen * wUi), self.n) * self.dS
                for iUi, wUi in zip(self.iUs, self.wUs)
            ])
            self.U_terms = (
                # decay and secretion
                self.U_decay + self.U_secretion +
                # diffusion
                self.jump_gUw + self.U_diffusion +
                # penalties (to enforce continuity)
                self.U_penalty + self.gU_penalty)
        if not hasattr(self, 'all_terms'):
            self.all_terms = self.rho_terms + self.U_terms
        if not hasattr(self, 'J_terms'):
            self.J_terms = fe.derivative(self.all_terms, self.sol)

    def ddt(self, t, debug=False):
        """Calculate time derivative of rho and U

        Results are left in self.dsol as a two-component vector function.
        """
        self.setup_problem(t, debug=debug)
        self.b = fe.assemble(self.all_terms)
        return fe.solve(self.A, self.dsol.vector(), self.b, self.solver_type)