.. _ode_solver:

ODE_SOLVER
###########

An optional section that defines how an |popy| will solve the |odes| in the |derivatives| section. 

Note the |ode_solver| section is optional, if you have no |derivatives| section then you do not need to have an |ode_solver| section.

The |ode_solver| section is available in the following scripts:-

* :ref:`tut_script`
* :ref:`gen_script`
* :ref:`fit_script`
* :ref:`sim_script`
* :ref:`mtut_script`
* :ref:`mgen_script`
* :ref:`mfit_script`
* :ref:`msim_script`

|ie| Any script that processes |pkpd| models and may have a |derivatives| section, may also have an |ode_solver| section.

.. _ode_solver_options:

ODE_SOLVER Options
===================

There are three principle |ode_solver| options available:-

* NO_SOLVER - use if there is no |derivatives| section
* ANALYTIC - use if |derivatives| contains only |cmp_funcs|
* SCIPY_ODEINT - numerical solver - use if |derivatives| contains |dx| equations

The options above are tied to the nature of the |derivatives| section. Some examples of |derivatives| sections and appropriate |ode_solver| settings are shown below.

.. _example_ode_solver_no_solver:

Example ODE_SOLVER using NO_SOLVER
===========================================

In the case where the |popy| script contains no |derivatives| section or the |derivatives| section is null:-

.. code-block:: pyml

    DERIVATIVES: |
    
Then the |popy| script should contain no |ode_solver| section or the |ode_solver| should also be null:-

.. code-block:: pyml
    
    ODE_SOLVER:
        NO_SOLVER: {}

If the |derivatives| section is null and |ode_solver| is set to 'ANALYTIC' or 'SCIPY_ODEINT', then |popy| should issue a warning, but the |ode_solver| section will otherwise have no effect.

Note, the simplest solution if you have **no** compartment model is to just remove the |derivatives| and |ode_solver| sections from the script completely.

.. _example_ode_solver_analytic:

Example ODE_SOLVER using ANALYTIC
===========================================

In the case where the |popy| script contains a |derivatives| section consisting of only an |cmp_func|:-

.. code-block:: pyml

    DERIVATIVES: |
        s[DEPOT,CENTRAL,PERI] = @dep_two_cmp_cl{dose:@bolus{amt:c[AMT]}
    
Then the |ode_solver| section should be:-

.. code-block:: pyml
    
    ODE_SOLVER:
        ANALYTIC: {}

If the |derivatives| section contains only an |cmp_func| and |ode_solver| is set to 'NO_SOLVER' or 'SCIPY_ODEINT', then |popy| should issue a warning, but the |ode_solver| section will otherwise **automatically** switch to 'ANALYTIC'.


.. _example_ode_solver_scipy_odeint:

Example ODE_SOLVER using SCIPY_ODEINT
===========================================

In the case where the |popy| script contains a |derivatives| section consisting of |odes| with |dx| variables on the |lhs| and |sx| on the |rhs|, for example:-

.. literalinclude:: 
    /_autogen/quick_start/builtin_fit_example/fit_sections/DERIVATIVES.pyml
    :language: pyml
    
Then the |ode_solver| section should use 'SCIPY_ODEINT', for example:-

.. code-block:: pyml
    
    ODE_SOLVER:
        SCIPY_ODEINT:
            atol: 1e-06
            rtol: 1e-06
            max_nsteps: 10000000

If the |derivatives| section contains |dx| equations and |ode_solver| is set to 'NO_SOLVER' or 'ANALYTIC', then |popy| should issue an error when checking the script, because a numerical solver is required.

SCIPY_ODEINT is a wrapper around the following |python| numerical |ode| solver from the |scipy| package:-

.. code-block:: python

    scipy.integrate.odeint

Which in turn is a wrapper around the |fortran| |lsoda| solver [Radhakrishnan1994]_. The same |ode| solver that is used by the |nonmem| ADVAN13 routine. You can read more about this integrator here in the |scipy| documentation:-

http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.integrate.odeint.html

Note that |popy| exposes the following parameters of |lsoda|:-

* atol - Additive tolerance of the |lsoda| error control
* rtol - Relative tolerance of the |lsoda| error control
* max_nsteps - Maximum number of steps for |lsoda| integrator (called 'mxstep' in link above)

All of the parameters above are optional with the following default values:-

* atol: 1e-12
* rtol: 1e-12
* max_nsteps: 10000000
 
Hence the following |ode_solver| section:-

.. code-block:: pyml
    
    ODE_SOLVER: {SCIPY_ODEINT: {}}
   
Is equivalent to:-
   
.. code-block:: pyml
    
    ODE_SOLVER:
        SCIPY_ODEINT:
            atol: 1e-12
            rtol: 1e-12
            max_nsteps: 10000000