.. _model_params:

MODEL_PARAMS
#############

A required :term:`verbatim` section that creates |mx| variables for each row of the data set. Taking the previously defined |fx|, |rx| and |cx| as input.

The |model_params| section is used in the following scripts:-

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

|ie| Any script that defines a mixed effect model.

.. _example_model_params:

Example MODEL_PARAMS section
=======================================

The example below is used in :ref:`builtin_fit_example`.

.. literalinclude:: 
    /_autogen/quick_start/builtin_fit_example/fit_sections/MODEL_PARAMS.pyml
    :language: pyml

In the example above the main parameters are defined with the following structure:-

.. code-block:: pyml

    MODEL_PARAMS: |
        m[X] = f[X] * exp(r[X])
    
This form ensures that the |mx| have a |lognorm_dist| and are therefore constrained to be positive, assuming the |fx| input is positive.  An alternative formulation is:-

.. code-block:: pyml

    MODEL_PARAMS: |
        m[X] = f[X] + r[X]
        
Which means the |mx| values have a |norm_dist|. However if the variance of the |rx| is large (relative to |fx|) this can result in |mx| having negative values sometimes. Often negative values are |not| physically sensible in |pkpd| models (|eg| a negative |clear|).

Parameters can also be defined here in terms of |cx| values from the |data_file|. For example, if an individual's weight has an effect on the volume of distribution, it could be defined as:-

.. code-block:: pyml

    MODEL_PARAMS: |
        m[V] = (f[V] + c[WT]*f[WT_EFF]) * exp(r[V])

Here the :pyml:`f[WT_EFF]` is an extra parameter to be estimated by |popy| that needs to be defined in the |level_params| section.

Another common pattern in |model_params| is the incorporation of |iov| |rx| variables. This can be done using declarations like:-

.. code-block:: pyml

    MODEL_PARAMS: |
        m[X] = f[X] * exp(r[X] + r[X_iov])

Where in |level_params|:-

* |fx| is typically defined in the first |global| level 
* |rx| is typically defined in the second |indiv| level 
* :pyml:`r[X_iov]` is typically defined in the third |iov| level 
        
This is a common pattern in |popy|. It allows the simple combination of |rx| from different levels, without using cumbersome and error prone :python:`if` statements. Although you can use :python:`if` statements if you wish. For example a more complex |covariate| example:-

.. code-block:: pyml

    if c[GENDER] == "male":
        m[Y] = f[Y_male] * exp(r[Y])
    else:
        m[Y] = f[Y_female] * exp(r[Y])

Here the :pyml:`c[GENDER]` field from the |data_file| is used as a switch to decide whether to use :pyml:`f[Y_male]` or :pyml:`f[Y_female]` for each row as appropriate.
        
.. _static_var_model_params:

Static Workspace Variables in MODEL_PARAMS
===========================================

The |model_params| function operates **independently** on each row of the |data_file| and relies on the |level_params| to arrange the |fx| and |rx| correctly in each data row (which |popy| handles for you).

However, there may be times when you want a variable's value to persist from one row to the next. In the |model_params| you can explicitly define **static** workspace variables using the notation |wx|. For example:-

.. code-block:: pyml

    if c[TIME] < 0.0:
        w[PERSISTENT] = 1.0
    else:
        w[PERSISTENT] *= 1.1
        
This fairly artificial example keeps updating the variable :pyml:`w[PERSISTENT]` between rows. Here the |python| notation '\*=' multiplies the variable :pyml:`w[PERSISTENT]` by itself and the number 1.1. Note by default all variables in |popy| are local, so a naive attempt to do this say:-

.. code-block:: pyml

    if c[TIME] < 0.0:
        PERSISTENT = 1.0
    else:
        PERSISTENT *= 1.1

Would fail with a |python| error, because in the line 'PERSISTENT \*= 1.1', the local variable 'PERSISTENT' has |not| been previously defined, so can |not| multiply itself by 1.1. 

The |wx| notation provides a means of remembering variable values between rows. Note having all variables as static by default (like in |nonmem|) is a bad idea as it makes it easier to introduce hard to find coding errors. 

.. _rules_model_params:
    
Rules for MODEL_PARAMS section
=======================================

Like all :term:`verbatim` sections the |model_params| section of the config file accepts free form |python| :term:`pseudocode`, but there are some rules regarding which variables are allowed in a |model_params| section as follows:- 

* **Only** new |mx| variables and local |python| variables can be defined on the |lhs|
* |fx|, |rx|, |cx| and |mx| are allowed on the |rhs| of expressions
* |cx| on the |rhs| and within :python:`if` statements must be in the |data_file| or defined in the |preprocess| section, in a :ref:`fit_script` 
* |cx| on the |rhs| must be defined within the |level_params| in a :ref:`gen_script` or :ref:`tut_script`.
* newly declared |mx| on the |lhs| must have unique names

So you can |not| use |dx|, |px|, |sx| or |tx| etc variables in this section. 

Like all :term:`verbatim` sections it is possible to introduce syntax errors by writing malformed |python|. Any coding errors will be reported by |popy| when attempting to compile or run the |model_params| function as a temporary .py file.


        