Exemple #1
0
class ProtPKPDDissolutionPKSimulation(ProtPKPD):
    """ This protocol simulates the pharmacokinetic response of an ODE model when it is given a single dose of
        an drug whose release is modelled by an in vitro fitting and an in vitro-in vivo correlation."""

    _label = 'simulate PK response'

    #--------------------------- DEFINE param functions --------------------------------------------
    def _defineParams(self, form):
        form.addSection('Input')
        form.addParam(
            'inputInVitro',
            params.PointerParam,
            label="Dissolution profiles in vitro",
            pointerClass='PKPDFitting',
            help='Select a fitting with dissolution profiles. '
            'It is assumed that the input dissolution profile is a percentage, between 0 and 100'
        )
        form.addParam(
            'inputPK',
            params.PointerParam,
            label="Pharmacokinetic model",
            pointerClass='PKPDFitting',
            help='Select the PK model to be simulated with this input')
        form.addParam(
            'usePKExperiment',
            params.BooleanParam,
            label="Use experiment from PK fitting",
            default=True,
            help='If True, the PK parameters are taken from the same fitting. '
            'If False, they are taken from another experiment')
        form.addParam(
            'inputPKOtherExperiment',
            params.PointerParam,
            label="Experiment with PK parameters",
            pointerClass='PKPDExperiment',
            condition='not usePKExperiment',
            help=
            'The experiment must have all the PK parameters specified by the PK model'
        )
        form.addParam(
            'ignorePKbioavailability',
            params.BooleanParam,
            default=False,
            label='Ignore PK bioavailability',
            help=
            'Ignore the bioavailability from the PK if it has been considered in the IVIVC'
        )
        form.addParam(
            'conversionType',
            params.EnumParam,
            label='Time/Response scaling',
            choices=['IVIVC', 'Levy plot', 'None'],
            default=0,
            help=
            'To convert the dissolution profile into an absorption profile you may use an IVIVC (Fabs output) or a Levy plot. '
            'The Levy plot can better represent what is happening in reality with patients.'
        )
        form.addParam(
            'inputIvIvC',
            params.PointerParam,
            label="In vitro-In vivo correlation",
            condition='conversionType==0',
            pointerClass='PKPDExperiment',
            help='Select the Fabs output of an in vitro-in vivo correlation')
        form.addParam('inputLevy',
                      params.PointerParam,
                      label="Levy plot",
                      condition='conversionType==1',
                      pointerClass='PKPDExperiment',
                      help='Select the output of a Levy plot protocol')
        form.addParam('inputDose', params.FloatParam, label="Dose", default=1,
                      help='Make sure that it is in the same units as the ones at which the PK was estimated. '\
                           'This dose will be given simpy once (single dose).')
        form.addParam(
            'includeTlag',
            params.BooleanParam,
            label="Include PK tlag",
            default=True,
            help=
            'If you include the tlag (if available), the simulations will be done with the same PK tlag as '
            'the input PK population. If not, tlag will be set to 0.')
        form.addParam(
            'allCombinations',
            params.BooleanParam,
            label="All combinations of dissolutions/PK",
            default=False,
            help=
            'If set to True, then all combinations of dissolutions and PK profiles are tested. '
            'Otherwise, only a random subset is chosen')
        form.addParam('inputN',
                      params.IntParam,
                      label="Number of simulations",
                      default=100,
                      condition='not allCombinations')
        form.addParam('t0',
                      params.FloatParam,
                      label="Initial time (h)",
                      default=0)
        form.addParam('tF',
                      params.FloatParam,
                      label="Final time (h)",
                      default=48)
        form.addParam('addIndividuals',
                      params.BooleanParam,
                      label="Add individual simulations",
                      default=True,
                      expertLevel=LEVEL_ADVANCED,
                      help="Individual simulations are added to the output")
        form.addParam(
            'NCAt0',
            params.StringParam,
            label="NCA Initial time (h)",
            default="",
            help=
            "The non-compartimental analysis is performed within NCA t0 and NCA tF. Leave empty for the whole period"
        )
        form.addParam(
            'NCAtF',
            params.StringParam,
            label="NCA Final time (h)",
            default="",
            help=
            "The non-compartimental analysis is performed within NCA t0 and NCA tF. Leave empty for the whole period"
        )

    #--------------------------- INSERT steps functions --------------------------------------------
    def _insertAllSteps(self):
        self._insertFunctionStep('simulate',
                                 self.inputInVitro.get().getObjId(),
                                 self.inputPK.get().getObjId(),
                                 self.inputDose.get(), self.inputN.get())
        self._insertFunctionStep('createOutputStep')

    #--------------------------- STEPS functions --------------------------------------------
    def getInVitroModels(self):
        fnFitting = self.inputInVitro.get().fnFitting
        cls = "PKPDSampleFitBootstrap" if fnFitting.get().find(
            "bootstrap") != -1 else ""
        self.fittingInVitro = PKPDFitting(cls)
        self.fittingInVitro.load(fnFitting)
        self.invitroClsName = self.fittingInVitro.modelDescription.split(
            '(')[1].split(')')[0]

        klass = globals()[self.invitroClsName]
        self.dissolutionModel = klass()
        self.dissolutionModel.allowTlag = "tlag" in self.fittingInVitro.modelParameters
        self.dissolutionPopulation = cls != ""

    def getScaling(self):
        self.allTimeScalings = {}
        self.allResponseScalings = {}
        if self.conversionType.get() == 0:
            experiment = self.readExperiment(self.inputIvIvC.get().fnPKPD,
                                             show=False)
        elif self.conversionType.get() == 1:
            experiment = self.readExperiment(self.inputLevy.get().fnPKPD,
                                             show=False)
        elif self.conversionType.get() == 2:
            experiment = self.readExperiment(
                self.inputInVitro.get().fnExperiment, show=False)
            tvar = experiment.getTimeVariable()
        for sampleName, sample in experiment.samples.items():
            if self.conversionType.get() == 0:
                vivo = sample.getValues("tvivo")
                vitro = sample.getValues("tvitroReinterpolated")
            elif self.conversionType.get() == 1:
                vivo = sample.getValues("tvivo")
                vitro = sample.getValues("tvitro")
            elif self.conversionType.get() == 2:
                vivo = sample.getValues(tvar)
                vitro = sample.getValues(tvar)
            if self.conversionType.get() != 2:
                fromSample = sample.getDescriptorValue("from")
                fromIndividual, _ = fromSample.split("---")
            else:
                fromSample = sample.sampleName
                fromIndividual = sample.sampleName
            if not fromIndividual in self.allTimeScalings.keys():
                self.allTimeScalings[fromIndividual] = []
            self.allTimeScalings[fromIndividual].append(
                (np.asarray(vitro, dtype=np.float64),
                 np.asarray(vivo, dtype=np.float64)))

            if self.conversionType.get() == 0:
                vivo = sample.getValues("FabsPredicted")
                vitro = sample.getValues("AdissolReinterpolated")
                if not fromIndividual in self.allResponseScalings.keys():
                    self.allResponseScalings[fromIndividual] = []
                self.allResponseScalings[fromIndividual].append(
                    (np.asarray(vitro, dtype=np.float64),
                     np.asarray(vivo, dtype=np.float64)))

    def getPKModels(self):
        fnFitting = self.inputPK.get().fnFitting
        cls = "PKPDSampleFitBootstrap" if fnFitting.get().find(
            "bootstrap") != -1 else ""
        self.fittingPK = PKPDFitting(cls)
        self.fittingPK.load(fnFitting)
        modelDescription = self.fittingPK.modelDescription.split(';')[
            1]  # Before ; there is the drug source description
        self.pkClsName = modelDescription.split('(')[1].split(')')[0]

        klass = globals()[self.pkClsName]
        self.pkModel = klass()
        self.pkModel.t0 = self.t0.get()
        self.pkModel.tF = self.tF.get()
        self.timeUnits = self.fittingPK.getTimeUnits().unit
        if self.timeUnits == PKPDUnit.UNIT_TIME_MIN:
            self.pkModel.t0 *= 60
            self.pkModel.tF *= 60
        self.pkModel.drugSource = DrugSource()
        dose = createDeltaDose(self.inputDose.get(),
                               via=createVia("Oral; numerical"))
        self.pkModel.drugSource.setDoses([dose], self.pkModel.t0,
                                         self.pkModel.tF)
        self.pkPopulation = cls != ""
        self.pkNParams = self.pkModel.getNumberOfParameters()

        self.tlagIdx = None
        if self.includeTlag.get():
            i = 0
            for prmName in self.fittingPK.modelParameters:
                if prmName.endswith('_tlag'):
                    self.tlagIdx = i
                    print("Found tlag in %s at position %d" % (prmName, i))
                    break
                i += 1
        self.bioavailabilityIdx = None
        if not self.ignorePKbioavailability.get():
            i = 0
            for prmName in self.fittingPK.modelParameters:
                if prmName.endswith('_bioavailability'):
                    self.bioavailabilityIdx = i
                    print("Found bioavailabilityIdx in %s at position %d" %
                          (prmName, i))
                    break
                i += 1

    def addSample(self, sampleName, t, y, fromSamples):
        newSample = PKPDSample()
        newSample.sampleName = sampleName
        newSample.variableDictPtr = self.outputExperiment.variables
        newSample.doseDictPtr = self.outputExperiment.doses
        newSample.descriptors = {}
        newSample.doseList = ["Bolus"]
        newSample.addMeasurementPattern([self.fittingPK.predicted.varName])
        newSample.addMeasurementColumn("t", t)
        newSample.addMeasurementColumn(self.fittingPK.predicted.varName, y)

        newSample.descriptors["AUC0t"] = self.AUC0t
        newSample.descriptors["AUMC0t"] = self.AUMC0t
        newSample.descriptors["MRT"] = self.MRT
        newSample.descriptors["Cmax"] = self.Cmax
        newSample.descriptors["Tmax"] = self.Tmax

        self.outputExperiment.samples[sampleName] = newSample
        self.outputExperiment.addLabelToSample(sampleName, "from",
                                               "individual---vesel",
                                               fromSamples)

    def NCA(self, t, C):
        self.AUC0t = 0
        self.AUMC0t = 0
        t0 = t[0]
        tperiod0 = 0  # Time at which the dose was given
        T0 = 0
        TF = np.max(t)
        if self.NCAt0.get() != "" and self.NCAtF.get() != "":
            T0 = float(self.NCAt0.get())
            TF = float(self.NCAtF.get())
            if self.timeUnits == PKPDUnit.UNIT_TIME_MIN:
                T0 *= 60
                TF *= 60

        for idx in range(0, t.shape[0] - 1):
            if t[idx] >= T0 and t[idx] <= TF:
                dt = (t[idx + 1] - t[idx])
                if C[idx + 1] >= C[idx]:  # Trapezoidal in the raise
                    self.AUC0t += 0.5 * dt * (C[idx] + C[idx + 1])
                    self.AUMC0t += 0.5 * dt * (C[idx] * t[idx] +
                                               C[idx + 1] * t[idx + 1])
                else:  # Log-trapezoidal in the decay
                    decrement = C[idx] / C[idx + 1]
                    K = math.log(decrement)
                    B = K / dt
                    self.AUC0t += dt * (C[idx] - C[idx + 1]) / K
                    self.AUMC0t += (C[idx] * (t[idx] - tperiod0) - C[idx + 1] *
                                    (t[idx + 1] - tperiod0)) / B - (
                                        C[idx + 1] - C[idx]) / (B * B)

                if idx == 0:
                    self.Cmax = C[idx]
                    self.Tmax = t[idx] - t0
                else:
                    if C[idx] > self.Cmax:
                        self.Cmax = C[idx]
                        self.Tmax = t[idx] - t0

        self.MRT = self.AUMC0t / self.AUC0t

        print("   Cmax=%f [%s]" % (self.Cmax, strUnit(self.Cunits.unit)))
        print("   Tmax=%f [%s]" % (self.Tmax, strUnit(self.timeUnits)))
        print("   AUC0t=%f [%s]" % (self.AUC0t, strUnit(self.AUCunits)))
        print("   AUMC0t=%f [%s]" % (self.AUMC0t, strUnit(self.AUMCunits)))
        print("   MRT=%f [%s]" % (self.MRT, strUnit(self.timeUnits)))

    def simulate(self, objId1, objId2, inputDose, inputN):
        import sys
        self.getInVitroModels()
        self.getScaling()
        self.getPKModels()

        if not self.usePKExperiment:
            otherPKExperiment = PKPDExperiment()
            otherPKExperiment.load(self.inputPKOtherExperiment.get().fnPKPD)

        self.outputExperiment = PKPDExperiment()
        tvar = PKPDVariable()
        tvar.varName = "t"
        tvar.varType = PKPDVariable.TYPE_NUMERIC
        tvar.role = PKPDVariable.ROLE_TIME
        tvar.units = createUnit(self.fittingPK.predictor.units.unit)

        self.Cunits = self.fittingPK.predicted.units
        self.AUCunits = multiplyUnits(tvar.units.unit, self.Cunits.unit)
        self.AUMCunits = multiplyUnits(tvar.units.unit, self.AUCunits)
        if self.addIndividuals.get():
            self.outputExperiment.variables["t"] = tvar
            self.outputExperiment.variables[
                self.fittingPK.predicted.varName] = self.fittingPK.predicted
            self.outputExperiment.general[
                "title"] = "Simulated ODE response from IVIVC dissolution profiles"
            self.outputExperiment.general[
                "comment"] = "Simulated ODE response from IVIVC dissolution profiles"
            for via, _ in self.pkModel.drugSource.vias:
                self.outputExperiment.vias[via.viaName] = via
            for dose in self.pkModel.drugSource.parsedDoseList:
                self.outputExperiment.doses[dose.doseName] = dose

            AUCvar = PKPDVariable()
            AUCvar.varName = "AUC0t"
            AUCvar.varType = PKPDVariable.TYPE_NUMERIC
            AUCvar.role = PKPDVariable.ROLE_LABEL
            AUCvar.units = createUnit(strUnit(self.AUCunits))

            AUMCvar = PKPDVariable()
            AUMCvar.varName = "AUMC0t"
            AUMCvar.varType = PKPDVariable.TYPE_NUMERIC
            AUMCvar.role = PKPDVariable.ROLE_LABEL
            AUMCvar.units = createUnit(strUnit(self.AUMCunits))

            MRTvar = PKPDVariable()
            MRTvar.varName = "MRT"
            MRTvar.varType = PKPDVariable.TYPE_NUMERIC
            MRTvar.role = PKPDVariable.ROLE_LABEL
            MRTvar.units = createUnit(
                self.outputExperiment.getTimeUnits().unit)

            Cmaxvar = PKPDVariable()
            Cmaxvar.varName = "Cmax"
            Cmaxvar.varType = PKPDVariable.TYPE_NUMERIC
            Cmaxvar.role = PKPDVariable.ROLE_LABEL
            Cmaxvar.units = createUnit(strUnit(self.Cunits.unit))

            Tmaxvar = PKPDVariable()
            Tmaxvar.varName = "Tmax"
            Tmaxvar.varType = PKPDVariable.TYPE_NUMERIC
            Tmaxvar.role = PKPDVariable.ROLE_LABEL
            Tmaxvar.units = createUnit(
                self.outputExperiment.getTimeUnits().unit)

            self.outputExperiment.variables["AUC0t"] = AUCvar
            self.outputExperiment.variables["AUMC0t"] = AUMCvar
            self.outputExperiment.variables["MRT"] = MRTvar
            self.outputExperiment.variables["Cmax"] = Cmaxvar
            self.outputExperiment.variables["Tmax"] = Tmaxvar

        t = np.arange(self.pkModel.t0, self.pkModel.tF, 1)

        if self.usePKExperiment:
            NPKFits = len(self.fittingPK.sampleFits)
            invivoFits = self.fittingPK.sampleFits
        else:
            NPKFits = len(otherPKExperiment.samples)
            invivoFits = [x for x in otherPKExperiment.samples.values()]
            for sample in invivoFits:
                sample.parameters = [
                    float(x) for x in sample.getDescriptorValues(
                        self.fittingPK.modelParameters)
                ]

        NDissolFits = len(self.fittingInVitro.sampleFits)

        if self.allCombinations:
            inputN = NPKFits * NDissolFits

        AUCarray = np.zeros(inputN)
        AUMCarray = np.zeros(inputN)
        MRTarray = np.zeros(inputN)
        CmaxArray = np.zeros(inputN)
        TmaxArray = np.zeros(inputN)

        for i in range(0, inputN):
            print("Simulation no. %d ----------------------" % i)

            # Get a random PK model
            if self.allCombinations:
                nfit = int(i / NDissolFits)
            else:
                nfit = int(random.uniform(0, NPKFits))
            sampleFitVivo = invivoFits[nfit]
            print("In vivo sample name=", sampleFitVivo.sampleName)
            if self.pkPopulation:
                nbootstrap = int(
                    random.uniform(0, sampleFitVivo.parameters.shape[0]))
                pkPrmAll = sampleFitVivo.parameters[nbootstrap, :]
            else:
                pkPrmAll = sampleFitVivo.parameters
            pkPrm = pkPrmAll[-self.pkNParams:]  # Get the last Nparams
            print("PK parameters: ", pkPrm)

            tlag = 0
            if self.includeTlag.get() and (not self.tlagIdx is None):
                tlag = pkPrmAll[self.tlagIdx]
                print("tlag: ", tlag)
            bioavailability = 1
            if not self.bioavailabilityIdx is None:
                bioavailability = pkPrmAll[self.bioavailabilityIdx]
                print("bioavailability: ", bioavailability)

            # Get a dissolution profile
            if self.allCombinations:
                nfit = i % NDissolFits
            else:
                nfit = int(random.uniform(0, NDissolFits))
            sampleFitVitro = self.fittingInVitro.sampleFits[nfit]
            if self.dissolutionPopulation:
                nbootstrap = int(
                    random.uniform(0, sampleFitVitro.parameters.shape[0]))
                dissolutionPrm = sampleFitVitro.parameters[nbootstrap, :]
            else:
                dissolutionPrm = sampleFitVitro.parameters
            print(
                "Dissolution parameters: ",
                np.array2string(np.asarray(dissolutionPrm, dtype=np.float64),
                                max_line_width=1000))
            sys.stdout.flush()

            if sampleFitVivo.sampleName in self.allTimeScalings:
                keyToUse = sampleFitVivo.sampleName
            elif len(self.allTimeScalings) == 1:
                keyToUse = list(self.allTimeScalings.keys())[0]
            else:
                raise Exception("Cannot find %s in the scaling keys" %
                                sampleFitVivo.sampleName)
            nfit = int(random.uniform(0, len(self.allTimeScalings[keyToUse])))

            tvitroLevy, tvivoLevy = self.allTimeScalings[keyToUse][nfit]
            tvivoLevyUnique, tvitroLevyUnique = uniqueFloatValues(
                tvivoLevy, tvitroLevy)
            BLevy = InterpolatedUnivariateSpline(tvivoLevyUnique,
                                                 tvitroLevyUnique,
                                                 k=1)

            tvitro = np.asarray(BLevy(t), dtype=np.float64)
            A = np.clip(
                self.dissolutionModel.forwardModel(dissolutionPrm, tvitro)[0],
                0, 100)

            if self.conversionType.get() == 0:
                # In vitro-in vivo correlation
                Adissol, Fabs = self.allResponseScalings[keyToUse][nfit]
                AdissolUnique, FabsUnique = uniqueFloatValues(Adissol, Fabs)
                B = InterpolatedUnivariateSpline(AdissolUnique,
                                                 FabsUnique,
                                                 k=1)
                A = np.asarray(B(A), dtype=np.float64)

            # Set the dissolution profile
            self.pkModel.drugSource.getVia().viaProfile.setXYValues(t, A)
            C = self.pkModel.forwardModel(
                pkPrm, [t])[0]  # forwardModel returns a list of arrays
            if tlag != 0.0:
                B = interp1d(t, C)
                C = B(np.clip(t - tlag, 0.0, None))
                C[0:int(tlag)] = 0.0
            C *= bioavailability

            self.NCA(t, C)
            AUCarray[i] = self.AUC0t
            AUMCarray[i] = self.AUMC0t
            MRTarray[i] = self.MRT
            CmaxArray[i] = self.Cmax
            TmaxArray[i] = self.Tmax

            if self.addIndividuals:
                self.addSample(
                    "Simulation_%d" % i, t, C, "%s---%s" %
                    (sampleFitVivo.sampleName, sampleFitVitro.sampleName))

        # Report NCA statistics
        alpha_2 = (100 - 95) / 2
        limits = np.percentile(AUCarray, [alpha_2, 100 - alpha_2])
        fhSummary = open(self._getPath("summary.txt"), "w")
        self.doublePrint(
            fhSummary, "AUC %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (95, limits[0], limits[1], strUnit(
                self.AUCunits), np.mean(AUCarray)))
        limits = np.percentile(AUMCarray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "AUMC %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (95, limits[0], limits[1], strUnit(
                self.AUMCunits), np.mean(AUMCarray)))
        limits = np.percentile(MRTarray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "MRT %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (95, limits[0], limits[1], strUnit(
                self.timeUnits), np.mean(MRTarray)))
        limits = np.percentile(CmaxArray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "Cmax %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (95, limits[0], limits[1], strUnit(
                self.Cunits.unit), np.mean(CmaxArray)))
        limits = np.percentile(TmaxArray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "Tmax %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (95, limits[0], limits[1], strUnit(
                self.timeUnits), np.mean(TmaxArray)))
        fhSummary.close()

        if self.addIndividuals:
            self.outputExperiment.write(self._getPath("experiment.pkpd"),
                                        writeToExcel=False)

    def createOutputStep(self):
        if self.addIndividuals:
            self._defineOutputs(outputExperiment=self.outputExperiment)
            self._defineSourceRelation(self.inputInVitro.get(),
                                       self.outputExperiment)
            self._defineSourceRelation(self.inputPK.get(),
                                       self.outputExperiment)
            if self.conversionType.get() == 0:
                self._defineSourceRelation(self.inputIvIvC.get(),
                                           self.outputExperiment)
            elif self.conversionType.get() == 1:
                self._defineSourceRelation(self.inputLevy.get(),
                                           self.outputExperiment)

    def _validate(self):
        retval = []
        if self.conversionType.get(
        ) == 0 and not "experimentFabs" in self.inputIvIvC.get().fnPKPD.get():
            retval.append(
                "If the conversion is done from IVIVC, then you must take the Fabs output"
            )
        return retval

    def _summary(self):
        retval = []
        retval.append('Dose=%f' % self.inputDose.get())
        retval.append('No. simulations=%d' % self.inputN.get())
        retval.append(' ')
        self.addFileContentToMessage(retval, self._getPath("summary.txt"))
        return retval
Exemple #2
0
class ProtPKPDODESimulate(ProtPKPDODEBase):
    """ Simulate a population of ODE parameters.
    These parameters can be specifically given, from a bootstrap population, a previous fitting, or an experiment.

    AUC0t and AUMC0t are referred to each dose (that is, t=0 is the beginning of the dose). Ctau is the concentration
    at the end of the dose.

    Tmin, Tmax, and Ttau are referred to the beginning of the dose.

    This protocol writes an Excel file (nca.xlsx) in which all the simulations and doses are written."""

    _label = 'PK simulate'

    PRM_POPULATION = 0
    PRM_USER_DEFINED = 1
    PRM_FITTING = 2
    PRM_EXPERIMENT = 3

    SRC_ODE = 0
    SRC_LIST = 1

    VIATYPE_IV = 0
    VIATYPE_EV0 = 1
    VIATYPE_EV1 = 2

    PKTYPE_COMP1 = 0
    PKTYPE_COMP2 = 1
    PKTYPE_COMP2CLCLINT = 2

    #--------------------------- DEFINE param functions --------------------------------------------
    def _defineParams(self, form):
        form.addSection('Input')
        form.addParam(
            'odeSource',
            params.EnumParam,
            label='Source of ODE type',
            choices=['A previous PK fit', 'List'],
            default=self.SRC_ODE,
            help=
            'The input model is used to define the absorbtion via and the type of distribution and elimination. '
            'Its parameters are not important, only its types')
        form.addParam('inputODE', params.PointerParam, label="Input ODE model", condition='odeSource==0',
                      pointerClass='ProtPKPDMonoCompartment, ProtPKPDTwoCompartments, ProtPKPDMonoCompartmentPD, ProtPKPDTwoCompartmentsBothPD, '\
                                   'ProtPKPDODERefine, ProtPKPDTwoCompartmentsClint, ProtPKPDTwoCompartmentsClintCl',
                      help='Select a run of an ODE model. This input is used to learn the kind of model that is needed. The parameters are '
                           'taken from the sources below')
        form.addParam('viaType',
                      params.EnumParam,
                      label='Input via',
                      condition='odeSource==1',
                      default=0,
                      choices=[
                          'Intravenous (iv)',
                          'Extra vascular, 0th order absorption (ev0)',
                          'Extra vascular, 1st order absorption (ev1)'
                      ],
                      help='Parameters:\n'
                      'iv (vianame=Intravenous): no parameters\n'
                      'ev0 (vianame=Oral): Rin, tlag, F (bioavailability)\n'
                      'ev1 (vianame=Oral): Ka, tlag, F (bioavailability)\n')
        form.addParam(
            'viaPrm',
            params.TextParam,
            label="Via parameters",
            height=8,
            default="",
            condition="odeSource==1",
            help=
            'Specify the parameters for the via separated by commas. Example: \n'
            'prm1, prm2, prm3\n'
            '\n'
            'Parameters:\n'
            'iv: no parameters\n'
            'ev0: Rin, tlag, F (bioavailability)\n'
            'ev1: Ka, tlag, F (bioavailability)\n')
        form.addParam('pkType',
                      params.EnumParam,
                      label='PK model',
                      condition='odeSource==1',
                      default=0,
                      choices=[
                          '1 compartment', '2 compartments',
                          '2 compartments Cl+Clint'
                      ],
                      help='Parameters:\n'
                      '1 compartment: Cl, V\n'
                      '2 compartments: Cl, V, Clp, Vp\n'
                      '2 compartments Cl+Clint: Vmax, Km, Cl, V, Clp, Vp\n')
        form.addParam('timeUnits',
                      params.StringParam,
                      label='Time units',
                      default='min',
                      condition="odeSource==1",
                      help='min or h')
        form.addParam('volumeUnits',
                      params.StringParam,
                      label='Volume units',
                      default='L',
                      condition="odeSource==1",
                      help='mL or L')
        form.addParam(
            'paramsSource',
            params.EnumParam,
            label="Source of parameters",
            condition="odeSource==0",
            choices=[
                'ODE Bootstrap', 'User defined', 'ODE Fitting', 'Experiment'
            ],
            default=0,
            help=
            "Choose a population of parameters, your own parameters or a previously fitted set of measurements"
        )
        form.addParam(
            'inputPopulation',
            params.PointerParam,
            label="Input population",
            condition="paramsSource==0 and odeSource==0",
            pointerClass='PKPDFitting',
            pointerCondition="isPopulation",
            help='It must be a fitting coming from a bootstrap sample')
        form.addParam(
            'prmUser',
            params.TextParam,
            label="Simulation parameters",
            height=8,
            default="",
            condition="paramsSource==1 or odeSource==1",
            help=
            'Specify the parameters for the simulation separated by commas. '
            'The parameters must be written in the same order as they are written by the protocol '
            'that generated the ODE model. Example: \n'
            'prm1, prm2, prm3, prm4\n'
            'prmA, prmB, prmC, prmD\n'
            '\n'
            'If the parameters are not taken from a previous ODE:\n'
            '1 compartment: Cl, V\n'
            '2 compartments: Cl, V, Clp, Vp\n'
            '2 compartments Cl+Clint: Vmax, Km, Cl, V, Clp, Vp\n')
        form.addParam(
            'inputFitting',
            params.PointerParam,
            label="Input ODE fitting",
            condition="paramsSource==2 and odeSource==0",
            pointerClass='PKPDFitting',
            help='It must be a fitting coming from a compartmental PK fitting')
        form.addParam(
            'inputExperiment',
            params.PointerParam,
            label="Input experiment",
            condition="paramsSource==3 and odeSource==0",
            pointerClass='PKPDExperiment',
            help=
            'It must contain the parameters required for the simulation (Cl, V, Clp, Vp, ...)'
        )
        form.addParam('doses', params.TextParam, label="Doses", height=5, width=50,
                      default="RepeatedBolus ; via=Oral; repeated_bolus; t=0:24:120 h; d=60 mg",
                      help="Structure: [Dose Name] ; [Via] ; [Dose type] ; [time] ; [dose] \n"\
                           "The dose name should have no space or special character\n"\
                           "The via should be one present in the input experiment to the ODE model.\n"\
                           "Valid units are: h, mg, ug, ...\n"\
                           "The description is either a bolus or an infusion as shown in the examples\n"\
                           "\nIt is important that there are two semicolons.\n"\
                           "Examples:\n"\
                           "Infusion0; via=Intravenous; infusion; t=0.500000:0.750000 h; d=60 mg/h\n"\
                           "Bolus0; via=Oral; bolus; t=0.000000 min; d=60 mg\n"\
                           "RepeatedBolus; via=Oral; repeated_bolus; t=0:24:120 h; d=60 mg")
        form.addParam('t0',
                      params.FloatParam,
                      label="Initial time (see help)",
                      default=0,
                      help="Same units as input experiment")
        form.addParam('tF',
                      params.FloatParam,
                      label="Final time (see help)",
                      default=24 * 7,
                      help="Same units as input experiment")
        form.addParam('sampling',
                      params.FloatParam,
                      label="Sampling time (see help)",
                      default=1,
                      help="Same units as input experiment")
        form.addParam('Nsimulations',
                      params.IntParam,
                      label="Simulation samples",
                      default=200,
                      condition="paramsSource==0 and odeSource==0",
                      expertLevel=LEVEL_ADVANCED,
                      help='Number of simulations')
        form.addParam(
            'addStats',
            params.BooleanParam,
            label="Add simulation statistics",
            default=True,
            condition="paramsSource==0 and odeSource==0",
            expertLevel=LEVEL_ADVANCED,
            help=
            "Mean, lower and upper confidence levels are added to the output")
        form.addParam(
            'confidenceLevel',
            params.FloatParam,
            label="Confidence interval",
            default=95,
            expertLevel=LEVEL_ADVANCED,
            help='Confidence interval for the fitted parameters',
            condition="addStats and paramsSource==0 and odeSource==0")
        form.addParam('addIndividuals',
                      params.BooleanParam,
                      label="Add individual simulations",
                      default=False,
                      expertLevel=LEVEL_ADVANCED,
                      help="Individual simulations are added to the output")

    #--------------------------- INSERT steps functions --------------------------------------------
    def _insertAllSteps(self):
        self._insertFunctionStep('runSimulate', self.Nsimulations.get(),
                                 self.confidenceLevel.get(), self.doses.get())
        self._insertFunctionStep('createOutputStep')

    #--------------------------- STEPS functions --------------------------------------------
    def addSample(self, sampleName, doseName, simulationsX, y):
        newSample = PKPDSample()
        newSample.sampleName = sampleName
        newSample.variableDictPtr = self.outputExperiment.variables
        newSample.doseDictPtr = self.outputExperiment.doses
        newSample.descriptors = {}
        newSample.doseList = [doseName]
        tsample = np.arange(0.0, np.max(simulationsX), self.sampling.get())
        if type(self.varNameY) != list:
            newSample.addMeasurementPattern([self.varNameY])
            B = InterpolatedUnivariateSpline(simulationsX, y, k=1)
            newSample.addMeasurementColumn("t", tsample)
            newSample.addMeasurementColumn(self.varNameY, B(tsample))
        else:
            for j in range(len(self.varNameY)):
                newSample.addMeasurementPattern([self.varNameY[j]])
            newSample.addMeasurementColumn("t", tsample)
            for j in range(len(self.varNameY)):
                B = InterpolatedUnivariateSpline(simulationsX, y[j], k=1)
                newSample.addMeasurementColumn(self.varNameY[j], B(tsample))
        newSample.descriptors["FromSample"] = self.fromSample
        newSample.descriptors["AUC0t"] = self.AUC0t
        newSample.descriptors["AUMC0t"] = self.AUMC0t
        newSample.descriptors["MRT"] = self.MRT
        newSample.descriptors["Cmax"] = self.Cmax
        newSample.descriptors["Cmin"] = self.Cmin
        newSample.descriptors["Cavg"] = self.Cavg
        newSample.descriptors["Tmax"] = self.Tmax
        newSample.descriptors["Tmin"] = self.Tmin
        self.outputExperiment.samples[sampleName] = newSample

    def NCA(self, t, C):
        AUClist = []
        AUMClist = []
        Cminlist = []
        Cavglist = []
        Cmaxlist = []
        Ctaulist = []
        Tmaxlist = []
        Tminlist = []
        Ttaulist = []
        Ndoses = len(self.drugSource.parsedDoseList)
        for ndose in range(0, max(Ndoses, 1)):
            tperiod0 = self.drugSource.parsedDoseList[ndose].t0
            if ndose + 1 < Ndoses:
                tperiodF = self.drugSource.parsedDoseList[
                    ndose + 1].t0 - self.model.deltaT
            else:
                tperiodF = np.max(t) - 1
            idx0 = find_nearest(t, tperiod0)
            idxF = find_nearest(t, tperiodF)
            if idxF >= len(t) - 1:
                idxF = len(t) - 2

            AUC0t = 0
            AUMC0t = 0
            t0 = t[idx0 + 1]
            for idx in range(idx0, idxF + 1):
                dt = (t[idx + 1] - t[idx])
                if C[idx + 1] >= C[idx]:  # Trapezoidal in the raise
                    AUC0t += 0.5 * dt * (C[idx] + C[idx + 1])
                    AUMC0t += 0.5 * dt * (C[idx] * t[idx] +
                                          C[idx + 1] * t[idx + 1])
                else:  # Log-trapezoidal in the decay
                    decrement = C[idx] / C[idx + 1]
                    K = math.log(decrement)
                    B = K / dt
                    AUC0t += dt * (C[idx] - C[idx + 1]) / K
                    AUMC0t += (C[idx] * (t[idx] - tperiod0) - C[idx + 1] *
                               (t[idx + 1] - tperiod0)) / B - (
                                   C[idx + 1] - C[idx]) / (B * B)

                if idx == idx0:
                    Cmax = C[idx]
                    Tmax = t[idx] - t0
                    Cmin = C[idx]
                    Tmin = t[idx] - t0
                else:
                    if C[idx] < Cmin:
                        Cmin = C[idx]
                        Tmin = t[idx] - t0
                    elif C[idx] > Cmax:
                        Cmax = C[idx]
                        Tmax = t[idx] - t0
                        # if ndose==0:
                        #     Cmin=C[idx]
                        #     Tmin=t[idx]-t0
            AUClist.append(AUC0t)
            AUMClist.append(AUMC0t)
            Cminlist.append(Cmin)
            Cmaxlist.append(Cmax)
            Ctaulist.append(C[idxF])
            Ttaulist.append(t[idxF] - t0)
            Tmaxlist.append(Tmax)
            Tminlist.append(Tmin)
            Cavglist.append(AUC0t / (t[idxF] - t[idx0]))

        print("Fluctuation = Cmax/Cmin")
        print("Accumulation(1) = Cavg(n)/Cavg(1) %")
        print("Accumulation(n) = Cavg(n)/Cavg(n-1) %")
        print("Steady state fraction(n) = Cavg(n)/Cavg(last) %")
        for ndose in range(0, len(AUClist)):
            if Cminlist[ndose] != 0:
                fluctuation = Cmaxlist[ndose] / Cminlist[ndose]
            else:
                fluctuation = np.nan
            if ndose > 0:
                accumn = Cavglist[ndose] / Cavglist[ndose - 1]
            else:
                accumn = 0
            print("Dose #%d t=[%f,%f]: Cavg= %f [%s] Cmin= %f [%s] Tmin= %d [%s] Cmax= %f [%s] Tmax= %d [%s] Ctau= %f [%s] Ttau= %d [%s] Fluct= %f %% Accum(1)= %f %% Accum(n)= %f %% SSFrac(n)= %f %% AUC= %f [%s] AUMC= %f [%s]"%\
                  (ndose+1,t[idx0],t[idxF],Cavglist[ndose], strUnit(self.Cunits.unit), Cminlist[ndose],
                   strUnit(self.Cunits.unit), int(Tminlist[ndose]), strUnit(self.outputExperiment.getTimeUnits().unit),
                   Cmaxlist[ndose], strUnit(self.Cunits.unit),
                   int(Tmaxlist[ndose]), strUnit(self.outputExperiment.getTimeUnits().unit),
                   Ctaulist[ndose], strUnit(self.Cunits.unit), int(Ttaulist[ndose]),
                   strUnit(self.outputExperiment.getTimeUnits().unit),
                   fluctuation*100, Cavglist[ndose]/Cavglist[0]*100, accumn*100, Cavglist[ndose]/Cavglist[-1]*100,
                   AUClist[ndose],strUnit(self.AUCunits),
                   AUMClist[ndose],strUnit(self.AUMCunits)))

        self.AUC0t = float(AUClist[-1])
        self.AUMC0t = float(AUMClist[-1])
        self.MRT = self.AUMC0t / self.AUC0t
        self.Cmin = float(Cminlist[-1])
        self.Cmax = float(Cmaxlist[-1])
        self.Tmin = float(Tminlist[-1])
        self.Tmax = float(Tmaxlist[-1])
        self.Cavg = float(Cavglist[-1])
        self.Ctau = float(Ctaulist[-1])
        self.Ttau = float(Ttaulist[-1])
        self.fluctuation = self.Cmax / self.Cmin if self.Cmin > 0 else np.nan
        self.percentageAccumulation = Cavglist[-1] / Cavglist[0] if Cavglist[
            0] > 0 else np.nan

        print("   AUC0t=%f [%s]" % (self.AUC0t, strUnit(self.AUCunits)))
        print("   AUMC0t=%f [%s]" % (self.AUMC0t, strUnit(self.AUMCunits)))
        print("   MRT=%f [%s]" %
              (self.MRT, strUnit(self.outputExperiment.getTimeUnits().unit)))
        print("   Cmax=%f [%s]" % (self.Cmax, strUnit(self.Cunits.unit)))
        print("   Cmin=%f [%s]" % (self.Cmin, strUnit(self.Cunits.unit)))
        print("   Cavg=%f [%s]" % (self.Cavg, strUnit(self.Cunits.unit)))
        print("   Ctau=%f [%s]" % (self.Ctau, strUnit(self.Cunits.unit)))
        print("   Tmax=%f [%s]" %
              (self.Tmax, strUnit(self.outputExperiment.getTimeUnits().unit)))
        print("   Tmin=%f [%s]" %
              (self.Tmin, strUnit(self.outputExperiment.getTimeUnits().unit)))
        print("   Ttau=%f [%s]" %
              (self.Ttau, strUnit(self.outputExperiment.getTimeUnits().unit)))
        return AUClist, AUMClist, Cminlist, Cavglist, Cmaxlist, Ctaulist, Tmaxlist, Tminlist, Ttaulist

    def runSimulate(self, Nsimulations, confidenceInterval, doses):
        if self.odeSource.get() == self.SRC_ODE:
            self.protODE = self.inputODE.get()
            if hasattr(self.protODE, "outputExperiment"):
                self.experiment = self.readExperiment(
                    self.protODE.outputExperiment.fnPKPD)
            elif hasattr(self.protODE, "outputExperiment1"):
                self.experiment = self.readExperiment(
                    self.protODE.outputExperiment1.fnPKPD)
            else:
                raise Exception(
                    "Cannot find an outputExperiment in the input ODE")
            if self.paramsSource.get() == ProtPKPDODESimulate.PRM_POPULATION:
                self.fitting = self.readFitting(
                    self.inputPopulation.get().fnFitting,
                    cls="PKPDSampleFitBootstrap")
            elif self.paramsSource.get() == ProtPKPDODESimulate.PRM_FITTING:
                self.fitting = self.readFitting(
                    self.inputFitting.get().fnFitting)
            else:
                # User defined or experiment
                if hasattr(self.protODE, "outputFitting"):
                    self.fitting = self.readFitting(
                        self.protODE.outputFitting.fnFitting)
                elif hasattr(self.protODE, "outputFitting1"):
                    self.fitting = self.readFitting(
                        self.protODE.outputFitting1.fnFitting)
            self.varNameX = self.fitting.predictor.varName
            if type(self.fitting.predicted) != list:
                self.varNameY = self.fitting.predicted.varName
            else:
                self.varNameY = [var.varName for var in self.fitting.predicted]
            tunits = self.experiment.getTimeUnits().unit
        else:
            self.varNameX = 't'
            self.varNameY = 'C'
            self.fitting = None
            self.experiment = None
            self.protODE = None
            tunits = unitFromString(self.timeUnits.get())

        # Create drug source
        self.clearGroupParameters()
        self.createDrugSource()

        # Create output object
        self.outputExperiment = PKPDExperiment()
        self.outputExperiment.general["title"] = "Simulated ODE response"
        self.outputExperiment.general["comment"] = "Simulated ODE response"

        # Create the predictor variable
        tvar = PKPDVariable()
        tvar.varName = "t"
        tvar.varType = PKPDVariable.TYPE_NUMERIC
        tvar.role = PKPDVariable.ROLE_TIME
        tvar.units = createUnit(tunits)
        self.outputExperiment.variables[self.varNameX] = tvar

        # Vias
        if self.odeSource.get() == self.SRC_ODE:
            self.outputExperiment.vias = self.experiment.vias
        else:
            # "[ViaName]; [ViaType]; [tlag]; [bioavailability]"
            viaPrmList = [
                token for token in self.viaPrm.get().strip().split(',')
            ]
            if self.viaType.get() == self.VIATYPE_IV:
                tokens = [
                    "Intravenous", "iv", "tlag=0 min", "bioavailability=1"
                ]
            elif self.viaType.get() == self.VIATYPE_EV0:
                tokens = ["Oral", "ev0"] + [
                    "tlag=" + viaPrmList[-2].strip() + " " +
                    self.timeUnits.get()
                ] + ["bioavailability=" + viaPrmList[-1].strip()]
            elif self.viaType.get() == self.VIATYPE_EV1:
                tokens = ["Oral", "ev1"] + [
                    "tlag=" + viaPrmList[-2].strip() + " " +
                    self.timeUnits.get()
                ] + ["bioavailability=" + viaPrmList[-1].strip()]

            vianame = tokens[0]
            self.outputExperiment.vias[vianame] = PKPDVia(
                ptrExperiment=self.outputExperiment)
            self.outputExperiment.vias[vianame].parseTokens(tokens)

        # Read the doses
        dunits = PKPDUnit.UNIT_NONE
        for line in self.doses.get().replace('\n', ';;').split(';;'):
            tokens = line.split(';')
            if len(tokens) < 5:
                print("Skipping dose: ", line)
                continue
            dosename = tokens[0].strip()
            self.outputExperiment.doses[dosename] = PKPDDose()
            self.outputExperiment.doses[dosename].parseTokens(
                tokens, self.outputExperiment.vias)
            dunits = self.outputExperiment.doses[dosename].getDoseUnits()

        # Create predicted variables
        if self.odeSource.get() == self.SRC_ODE:
            if type(self.fitting.predicted) != list:
                self.outputExperiment.variables[
                    self.varNameY] = self.experiment.variables[self.varNameY]
            else:
                for varName in self.varNameY:
                    self.outputExperiment.variables[
                        varName] = self.experiment.variables[varName]
        else:
            Cvar = PKPDVariable()
            Cvar.varName = "C"
            Cvar.varType = PKPDVariable.TYPE_NUMERIC
            Cvar.role = PKPDVariable.ROLE_MEASUREMENT
            Cvar.units = createUnit(
                divideUnits(dunits.unit,
                            unitFromString(self.volumeUnits.get())))
            self.outputExperiment.variables[self.varNameY] = Cvar

        # Setup model
        if self.odeSource.get() == self.SRC_ODE:
            self.model = self.protODE.createModel()
            if hasattr(self.protODE, "deltaT"):
                self.model.deltaT = self.protODE.deltaT.get()
        else:
            if self.pkType.get() == self.PKTYPE_COMP1:
                self.model = PK_Monocompartment()
            elif self.pkType.get() == self.PKTYPE_COMP2:
                self.model = PK_Twocompartment()
            elif self.pkType.get() == self.PKTYPE_COMP2CLCLINT:
                self.model = PK_TwocompartmentsClintCl()

        self.model.setExperiment(self.outputExperiment)
        self.model.setXVar(self.varNameX)
        self.model.setYVar(self.varNameY)
        Nsamples = int(
            math.ceil((self.tF.get() - self.t0.get()) / self.model.deltaT)) + 1
        self.model.x = [
            self.t0.get() + i * self.model.deltaT for i in range(0, Nsamples)
        ]
        self.modelList.append(self.model)

        auxSample = PKPDSample()
        auxSample.descriptors = {}
        auxSample.doseDictPtr = self.outputExperiment.doses
        auxSample.variableDictPtr = self.outputExperiment.variables
        auxSample.doseList = self.outputExperiment.doses.keys()
        auxSample.interpretDose()
        self.drugSource.setDoses(auxSample.parsedDoseList,
                                 self.t0.get() - 10,
                                 self.tF.get() + 10)
        self.model.drugSource = self.drugSource

        # Check units
        # Dunits = self.outputExperiment.doses[dosename].dunits
        # Cunits = self.experiment.variables[self.varNameY].units

        # Process user parameters
        if self.odeSource.get() == self.SRC_ODE:
            if self.paramsSource == ProtPKPDODESimulate.PRM_POPULATION:
                Nsimulations = self.Nsimulations.get()
            elif self.paramsSource == ProtPKPDODESimulate.PRM_USER_DEFINED:
                lines = self.prmUser.get().strip().replace('\n',
                                                           ';;').split(';;')
                Nsimulations = len(lines)
                prmUser = []
                for line in lines:
                    tokens = line.strip().split(',')
                    prmUser.append([float(token) for token in tokens])
            elif self.paramsSource == ProtPKPDODESimulate.PRM_FITTING:
                Nsimulations = len(self.fitting.sampleFits)
            else:
                self.inputExperiment = self.readExperiment(
                    self.inputExperiment.get().fnPKPD)
                Nsimulations = len(self.inputExperiment.samples)
                inputSampleNames = self.inputExperiment.samples.keys()
        else:
            lines = self.prmUser.get().strip().replace('\n', ';;').split(';;')
            Nsimulations = len(lines)
            prmUser = []
            for line in lines:
                tokens = line.strip().split(',')
                prmUser.append([float(token) for token in tokens])

        # Simulate the different responses
        simulationsX = self.model.x
        simulationsY = np.zeros(
            (Nsimulations, len(simulationsX), self.getResponseDimension()))
        AUCarray = np.zeros(Nsimulations)
        AUMCarray = np.zeros(Nsimulations)
        MRTarray = np.zeros(Nsimulations)
        CminArray = np.zeros(Nsimulations)
        CmaxArray = np.zeros(Nsimulations)
        CavgArray = np.zeros(Nsimulations)
        CtauArray = np.zeros(Nsimulations)
        TminArray = np.zeros(Nsimulations)
        TmaxArray = np.zeros(Nsimulations)
        TtauArray = np.zeros(Nsimulations)
        fluctuationArray = np.zeros(Nsimulations)
        percentageAccumulationArray = np.zeros(Nsimulations)

        wb = openpyxl.Workbook()
        wb.active.title = "Simulations"
        for i in range(0, Nsimulations):
            self.setTimeRange(None)

            if self.odeSource.get() == self.SRC_ODE:
                if self.paramsSource == ProtPKPDODESimulate.PRM_POPULATION:
                    # Take parameters randomly from the population
                    nfit = int(random.uniform(0, len(self.fitting.sampleFits)))
                    sampleFit = self.fitting.sampleFits[nfit]
                    nprm = int(random.uniform(0,
                                              sampleFit.parameters.shape[0]))
                    parameters = sampleFit.parameters[nprm, :]
                    self.fromSample = "Population %d" % nprm
                elif self.paramsSource == ProtPKPDODESimulate.PRM_USER_DEFINED:
                    parameters = np.asarray(prmUser[i], np.double)
                    self.fromSample = "User defined"
                elif self.paramsSource == ProtPKPDODESimulate.PRM_FITTING:
                    parameters = self.fitting.sampleFits[i].parameters
                    self.fromSample = self.fitting.sampleFits[i].sampleName
                else:
                    parameters = []
                    self.fromSample = inputSampleNames[i]
                    sample = self.inputExperiment.samples[self.fromSample]
                    for prmName in self.fitting.modelParameters:
                        parameters.append(
                            float(sample.getDescriptorValue(prmName)))
            else:
                parameters = np.asarray(viaPrmList[:-2] + prmUser[i],
                                        np.double)
                self.fromSample = "User defined"

            print("From sample name: %s" % self.fromSample)
            print("Simulated sample %d: %s" % (i, str(parameters)))

            # Prepare source and this object
            self.drugSource.setDoses(auxSample.parsedDoseList, self.model.t0,
                                     self.model.tF)
            if self.protODE is not None:
                self.protODE.configureSource(self.drugSource)
            self.model.drugSource = self.drugSource
            parameterNames = self.getParameterNames(
            )  # Necessary to count the number of source and PK parameters

            # Prepare the model
            self.setParameters(parameters)
            y = self.forwardModel(parameters,
                                  [simulationsX] * self.getResponseDimension())

            # Create AUC, AUMC, MRT variables and units
            if i == 0:
                if type(self.varNameY) != list:
                    self.Cunits = self.outputExperiment.variables[
                        self.varNameY].units
                else:
                    self.Cunits = self.outputExperiment.variables[
                        self.varNameY[0]].units
                self.AUCunits = multiplyUnits(tvar.units.unit,
                                              self.Cunits.unit)
                self.AUMCunits = multiplyUnits(tvar.units.unit, self.AUCunits)

                if self.addStats or self.addIndividuals:
                    fromvar = PKPDVariable()
                    fromvar.varName = "FromSample"
                    fromvar.varType = PKPDVariable.TYPE_TEXT
                    fromvar.role = PKPDVariable.ROLE_LABEL
                    fromvar.units = createUnit("none")

                    AUCvar = PKPDVariable()
                    AUCvar.varName = "AUC0t"
                    AUCvar.varType = PKPDVariable.TYPE_NUMERIC
                    AUCvar.role = PKPDVariable.ROLE_LABEL
                    AUCvar.units = createUnit(strUnit(self.AUCunits))

                    AUMCvar = PKPDVariable()
                    AUMCvar.varName = "AUMC0t"
                    AUMCvar.varType = PKPDVariable.TYPE_NUMERIC
                    AUMCvar.role = PKPDVariable.ROLE_LABEL
                    AUMCvar.units = createUnit(strUnit(self.AUMCunits))

                    MRTvar = PKPDVariable()
                    MRTvar.varName = "MRT"
                    MRTvar.varType = PKPDVariable.TYPE_NUMERIC
                    MRTvar.role = PKPDVariable.ROLE_LABEL
                    MRTvar.units = createUnit(
                        self.outputExperiment.getTimeUnits().unit)

                    Cmaxvar = PKPDVariable()
                    Cmaxvar.varName = "Cmax"
                    Cmaxvar.varType = PKPDVariable.TYPE_NUMERIC
                    Cmaxvar.role = PKPDVariable.ROLE_LABEL
                    Cmaxvar.units = createUnit(strUnit(self.Cunits.unit))

                    Tmaxvar = PKPDVariable()
                    Tmaxvar.varName = "Tmax"
                    Tmaxvar.varType = PKPDVariable.TYPE_NUMERIC
                    Tmaxvar.role = PKPDVariable.ROLE_LABEL
                    Tmaxvar.units = createUnit(
                        self.outputExperiment.getTimeUnits().unit)

                    Cminvar = PKPDVariable()
                    Cminvar.varName = "Cmin"
                    Cminvar.varType = PKPDVariable.TYPE_NUMERIC
                    Cminvar.role = PKPDVariable.ROLE_LABEL
                    Cminvar.units = createUnit(strUnit(self.Cunits.unit))

                    Tminvar = PKPDVariable()
                    Tminvar.varName = "Tmin"
                    Tminvar.varType = PKPDVariable.TYPE_NUMERIC
                    Tminvar.role = PKPDVariable.ROLE_LABEL
                    Tminvar.units = createUnit(
                        self.outputExperiment.getTimeUnits().unit)

                    Cavgvar = PKPDVariable()
                    Cavgvar.varName = "Cavg"
                    Cavgvar.varType = PKPDVariable.TYPE_NUMERIC
                    Cavgvar.role = PKPDVariable.ROLE_LABEL
                    Cavgvar.units = createUnit(strUnit(self.Cunits.unit))

                    self.outputExperiment.variables["FromSample"] = fromvar
                    self.outputExperiment.variables["AUC0t"] = AUCvar
                    self.outputExperiment.variables["AUMC0t"] = AUMCvar
                    self.outputExperiment.variables["MRT"] = MRTvar
                    self.outputExperiment.variables["Cmax"] = Cmaxvar
                    self.outputExperiment.variables["Tmax"] = Tmaxvar
                    self.outputExperiment.variables["Cmin"] = Cminvar
                    self.outputExperiment.variables["Tmin"] = Tminvar
                    self.outputExperiment.variables["Cavg"] = Cavgvar

                excelWriteRow([
                    "simulationName", "fromSample", "doseNumber",
                    "AUC [%s]" % strUnit(self.AUCunits),
                    "AUMC [%s]" % strUnit(self.AUMCunits),
                    "Cmin [%s]" % strUnit(self.Cunits.unit),
                    "Cavg [%s]" % strUnit(self.Cunits.unit),
                    "Cmax [%s]" % strUnit(self.Cunits.unit),
                    "Ctau [%s]" % strUnit(self.Cunits.unit),
                    "Tmin [%s]" %
                    strUnit(self.outputExperiment.getTimeUnits().unit),
                    "Tmax [%s]" %
                    strUnit(self.outputExperiment.getTimeUnits().unit),
                    "Ttau [%s]" %
                    strUnit(self.outputExperiment.getTimeUnits().unit)
                ],
                              wb,
                              1,
                              bold=True)
                wbRow = 2

            # Evaluate AUC, AUMC and MRT in the last full period
            AUClist, AUMClist, Cminlist, Cavglist, Cmaxlist, Ctaulist, Tmaxlist, Tminlist, Ttaulist = self.NCA(
                self.model.x, y[0])
            for doseNo in range(0, len(AUClist)):
                excelWriteRow([
                    "Simulation_%d" % i, self.fromSample, doseNo,
                    AUClist[doseNo], AUMClist[doseNo], Cminlist[doseNo],
                    Cavglist[doseNo], Cmaxlist[doseNo], Ctaulist[doseNo],
                    Tminlist[doseNo], Tmaxlist[doseNo], Ttaulist[doseNo]
                ], wb, wbRow)
                wbRow += 1

            # Keep results
            for j in range(self.getResponseDimension()):
                simulationsY[i, :, j] = y[j]
            AUCarray[i] = self.AUC0t
            AUMCarray[i] = self.AUMC0t
            MRTarray[i] = self.MRT
            CminArray[i] = self.Cmin
            CmaxArray[i] = self.Cmax
            CavgArray[i] = self.Cavg
            CtauArray[i] = self.Ctau
            TminArray[i] = self.Tmin
            TmaxArray[i] = self.Tmax
            TtauArray[i] = self.Ttau
            fluctuationArray[i] = self.fluctuation
            percentageAccumulationArray[i] = self.percentageAccumulation
            if self.addIndividuals or self.paramsSource != ProtPKPDODESimulate.PRM_POPULATION:
                self.addSample("Simulation_%d" % i, dosename, simulationsX,
                               y[0])

        # Report NCA statistics
        fhSummary = open(self._getPath("summary.txt"), "w")
        alpha_2 = (100 - self.confidenceLevel.get()) / 2
        limits = np.percentile(AUCarray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "AUC %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (self.confidenceLevel.get(), limits[0], limits[1],
             strUnit(self.AUCunits), np.mean(AUCarray)))
        limits = np.percentile(AUMCarray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "AUMC %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (self.confidenceLevel.get(), limits[0], limits[1],
             strUnit(self.AUMCunits), np.mean(AUMCarray)))
        limits = np.percentile(MRTarray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "MRT %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (self.confidenceLevel.get(), limits[0], limits[1],
             strUnit(self.outputExperiment.getTimeUnits().unit),
             np.mean(MRTarray)))
        limits = np.percentile(CminArray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "Cmin %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (self.confidenceLevel.get(), limits[0], limits[1],
             strUnit(self.Cunits.unit), np.mean(CminArray)))
        limits = np.percentile(CmaxArray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "Cmax %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (self.confidenceLevel.get(), limits[0], limits[1],
             strUnit(self.Cunits.unit), np.mean(CmaxArray)))
        limits = np.percentile(CavgArray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "Cavg %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (self.confidenceLevel.get(), limits[0], limits[1],
             strUnit(self.Cunits.unit), np.mean(CavgArray)))
        limits = np.percentile(CtauArray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "Ctau %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (self.confidenceLevel.get(), limits[0], limits[1],
             strUnit(self.Cunits.unit), np.mean(CtauArray)))
        limits = np.percentile(TminArray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "Tmin %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (self.confidenceLevel.get(), limits[0], limits[1],
             strUnit(self.outputExperiment.getTimeUnits().unit),
             np.mean(TminArray)))
        limits = np.percentile(TmaxArray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "Tmax %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (self.confidenceLevel.get(), limits[0], limits[1],
             strUnit(self.outputExperiment.getTimeUnits().unit),
             np.mean(TmaxArray)))
        limits = np.percentile(TtauArray, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary, "Ttau %f%% confidence interval=[%f,%f] [%s] mean=%f" %
            (self.confidenceLevel.get(), limits[0], limits[1],
             strUnit(self.outputExperiment.getTimeUnits().unit),
             np.mean(TtauArray)))
        aux = fluctuationArray[~np.isnan(fluctuationArray)]
        if len(aux) > 0:
            limits = np.percentile(aux, [alpha_2, 100 - alpha_2])
            self.doublePrint(
                fhSummary,
                "Fluctuation %f%% confidence interval=[%f,%f] [%%] mean=%f" %
                (self.confidenceLevel.get(), limits[0] * 100, limits[1] * 100,
                 np.mean(aux) * 100))
        aux = percentageAccumulationArray[~np.isnan(percentageAccumulationArray
                                                    )]
        limits = np.percentile(aux, [alpha_2, 100 - alpha_2])
        self.doublePrint(
            fhSummary,
            "Accum(1) %f%% confidence interval=[%f,%f] [%%] mean=%f" %
            (self.confidenceLevel.get(), limits[0] * 100, limits[1] * 100,
             np.mean(aux) * 100))
        fhSummary.close()

        # Calculate statistics
        if self.addStats and self.odeSource == 0 and self.paramsSource == 0:
            if self.paramsSource != ProtPKPDODESimulate.PRM_USER_DEFINED:
                limits = np.percentile(simulationsY, [alpha_2, 100 - alpha_2],
                                       axis=0)

                print("Lower limit NCA")
                self.NCA(simulationsX, limits[0])
                self.fromSample = "LowerLimit"
                self.addSample("LowerLimit", dosename, simulationsX, limits[0])

            print("Mean profile NCA")
            if self.getResponseDimension() == 1:
                mu = np.mean(simulationsY, axis=0)
                self.NCA(simulationsX, mu)
            else:
                mu = []
                for j in range(self.getResponseDimension()):
                    mu.append(np.mean(simulationsY[:, :, j], axis=0))
                self.NCA(simulationsX, mu[0])
            self.fromSample = "Mean"
            self.addSample("Mean", dosename, simulationsX, mu)

            if self.paramsSource != ProtPKPDODESimulate.PRM_USER_DEFINED:
                print("Upper limit NCA")
                self.NCA(simulationsX, limits[1])
                self.fromSample = "UpperLimit"
                self.addSample("UpperLimit", dosename, simulationsX, limits[1])

        self.outputExperiment.write(self._getPath("experiment.pkpd"))
        wb.save(self._getPath("nca.xlsx"))

    def createOutputStep(self):
        self._defineOutputs(outputExperiment=self.outputExperiment)
        if self.odeSource.get() == self.SRC_ODE:
            self._defineSourceRelation(self.inputODE.get(),
                                       self.outputExperiment)
            if self.paramsSource == ProtPKPDODESimulate.PRM_POPULATION:
                self._defineSourceRelation(self.inputPopulation.get(),
                                           self.outputExperiment)
            elif self.paramsSource == ProtPKPDODESimulate.PRM_POPULATION:
                self._defineSourceRelation(self.inputFitting.get(),
                                           self.outputExperiment)

    #--------------------------- INFO functions --------------------------------------------
    def _summary(self):
        msg = []
        msg.append("Dose: %s" % self.doses.get())
        if self.odeSource == ProtPKPDODESimulate.SRC_ODE and self.paramsSource == ProtPKPDODESimulate.PRM_POPULATION:
            msg.append("Number of simulations: %d" % self.Nsimulations.get())
        elif self.odeSource == ProtPKPDODESimulate.SRC_LIST or self.paramsSource == ProtPKPDODESimulate.PRM_USER_DEFINED:
            msg.append("Parameters:\n" + self.prmUser.get())
        else:
            msg.append("Parameters from previous fitting")
        msg.append(" ")
        self.addFileContentToMessage(msg, self._getPath("summary.txt"))
        return msg

    def _validate(self):
        msg = []
        if self.odeSource == self.SRC_ODE and \
            self.paramsSource == ProtPKPDODESimulate.PRM_POPULATION and \
            not self.inputPopulation.get().fnFitting.get().endswith("bootstrapPopulation.pkpd"):
            msg.append("Population must be a bootstrap sample")
        return msg