Steady State diffusion solver ----------------------------- Often in the multi-scale simulations we have to deal with chemicals which have drastically different diffusion constants. For slow diffusion fields we can use standard explicit solvers (*e.g.* ``FlexibleDiffusionSolverFE``) but once the diffusion constant becomes large the number of extra calls to explicit solvers becomes so large that solving diffusion equation using Forward-Euler based solvers is simply impractical. In situations where the diffusion constant is so large that the solution of the diffusion equation is not that much different from the asymptotic solution (i.e. at :math:`t=\infty`) it is often more convenient to use ``SteadyStateDiffusion`` solver which solves Helmholtz equation: .. math:: :nowrap: \begin{eqnarray} \nabla^2c-kc=F \end{eqnarray} where :math:`F` is a source function of the coordinates - it is an input to the equation, :math:`k` is decay constant and :math:`c` is the concentration. The :math:`F` function in CC3D is either given implicitly by specifying cellular secretion or explicitly by specifying concentration :math:`c` before solving Helmholtz equation. The CC3D stead state diffusion solvers are stable and allow solutions for large values of diffusion constants. The example syntax for the steady-state solver is shown below: .. code-block:: xml INIT 1.0 0.01 1.0 The syntax is is similar (actually, almost identical) to the syntax of the ``FlexibleDiffusionSolverFE``. The only difference is that while ``FlexibleDiffusionSolverFE`` works in in both 2D and 3D users need to specify the dimensionality of the steady state solver. We use .. code-block:: xml for 2D simulations when all the cells lie in the ``xy`` plane and .. code-block:: xml for simulations in 3D. .. note:: We can use Python to control `secretion `_ in the steady state solvers but it requires a little bit of low level coding. Implementing secretion in steady state diffusion solver is different from "regular" Forward Euler solvers. Steady state solver takes secretion rate that is specified at ``t=0`` and returns the solution at ``t=∞``. For a large diffusion constants we approximate solution to the PDE during one MCS by using solution at`` t=∞``. However, this means that if at each MCS secretion changes we have to do three things 1) zero entire field, 2) set secretion rate 3) solve steady state solver. The reason we need to zero entire field is because any value left in the field at ``mcs=N`` is interpreted by the solver as a secretion constant at this location at ``mcs=N+1``. **Moreover, the the secretion constant needs to have negative value if we want to secrete positive amount of substance - this weird requirements comes from the fact that we re using 3\ :sup:`rd` party solver which inverts signs of the secretion constants.** An example below demonstrates how we control secretion of the steady state in Python. First we need to include tag ```` in the XML definition of the solver: .. code-block:: xml FGF 1.00 0.00001 In Python the code to control the secretion involves iteration over every pixel and adjusting concentration (which as we mentioned will be interpreted by the solver as a secretion constant) and we have to make sure that we inherit from ``SecretionBasePy`` not ``SteppableBasePy`` to ensure proper ordering of calls to Python module and the C++ diffusion solver. .. note:: Make sure you inherit from ``SecretionBasePy`` when you try to manage secretion in the steady state solver using Python. This will ensure proper ordering of calls to steppable and to C++ solver code. .. note:: Once you use ```` tag in the XML all secretion tags in the ``SecretionData`` will be ignored. In other words, for this solver you cannot mix secretion specification in Python and secretion specification in the XML. .. code-block:: python def __init__(self, _simulator, _frequency=1): SecretionBasePy.__init__(self, _simulator, _frequency) def start(self): self.field = CompuCell.getConcentrationField \ (self.simulator, "FGF") secrConst = 10 for x, y, z in self.everyPixel(1, 1, 1): cell = self.cellField[x, y, z] if cell and cell.type == 1: self.field[x, y, z] = -secrConst else: self.field[x, y, z] = 0.0 def step(self, mcs): secrConst = mcs for x, y, z in self.everyPixel(1, 1, 1): cell = self.cellField[x, y, z] if cell and cell.type == 1: self.field[x, y, z] = -secrConst else: self.field[x, y, z] = 0.0 .. warning:: Notice that all the pixels that do not secrete **have to be 0.0** as mentioned above. **If you don’t initialize field values in the non-secreting pixels to 0.0 you will get wrong results**. The above code, with comments, is available in our Demo suite (``Demos/SteppableDemos/SecretionSteadyState`` or ``Demos/SteppableDemos/SteadyStateDiffusionSolver``).