# Generate multiple data sets and Fit using a Two Compartment Model

The previous example showed how to Generate data and Fit using a Two Compartment Model, which generates data from a model then fits the model to the synthetic data.

In PoPy it is also possible to iterate over a Tut Script, using what we call a MTut Script or multi-tutorial script and apply the generate/fit process multiple times. In this example we will demonstrate a MTut Script using the same two compartment model with absorption and bolus dosing, as used in the Complex PopPK Tut Script example, see Fig. 44:-

Fig. 44 Two compartment model with depot dosing for MTut Script.

An MTut Script is a tool for investigating identifiability of PK/PD models, but gives you more information than a Tut Script because the synthetic population data is sampled multiple times, which gives a spread of fitted `f[X]` results for each model parameter.

## Running the MTut Script

This multi tutorial example make use of this single file:-

    c:\PoPy\examples\builtin_mtut_example.pyml

Open a PoPy Command Prompt to setup the PoPy environment in this folder:-

    c:\PoPy\examples\

With the PoPy environment enabled, you can open the script using:-

    $ popy_edit builtin_mtut_example.pyml

Again, with the PoPy environment enabled, call popy_run on the MTut Script from the command line:-

    $ popy_run builtin_mtut_example.pyml

Running an mtut script can take a considerable amount of time, as it is equivalent to running a Tut Script multiple times. However in this toy example we only run the fit/gen cycle 30 times and limit the `f[X]` parameters that are estimated.

## Syntax of MTut Script

The MTut Script is very similar to the Tut Script in Syntax of Tut Script, the primary difference is that the MTut Script specifies the number of populations to sample as follows:-

    OUTPUT_OPTIONS: {n_pop_samples: 30}

Like a Tut Script, the MTut Script encodes both the data generation and fitting in the LEVEL_PARAMS section. In this example the LEVEL_PARAMS section is slightly different from Syntax of Tut Script, as follows:-

    LEVEL_PARAMS:
        GLOBAL:
            split_field: None
            split_dict: {}
            gen_params: |
                c[AMT] = 100.0
                f[KA] ~ unif(0.1,0.3)
                f[CL] ~ unif(7.0, 13.0)
                f[V1] ~ unif(30.0,70.0)
                f[Q] ~ unif(0.5,1.5)
                f[V2] = 80
                f[KA_isv,CL_isv,V1_isv,Q_isv,V2_isv] = [
                    [0.1],
                    [0.01, 0.03],
                    [0.01, -0.01, 0.09],
                    [0.01, 0.02, 0.01, 0.07],
                    [0.01, 0.02, 0.01, 0.01, 0.05],
                ]
                f[PNOISE] ~ unif(0.1, 0.2)
            fit_params: |
                f[KA] ~ P0.2
                f[CL] ~ P10.0
                f[V1] ~ P50.0
                f[Q] ~ P1.0
                f[V2] = 80
                f[KA_isv,CL_isv,V1_isv,Q_isv,V2_isv] = [
                    [0.1],
                    [0.01, 0.03],
                    [0.01, -0.01, 0.09],
                    [0.01, 0.02, 0.01, 0.07],
                    [0.01, 0.02, 0.01, 0.01, 0.05],
                ]
                f[PNOISE] ~ P0.15
        INDIV:
            split_field: ID
            split_dict: "*"
            gen_params: |
                c[ID] = sequential(10)
                t[DOSE] = 2.0
                t[OBS] ~ unif(1.0, 50.0; 5)
                # t[OBS] = range(1.0, 50.0; 5)
                r[KA, CL, V1, Q, V2] ~ mnorm([0,0,0,0,0], f[KA_isv,CL_isv,V1_isv,Q_isv,V2_isv])
            fit_params: |
                r[KA, CL, V1, Q, V2] ~ mnorm([0,0,0,0,0], f[KA_isv,CL_isv,V1_isv,Q_isv,V2_isv])

The gen_params section at the GLOBAL level defines the following `f[X]`:-

    f[KA] ~ unif(0.1,0.3)
    f[CL] ~ unif(7.0, 13.0)
    f[V1] ~ unif(30.0,70.0)
    f[Q] ~ unif(0.5,1.5)
    f[V2] = 80
    f[PNOISE] ~ unif(0.1, 0.2)

This means that when running the MGen Script the `f[KA]`, `f[CL]`, `f[V1]`, `f[Q]` and `f[PNOISE]` are sampled 30 times to create 30 different synthetic data sets with different true `f[X]` values. The `f[V2]` parameters in contrast always has the constant value 80. Note the `f[KA_isv, CL_isv, V1_isv, Q_isv, V2_isv]` matrix is also constant here.

Where as the fit_params section at the GLOBAL section tries to estimate the following `f[X]`:-

    f[KA] ~ P0.2
    f[CL] ~ P10.0
    f[V1] ~ P50.0
    f[Q] ~ P1.0
    f[V2] = 80
    f[PNOISE] ~ P0.15

These entries mean that when running the MFit Script the `f[KA]`, `f[CL]`, `f[V1]`, `f[Q]` and `f[PNOISE]` are estimated for each of the 30 synthetic data sets created by the MGen Script. The starting values for the parameter fitting are here set to the middle of the sampling region used in the gen_params. For example `f[KA]` has initial value 0.2, whereas the true value lies in the region [0.1,0.3] for each synthetic data set. `f[V2]` is not estimated at all, it is merely set to the true value i.e. 80. Similarly `f[KA_isv, CL_isv, V1_isv, Q_isv, V2_isv]` is set to the true constant value here. The ‘P’ in the fit_params forces the fitting process to estimate positive values.

Restricting the estimation to 5 `f[X]` parameters greatly speeds up the run time of MFit Script, which makes this toy example much faster to run. The MTut Script is only sampling 30 synthetic data sets, which is a relatively small number. You could increase this if you wish to obtain more detailed scatter plots (see below). Most of the run time is taken up by MFit Script, the MGen Script is relatively quick to run, as it only has to evaluate the ordinary differential equations in the DERIVATIVES block once per synthetic data set, where as the MFit Script must evaluate the ordinary differential equations multiple times to estimate `f[X]` for each synthetic data set.

## Summary of MTut Results

The MTut Script should generate an output folder containing three new scripts:-

    builtin_mtut_example.pyml_output/
        builtin_mtut_example_mgen.pyml
        builtin_mtut_example_mfit.pyml
        builtin_mtut_example_mcomp.pyml

The purpose of each of theses scripts is as follows:-

Table 31 Scripts output by a multi tutorial script
Script | Purpose | Documentation
--- | --- | ---
*_mgen.pyml | Generate multiple synthetic data sets from model | MGen Script
*_mfit.pyml | Fit model to multiple synthetic data sets | MFit Script
*_mcomp.pyml | Compare gen model and fit model `f[X]` | MComp Script

The MGen Script is very similar to the Gen Script described in Generate a Two Compartment PopPK Data Set and the MFit Script is very similar to the Fit Script described in Fitting a Two Compartment PopPK Model. See Files Generated by MTut Script for more info.

Therefore here we mainly discuss the MComp Script outputs, which processes the results of MGen Script and MFit Script. The simplest output is a visual comparison of the true and fitted `f[X]` values as shown in Table 32 and Table 33.

Table 32 Fitted model vs true scatter plots for `f[KA]` and `f[CL]`

In Table 32 the blue dots are a scatter plot of fitted `f[X]` vs true `f[X]`. The green dots are initial `f[X]` vs true `f[X]`. For example in the case of `f[CL]` the true values are sampled as follows:-

    f[CL] ~ unif(7.0, 13.0)

i.e. the true values are uniformly sampled in the range [7.0,13.0]. The fitted `f[CL]` parameters are specified as follows:-

    f[CL] ~ P10.0

The initial values for `f[CL]` are always 10.0. see green dots in a horizontal line on the right graph in Table 32. The ‘P’ specifies that the fitting value of `f[CL]` is restricted to positive numbers. The final fitting values are the blue dots on the right graph in Table 32. For `f[CL]` the blue dots are clustered along the black 45 degree line. Hence fitting for `f[CL]` works well, this agrees with the initial findings in Fitting a Two Compartment PopPK Model. The `f[KA]` fitted values (blue dots) on the left graph in Table 32 are less obviously grouped around the 45 degree line. Especially the outlier when the fitted value is estimated at 0.48 compared to a true value of 0.21.

Table 33 Fitted model vs true scatter plots for `f[Q]` and `f[V1]` and `f[PNOISE]`

In Table 33 (left graph) the `f[Q]` rate results for the Peripheral compartment are reasonably well estimated, apart from one outlier. The `f[V1]` parameter (middle graph) is really badly estimated, there does not seem to any correlation between true `f[V1]` values and fitted `f[V1]` values at all. The `f[PNOISE]` parameter (right graph) is reasonably well estimated, with the results mostly lying along the 45 degree line.

In this case, the `f[V1]` parameter (the volume of distribution of the Central compartment) is shown to be hard to estimate, compared to `f[CL]` and `f[Q]` clearances. The `f[KA]` estimate is a bit unstable, possibly due to this parameter requiring time point data shortly after the dose, which does not always exist. `f[PNOISE]` is reasonably well estimated, but it’s likely that higher values of `f[PNOISE]` make the other parameters harder to identify.

Given the relatively small amount of data generated (50 data points spread across 10 individuals), the results in Table 32 and Table 33 are reasonably good.

## Re-run the MTut Script

You can familiarise yourself with PoPy’s various features by tweaking the multi-tutorial example and re-running. A simple way of avoiding overwriting previous results is to do:-

    $ copy builtin_mtut_example.pyml builtin_mtut_example_v2.pyml
    $ popy_edit builtin_mtut_example_v2.pyml

Then when you are happy with the edited file do:-

    $ popy_run builtin_mtut_example_v2.pyml

For example, you can adjust the amount of data generated, in this section:-

    LEVEL_PARAMS:
        INDIV:
            gen_params: |
                c[ID] = sequential(10)
                t[DOSE] = 2.0
                t[OBS] ~ unif(1.0, 50.0; 5)
                # t[OBS] = range(1.0, 50.0; 5)

Note the ‘range’ function can sample time points evenly (instead of randomly). This usually makes the model fitting easier as the data points span the time range better. You can experiment with the number of doses or make a random sample of dose times for each individual.

You could also change the initial fitting `f[X]` values, to make the `f[X]` estimation process start further away from the true `f[X]` values. You could also attempt to estimate more (or fewer) parameters, for example try estimating the `f[KA_isv, CL_isv, V1_isv, Q_isv, V2_isv]` covariance matrix.

You can also edit the underlying model or compartment structure, see the MODEL_PARAMS or DERIVATIVES sections.

## Convert Tut to MTut

A MTut Script is essentially a Tut Script with an extra loop. Therefore it’s fairly simple to convert an existing tutorial script to a multi tutorial script. First change the script type from tut->mtut:-

    METHOD_OPTIONS: {py_module: mtut}

Add the mtut OUTPUT_OPTIONS section:-

    OUTPUT_OPTIONS: {n_pop_samples: 30}

Change the OUTPUT_SCRIPTS section to this:-

    OUTPUT_SCRIPTS:
        MGEN: {output_mode: gen_script_and_run}
        MFIT: {output_mode: gen_script_and_run}
        MCOMP: {output_mode: gen_script_and_run, dot_size: 12}

You should then be able to run your new mtut script.