示例#1
0
def project_gradient_neumann(
        f0,
        degree=None,
        mesh=None,
        solver_type='gmres',
        preconditioner_type='default'
    ):
    """Find an approximation to f0 that has the same gradient

    The resulting function also satisfies homogeneous Neumann boundary
    conditions.

    Parameters:    
    f0: the function to approximate
    mesh=None: the mesh on which to approximate it If not provided, the
        mesh is extracted from f0.
    degree=None: degree of the polynomial approximation. extracted
        from f0 if not provided. 
    solver_type='gmres': The linear solver type to use.
    preconditioner_type='default': Preconditioner type to use
    """
    if not mesh: mesh = f0.function_space().mesh()
    element = f0.ufl_element()
    if not degree:
        degree = element.degree()
    CE = FiniteElement('CG', mesh.ufl_cell(), degree)
    CS = FunctionSpace(mesh, CE)
    DE = FiniteElement('DG', mesh.ufl_cell(), degree)
    DS = FunctionSpace(mesh, DE)
    CVE = VectorElement('CG', mesh.ufl_cell(), degree - 1)
    CV = FunctionSpace(mesh, CVE)
    RE = FiniteElement('R', mesh.ufl_cell(), 0)
    R = FunctionSpace(mesh, RE)
    CRE = MixedElement([CE, RE])
    CR = FunctionSpace(mesh, CRE)
    f = fe.project(f0, CS,
                   solver_type=solver_type,
                   preconditioner_type=preconditioner_type)
    g = fe.project(fe.grad(f), CV,
                   solver_type=solver_type,
                   preconditioner_type=preconditioner_type)
    lf = fe.project(fe.nabla_div(g), CS,
                    solver_type=solver_type,
                    preconditioner_type=preconditioner_type)
    tf, tc = TrialFunction(CR)
    wf, wc = TestFunctions(CR)
    dx = Measure('dx', domain=mesh,
                 metadata={'quadrature_degree': min(degree, 10)})
    a = (fe.dot(fe.grad(tf), fe.grad(wf)) + tc * wf + tf * wc) * dx
    L = (f * wc - lf * wf) * dx
    igc = Function(CR)
    fe.solve(a == L, igc,
             solver_parameters={'linear_solver': solver_type,
                                 'preconditioner': preconditioner_type}
    )
    ig, c = igc.sub(0), igc.sub(1)
    igd = fe.project(ig, DS,
                     solver_type=solver_type,
                     preconditioner_type=preconditioner_type)
    return igd
示例#2
0
            precice.action_write_iteration_checkpoint()):  # write checkpoint
        precice.store_checkpoint(u_n, t, n)

    read_data = precice.read_data()

    # Update the coupling expression with the new read data
    precice.update_coupling_expression(coupling_expression, read_data)

    dt.assign(np.min([fenics_dt, precice_dt]))

    # Compute solution
    solve(a == L, u_np1, bcs)

    # Dirichlet problem obtains flux from solution and sends flux on boundary to Neumann problem
    determine_heat_flux(V_g, u_np1, k, fluxes)
    fluxes_y = fluxes.sub(1)  # only exchange y component of flux.
    precice.write_data(fluxes_y)

    precice_dt = precice.advance(dt(0))

    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
        n = n_cp
    else:  # update solution
        u_n.assign(u_np1)
        t += float(dt)
        n += 1
示例#3
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)
示例#4
0
文件: heat.py 项目: precice/tutorials
    read_data = precice.read_data()

    # Update the coupling expression with the new read data
    precice.update_coupling_expression(coupling_expression, read_data)

    dt.assign(np.min([fenics_dt, precice_dt]))

    # Compute solution u^n+1, use bcs u_D^n+1, u^n and coupling bcs
    solve(a == L, u_np1, bcs)

    # Write data to preCICE according to which problem is being solved
    if problem is ProblemType.DIRICHLET:
        # Dirichlet problem reads temperature and writes flux on boundary to Neumann problem
        determine_gradient(V_g, u_np1, flux)
        flux_x = interpolate(flux.sub(0), W)
        precice.write_data(flux_x)
    elif problem is ProblemType.NEUMANN:
        # Neumann problem reads flux and writes temperature on boundary to Dirichlet problem
        precice.write_data(u_np1)

    precice_dt = precice.advance(dt(0))

    # roll back to checkpoint
    if precice.is_action_required(precice.action_read_iteration_checkpoint()):
        u_cp, t_cp, n_cp = precice.retrieve_checkpoint()
        u_n.assign(u_cp)
        t = t_cp
        n = n_cp
    else:  # update solution
        u_n.assign(u_np1)
示例#5
0
文件: ksdgvar.py 项目: leonavery/KSDG
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)
示例#6
0
文件: ksdgvar.py 项目: leonavery/KSDG
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)
示例#7
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()
示例#8
0
class EKKSDGSolver(KSDGSolver):
    """KSDSolver that uses the Epshteyn and Kurganov scheme for rho.

    Overrides the setup_problem method of KSDGSolver.
    """
    def setup_problem(self, debug=False):
        #
        # assemble the matrix, if necessary (once for all time points)
        #
        if not hasattr(self, 'A'):
            drho_integral = self.tdrho * self.wrho * self.dx
            dU_integral = self.tdU * self.wU * self.dx
            self.A = fe.assemble(drho_integral + dU_integral)
            # if self.solver_type == 'lu':
            #     self.solver = fe.LUSolver(
            #         self.A,
            #         method=self.solver_type
            #     )
            #     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.drho, self.dU = self.dsol.sub(0), self.dsol.sub(1)
        #
        # assemble RHS (has to be done for each time point)
        #
        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.flux = self.v * self.irho
            self.vn = ufl.max_value(ufl.dot(self.v, self.n), 0)
            self.facet_flux = (self.vn('+') * self.irho('+') -
                               self.vn('-') * self.irho('-'))
            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.facet_flux = (
            #     self.vn('+')*self.rho('+') - self.vn('-')*self.rho('-')
            # )
            # 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.jump_grhow = (
                self.s2 * ufl.jump(self.wrho * ufl.grad(self.irho), self.n) *
                self.dS)
            self.rho_diffusion = -self.s2 * ufl.dot(ufl.grad(
                self.irho), ufl.grad(self.wrho)) * self.dx
            # self.rho_penalty = -(
            #     (self.rhopen * self.degree**2 / self.havg) *
            #     ufl.dot(ufl.jump(self.rho, 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 = (
                # advection terms
                self.rho_flux_jump + self.rho_grad_move +
                # diffusive terms
                self.rho_diffusion + self.jump_grhow +
                # penalty terms (to enforce continuity)
                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, '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)
示例#9
0
class KSDGSolverMultiple(KSDGSolver):
    default_params = dict(
        rho_min = 1e-7,
        U_min = 1e-7,
        width = 1.0,
        rhopen = 10,
        Upen = 1,
        grhopen = 1,
        gUpen = 1,
        ligands = None,
    )

    def __init__(
            self,
            mesh=None,
            width=1.0,
            dim=1,
            nelements=8,
            degree=2,
            parameters={},
            V=(lambda U: U),
            U0=[],
            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
            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
            nligands=1, number of ligands
        V=(lambda Us: 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
        """
        logMULTIPLE('creating KSDGSolverMultiple')
        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,
            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.nligands = ligands.nligands()
        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
        logMULTIPLE('self.mesh', self.mesh)
        logMULTIPLE('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')
        ]
        logMULTIPLE('self.VS', self.VS)
        self.sol = Function(self.VS)                  # sol, current soln
        logMULTIPLE('self.sol', self.sol)
        self.srho, self.sUs = self.sol.sub(0), self.sol.split()[1:]
        splitsol = fe.split(self.sol)
        self.irho, self.iUs = splitsol[0], splitsol[1:]
        tfs = TestFunctions(self.VS)
        self.wrho, self.wUs = tfs[0], tfs[1:]
        self.tdsol = TrialFunction(self.VS)
        splittdsol = fe.split(self.tdsol)
        self.tdrho, self.tdUs = splittdsol[0], splittdsol[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)
            def realV(Us, rho):
                return V(Us, rho)
        except TypeError:
            def realV(Us, rho):
                return V(Us)
        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.t0 = t0
        #
        # initialize state
        #
        logMULTIPLE('restarting')
        self.restart()
        logMULTIPLE('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] * (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):
        logMULTIPLE('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)
        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)) 
        logMULTIPLE('U0s assign returned')
        
    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 = sum(
                [tdUi*wUi*self.dx for tdUi,wUi in zip(self.tdUs, self.wUs)]
            )
            self.A = fe.assemble(self.drho_integral + self.dU_integral)
            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.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.iUs, 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.Upen = self.params['Upen']
            self.gUpen = self.params['gUpen']
            self.U_decay = sum(
                [-lig.gamma * iUi * wUi * self.dx for
                 lig,iUi,wUi in
                 zip(self.ligands.ligands(), self.iUs, self.wUs)]
            )
            self.U_secretion = sum(
                [lig.s * self.irho * wUi * self.dx for
                 lig,wUi in zip(self.ligands.ligands(), self.wUs)]
            )
            self.jump_gUw = sum(
                [lig.D * ufl.jump(wUi * ufl.grad(iUi), self.n) * self.dS
                for lig,wUi,iUi in
                zip(self.ligands.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.ligands.ligands(), self.iUs, self.wUs)]
            )
            self.U_penalty = sum(
                [-(self.Upen*self.degree**2/self.havg) *
                 ufl.dot(ufl.jump(iUi, self.n),
                         ufl.jump(wUi, self.n))*self.dS for
                 iUi,wUi in zip(self.iUs, self.wUs)]
            )
            self.gU_penalty = -self.gUpen * self.degree**2 * sum(
                [ufl.jump(ufl.grad(iUi), self.n) *
                 ufl.jump(ufl.grad(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, 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)
示例#10
0
def project_gradient(
        f0,
        mesh=None,
        degree=None,
        debug=False,
        solver_type='gmres',
        preconditioner_type='default'
    ):
    """Find an approximation to f0 that has the same gradient

    Parameters:    
    f0: the function to approximate
    mesh=None: the mesh on which to approximate it. If not provided, the
        mesh is extracted from f0.
    degree=None: degree of the polynomial approximation. extracted
        from f0 if not provided. 
    solver_type='gmres': The linear solver type to use.
    preconditioner_type='default': Preconditioner type to use
    """
    if not mesh: mesh = f0.function_space().mesh()
    element = f0.ufl_element()
    if not degree:
        degree = element.degree()
    CE = FiniteElement('CG', mesh.ufl_cell(), degree)
    CS = FunctionSpace(mesh, CE)
    DE = FiniteElement('DG', mesh.ufl_cell(), degree)
    DS = FunctionSpace(mesh, DE)
    CVE = VectorElement('CG', mesh.ufl_cell(), degree - 1)
    CV = FunctionSpace(mesh, CVE)
    RE = FiniteElement('R', mesh.ufl_cell(), 0)
    R = FunctionSpace(mesh, RE)
    CRE = MixedElement([CE, RE])
    CR = FunctionSpace(mesh, CRE)
    f = fe.project(f0, CS,
                   solver_type=solver_type,
                   preconditioner_type= preconditioner_type)
    g = fe.project(fe.grad(f), CV,
                   solver_type=solver_type,
                   preconditioner_type= preconditioner_type)
    tf, tc = TrialFunction(CR)
    wf, wc = TestFunctions(CR)
    dx = Measure('dx', domain=mesh,
                 metadata={'quadrature_degree': min(degree, 10)})
    a = (fe.dot(fe.grad(tf), fe.grad(wf)) + tc * wf + tf * wc) * fe.dx
    L = (f * wc + fe.dot(g, fe.grad(wf))) * fe.dx
    igc = Function(CR)
    fe.solve(a == L, igc,
             solver_parameters={'linear_solver': solver_type,
                                 'preconditioner': preconditioner_type}
    )
    if debug:
        print('igc', igc.vector()[:])
    assigner = FunctionAssigner(CS, CR.sub(0))
#    ig = Function(CS)
#    assigner.assign(ig, igc.sub(0))
#    fe.assign(ig, igc.sub(0))
#    if debug:
#        print('ig', igc.sub(0).vector()[:])
    igd = fe.project(igc.sub(0), DS,
                     solver_type=solver_type,
                     preconditioner_type=preconditioner_type)
    if debug:
        print('igd', igd.vector()[:])
    return igd