# **************************************************************************
# *
# * Authors: Carlos Oscar Sorzano (info@kinestat.com)
# *
# * Kinestat Pharma
# *
# * This program is free software; you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation; either version 2 of the License, or
# * (at your option) any later version.
# *
# * This program is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program; if not, write to the Free Software
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# * 02111-1307 USA
# *
# * All comments concerning this program package may be sent to the
# * e-mail address 'info@kinestat.com'
# *
# **************************************************************************
import pyworkflow.protocol.params as params
from pyworkflow.protocol.constants import LEVEL_ADVANCED
from pkpd.models.dissolution_models import *
from pkpd.protocols.protocol_pkpd import ProtPKPD
from pkpd.objects import PKPDExperiment, PKPDSample, PKPDVariable, PKPDFitting, PKPDSampleFit
from pkpd.pkpd_units import createUnit, strUnit
# tested in test_workflow_dissolution.py
# Tested in test_workflow_ivivc.py
# Tested in test_workflow_ivivc2.py
[docs]class ProtPKPDDissolutionSimulate(ProtPKPD):
""" Simulate a dissolution profile\n
Protocol created by http://www.kinestatpharma.com\n"""
_label = 'simulate dissolution'
# --------------------------- DEFINE param functions --------------------------------------------
def _defineParams(self, form):
form.addSection('Input')
form.addParam('allowTlag', params.BooleanParam,
label="Allow lag", default=False,
help='Allow lag time before starting dissolution (t-tlag)')
form.addParam('modelType', params.EnumParam, choices=["Zero order", "First order", "Fractional", "Weibull",
"Double Weibull", "Triple Weibull", "Higuchi",
"Korsmeyer-Peppas", "Hixson-Crowell", "Hopfenberg",
"Hill",
"Makoid-Banakar",
"Splines2", "Splines3", "Splines4", "Spline5", "Splines6",
"Splines7", "Splines8", "Splines9", "Splines10"],
label="Dissolution model", default=3,
help='Zero order: Y=K*(t-[tlag])\n' \
'First order: Y=Ymax*(1-exp(-beta*(t-[tlag])))\n' \
'Fractional order: Y=Ymax-pow(Ymax^alpha-alpha*beta*t,1/alpha))\n' \
'Weibull: Y=Ymax*(1-exp(-lambda*t^b))\n' \
'Double Weibull: Y=Ymax*(F1*(1-exp(-lambda1*t^b1))+(1-F1)*(1-exp(-lambda2*(t-tlag2)^b2)))\n' \
'Triple Weibull: Y=Ymax*(F1*(1-exp(-lambda1*t^b1))+F2*(1-exp(-lambda2*(t-tlag2)^b2))+(1-F1-F2)*(1-exp(-lambda3*(t-tlag3)^b3)))\n' \
'Higuchi: Y=Ymax*t^0.5\n' \
'Korsmeyer-Peppas: Y=Ymax*t^m\n' \
'Hixson-Crowell: Y=Ymax*(1-(1-K*t)^3)\n'
'Hopfenberg: Y=Ymax*(1-(1-K*t)^m)\n'
'Hill: Y = Ymax*t^d/(g^d+t^d)\n'
'Makoid-Banakar: Ymax*(t/tmax)^b*exp(b*(1-t/tmax))\n'
'SplinesN: Y= Ymax*Bspline(t;N,tmax)\n')
form.addParam('parameters', params.StringParam, label="Parameters", default="",
help='Parameter values for the simulation.\nExample: 2;5 is 2 for the first parameter, 5 for the second parameter\n'
'Zero order: [tlag];K\n'
'First order: [tlag];Ymax;beta\n'
'Fractional order: [tlag]; Ymax;beta;alpha\n'
'Weibull: [tlag]; Ymax;lambda;b\n'
'Double Weibull: [tlag]; Ymax; lambda1; b1; F1; tlag2; lambda2; b2\n'
'Triple Weibull: [tlag]; Ymax; lambda1; b1; F1; tlag2; lambda2; b2; F2; tlag3; lambda3; b3\n'
'Higuchi: [tlag]; Ymax\n'
'Korsmeyer-Peppas: [tlag]; Ymax; m\n'
'Hixson-Crowell: [tlag]; Ymax; K\n'
'Hopfenberg: [tlag]; Ymax; K; m\n'
'Hill: [tlag]; Ymax; g; d\n'
'Makoid-Banakar: [tlag]; Ymax; Tmax; b\n'
'SplinesN: [tlag]; Ymax; tmax; c1; c2; ...; cN\n')
form.addParam('timeUnits', params.EnumParam, choices=["min", "h"],
label="Time units", default=0)
form.addParam('resampleT', params.FloatParam, label='Simulation model time step=', default=1)
form.addParam('resampleT0', params.FloatParam, label='Initial time for simulation', default=0)
form.addParam('resampleTF', params.FloatParam, label='Final time for simulation', default=100)
form.addParam('AUnits', params.StringParam, label="Dissolution units", default="%",
help="%, mg/dL, ...")
form.addParam('noiseType', params.EnumParam, label="Type of noise to add",
choices=["None", "Additive", "Multiplicative"],
default=0, expertLevel=LEVEL_ADVANCED,
help='Additive: noise is normally distributed (mean=0 and standard deviation=sigma)\n' \
'Multiplicative: noise is normally distributed (mean=0 and standard deviation=sigma*X)\n')
form.addParam('noiseSigma', params.FloatParam, label="Noise sigma",
default=0.0, expertLevel=LEVEL_ADVANCED, condition="noiseType>0",
help='See help of Type of noise to add\n')
# --------------------------- STEPS functions --------------------------------------------
def _insertAllSteps(self):
self._insertFunctionStep('runSimulate')
self._insertFunctionStep('createOutputStep')
[docs] def addNoise(self, y):
if self.noiseType.get() == 0:
return y
elif self.noiseType.get() == 1:
return y + np.random.normal(0.0, self.noiseSigma.get(), y.shape)
elif self.noiseType.get() == 2:
return y * (1 + np.random.normal(0.0, self.noiseSigma.get(), y.shape))
[docs] def runSimulate(self):
tvar = PKPDVariable()
tvar.varName = "t"
tvar.varType = PKPDVariable.TYPE_NUMERIC
tvar.role = PKPDVariable.ROLE_TIME
if self.timeUnits.get() == 0:
tvar.units = createUnit("min")
elif self.timeUnits.get() == 1:
tvar.units = createUnit("h")
Avar = PKPDVariable()
Avar.varName = "A"
Avar.varType = PKPDVariable.TYPE_NUMERIC
Avar.role = PKPDVariable.ROLE_MEASUREMENT
if self.AUnits.get() != "%":
Avar.units = createUnit(self.AUnits.get())
else:
Avar.units = createUnit("none")
self.experimentSimulated = PKPDExperiment()
self.experimentSimulated.variables["t"] = tvar
self.experimentSimulated.variables["A"] = Avar
self.experimentSimulated.general["title"] = "Simulated dissolution profile"
self.experimentSimulated.general["comment"] = ""
if self.modelType.get() == 0:
self.model = Dissolution0()
elif self.modelType.get() == 1:
self.model = Dissolution1()
elif self.modelType.get() == 2:
self.model = DissolutionAlpha()
elif self.modelType.get() == 3:
self.model = DissolutionWeibull()
elif self.modelType.get() == 4:
self.model = DissolutionDoubleWeibull()
elif self.modelType.get() == 5:
self.model = DissolutionTripleWeibull()
elif self.modelType.get() == 6:
self.model = DissolutionHiguchi()
elif self.modelType.get() == 7:
self.model = DissolutionKorsmeyer()
elif self.modelType.get() == 8:
self.model = DissolutionHixson()
elif self.modelType.get() == 9:
self.model = DissolutionHopfenberg()
elif self.modelType.get() == 10:
self.model = DissolutionHill()
elif self.modelType.get() == 11:
self.model = DissolutionMakoidBanakar()
elif self.modelType.get() == 12:
self.model = DissolutionSplines2()
elif self.modelType.get() == 13:
self.model = DissolutionSplines3()
elif self.modelType.get() == 14:
self.model = DissolutionSplines4()
elif self.modelType.get() == 15:
self.model = DissolutionSplines5()
elif self.modelType.get() == 16:
self.model = DissolutionSplines6()
elif self.modelType.get() == 17:
self.model = DissolutionSplines7()
elif self.modelType.get() == 18:
self.model = DissolutionSplines8()
elif self.modelType.get() == 19:
self.model = DissolutionSplines9()
elif self.modelType.get() == 20:
self.model = DissolutionSplines10()
self.model.allowTlag = self.allowTlag.get()
self.model.parameters = [float(x) for x in self.parameters.get().split(';')]
newSample = PKPDSample()
newSample.sampleName = "simulatedProfile"
newSample.variableDictPtr = self.experimentSimulated.variables
newSample.descriptors = {}
newSample.addMeasurementPattern(["A"])
t0 = self.resampleT0.get()
tF = self.resampleTF.get()
deltaT = self.resampleT.get()
t = np.arange(t0, tF + deltaT, deltaT)
self.model.setExperiment(self.experimentSimulated)
self.model.setXVar("t")
self.model.setYVar("A")
self.model.calculateParameterUnits(newSample)
y = self.model.forwardModel(self.model.parameters, t)
newSample.addMeasurementColumn("t", t)
newSample.addMeasurementColumn("A", y[0])
self.experimentSimulated.samples[newSample.sampleName] = newSample
fnExperiment = self._getPath("experimentSimulated.pkpd")
self.experimentSimulated.write(fnExperiment)
self.fittingSimulated = PKPDFitting()
self.fittingSimulated.fnExperiment.set(fnExperiment)
self.fittingSimulated.predictor=self.experimentSimulated.variables["t"]
self.fittingSimulated.predicted=self.experimentSimulated.variables["A"]
self.fittingSimulated.modelDescription=self.model.getDescription()
self.fittingSimulated.modelParameters = self.model.getParameterNames()
self.fittingSimulated.modelParameterUnits = self.model.parameterUnits
sampleFit = PKPDSampleFit()
sampleFit.sampleName = newSample.sampleName
sampleFit.x = [t]
sampleFit.y = y
sampleFit.yp = y
sampleFit.yl = y
sampleFit.yu = y
sampleFit.parameters = self.model.parameters
sampleFit.modelEquation = self.model.getEquation()
sampleFit.R2 = -1
sampleFit.R2adj = -1
sampleFit.AIC = -1
sampleFit.AICc = -1
sampleFit.BIC = -1
sampleFit.significance = ["NA" for prm in sampleFit.parameters]
sampleFit.lowerBound = ["NA" for prm in sampleFit.parameters]
sampleFit.upperBound = ["NA" for prm in sampleFit.parameters]
self.fittingSimulated.sampleFits.append(sampleFit)
fnFitting = self._getPath("fittingSimulated.pkpd")
self.fittingSimulated.write(fnFitting)
[docs] def createOutputStep(self):
self._defineOutputs(outputExperiment=self.experimentSimulated)
self._defineOutputs(outputFitting=self.fittingSimulated)