It is worth running this test suite with build=GccOpt_ndebug
The same includes as the previous tutorial
A cell factory used in one of the tests
Mechano-electric feedback, and alternative boundary conditions#
Let us now run a simulation with mechano-electric feedback (MEF), and with different boundary conditions.
If we want to use MEF, where the stretch (in the fibre-direction) couples back to the cell
model and is used in stretch-activated channels (SACs), we can’t just let Chaste convert
from cellml to C++ code as usual (see electro-physiology tutorials on how cell model files
are autogenerated from CellML during compilation), since these files don’t use stretch and don’t
have SACs. We have to use chaste_codegen to create a cell model class for us, rename and save it, and
manually add the SAC current.
There is one example of this already in the code-base, which we will use it the following
simulation. It is the Noble 98 model, with a SAC added that depends linearly on stretches (>1).
It is found in the file NobleVargheseKohlNoble1998WithSac.hpp, and defines a class called
CML_noble_varghese_kohl_noble_1998_basic_with_sac.
To add a SAC current to (or otherwise alter) your favourite cell model, you have to
auto-generate the non-SAC C++ code at the command line, following the guide Code Generation From CellML.
Copy and rename the resultant .hpp and .cpp files (which can be found in the same folder as the
input cellml). For example, rename everything to LuoRudy1991WithSac. Then alter the class
to overload the method AbstractCardiacCell::SetStretch(double stretch) to store the stretch,
and then implement the SAC in the GetIIonic() method. NobleVargheseKohlNoble1998WithSac.cpp
provides an example of the changes that need to be made.
Let us create a cell factory returning these Noble98 SAC cells, but with no stimulus - the
SAC switching on will lead be to activation.
Construct two meshes are before, in 2D
Collect the fixed nodes. This time we directly specify the new locations. We say the
nodes on $X=0$ are to be fixed, setting the deformed $x=0$, but leaving $y$ to be free
(sliding boundary conditions). This functionality is described in more detail in the
solid mechanics tutorials.
Now specify tractions on the top and bottom surfaces. For full descriptions of how
to apply tractions see the solid mechanics tutorials. Here, we collect the boundary
elements on the bottom and top surfaces, and apply inward tractions - this will have the
effect of stretching the tissue in the $X$-direction.
Now set up the problem. We will use a compressible approach.
Set the fixed node and traction info.
Now say that the deformation should affect the electro-physiology
Set the end time, create the problem, and solve
Nothing exciting happens in the simulation as it is currently written. To get some interesting occurring,
alter the SAC conductance in the cell model from 0.035 to 0.35 (micro-Siemens).
(look for the line const double g_sac = 0.035 in NobleVargheseKohlNoble1998WithSac.hpp).
Rerun and visualise as usual, using Cmgui. By visualising the voltage on the deforming mesh, you can see that the
voltage gradually increases due to the SAC, since the tissue is stretched, until the threshold is reached
and activation occurs.
For MEF simulations, we may want to visualise the electrical results on the electrics mesh using
Meshalyzer, for example to more easily visualise action potentials. This isn’t (and currently
can’t be) created by CardiacElectroMechanicsProblem. We can use a converter as follows
to post-process:
Some other notes. If you want to apply time-dependent traction boundary conditions, this is possible by
specifying the traction in functional form - see solid mechanics tutorials. Similarly, more natural
‘pressure acting on the deformed body’ boundary conditions are possible - see below tutorial.
Robustness: Sometimes the nonlinear solver doesn’t converge, and will give an error. This can be due to either
a non-physical (or not very physical) situation, or just because the current guess is quite far
from the solution and the solver can’t find the solution (this can occur in nonlinear elasticity
problems if the loading is large, for example). Current work in progress is on making the solver
more robust, and also on parallelising the solver. One option when a solve fails is to decrease the
mechanics timestep.
Ignore the following, it is just to check nothing has changed.
Next, we run a simulation on a 2d annulus, with an internal pressure applied.
The following should require little explanation now
Increase this end time to see more contraction
The elasticity solvers have two nonlinear solvers implemented, one hand-coded and one which uses PETSc’s SNES
solver. The latter is not the default but can be more robust (and will probably be the default in later
versions). This is how it can be used. (This option can also be called if the compiled binary is run from
the command line (see Building the Cardiac Executable) and run it using the option “-mech_use_snes”).
Now let us collect all the boundary elements on the inner (endocardial) surface. The following
uses knowledge about the geometry - the inner surface is $r=0.3$, the outer is $r=0.5$.
This is how to set the pressure to be applied to these boundary elements. The negative sign implies
inward pressure.
The solver computes the equilibrium solution (given the pressure loading) before the first timestep.
As there is a big deformation from the undeformed state to this loaded state, the nonlinear solver may
not converge. The following increments the loading (solves with $p=-1/3$, then $p=-2/3$, then $p=-1$), which
allows convergence to occur.
If we want stresses and strains output, we can do the following. The deformation gradients and 2nd PK stresses
for each element will be written at the requested times.
Since this test involves a large deformation at t=0, several Newton iterations are required. To see how the nonlinear
solve is progressing, you can run from the binary from the command line with the command line argument -mech_verbose.
Visualise using cmgui, and note the different shapes at $t=-1$ (undeformed) and $t=0$ (loaded)
Note: if you want to have a time-dependent pressure, you can replace the second parameter (the pressure)
in SetApplyNormalPressureOnDeformedSurface() with a function pointer (the name of a function) which returns
the pressure as a function of time.