 .. _nonmem_ctl_to_popy_fit:

|nm_ctl_file| to |popy| Fit Script
#####################################

The |nm_ctl_file| has the same purpose as the |popy| :ref:`fit_script`, |ie| to estimate the |fes| given a |pkpd| model and a |data_file|. :numref:`table_nonmem_fit_sections` lists the equivalent script file sections.

.. _table_nonmem_fit_sections:

.. list-table:: Nonmem to |popy| Fitting
    :header-rows: 1

    * - |nonmem|
      - |popy|
      - Comment
      
    * - :ref:`$PROBLEM`
      - |description| 
      - Model Description
        
    * - :ref:`$DATA`
      - |file_paths| 
      - Input data file path

    * - :ref:`$INPUT`
      - |data_fields| 
      - Data header information
        
    * - :ref:`$SUBROUTINES`
      - |ode_solver| 
      - |ode| method
        
    * - :ref:`$MODEL`
      - |na|
      - Number of compartments
        
    * - :ref:`$THETA`
      - |level_params| 
      - Main |fes|   
     
    * - :ref:`$OMEGA`
      - |level_params| 
      - Variance of |res|   
        
    * - :ref:`$SIGMA`
      - |level_params| 
      - Variance of measurement noise
        
    * - :ref:`$PK`
      - |model_params| 
      - Model Parameters
        
    * - :ref:`$DES`
      - |derivatives| 
      - |ode| system
      
    * - :ref:`$ERROR`
      - |predictions| 
      - Likelihood model
        
    * - :ref:`$ESTIMATION`
      - |fit_methods| 
      - Fitting algorithms
        
.. _$problem:
    
$PROBLEM
===========

|nonmem|:-

.. code-block:: none
    
    $PROBLEM My pkpd model
    
to |popy|:-

.. code-block:: pyml

    DESCRIPTION: {title: My pkpd model}

Note in |popy| you can optionally add additional fields 'name', 'author', 'abstract' and 'keywords', see |description| section for examples. 


.. _$data:
    
$DATA
=========

|nonmem|:-

.. code-block:: none
    
    $DATA my_nm_data.csv 
    
to |popy|:-

.. code-block:: pyml

    FILE_PATHS: {input_data_file: my_popy_data.csv}

Here the file 'my_nm_data.csv' is assumed to be in the same directory as the |nonmem| script file. Similarly 'my_popy_data.csv' will need to be in the same directory as the |popy| script.

Note you will need to convert the data file to a valid |popy| |data_file|, see :ref:`nonmem_dat_to_popy_dat`.


.. _$input:
    
$INPUT
=========

|nonmem|:-

.. code-block:: none
    
    $INPUT EVID ID TIME CMT AMT DV MDV  
    
to |popy|:-

.. code-block:: pyml

    DATA_FIELDS: {type_field: TYPE, id_field: ID, time_field: TIME}

Note that the |nonmem| '$INPUT' section redefines the header file for the data file. |popy| simply uses the header supplied in the .csv data file.

In |popy| you only need to specify the following fields in the |data_fields| section:-

* :ref:`type_field` - equivalent to |nonmem| EVID
* :ref:`id_field` - same as |nonmem| ID
* :ref:`time_field` - same as |nonmem| TIME

If you miss out the |data_fields| section completely. Then you just need the default field names of 'TYPE', 'ID' and 'TIME' to be present in your |data_file|.

See :ref:`nonmem_dat_to_popy_dat` for a more detailed discussion of the data format differences between |nonmem| and |popy|.
    
    
.. _$subroutines:
    
$SUBROUTINES
==============

.. _$subroutines_advan13:

ADVAN13 Example
-------------------

|nonmem|:-

.. code-block:: none
    
    $SUBROUTINES ADVAN13 TOL=6
    
to |popy|:-

.. code-block:: pyml

    ODE_SOLVER: {SCIPY_ODEINT: {rtol: 1e-06}}

In the |nonmem| script 'ADVAN13' specifies the |lsoda| solver which is used to compute numerical solutions for the |odes| specified in the |nonmem| $DES block.

The equivalent of this in |popy| is to specify 'SCIPY_ODEINT' in the |ode_solver| section, which is a |python| wrapper around the |lsoda| solver. The 'SCIPY_ODEINT' parameters are as follows:-

* rtol - relative tolerance of |lsoda| solver, equivalent of TOL in |nonmem| ($SUBROUTINES section)
* atol - additive tolerance of |lsoda| solver, equivalent of ATOL in |nonmem| ($ESTIMATION section)
* max_nsteps - maximum number of steps allowed for |lsoda| solver, an exposed parameter in |popy| with a default value of '1e7'. In |nonmem| this is fixed to '750' and |not| configurable. 

See |ode_solver| section for more information on the 'SCIPY_ODEINT' solver and other solvers available in |popy|.

.. _$subroutines_advan_anal:

ADVAN 1,2,3,4,11,12 Example
----------------------------

Note if an analytic 'ADVAN' model is used in |nonmem| for example:-

.. code-block:: none
    
    $SUBROUTINES  ADVAN2 TRANS2

Then this is converted to |popy| in a slightly different way, the |ode_solver| is as follows:-

.. code-block:: pyml

    ODE_SOLVER: {ANALYTIC:{}}
    
And the |derivatives| section is as follows:-

.. code-block:: pyml

    DERIVATIVES: |
        s[ABS,CEN] = @dep_one_cmp_cl{
            dose: @bolus{amt:c[AMT]}, KA: m[KA], 
            CL: m[CL], V: m[V]}
    
Here the '@dep_one_cmp_cl' compartment function expects the parameters :pyml:`m[KA] m[CL] m[V]` to be defined in |model_params|, similar to how |nonmem| 'ADVAN2' expects KA, CL and V to be defined in the $PK section. Also |popy| expects |camt| to be defined in the |data_file| and |nonmem| expects the 'AMT' field to exist in the |nonmem| data file. 

Note |popy| is explicit about the input parameter names that are required for compartment model functions and you can easily change the name of the input |cx| and |mx| parameters in your script file. In |nonmem| you just have to know what the fixed magic words and conventions are. For example, you need to declare the variables 'KA', 'CL' and 'V' carefully in the |nonmem| $PK section and have an 'AMT' field in your data file.

The |popy| equivalents of the various 'ADVAN' analytic compartment models are discussed in the :ref:`nonmem_advan_to_popy` section below.
    
.. _$model:
    
$MODEL
===========

|nonmem|:-

.. code-block:: none
    
    $MODEL NCOMPARTMENTS=1

This does |not| have any equivalent in |popy|, you do |not| need to specify the number of compartments in the |derivatives| section. |popy| can count!
    
.. _$theta:
    
$THETA
=========

|nonmem|:-

.. code-block:: none
     
    $THETA 
        (0.001, 0.05, 1)  ; KE
        
to |popy|:-

.. code-block:: pyml

    LEVEL_PARAMS:
        GLOBAL:
            params: |
                f[KE] ~ unif(0.001, 1) 0.05
                
    
In |popy| the |fe| :pyml:`f[KE]` is explicitly declared at the |global| level, |ie| there is one value for this parameter over the population.

The limits on :pyml:`f[KE]` are defined using a |unif_dist|, with the initial value added as suffix.

Note that comments have been added to the |nonmem| notation here to make it more readable. |popy| insists on names for all variables.
    
.. _$omega:
    
$OMEGA
=========

|nonmem|:-

.. code-block:: none
     
    $OMEGA 
        0.1  ; KE_isv
    
to |popy|:-

.. code-block:: pyml

    LEVEL_PARAMS:
        GLOBAL:
            params: |
                f[KE_isv] ~ unif(0.001, +inf) 0.1
    
In |popy| the |fe| variance parameter :pyml:`f[KE_isv]` is explicitly declared at the |global| level, |ie| there is one value for this parameter over the population.

The limits on :pyml:`f[KE_isv]` are defined using a |unif_dist|, with the initial value added as a suffix. In |popy| you can use the equivalent shorter notation:-

.. code-block:: pyml

    f[KE_isv] ~ P 0.1

Note that |popy| can deduce automatically that :pyml:`f[KE_isv]` is a variance by examining the :pyml:`r[KE]` |norm_dist| definition from the |indiv| level:-

.. code-block:: pyml

    LEVEL_PARAMS:
        INDIV:
            params: |
                r[KE] ~ norm(0, f[KE_isv]) 
    
.. _$sigma:
    
$SIGMA
==========

|nonmem|:-

.. code-block:: none
    
    $SIGMA 
        0.001 FIX ; ANOISE
        0.2 ; PNOISE
    
to |popy|:-

.. code-block:: pyml

    LEVEL_PARAMS:
        GLOBAL:
            params: |
                f[ANOISE] = 0.001
                f[PNOISE] ~ P0.2
    
In |popy| the |fe| noise variance parameters :pyml:`f[ANOISE]` and :pyml:`f[PNOISE]` are explicitly declared at the |global| level, |ie| there is one value for both parameters over the population.

The '=' sign is used instead of the |nonmem| 'FIX' keyword. The '~P' notation is a shortcut for defining a |unif_dist|, which is equivalent to the following:-

.. code-block:: pyml

    f[PNOISE] ~ unif(0.001, +inf) 0.2

The initial value 0.2 is added as a suffix after the distribution.

Note that |popy| can deduce automatically that :pyml:`f[ANOISE]` and :pyml:`f[PNOISE]` are noise parameters (|ie| sigma like variables) by examining the :pyml:`c[DV_CENTRAL]` |norm_dist| likelihood definition from the |predictions| section:-

.. code-block:: pyml

    PREDICTIONS: |
        p[DV_CENTRAL] = s[CENTRAL]
        var = m[ANOISE] + m[PNOISE]*p[DV_CENTRAL]**2 
        c[DV_CENTRAL] ~ norm(p[DV_CENTRAL], var)

Where :pyml:`m[ANOISE]` and :pyml:`m[PNOISE]` are copies of the :pyml:`f[ANOISE]` and :pyml:`f[PNOISE]` parameters as defined in the |model_params| section.
        
.. _$pk:
    
$PK
===========

|nonmem|:-

.. code-block:: none
    
    $PK
        KE = THETA(1) * exp(ETA(1))
    
to |popy|:-

.. code-block:: pyml

    MODEL_PARAMS: |
        m[KE] = f[KE] * exp(r[KE])
        m[ANOISE] = f[ANOISE]
        m[PNOISE] = f[PNOISE]

The |popy| |model_params| section has exactly the same purpose as the |nonmem| $PK section. Some differences are:-

* |nonmem| uses numbered THETA and ETA variables, whilst |popy| uses named |fx| and |rx| variables.
* |nonmem| uses bare variable names on the |lhs| of equations in the $PK section and exports **all** of the variables for use in the :ref:`$DES` and :ref:`$ERROR` sections (just 'KE' in this case). |popy| requires explicit use of the |mx| syntax to export variables from the |model_params| section.
 
You can use the local variable syntax in the |popy| |model_params| section |eg|:-

.. code-block:: pyml

    MODEL_PARAMS: |
        f = f[KE]
        r = r[KE]
        m = f * exp(r)
        m[KE] = m
        
However only :pyml:`m[KE]` will be available in the |popy| |derivatives| and |predictions| sections |not| 'f', 'r' or 'm'. Some other differences are:-

* |popy| requires that the :pyml:`f[ANOISE]` and :pyml:`f[PNOISE]` variables (which are sigmas in |nonmem|) are converted to |mx| variables, because |fx| variables can |not| be used as inputs to the |derivatives| and |predictions| sections.

* |nonmem| implements |iov| using 'if' statements. |popy| |model_params| can handle |iov| |rx| variables without using 'if' statements, see :ref:`ddmore0238_conv_example`. 

* |nonmem| converts the $PK section into a |fortran| function, whilst |popy| converts the |model_params| section into executable |cpp| code.

Otherwise the $PK and |model_params| sections are quite similar. They both accept procedural :term:`pseudocode`, |eg| 'if' statements |etc| and compute model variables for each row of the data set when fitting |pkpd| models.

.. _$des:
    
$DES
===========

|nonmem|:-

.. code-block:: none
    
    $DES
        DADT(1) = -KE*A(1)
    
to |popy|:-

.. code-block:: pyml

    DERIVATIVES: |
        d[CENTRAL] = @bolus{amt:c[AMT]} - m[KE]*s[CENTRAL]

The |popy| |derivatives| section has exactly the same of purpose as the |nonmem| $DES section. Some differences are:-

* |nonmem| uses numbered DADT and A variables, whilst |popy| uses named |dx| and |sx| variables.

* |nonmem| will allow you to use any previously specified variables as input, whereas |popy| restricts input variables to be of type |cx| and |mx|.

* |nonmem| specifies the dosing time, amount and compartment (either bolus or infusion) entirely from the data file, however |popy| has :ref:`dosing_functions` that have explicitly declared input parameters. The dosing compartment is clearly defined by the dose function location in the |derivatives| section. The time of each dose is still defined in the |data_file|. See :ref:`dosing_fields` for more info.
 
* |nonmem| uses the magic variable 'T' to specify continuous time in the $DES section, |popy| uses the more obviously magic |xtime| variable to do the same thing.
 
* |nonmem| converts the $DES section into a |fortran| function, whilst |popy| converts the |derivatives| section into a |cpp| function, that can be called by a numerical |ode| solver.

Otherwise the $DES and |derivatives| sections are quite similar. They both accept procedural :term:`pseudocode`, |eg| 'if' statements |etc| and generate code that be executed by a numerical |ode| solver to compute compartment model amount/state variables at time points specified in the data file.  

.. _$error:
    
$ERROR
===========

|nonmem|:-

.. code-block:: none
    
    $ERROR 
        Y = F + EPS(1) + F*EPS(2) 
    
to |popy|:-

.. code-block:: pyml

    PREDICTIONS: |
        p[DV_CENTRAL] = s[CENTRAL]
        var = m[ANOISE] + m[PNOISE] * p[DV_CENTRAL]**2
        c[DV_CENTRAL] ~ norm(p[DV_CENTRAL], var)
    
The |popy| |predictions| section has the same of purpose as the |nonmem| $ERROR section. They both compare observations from the |data_file| with model predictions. In a fitting script these sections compute a likelihood, when simulating these sections produce a noisy measurement prediction.

Some differences are:-
    
* The |nonmem| $error section is much shorter, it relies on the convention that 'Y' is the dependant variable 'DV' column from the data file and 'F' is the model prediction for the compartment specified by the 'CMT' column in the data file. 
  
* The |popy| |predictions| explicitly creates |px| prediction variables, often based on |sx| amounts from the compartment model, which all have human readable names.

* The |nonmem| likelihood is expressed as a sum of 'EPS' normal distributions, which |nonmem| combines into a single |norm_dist| to compute the likelihood for each data row.

* The |popy| likelihood is constructed using the '~' notation and uses named :ref:`distributions`.

In |popy| you have to work out how to compute the |norm_dist| parameters yourself. Here the mean is simply the model prediction :pyml:`p[DV_CENTRAL]`. The :pyml:`m[ANOISE]` and :pyml:`m[PNOISE]` parameters are separate additive and proportional variances, so have a combined variance as follows:-

.. code-block:: pyml

    var = m[ANOISE] + m[PNOISE] * p[DV_CENTRAL]**2

Otherwise the $ERROR and |predictions| sections are quite similar. They both accept procedural :term:`pseudocode`, |eg| 'if' statements |etc| and generate code that can be used to compute a likelihood value for each row of a data set (when fitting).
    
.. _$estimation:
    
$ESTIMATION
=============

|nonmem|:-

.. code-block:: none
    
    $ESTIMATION METHOD=ITS INTERACTION PRINT=1 MAXEVALS=100
    
Or 
    
.. code-block:: none
    
    $ESTIMATION METHOD=FOCE INTERACTION PRINT=1 MAXEVALS=100
    
to |popy|:-

.. code-block:: pyml

    FIT_METHODS: [JOE: {max_n_main_iterations: 100}]

Note estimation methods can be called sequentially in |nonmem|:-

.. code-block:: none
    
    $ESTIMATION METHOD=ITS INTERACTION PRINT=1 MAXEVALS=100
    $ESTIMATION METHOD=FOCE INTERACTION PRINT=1 MAXEVALS=100

Similarly the |joe| fitting method can be called sequentially in |popy| as:-

.. code-block:: pyml

    FIT_METHODS: 
        - JOE: {max_n_main_iterations: 100}
        - JOE: {max_n_main_iterations: 100}
    
In |popy| the |joe| fitting method, optimises the same |objv| as |nonmem| |foce| and |its|, but the fitting algorithm is slightly different.
    


    
..  comment
    should we add this comparison later?
    Nonmem Simulation to |popy| MSim Script
    ========================================

    




